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 = ¶ms->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 ¶ms->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, ¶ms->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 = ¶ms->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 ¶ms->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, ¶ms->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 = ¶ms->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 ¶ms->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, ¶ms->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, ¶ms->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 = ¶ms->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 ¶ms->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, ¶ms->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 = ¶ms->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, ¶ms->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, ¶ms->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