Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : /*
3 : * Test cases for the drm_plane_helper functions
4 : *
5 : * Copyright (c) 2022 MaĆra Canal <mairacanal@riseup.net>
6 : */
7 :
8 : #include <kunit/test.h>
9 :
10 : #include <drm/drm_atomic_helper.h>
11 : #include <drm/drm_framebuffer.h>
12 : #include <drm/drm_modes.h>
13 : #include <drm/drm_rect.h>
14 :
15 : static const struct drm_crtc_state crtc_state = {
16 : .crtc = ZERO_SIZE_PTR,
17 : .enable = true,
18 : .active = true,
19 : .mode = {
20 : DRM_MODE("1024x768", 0, 65000, 1024, 1048,
21 : 1184, 1344, 0, 768, 771, 777, 806, 0,
22 : DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
23 : },
24 : };
25 :
26 : struct drm_check_plane_state_test {
27 : const char *name;
28 : const char *msg;
29 : struct {
30 : unsigned int x;
31 : unsigned int y;
32 : unsigned int w;
33 : unsigned int h;
34 : } src, src_expected;
35 : struct {
36 : int x;
37 : int y;
38 : unsigned int w;
39 : unsigned int h;
40 : } crtc, crtc_expected;
41 : unsigned int rotation;
42 : int min_scale;
43 : int max_scale;
44 : bool can_position;
45 : };
46 :
47 0 : static int drm_plane_helper_init(struct kunit *test)
48 : {
49 0 : const struct drm_check_plane_state_test *params = test->param_value;
50 : struct drm_plane *plane;
51 : struct drm_framebuffer *fb;
52 : struct drm_plane_state *mock;
53 :
54 0 : plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
55 0 : KUNIT_ASSERT_NOT_NULL(test, plane);
56 :
57 0 : fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
58 0 : KUNIT_ASSERT_NOT_NULL(test, fb);
59 0 : fb->width = 2048;
60 0 : fb->height = 2048;
61 :
62 0 : mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
63 0 : KUNIT_ASSERT_NOT_NULL(test, mock);
64 0 : mock->plane = plane;
65 0 : mock->crtc = ZERO_SIZE_PTR;
66 0 : mock->fb = fb;
67 0 : mock->rotation = params->rotation;
68 0 : mock->src_x = params->src.x;
69 0 : mock->src_y = params->src.y;
70 0 : mock->src_w = params->src.w;
71 0 : mock->src_h = params->src.h;
72 0 : mock->crtc_x = params->crtc.x;
73 0 : mock->crtc_y = params->crtc.y;
74 0 : mock->crtc_w = params->crtc.w;
75 0 : mock->crtc_h = params->crtc.h;
76 :
77 0 : test->priv = mock;
78 :
79 0 : return 0;
80 : }
81 :
82 0 : static void check_src_eq(struct kunit *test, struct drm_plane_state *plane_state,
83 : unsigned int src_x, unsigned int src_y,
84 : unsigned int src_w, unsigned int src_h)
85 : {
86 0 : struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
87 :
88 0 : KUNIT_ASSERT_GE_MSG(test, plane_state->src.x1, 0,
89 : "src x coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT,
90 : plane_state->src.x1, DRM_RECT_FP_ARG(&plane_state->src));
91 :
92 0 : KUNIT_ASSERT_GE_MSG(test, plane_state->src.y1, 0,
93 : "src y coordinate %x should never be below 0, src: " DRM_RECT_FP_FMT,
94 : plane_state->src.y1, DRM_RECT_FP_ARG(&plane_state->src));
95 :
96 0 : KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->src, &expected),
97 : "dst: " DRM_RECT_FP_FMT ", expected: " DRM_RECT_FP_FMT,
98 : DRM_RECT_FP_ARG(&plane_state->src), DRM_RECT_FP_ARG(&expected));
99 0 : }
100 :
101 0 : static void check_crtc_eq(struct kunit *test, struct drm_plane_state *plane_state,
102 : int crtc_x, int crtc_y,
103 : unsigned int crtc_w, unsigned int crtc_h)
104 : {
105 0 : struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, crtc_h);
106 :
107 0 : KUNIT_EXPECT_TRUE_MSG(test, drm_rect_equals(&plane_state->dst, &expected),
108 : "dst: " DRM_RECT_FMT ", expected: " DRM_RECT_FMT,
109 : DRM_RECT_ARG(&plane_state->dst), DRM_RECT_ARG(&expected));
110 0 : }
111 :
112 0 : static void drm_test_check_plane_state(struct kunit *test)
113 : {
114 0 : const struct drm_check_plane_state_test *params = test->param_value;
115 0 : struct drm_plane_state *plane_state = test->priv;
116 :
117 0 : KUNIT_ASSERT_EQ_MSG(test,
118 : drm_atomic_helper_check_plane_state(plane_state, &crtc_state,
119 : params->min_scale,
120 : params->max_scale,
121 : params->can_position, false),
122 : 0, params->msg);
123 0 : KUNIT_EXPECT_TRUE(test, plane_state->visible);
124 0 : check_src_eq(test, plane_state, params->src_expected.x, params->src_expected.y,
125 : params->src_expected.w, params->src_expected.h);
126 0 : check_crtc_eq(test, plane_state, params->crtc_expected.x, params->crtc_expected.y,
127 : params->crtc_expected.w, params->crtc_expected.h);
128 0 : }
129 :
130 0 : static void drm_check_plane_state_desc(const struct drm_check_plane_state_test *t,
131 : char *desc)
132 : {
133 0 : sprintf(desc, "%s", t->name);
134 0 : }
135 :
136 : static const struct drm_check_plane_state_test drm_check_plane_state_tests[] = {
137 : {
138 : .name = "clipping_simple",
139 : .msg = "Simple clipping check should pass",
140 : .src = { 0, 0,
141 : 2048 << 16,
142 : 2048 << 16 },
143 : .crtc = { 0, 0, 2048, 2048 },
144 : .rotation = DRM_MODE_ROTATE_0,
145 : .min_scale = DRM_PLANE_NO_SCALING,
146 : .max_scale = DRM_PLANE_NO_SCALING,
147 : .can_position = false,
148 : .src_expected = { 0, 0, 1024 << 16, 768 << 16 },
149 : .crtc_expected = { 0, 0, 1024, 768 },
150 : },
151 : {
152 : .name = "clipping_rotate_reflect",
153 : .msg = "Rotated clipping check should pass",
154 : .src = { 0, 0,
155 : 2048 << 16,
156 : 2048 << 16 },
157 : .crtc = { 0, 0, 2048, 2048 },
158 : .rotation = DRM_MODE_ROTATE_90 | DRM_MODE_REFLECT_X,
159 : .min_scale = DRM_PLANE_NO_SCALING,
160 : .max_scale = DRM_PLANE_NO_SCALING,
161 : .can_position = false,
162 : .src_expected = { 0, 0, 768 << 16, 1024 << 16 },
163 : .crtc_expected = { 0, 0, 1024, 768 },
164 : },
165 : {
166 : .name = "positioning_simple",
167 : .msg = "Simple positioning should work",
168 : .src = { 0, 0, 1023 << 16, 767 << 16 },
169 : .crtc = { 0, 0, 1023, 767 },
170 : .rotation = DRM_MODE_ROTATE_0,
171 : .min_scale = DRM_PLANE_NO_SCALING,
172 : .max_scale = DRM_PLANE_NO_SCALING,
173 : .can_position = true,
174 : .src_expected = { 0, 0, 1023 << 16, 767 << 16 },
175 : .crtc_expected = { 0, 0, 1023, 767 },
176 : },
177 : {
178 : .name = "upscaling",
179 : .msg = "Upscaling exactly 2x should work",
180 : .src = { 0, 0, 512 << 16, 384 << 16 },
181 : .crtc = { 0, 0, 1024, 768 },
182 : .rotation = DRM_MODE_ROTATE_0,
183 : .min_scale = 0x8000,
184 : .max_scale = DRM_PLANE_NO_SCALING,
185 : .can_position = false,
186 : .src_expected = { 0, 0, 512 << 16, 384 << 16 },
187 : .crtc_expected = { 0, 0, 1024, 768 },
188 : },
189 : {
190 : .name = "downscaling",
191 : .msg = "Should succeed with exact scaling limit",
192 : .src = { 0, 0, 2048 << 16, 1536 << 16 },
193 : .crtc = { 0, 0, 1024, 768 },
194 : .rotation = DRM_MODE_ROTATE_0,
195 : .min_scale = DRM_PLANE_NO_SCALING,
196 : .max_scale = 0x20000,
197 : .can_position = false,
198 : .src_expected = { 0, 0, 2048 << 16, 1536 << 16 },
199 : .crtc_expected = { 0, 0, 1024, 768 },
200 : },
201 : {
202 : .name = "rounding1",
203 : .msg = "Should succeed by clipping to exact multiple",
204 : .src = { 0, 0, 0x40001, 0x40001 },
205 : .crtc = { 1022, 766, 4, 4 },
206 : .rotation = DRM_MODE_ROTATE_0,
207 : .min_scale = DRM_PLANE_NO_SCALING,
208 : .max_scale = 0x10001,
209 : .can_position = true,
210 : .src_expected = { 0, 0, 2 << 16, 2 << 16 },
211 : .crtc_expected = { 1022, 766, 2, 2 },
212 : },
213 : {
214 : .name = "rounding2",
215 : .msg = "Should succeed by clipping to exact multiple",
216 : .src = { 0x20001, 0x20001, 0x4040001, 0x3040001 },
217 : .crtc = { -2, -2, 1028, 772 },
218 : .rotation = DRM_MODE_ROTATE_0,
219 : .min_scale = DRM_PLANE_NO_SCALING,
220 : .max_scale = 0x10001,
221 : .can_position = false,
222 : .src_expected = { 0x40002, 0x40002, 1024 << 16, 768 << 16 },
223 : .crtc_expected = { 0, 0, 1024, 768 },
224 : },
225 : {
226 : .name = "rounding3",
227 : .msg = "Should succeed by clipping to exact multiple",
228 : .src = { 0, 0, 0x3ffff, 0x3ffff },
229 : .crtc = { 1022, 766, 4, 4 },
230 : .rotation = DRM_MODE_ROTATE_0,
231 : .min_scale = 0xffff,
232 : .max_scale = DRM_PLANE_NO_SCALING,
233 : .can_position = true,
234 : /* Should not be rounded to 0x20001, which would be upscaling. */
235 : .src_expected = { 0, 0, 2 << 16, 2 << 16 },
236 : .crtc_expected = { 1022, 766, 2, 2 },
237 : },
238 : {
239 : .name = "rounding4",
240 : .msg = "Should succeed by clipping to exact multiple",
241 : .src = { 0x1ffff, 0x1ffff, 0x403ffff, 0x303ffff },
242 : .crtc = { -2, -2, 1028, 772 },
243 : .rotation = DRM_MODE_ROTATE_0,
244 : .min_scale = 0xffff,
245 : .max_scale = DRM_PLANE_NO_SCALING,
246 : .can_position = false,
247 : .src_expected = { 0x3fffe, 0x3fffe, 1024 << 16, 768 << 16 },
248 : .crtc_expected = { 0, 0, 1024, 768 },
249 : },
250 : };
251 :
252 0 : KUNIT_ARRAY_PARAM(drm_check_plane_state, drm_check_plane_state_tests, drm_check_plane_state_desc);
253 :
254 0 : static void drm_test_check_invalid_plane_state(struct kunit *test)
255 : {
256 0 : const struct drm_check_plane_state_test *params = test->param_value;
257 0 : struct drm_plane_state *plane_state = test->priv;
258 :
259 0 : KUNIT_ASSERT_LT_MSG(test,
260 : drm_atomic_helper_check_plane_state(plane_state, &crtc_state,
261 : params->min_scale,
262 : params->max_scale,
263 : params->can_position, false),
264 : 0, params->msg);
265 0 : }
266 :
267 : static const struct drm_check_plane_state_test drm_check_invalid_plane_state_tests[] = {
268 : {
269 : .name = "positioning_invalid",
270 : .msg = "Should not be able to position on the crtc with can_position=false",
271 : .src = { 0, 0, 1023 << 16, 767 << 16 },
272 : .crtc = { 0, 0, 1023, 767 },
273 : .rotation = DRM_MODE_ROTATE_0,
274 : .min_scale = DRM_PLANE_NO_SCALING,
275 : .max_scale = DRM_PLANE_NO_SCALING,
276 : .can_position = false,
277 : },
278 : {
279 : .name = "upscaling_invalid",
280 : .msg = "Upscaling out of range should fail",
281 : .src = { 0, 0, 512 << 16, 384 << 16 },
282 : .crtc = { 0, 0, 1024, 768 },
283 : .rotation = DRM_MODE_ROTATE_0,
284 : .min_scale = 0x8001,
285 : .max_scale = DRM_PLANE_NO_SCALING,
286 : .can_position = false,
287 : },
288 : {
289 : .name = "downscaling_invalid",
290 : .msg = "Downscaling out of range should fail",
291 : .src = { 0, 0, 2048 << 16, 1536 << 16 },
292 : .crtc = { 0, 0, 1024, 768 },
293 : .rotation = DRM_MODE_ROTATE_0,
294 : .min_scale = DRM_PLANE_NO_SCALING,
295 : .max_scale = 0x1ffff,
296 : .can_position = false,
297 : },
298 : };
299 :
300 0 : KUNIT_ARRAY_PARAM(drm_check_invalid_plane_state, drm_check_invalid_plane_state_tests,
301 : drm_check_plane_state_desc);
302 :
303 : static struct kunit_case drm_plane_helper_test[] = {
304 : KUNIT_CASE_PARAM(drm_test_check_plane_state, drm_check_plane_state_gen_params),
305 : KUNIT_CASE_PARAM(drm_test_check_invalid_plane_state,
306 : drm_check_invalid_plane_state_gen_params),
307 : {}
308 : };
309 :
310 : static struct kunit_suite drm_plane_helper_test_suite = {
311 : .name = "drm_plane_helper",
312 : .init = drm_plane_helper_init,
313 : .test_cases = drm_plane_helper_test,
314 : };
315 :
316 : kunit_test_suite(drm_plane_helper_test_suite);
317 :
318 : MODULE_LICENSE("GPL");
|