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 
drm_plane_helper_init(struct kunit * test)47 static int drm_plane_helper_init(struct kunit *test)
48 {
49 	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 	plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
55 	KUNIT_ASSERT_NOT_NULL(test, plane);
56 
57 	fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
58 	KUNIT_ASSERT_NOT_NULL(test, fb);
59 	fb->width = 2048;
60 	fb->height = 2048;
61 
62 	mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
63 	KUNIT_ASSERT_NOT_NULL(test, mock);
64 	mock->plane = plane;
65 	mock->crtc = ZERO_SIZE_PTR;
66 	mock->fb = fb;
67 	mock->rotation = params->rotation;
68 	mock->src_x = params->src.x;
69 	mock->src_y = params->src.y;
70 	mock->src_w = params->src.w;
71 	mock->src_h = params->src.h;
72 	mock->crtc_x = params->crtc.x;
73 	mock->crtc_y = params->crtc.y;
74 	mock->crtc_w = params->crtc.w;
75 	mock->crtc_h = params->crtc.h;
76 
77 	test->priv = mock;
78 
79 	return 0;
80 }
81 
check_src_eq(struct kunit * test,struct drm_plane_state * plane_state,unsigned int src_x,unsigned int src_y,unsigned int src_w,unsigned int src_h)82 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 	struct drm_rect expected = DRM_RECT_INIT(src_x, src_y, src_w, src_h);
87 
88 	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 	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 	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 }
100 
check_crtc_eq(struct kunit * test,struct drm_plane_state * plane_state,int crtc_x,int crtc_y,unsigned int crtc_w,unsigned int crtc_h)101 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 	struct drm_rect expected = DRM_RECT_INIT(crtc_x, crtc_y, crtc_w, crtc_h);
106 
107 	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 }
111 
drm_test_check_plane_state(struct kunit * test)112 static void drm_test_check_plane_state(struct kunit *test)
113 {
114 	const struct drm_check_plane_state_test *params = test->param_value;
115 	struct drm_plane_state *plane_state = test->priv;
116 
117 	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 	KUNIT_EXPECT_TRUE(test, plane_state->visible);
124 	check_src_eq(test, plane_state, params->src_expected.x, params->src_expected.y,
125 		     params->src_expected.w, params->src_expected.h);
126 	check_crtc_eq(test, plane_state, params->crtc_expected.x, params->crtc_expected.y,
127 		      params->crtc_expected.w, params->crtc_expected.h);
128 }
129 
drm_check_plane_state_desc(const struct drm_check_plane_state_test * t,char * desc)130 static void drm_check_plane_state_desc(const struct drm_check_plane_state_test *t,
131 				       char *desc)
132 {
133 	sprintf(desc, "%s", t->name);
134 }
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 KUNIT_ARRAY_PARAM(drm_check_plane_state, drm_check_plane_state_tests, drm_check_plane_state_desc);
253 
drm_test_check_invalid_plane_state(struct kunit * test)254 static void drm_test_check_invalid_plane_state(struct kunit *test)
255 {
256 	const struct drm_check_plane_state_test *params = test->param_value;
257 	struct drm_plane_state *plane_state = test->priv;
258 
259 	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 }
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 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");
319