at master 27 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Test cases for the drm_framebuffer functions 4 * 5 * Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net> 6 */ 7 8#include <kunit/device.h> 9#include <kunit/test.h> 10 11#include <drm/drm_device.h> 12#include <drm/drm_drv.h> 13#include <drm/drm_mode.h> 14#include <drm/drm_framebuffer.h> 15#include <drm/drm_fourcc.h> 16#include <drm/drm_kunit_helpers.h> 17#include <drm/drm_print.h> 18 19#include "../drm_crtc_internal.h" 20 21#define MIN_WIDTH 4 22#define MAX_WIDTH 4096 23#define MIN_HEIGHT 4 24#define MAX_HEIGHT 4096 25 26#define DRM_MODE_FB_INVALID BIT(2) 27 28struct drm_framebuffer_test { 29 int buffer_created; 30 struct drm_mode_fb_cmd2 cmd; 31 const char *name; 32}; 33 34static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = { 35{ .buffer_created = 1, .name = "ABGR8888 normal sizes", 36 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888, 37 .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 }, 38 } 39}, 40{ .buffer_created = 1, .name = "ABGR8888 max sizes", 41 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 42 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 43 } 44}, 45{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required", 46 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 47 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 }, 48 } 49}, 50{ .buffer_created = 0, .name = "ABGR8888 pitch less than min required", 51 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 52 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 }, 53 } 54}, 55{ .buffer_created = 0, .name = "ABGR8888 Invalid width", 56 .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 57 .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 }, 58 } 59}, 60{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle", 61 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 62 .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 63 } 64}, 65{ .buffer_created = 0, .name = "No pixel format", 66 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0, 67 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 68 } 69}, 70{ .buffer_created = 0, .name = "ABGR8888 Width 0", 71 .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 72 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 73 } 74}, 75{ .buffer_created = 0, .name = "ABGR8888 Height 0", 76 .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888, 77 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 78 } 79}, 80{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination", 81 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 82 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, 83 .pitches = { 4 * MAX_WIDTH, 0, 0 }, 84 } 85}, 86{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset", 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 }, 90 } 91}, 92 93/* 94 * All entries in members that represents per-plane values (@modifier, @handles, 95 * @pitches and @offsets) must be zero when unused. 96 */ 97{ .buffer_created = 0, .name = "ABGR8888 Buffer offset for inexistent plane", 98 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 99 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, UINT_MAX / 2, 0 }, 100 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 101 } 102}, 103 104{ .buffer_created = 0, .name = "ABGR8888 Invalid flag", 105 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 106 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 107 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_INVALID, 108 } 109}, 110{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers", 111 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 112 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 113 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 114 } 115}, 116{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier", 117 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 118 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 119 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 120 .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 }, 121 } 122}, 123{ .buffer_created = 0, 124 .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)", 125 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 126 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 127 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 128 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 129 } 130}, 131{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS", 132 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 133 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 134 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, 135 } 136}, 137{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS", 138 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 139 .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 140 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, 141 } 142}, 143{ .buffer_created = 1, .name = "NV12 Normal sizes", 144 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, 145 .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 }, 146 } 147}, 148{ .buffer_created = 1, .name = "NV12 Max sizes", 149 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 150 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 151 } 152}, 153{ .buffer_created = 0, .name = "NV12 Invalid pitch", 154 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 155 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 }, 156 } 157}, 158{ .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag", 159 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 160 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 161 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 162 } 163}, 164{ .buffer_created = 0, .name = "NV12 different modifier per-plane", 165 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 166 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 167 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 168 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 169 } 170}, 171{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE", 172 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 173 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 174 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 175 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, 176 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 177 } 178}, 179{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS", 180 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 181 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 182 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, 183 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 184 } 185}, 186{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane", 187 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 188 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 189 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 190 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE }, 191 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 192 } 193}, 194{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane", 195 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 196 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 197 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 198 } 199}, 200{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS", 201 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, 202 .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, 203 } 204}, 205{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", 206 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, 207 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 208 .pitches = { 600, 300, 300 }, 209 } 210}, 211{ .buffer_created = 1, .name = "YVU420 Normal sizes", 212 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, 213 .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, 214 } 215}, 216{ .buffer_created = 1, .name = "YVU420 Max sizes", 217 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 218 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), 219 DIV_ROUND_UP(MAX_WIDTH, 2) }, 220 } 221}, 222{ .buffer_created = 0, .name = "YVU420 Invalid pitch", 223 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 224 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1, 225 DIV_ROUND_UP(MAX_WIDTH, 2) }, 226 } 227}, 228{ .buffer_created = 1, .name = "YVU420 Different pitches", 229 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 230 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, 231 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, 232 } 233}, 234{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches", 235 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 236 .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH + 237 MAX_WIDTH * MAX_HEIGHT, MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT }, 238 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, 239 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, 240 } 241}, 242{ .buffer_created = 0, 243 .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS", 244 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 245 .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, 246 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 247 } 248}, 249{ .buffer_created = 0, 250 .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS", 251 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 252 .handles = { 1, 1, 1 }, 253 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 254 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 255 } 256}, 257{ .buffer_created = 0, 258 .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS", 259 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 260 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 261 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 262 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 263 } 264}, 265{ .buffer_created = 1, .name = "YVU420 Valid modifier", 266 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 267 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 268 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 269 AFBC_FORMAT_MOD_SPARSE }, 270 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 271 } 272}, 273{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane", 274 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 275 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 276 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR, 277 AFBC_FORMAT_MOD_SPARSE }, 278 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 279 } 280}, 281{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane", 282 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 283 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 284 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 285 AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE }, 286 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 287 } 288}, 289{ .buffer_created = 0, .name = "YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)", 290 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YUV420_10BIT, 291 .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 292 .modifier = { DRM_FORMAT_MOD_LINEAR, 0, 0 }, 293 .pitches = { MAX_WIDTH, 0, 0 }, 294 } 295}, 296{ .buffer_created = 1, .name = "X0L2 Normal sizes", 297 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2, 298 .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 } 299 } 300}, 301{ .buffer_created = 1, .name = "X0L2 Max sizes", 302 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 303 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 } 304 } 305}, 306{ .buffer_created = 0, .name = "X0L2 Invalid pitch", 307 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 308 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 } 309 } 310}, 311{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required", 312 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 313 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 314 } 315}, 316{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane", 317 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 318 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 319 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 320 } 321}, 322{ .buffer_created = 1, 323 .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set", 324 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 325 .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 }, 326 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 327 } 328}, 329{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set", 330 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 331 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 332 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, 333 } 334}, 335{ .buffer_created = 1, .name = "X0L2 Valid modifier", 336 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 337 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 338 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 339 } 340}, 341{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane", 342 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, 343 .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 }, 344 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 345 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 346 .flags = DRM_MODE_FB_MODIFIERS, 347 } 348}, 349}; 350 351/* 352 * This struct is intended to provide a way to mocked functions communicate 353 * with the outer test when it can't be achieved by using its return value. In 354 * this way, the functions that receive the mocked drm_device, for example, can 355 * grab a reference to this and actually return something to be used on some 356 * expectation. 357 */ 358struct drm_framebuffer_test_priv { 359 struct drm_device dev; 360 bool buffer_created; 361 bool buffer_freed; 362}; 363 364static struct drm_framebuffer *fb_create_mock(struct drm_device *dev, 365 struct drm_file *file_priv, 366 const struct drm_format_info *info, 367 const struct drm_mode_fb_cmd2 *mode_cmd) 368{ 369 struct drm_framebuffer_test_priv *priv = container_of(dev, typeof(*priv), dev); 370 371 priv->buffer_created = true; 372 return ERR_PTR(-EINVAL); 373} 374 375static struct drm_mode_config_funcs mock_config_funcs = { 376 .fb_create = fb_create_mock, 377}; 378 379static int drm_framebuffer_test_init(struct kunit *test) 380{ 381 struct device *parent; 382 struct drm_framebuffer_test_priv *priv; 383 struct drm_device *dev; 384 385 parent = drm_kunit_helper_alloc_device(test); 386 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent); 387 388 priv = drm_kunit_helper_alloc_drm_device(test, parent, typeof(*priv), 389 dev, 0); 390 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); 391 dev = &priv->dev; 392 393 dev->mode_config.min_width = MIN_WIDTH; 394 dev->mode_config.max_width = MAX_WIDTH; 395 dev->mode_config.min_height = MIN_HEIGHT; 396 dev->mode_config.max_height = MAX_HEIGHT; 397 dev->mode_config.funcs = &mock_config_funcs; 398 399 test->priv = priv; 400 return 0; 401} 402 403static void drm_test_framebuffer_create(struct kunit *test) 404{ 405 const struct drm_framebuffer_test *params = test->param_value; 406 struct drm_framebuffer_test_priv *priv = test->priv; 407 struct drm_device *dev = &priv->dev; 408 409 priv->buffer_created = false; 410 drm_internal_framebuffer_create(dev, &params->cmd, NULL); 411 KUNIT_EXPECT_EQ(test, params->buffer_created, priv->buffer_created); 412} 413 414static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc) 415{ 416 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 417} 418 419KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases, 420 drm_framebuffer_test_to_desc); 421 422/* Tries to create a framebuffer with modifiers without drm_device supporting it */ 423static void drm_test_framebuffer_modifiers_not_supported(struct kunit *test) 424{ 425 struct drm_framebuffer_test_priv *priv = test->priv; 426 struct drm_device *dev = &priv->dev; 427 struct drm_framebuffer *fb; 428 429 /* A valid cmd with modifier */ 430 struct drm_mode_fb_cmd2 cmd = { 431 .width = MAX_WIDTH, .height = MAX_HEIGHT, 432 .pixel_format = DRM_FORMAT_ABGR8888, .handles = { 1, 0, 0 }, 433 .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 434 .flags = DRM_MODE_FB_MODIFIERS, 435 }; 436 437 priv->buffer_created = false; 438 dev->mode_config.fb_modifiers_not_supported = 1; 439 440 fb = drm_internal_framebuffer_create(dev, &cmd, NULL); 441 KUNIT_EXPECT_EQ(test, priv->buffer_created, false); 442 KUNIT_EXPECT_EQ(test, PTR_ERR(fb), -EINVAL); 443} 444 445/* Parameters for testing drm_framebuffer_check_src_coords function */ 446struct drm_framebuffer_check_src_coords_case { 447 const char *name; 448 const int expect; 449 const unsigned int fb_size; 450 const uint32_t src_x; 451 const uint32_t src_y; 452 453 /* Deltas to be applied on source */ 454 const uint32_t dsrc_w; 455 const uint32_t dsrc_h; 456}; 457 458static const struct drm_framebuffer_check_src_coords_case 459drm_framebuffer_check_src_coords_cases[] = { 460 { .name = "Success: source fits into fb", 461 .expect = 0, 462 }, 463 { .name = "Fail: overflowing fb with x-axis coordinate", 464 .expect = -ENOSPC, .src_x = 1, .fb_size = UINT_MAX, 465 }, 466 { .name = "Fail: overflowing fb with y-axis coordinate", 467 .expect = -ENOSPC, .src_y = 1, .fb_size = UINT_MAX, 468 }, 469 { .name = "Fail: overflowing fb with source width", 470 .expect = -ENOSPC, .dsrc_w = 1, .fb_size = UINT_MAX - 1, 471 }, 472 { .name = "Fail: overflowing fb with source height", 473 .expect = -ENOSPC, .dsrc_h = 1, .fb_size = UINT_MAX - 1, 474 }, 475}; 476 477static void drm_test_framebuffer_check_src_coords(struct kunit *test) 478{ 479 const struct drm_framebuffer_check_src_coords_case *params = test->param_value; 480 const uint32_t src_x = params->src_x; 481 const uint32_t src_y = params->src_y; 482 const uint32_t src_w = (params->fb_size << 16) + params->dsrc_w; 483 const uint32_t src_h = (params->fb_size << 16) + params->dsrc_h; 484 const struct drm_framebuffer fb = { 485 .width = params->fb_size, 486 .height = params->fb_size 487 }; 488 int ret; 489 490 ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, &fb); 491 KUNIT_EXPECT_EQ(test, ret, params->expect); 492} 493 494static void 495check_src_coords_test_to_desc(const struct drm_framebuffer_check_src_coords_case *t, 496 char *desc) 497{ 498 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 499} 500 501KUNIT_ARRAY_PARAM(check_src_coords, drm_framebuffer_check_src_coords_cases, 502 check_src_coords_test_to_desc); 503 504/* 505 * Test if drm_framebuffer_cleanup() really pops out the framebuffer object 506 * from device's fb_list and decrement the number of framebuffers for that 507 * device, which is the only things it does. 508 */ 509static void drm_test_framebuffer_cleanup(struct kunit *test) 510{ 511 struct drm_framebuffer_test_priv *priv = test->priv; 512 struct drm_device *dev = &priv->dev; 513 struct list_head *fb_list = &dev->mode_config.fb_list; 514 struct drm_format_info format = { }; 515 struct drm_framebuffer fb1 = { .dev = dev, .format = &format }; 516 struct drm_framebuffer fb2 = { .dev = dev, .format = &format }; 517 518 /* This will result on [fb_list] -> fb2 -> fb1 */ 519 drm_framebuffer_init(dev, &fb1, NULL); 520 drm_framebuffer_init(dev, &fb2, NULL); 521 522 drm_framebuffer_cleanup(&fb1); 523 524 /* Now fb2 is the only one element on fb_list */ 525 KUNIT_ASSERT_TRUE(test, list_is_singular(&fb2.head)); 526 KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 1); 527 528 drm_framebuffer_cleanup(&fb2); 529 530 /* Now fb_list is empty */ 531 KUNIT_ASSERT_TRUE(test, list_empty(fb_list)); 532 KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 0); 533} 534 535/* 536 * Initialize a framebuffer, lookup its id and test if the returned reference 537 * matches. 538 */ 539static void drm_test_framebuffer_lookup(struct kunit *test) 540{ 541 struct drm_framebuffer_test_priv *priv = test->priv; 542 struct drm_device *dev = &priv->dev; 543 struct drm_format_info format = { }; 544 struct drm_framebuffer expected_fb = { .dev = dev, .format = &format }; 545 struct drm_framebuffer *returned_fb; 546 uint32_t id = 0; 547 int ret; 548 549 ret = drm_framebuffer_init(dev, &expected_fb, NULL); 550 KUNIT_ASSERT_EQ(test, ret, 0); 551 id = expected_fb.base.id; 552 553 /* Looking for expected_fb */ 554 returned_fb = drm_framebuffer_lookup(dev, NULL, id); 555 KUNIT_EXPECT_PTR_EQ(test, returned_fb, &expected_fb); 556 drm_framebuffer_put(returned_fb); 557 558 drm_framebuffer_cleanup(&expected_fb); 559} 560 561/* Try to lookup an id that is not linked to a framebuffer */ 562static void drm_test_framebuffer_lookup_inexistent(struct kunit *test) 563{ 564 struct drm_framebuffer_test_priv *priv = test->priv; 565 struct drm_device *dev = &priv->dev; 566 struct drm_framebuffer *fb; 567 uint32_t id = 0; 568 569 /* Looking for an inexistent framebuffer */ 570 fb = drm_framebuffer_lookup(dev, NULL, id); 571 KUNIT_EXPECT_NULL(test, fb); 572} 573 574/* 575 * Test if drm_framebuffer_init initializes the framebuffer successfully, 576 * asserting that its modeset object struct and its refcount are correctly 577 * set and that strictly one framebuffer is initialized. 578 */ 579static void drm_test_framebuffer_init(struct kunit *test) 580{ 581 struct drm_framebuffer_test_priv *priv = test->priv; 582 struct drm_device *dev = &priv->dev; 583 struct drm_format_info format = { }; 584 struct drm_framebuffer fb1 = { .dev = dev, .format = &format }; 585 struct drm_framebuffer_funcs funcs = { }; 586 int ret; 587 588 ret = drm_framebuffer_init(dev, &fb1, &funcs); 589 KUNIT_ASSERT_EQ(test, ret, 0); 590 591 /* Check if fb->funcs is actually set to the drm_framebuffer_funcs passed on */ 592 KUNIT_EXPECT_PTR_EQ(test, fb1.funcs, &funcs); 593 594 /* The fb->comm must be set to the current running process */ 595 KUNIT_EXPECT_STREQ(test, fb1.comm, current->comm); 596 597 /* The fb->base must be successfully initialized */ 598 KUNIT_EXPECT_NE(test, fb1.base.id, 0); 599 KUNIT_EXPECT_EQ(test, fb1.base.type, DRM_MODE_OBJECT_FB); 600 KUNIT_EXPECT_EQ(test, kref_read(&fb1.base.refcount), 1); 601 KUNIT_EXPECT_PTR_EQ(test, fb1.base.free_cb, &drm_framebuffer_free); 602 603 /* There must be just that one fb initialized */ 604 KUNIT_EXPECT_EQ(test, dev->mode_config.num_fb, 1); 605 KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.prev, &fb1.head); 606 KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.next, &fb1.head); 607 608 drm_framebuffer_cleanup(&fb1); 609} 610 611/* Try to init a framebuffer without setting its format */ 612static void drm_test_framebuffer_init_bad_format(struct kunit *test) 613{ 614 struct drm_framebuffer_test_priv *priv = test->priv; 615 struct drm_device *dev = &priv->dev; 616 struct drm_framebuffer fb1 = { .dev = dev, .format = NULL }; 617 struct drm_framebuffer_funcs funcs = { }; 618 int ret; 619 620 /* Fails if fb.format isn't set */ 621 ret = drm_framebuffer_init(dev, &fb1, &funcs); 622 KUNIT_EXPECT_EQ(test, ret, -EINVAL); 623} 624 625/* 626 * Test calling drm_framebuffer_init() passing a framebuffer linked to a 627 * different drm_device parent from the one passed on the first argument, which 628 * must fail. 629 */ 630static void drm_test_framebuffer_init_dev_mismatch(struct kunit *test) 631{ 632 struct drm_framebuffer_test_priv *priv = test->priv; 633 struct drm_device *right_dev = &priv->dev; 634 struct drm_device *wrong_dev; 635 struct device *wrong_dev_parent; 636 struct drm_format_info format = { }; 637 struct drm_framebuffer fb1 = { .dev = right_dev, .format = &format }; 638 struct drm_framebuffer_funcs funcs = { }; 639 int ret; 640 641 wrong_dev_parent = kunit_device_register(test, "drm-kunit-wrong-device-mock"); 642 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev_parent); 643 644 wrong_dev = __drm_kunit_helper_alloc_drm_device(test, wrong_dev_parent, 645 sizeof(struct drm_device), 646 0, 0); 647 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev); 648 649 /* Fails if fb->dev doesn't point to the drm_device passed on first arg */ 650 ret = drm_framebuffer_init(wrong_dev, &fb1, &funcs); 651 KUNIT_EXPECT_EQ(test, ret, -EINVAL); 652} 653 654static void destroy_free_mock(struct drm_framebuffer *fb) 655{ 656 struct drm_framebuffer_test_priv *priv = container_of(fb->dev, typeof(*priv), dev); 657 658 priv->buffer_freed = true; 659} 660 661static struct drm_framebuffer_funcs framebuffer_funcs_free_mock = { 662 .destroy = destroy_free_mock, 663}; 664 665/* 666 * In summary, the drm_framebuffer_free() function must implicitly call 667 * fb->funcs->destroy() and garantee that the framebufer object is unregistered 668 * from the drm_device idr pool. 669 */ 670static void drm_test_framebuffer_free(struct kunit *test) 671{ 672 struct drm_framebuffer_test_priv *priv = test->priv; 673 struct drm_device *dev = &priv->dev; 674 struct drm_mode_object *obj; 675 struct drm_framebuffer fb = { 676 .dev = dev, 677 .funcs = &framebuffer_funcs_free_mock, 678 }; 679 int id, ret; 680 681 priv->buffer_freed = false; 682 683 /* 684 * Mock a framebuffer that was not unregistered at the moment of the 685 * drm_framebuffer_free() call. 686 */ 687 ret = drm_mode_object_add(dev, &fb.base, DRM_MODE_OBJECT_FB); 688 KUNIT_ASSERT_EQ(test, ret, 0); 689 id = fb.base.id; 690 691 drm_framebuffer_free(&fb.base.refcount); 692 693 /* The framebuffer object must be unregistered */ 694 obj = drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_FB); 695 KUNIT_EXPECT_PTR_EQ(test, obj, NULL); 696 KUNIT_EXPECT_EQ(test, fb.base.id, 0); 697 698 /* Test if fb->funcs->destroy() was called */ 699 KUNIT_EXPECT_EQ(test, priv->buffer_freed, true); 700} 701 702static struct kunit_case drm_framebuffer_tests[] = { 703 KUNIT_CASE_PARAM(drm_test_framebuffer_check_src_coords, check_src_coords_gen_params), 704 KUNIT_CASE(drm_test_framebuffer_cleanup), 705 KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params), 706 KUNIT_CASE(drm_test_framebuffer_free), 707 KUNIT_CASE(drm_test_framebuffer_init), 708 KUNIT_CASE(drm_test_framebuffer_init_bad_format), 709 KUNIT_CASE(drm_test_framebuffer_init_dev_mismatch), 710 KUNIT_CASE(drm_test_framebuffer_lookup), 711 KUNIT_CASE(drm_test_framebuffer_lookup_inexistent), 712 KUNIT_CASE(drm_test_framebuffer_modifiers_not_supported), 713 { } 714}; 715 716static struct kunit_suite drm_framebuffer_test_suite = { 717 .name = "drm_framebuffer", 718 .init = drm_framebuffer_test_init, 719 .test_cases = drm_framebuffer_tests, 720}; 721 722kunit_test_suite(drm_framebuffer_test_suite); 723 724MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions"); 725MODULE_LICENSE("GPL");