384 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Test cases for the drm_framebuffer functions4 *5 * Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>6 */7 8#include <kunit/test.h>9 10#include <drm/drm_device.h>11#include <drm/drm_mode.h>12#include <drm/drm_fourcc.h>13#include <drm/drm_print.h>14 15#include "../drm_crtc_internal.h"16 17#define MIN_WIDTH 418#define MAX_WIDTH 409619#define MIN_HEIGHT 420#define MAX_HEIGHT 409621 22struct drm_framebuffer_test {23 int buffer_created;24 struct drm_mode_fb_cmd2 cmd;25 const char *name;26};27 28static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = {29{ .buffer_created = 1, .name = "ABGR8888 normal sizes",30 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888,31 .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 },32 }33},34{ .buffer_created = 1, .name = "ABGR8888 max sizes",35 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,36 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },37 }38},39{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required",40 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,41 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 },42 }43},44{ .buffer_created = 0, .name = "ABGR8888 pitch less than min required",45 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,46 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 },47 }48},49{ .buffer_created = 0, .name = "ABGR8888 Invalid width",50 .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,51 .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 },52 }53},54{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle",55 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,56 .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },57 }58},59{ .buffer_created = 0, .name = "No pixel format",60 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0,61 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },62 }63},64{ .buffer_created = 0, .name = "ABGR8888 Width 0",65 .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,66 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },67 }68},69{ .buffer_created = 0, .name = "ABGR8888 Height 0",70 .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888,71 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },72 }73},74{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination",75 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,76 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 },77 .pitches = { 4 * MAX_WIDTH, 0, 0 },78 }79},80{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset",81 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,82 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },83 .pitches = { 4 * MAX_WIDTH, 0, 0 },84 }85},86{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers",87 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,88 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },89 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,90 }91},92{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier",93 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,94 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },95 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,96 .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 },97 }98},99{ .buffer_created = 0,100 .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)",101 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,102 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },103 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,104 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },105 }106},107{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS",108 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,109 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },110 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },111 }112},113{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS",114 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,115 .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,116 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },117 }118},119{ .buffer_created = 1, .name = "NV12 Normal sizes",120 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,121 .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 },122 }123},124{ .buffer_created = 1, .name = "NV12 Max sizes",125 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,126 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },127 }128},129{ .buffer_created = 0, .name = "NV12 Invalid pitch",130 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,131 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 },132 }133},134{ .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag",135 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,136 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },137 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },138 }139},140{ .buffer_created = 0, .name = "NV12 different modifier per-plane",141 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,142 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,143 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },144 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },145 }146},147{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE",148 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,149 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,150 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,151 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },152 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },153 }154},155{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS",156 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,157 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,158 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },159 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },160 }161},162{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane",163 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,164 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,165 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,166 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE },167 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },168 }169},170{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane",171 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,172 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,173 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },174 }175},176{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS",177 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,178 .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 },179 }180},181{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier",182 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,183 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,184 .pitches = { 600, 300, 300 },185 }186},187{ .buffer_created = 1, .name = "YVU420 Normal sizes",188 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,189 .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 },190 }191},192{ .buffer_created = 1, .name = "YVU420 Max sizes",193 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,194 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2),195 DIV_ROUND_UP(MAX_WIDTH, 2) },196 }197},198{ .buffer_created = 0, .name = "YVU420 Invalid pitch",199 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,200 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1,201 DIV_ROUND_UP(MAX_WIDTH, 2) },202 }203},204{ .buffer_created = 1, .name = "YVU420 Different pitches",205 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,206 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,207 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },208 }209},210{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches",211 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,212 .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH +213 MAX_WIDTH * MAX_HEIGHT, MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT },214 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,215 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },216 }217},218{ .buffer_created = 0,219 .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS",220 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,221 .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },222 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },223 }224},225{ .buffer_created = 0,226 .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS",227 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,228 .handles = { 1, 1, 1 },229 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },230 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },231 }232},233{ .buffer_created = 0,234 .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS",235 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,236 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,237 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },238 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },239 }240},241{ .buffer_created = 1, .name = "YVU420 Valid modifier",242 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,243 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,244 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,245 AFBC_FORMAT_MOD_SPARSE },246 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },247 }248},249{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane",250 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,251 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,252 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR,253 AFBC_FORMAT_MOD_SPARSE },254 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },255 }256},257{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane",258 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,259 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,260 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,261 AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE },262 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },263 }264},265{ .buffer_created = 1, .name = "X0L2 Normal sizes",266 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2,267 .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 }268 }269},270{ .buffer_created = 1, .name = "X0L2 Max sizes",271 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,272 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 }273 }274},275{ .buffer_created = 0, .name = "X0L2 Invalid pitch",276 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,277 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 }278 }279},280{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required",281 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,282 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }283 }284},285{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane",286 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,287 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,288 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }289 }290},291{ .buffer_created = 1,292 .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set",293 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,294 .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 },295 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }296 }297},298{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set",299 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,300 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },301 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },302 }303},304{ .buffer_created = 1, .name = "X0L2 Valid modifier",305 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,306 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },307 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,308 }309},310{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane",311 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT,312 .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 },313 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },314 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },315 .flags = DRM_MODE_FB_MODIFIERS,316 }317},318};319 320static struct drm_framebuffer *fb_create_mock(struct drm_device *dev,321 struct drm_file *file_priv,322 const struct drm_mode_fb_cmd2 *mode_cmd)323{324 int *buffer_created = dev->dev_private;325 *buffer_created = 1;326 return ERR_PTR(-EINVAL);327}328 329static struct drm_mode_config_funcs mock_config_funcs = {330 .fb_create = fb_create_mock,331};332 333static int drm_framebuffer_test_init(struct kunit *test)334{335 struct drm_device *mock;336 337 mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);338 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mock);339 340 mock->mode_config.min_width = MIN_WIDTH;341 mock->mode_config.max_width = MAX_WIDTH;342 mock->mode_config.min_height = MIN_HEIGHT;343 mock->mode_config.max_height = MAX_HEIGHT;344 mock->mode_config.funcs = &mock_config_funcs;345 346 test->priv = mock;347 return 0;348}349 350static void drm_test_framebuffer_create(struct kunit *test)351{352 const struct drm_framebuffer_test *params = test->param_value;353 struct drm_device *mock = test->priv;354 int buffer_created = 0;355 356 mock->dev_private = &buffer_created;357 drm_internal_framebuffer_create(mock, ¶ms->cmd, NULL);358 KUNIT_EXPECT_EQ(test, params->buffer_created, buffer_created);359}360 361static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc)362{363 strcpy(desc, t->name);364}365 366KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases,367 drm_framebuffer_test_to_desc);368 369static struct kunit_case drm_framebuffer_tests[] = {370 KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params),371 { }372};373 374static struct kunit_suite drm_framebuffer_test_suite = {375 .name = "drm_framebuffer",376 .init = drm_framebuffer_test_init,377 .test_cases = drm_framebuffer_tests,378};379 380kunit_test_suite(drm_framebuffer_test_suite);381 382MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions");383MODULE_LICENSE("GPL");384