1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include <kunit/test.h>
4 
5 #include <drm/drm_device.h>
6 #include <drm/drm_file.h>
7 #include <drm/drm_format_helper.h>
8 #include <drm/drm_fourcc.h>
9 #include <drm/drm_framebuffer.h>
10 #include <drm/drm_gem_framebuffer_helper.h>
11 #include <drm/drm_mode.h>
12 #include <drm/drm_print.h>
13 #include <drm/drm_rect.h>
14 
15 #include "../drm_crtc_internal.h"
16 
17 #define TEST_BUF_SIZE 50
18 
19 struct convert_to_gray8_result {
20 	unsigned int dst_pitch;
21 	const u8 expected[TEST_BUF_SIZE];
22 };
23 
24 struct convert_to_rgb332_result {
25 	unsigned int dst_pitch;
26 	const u8 expected[TEST_BUF_SIZE];
27 };
28 
29 struct convert_to_rgb565_result {
30 	unsigned int dst_pitch;
31 	const u16 expected[TEST_BUF_SIZE];
32 	const u16 expected_swab[TEST_BUF_SIZE];
33 };
34 
35 struct convert_to_rgb888_result {
36 	unsigned int dst_pitch;
37 	const u8 expected[TEST_BUF_SIZE];
38 };
39 
40 struct convert_to_xrgb2101010_result {
41 	unsigned int dst_pitch;
42 	const u32 expected[TEST_BUF_SIZE];
43 };
44 
45 struct convert_xrgb8888_case {
46 	const char *name;
47 	unsigned int pitch;
48 	struct drm_rect clip;
49 	const u32 xrgb8888[TEST_BUF_SIZE];
50 	struct convert_to_gray8_result gray8_result;
51 	struct convert_to_rgb332_result rgb332_result;
52 	struct convert_to_rgb565_result rgb565_result;
53 	struct convert_to_rgb888_result rgb888_result;
54 	struct convert_to_xrgb2101010_result xrgb2101010_result;
55 };
56 
57 static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
58 	{
59 		.name = "single_pixel_source_buffer",
60 		.pitch = 1 * 4,
61 		.clip = DRM_RECT_INIT(0, 0, 1, 1),
62 		.xrgb8888 = { 0x01FF0000 },
63 		.gray8_result = {
64 			.dst_pitch = 0,
65 			.expected = { 0x4C },
66 		},
67 		.rgb332_result = {
68 			.dst_pitch = 0,
69 			.expected = { 0xE0 },
70 		},
71 		.rgb565_result = {
72 			.dst_pitch = 0,
73 			.expected = { 0xF800 },
74 			.expected_swab = { 0x00F8 },
75 		},
76 		.rgb888_result = {
77 			.dst_pitch = 0,
78 			.expected = { 0x00, 0x00, 0xFF },
79 		},
80 		.xrgb2101010_result = {
81 			.dst_pitch = 0,
82 			.expected = { 0x3FF00000 },
83 		},
84 	},
85 	{
86 		.name = "single_pixel_clip_rectangle",
87 		.pitch = 2 * 4,
88 		.clip = DRM_RECT_INIT(1, 1, 1, 1),
89 		.xrgb8888 = {
90 			0x00000000, 0x00000000,
91 			0x00000000, 0x10FF0000,
92 		},
93 		.gray8_result = {
94 			.dst_pitch = 0,
95 			.expected = { 0x4C },
96 		},
97 		.rgb332_result = {
98 			.dst_pitch = 0,
99 			.expected = { 0xE0 },
100 		},
101 		.rgb565_result = {
102 			.dst_pitch = 0,
103 			.expected = { 0xF800 },
104 			.expected_swab = { 0x00F8 },
105 		},
106 		.rgb888_result = {
107 			.dst_pitch = 0,
108 			.expected = { 0x00, 0x00, 0xFF },
109 		},
110 		.xrgb2101010_result = {
111 			.dst_pitch = 0,
112 			.expected = { 0x3FF00000 },
113 		},
114 	},
115 	{
116 		/* Well known colors: White, black, red, green, blue, magenta,
117 		 * yellow and cyan. Different values for the X in XRGB8888 to
118 		 * make sure it is ignored. Partial clip area.
119 		 */
120 		.name = "well_known_colors",
121 		.pitch = 4 * 4,
122 		.clip = DRM_RECT_INIT(1, 1, 2, 4),
123 		.xrgb8888 = {
124 			0x00000000, 0x00000000, 0x00000000, 0x00000000,
125 			0x00000000, 0x11FFFFFF, 0x22000000, 0x00000000,
126 			0x00000000, 0x33FF0000, 0x4400FF00, 0x00000000,
127 			0x00000000, 0x550000FF, 0x66FF00FF, 0x00000000,
128 			0x00000000, 0x77FFFF00, 0x8800FFFF, 0x00000000,
129 		},
130 		.gray8_result = {
131 			.dst_pitch = 0,
132 			.expected = {
133 				0xFF, 0x00,
134 				0x4C, 0x99,
135 				0x19, 0x66,
136 				0xE5, 0xB2,
137 			},
138 		},
139 		.rgb332_result = {
140 			.dst_pitch = 0,
141 			.expected = {
142 				0xFF, 0x00,
143 				0xE0, 0x1C,
144 				0x03, 0xE3,
145 				0xFC, 0x1F,
146 			},
147 		},
148 		.rgb565_result = {
149 			.dst_pitch = 0,
150 			.expected = {
151 				0xFFFF, 0x0000,
152 				0xF800, 0x07E0,
153 				0x001F, 0xF81F,
154 				0xFFE0, 0x07FF,
155 			},
156 			.expected_swab = {
157 				0xFFFF, 0x0000,
158 				0x00F8, 0xE007,
159 				0x1F00, 0x1FF8,
160 				0xE0FF, 0xFF07,
161 			},
162 		},
163 		.rgb888_result = {
164 			.dst_pitch = 0,
165 			.expected = {
166 				0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
167 				0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00,
168 				0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF,
169 				0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
170 			},
171 		},
172 		.xrgb2101010_result = {
173 			.dst_pitch = 0,
174 			.expected = {
175 				0x3FFFFFFF, 0x00000000,
176 				0x3FF00000, 0x000FFC00,
177 				0x000003FF, 0x3FF003FF,
178 				0x3FFFFC00, 0x000FFFFF,
179 			},
180 		},
181 	},
182 	{
183 		/* Randomly picked colors. Full buffer within the clip area. */
184 		.name = "destination_pitch",
185 		.pitch = 3 * 4,
186 		.clip = DRM_RECT_INIT(0, 0, 3, 3),
187 		.xrgb8888 = {
188 			0xA10E449C, 0xB1114D05, 0xC1A80303,
189 			0xD16C7073, 0xA20E449C, 0xB2114D05,
190 			0xC2A80303, 0xD26C7073, 0xA30E449C,
191 		},
192 		.gray8_result = {
193 			.dst_pitch = 5,
194 			.expected = {
195 				0x3C, 0x33, 0x34, 0x00, 0x00,
196 				0x6F, 0x3C, 0x33, 0x00, 0x00,
197 				0x34, 0x6F, 0x3C, 0x00, 0x00,
198 			},
199 		},
200 		.rgb332_result = {
201 			.dst_pitch = 5,
202 			.expected = {
203 				0x0A, 0x08, 0xA0, 0x00, 0x00,
204 				0x6D, 0x0A, 0x08, 0x00, 0x00,
205 				0xA0, 0x6D, 0x0A, 0x00, 0x00,
206 			},
207 		},
208 		.rgb565_result = {
209 			.dst_pitch = 10,
210 			.expected = {
211 				0x0A33, 0x1260, 0xA800, 0x0000, 0x0000,
212 				0x6B8E, 0x0A33, 0x1260, 0x0000, 0x0000,
213 				0xA800, 0x6B8E, 0x0A33, 0x0000, 0x0000,
214 			},
215 			.expected_swab = {
216 				0x330A, 0x6012, 0x00A8, 0x0000, 0x0000,
217 				0x8E6B, 0x330A, 0x6012, 0x0000, 0x0000,
218 				0x00A8, 0x8E6B, 0x330A, 0x0000, 0x0000,
219 			},
220 		},
221 		.rgb888_result = {
222 			.dst_pitch = 15,
223 			.expected = {
224 				0x9C, 0x44, 0x0E, 0x05, 0x4D, 0x11, 0x03, 0x03, 0xA8,
225 				0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 				0x73, 0x70, 0x6C, 0x9C, 0x44, 0x0E, 0x05, 0x4D, 0x11,
227 				0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 				0x03, 0x03, 0xA8, 0x73, 0x70, 0x6C, 0x9C, 0x44, 0x0E,
229 				0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 			},
231 		},
232 		.xrgb2101010_result = {
233 			.dst_pitch = 20,
234 			.expected = {
235 				0x03844672, 0x0444D414, 0x2A20300C, 0x00000000, 0x00000000,
236 				0x1B1705CD, 0x03844672, 0x0444D414, 0x00000000, 0x00000000,
237 				0x2A20300C, 0x1B1705CD, 0x03844672, 0x00000000, 0x00000000,
238 			},
239 		},
240 	},
241 };
242 
243 /*
244  * conversion_buf_size - Return the destination buffer size required to convert
245  * between formats.
246  * @dst_format: destination buffer pixel format (DRM_FORMAT_*)
247  * @dst_pitch: Number of bytes between two consecutive scanlines within dst
248  * @clip: Clip rectangle area to convert
249  *
250  * Returns:
251  * The size of the destination buffer or negative value on error.
252  */
253 static size_t conversion_buf_size(u32 dst_format, unsigned int dst_pitch,
254 				  const struct drm_rect *clip)
255 {
256 	const struct drm_format_info *dst_fi = drm_format_info(dst_format);
257 
258 	if (!dst_fi)
259 		return -EINVAL;
260 
261 	if (!dst_pitch)
262 		dst_pitch = drm_rect_width(clip) * dst_fi->cpp[0];
263 
264 	return dst_pitch * drm_rect_height(clip);
265 }
266 
267 static u32 *le32buf_to_cpu(struct kunit *test, const u32 *buf, size_t buf_size)
268 {
269 	u32 *dst = NULL;
270 	int n;
271 
272 	dst = kunit_kzalloc(test, sizeof(*dst) * buf_size, GFP_KERNEL);
273 	if (!dst)
274 		return NULL;
275 
276 	for (n = 0; n < buf_size; n++)
277 		dst[n] = le32_to_cpu((__force __le32)buf[n]);
278 
279 	return dst;
280 }
281 
282 static void convert_xrgb8888_case_desc(struct convert_xrgb8888_case *t,
283 				       char *desc)
284 {
285 	strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
286 }
287 
288 KUNIT_ARRAY_PARAM(convert_xrgb8888, convert_xrgb8888_cases,
289 		  convert_xrgb8888_case_desc);
290 
291 static void drm_test_fb_xrgb8888_to_gray8(struct kunit *test)
292 {
293 	const struct convert_xrgb8888_case *params = test->param_value;
294 	const struct convert_to_gray8_result *result = &params->gray8_result;
295 	size_t dst_size;
296 	__u8 *buf = NULL;
297 	__u32 *xrgb8888 = NULL;
298 	struct iosys_map dst, src;
299 
300 	struct drm_framebuffer fb = {
301 		.format = drm_format_info(DRM_FORMAT_XRGB8888),
302 		.pitches = { params->pitch, 0, 0 },
303 	};
304 
305 	dst_size = conversion_buf_size(DRM_FORMAT_R8, result->dst_pitch,
306 				       &params->clip);
307 	KUNIT_ASSERT_GT(test, dst_size, 0);
308 
309 	buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
310 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
311 	iosys_map_set_vaddr(&dst, buf);
312 
313 	xrgb8888 = le32buf_to_cpu(test, params->xrgb8888, TEST_BUF_SIZE);
314 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
315 	iosys_map_set_vaddr(&src, xrgb8888);
316 
317 	drm_fb_xrgb8888_to_gray8(&dst, &result->dst_pitch, &src, &fb, &params->clip);
318 	KUNIT_EXPECT_EQ(test, memcmp(buf, result->expected, dst_size), 0);
319 }
320 
321 static void drm_test_fb_xrgb8888_to_rgb332(struct kunit *test)
322 {
323 	const struct convert_xrgb8888_case *params = test->param_value;
324 	const struct convert_to_rgb332_result *result = &params->rgb332_result;
325 	size_t dst_size;
326 	__u8 *buf = NULL;
327 	__u32 *xrgb8888 = NULL;
328 	struct iosys_map dst, src;
329 
330 	struct drm_framebuffer fb = {
331 		.format = drm_format_info(DRM_FORMAT_XRGB8888),
332 		.pitches = { params->pitch, 0, 0 },
333 	};
334 
335 	dst_size = conversion_buf_size(DRM_FORMAT_RGB332, result->dst_pitch,
336 				       &params->clip);
337 	KUNIT_ASSERT_GT(test, dst_size, 0);
338 
339 	buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
340 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
341 	iosys_map_set_vaddr(&dst, buf);
342 
343 	xrgb8888 = le32buf_to_cpu(test, params->xrgb8888, TEST_BUF_SIZE);
344 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
345 	iosys_map_set_vaddr(&src, xrgb8888);
346 
347 	drm_fb_xrgb8888_to_rgb332(&dst, &result->dst_pitch, &src, &fb, &params->clip);
348 	KUNIT_EXPECT_EQ(test, memcmp(buf, result->expected, dst_size), 0);
349 }
350 
351 static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test)
352 {
353 	const struct convert_xrgb8888_case *params = test->param_value;
354 	const struct convert_to_rgb565_result *result = &params->rgb565_result;
355 	size_t dst_size;
356 	__u16 *buf = NULL;
357 	__u32 *xrgb8888 = NULL;
358 	struct iosys_map dst, src;
359 
360 	struct drm_framebuffer fb = {
361 		.format = drm_format_info(DRM_FORMAT_XRGB8888),
362 		.pitches = { params->pitch, 0, 0 },
363 	};
364 
365 	dst_size = conversion_buf_size(DRM_FORMAT_RGB565, result->dst_pitch,
366 				       &params->clip);
367 	KUNIT_ASSERT_GT(test, dst_size, 0);
368 
369 	buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
370 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
371 	iosys_map_set_vaddr(&dst, buf);
372 
373 	xrgb8888 = le32buf_to_cpu(test, params->xrgb8888, TEST_BUF_SIZE);
374 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
375 	iosys_map_set_vaddr(&src, xrgb8888);
376 
377 	drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, &params->clip, false);
378 	KUNIT_EXPECT_EQ(test, memcmp(buf, result->expected, dst_size), 0);
379 
380 	drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, &params->clip, true);
381 	KUNIT_EXPECT_EQ(test, memcmp(buf, result->expected_swab, dst_size), 0);
382 }
383 
384 static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test)
385 {
386 	const struct convert_xrgb8888_case *params = test->param_value;
387 	const struct convert_to_rgb888_result *result = &params->rgb888_result;
388 	size_t dst_size;
389 	__u8 *buf = NULL;
390 	__u32 *xrgb8888 = NULL;
391 	struct iosys_map dst, src;
392 
393 	struct drm_framebuffer fb = {
394 		.format = drm_format_info(DRM_FORMAT_XRGB8888),
395 		.pitches = { params->pitch, 0, 0 },
396 	};
397 
398 	dst_size = conversion_buf_size(DRM_FORMAT_RGB888, result->dst_pitch,
399 				       &params->clip);
400 	KUNIT_ASSERT_GT(test, dst_size, 0);
401 
402 	buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
403 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
404 	iosys_map_set_vaddr(&dst, buf);
405 
406 	xrgb8888 = le32buf_to_cpu(test, params->xrgb8888, TEST_BUF_SIZE);
407 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
408 	iosys_map_set_vaddr(&src, xrgb8888);
409 
410 	drm_fb_xrgb8888_to_rgb888(&dst, &result->dst_pitch, &src, &fb, &params->clip);
411 	KUNIT_EXPECT_EQ(test, memcmp(buf, result->expected, dst_size), 0);
412 }
413 
414 static void drm_test_fb_xrgb8888_to_xrgb2101010(struct kunit *test)
415 {
416 	const struct convert_xrgb8888_case *params = test->param_value;
417 	const struct convert_to_xrgb2101010_result *result = &params->xrgb2101010_result;
418 	size_t dst_size;
419 	__u32 *buf = NULL;
420 	__u32 *xrgb8888 = NULL;
421 	struct iosys_map dst, src;
422 
423 	struct drm_framebuffer fb = {
424 		.format = drm_format_info(DRM_FORMAT_XRGB8888),
425 		.pitches = { params->pitch, 0, 0 },
426 	};
427 
428 	dst_size = conversion_buf_size(DRM_FORMAT_XRGB2101010,
429 				       result->dst_pitch, &params->clip);
430 	KUNIT_ASSERT_GT(test, dst_size, 0);
431 
432 	buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
433 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
434 	iosys_map_set_vaddr(&dst, buf);
435 
436 	xrgb8888 = le32buf_to_cpu(test, params->xrgb8888, TEST_BUF_SIZE);
437 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
438 	iosys_map_set_vaddr(&src, xrgb8888);
439 
440 	drm_fb_xrgb8888_to_xrgb2101010(&dst, &result->dst_pitch, &src, &fb, &params->clip);
441 	buf = le32buf_to_cpu(test, buf, dst_size / sizeof(u32));
442 	KUNIT_EXPECT_EQ(test, memcmp(buf, result->expected, dst_size), 0);
443 }
444 
445 static struct kunit_case drm_format_helper_test_cases[] = {
446 	KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_gray8, convert_xrgb8888_gen_params),
447 	KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb332, convert_xrgb8888_gen_params),
448 	KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb565, convert_xrgb8888_gen_params),
449 	KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb888, convert_xrgb8888_gen_params),
450 	KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_xrgb2101010, convert_xrgb8888_gen_params),
451 	{}
452 };
453 
454 static struct kunit_suite drm_format_helper_test_suite = {
455 	.name = "drm_format_helper_test",
456 	.test_cases = drm_format_helper_test_cases,
457 };
458 
459 kunit_test_suite(drm_format_helper_test_suite);
460 
461 MODULE_DESCRIPTION("KUnit tests for the drm_format_helper APIs");
462 MODULE_LICENSE("GPL");
463 MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");
464