at for-next 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_mode_fb_cmd2 *mode_cmd) 367{ 368 struct drm_framebuffer_test_priv *priv = container_of(dev, typeof(*priv), dev); 369 370 priv->buffer_created = true; 371 return ERR_PTR(-EINVAL); 372} 373 374static struct drm_mode_config_funcs mock_config_funcs = { 375 .fb_create = fb_create_mock, 376}; 377 378static int drm_framebuffer_test_init(struct kunit *test) 379{ 380 struct device *parent; 381 struct drm_framebuffer_test_priv *priv; 382 struct drm_device *dev; 383 384 parent = drm_kunit_helper_alloc_device(test); 385 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent); 386 387 priv = drm_kunit_helper_alloc_drm_device(test, parent, typeof(*priv), 388 dev, 0); 389 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); 390 dev = &priv->dev; 391 392 dev->mode_config.min_width = MIN_WIDTH; 393 dev->mode_config.max_width = MAX_WIDTH; 394 dev->mode_config.min_height = MIN_HEIGHT; 395 dev->mode_config.max_height = MAX_HEIGHT; 396 dev->mode_config.funcs = &mock_config_funcs; 397 398 test->priv = priv; 399 return 0; 400} 401 402static void drm_test_framebuffer_create(struct kunit *test) 403{ 404 const struct drm_framebuffer_test *params = test->param_value; 405 struct drm_framebuffer_test_priv *priv = test->priv; 406 struct drm_device *dev = &priv->dev; 407 408 priv->buffer_created = false; 409 drm_internal_framebuffer_create(dev, &params->cmd, NULL); 410 KUNIT_EXPECT_EQ(test, params->buffer_created, priv->buffer_created); 411} 412 413static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc) 414{ 415 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 416} 417 418KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases, 419 drm_framebuffer_test_to_desc); 420 421/* Tries to create a framebuffer with modifiers without drm_device supporting it */ 422static void drm_test_framebuffer_modifiers_not_supported(struct kunit *test) 423{ 424 struct drm_framebuffer_test_priv *priv = test->priv; 425 struct drm_device *dev = &priv->dev; 426 struct drm_framebuffer *fb; 427 428 /* A valid cmd with modifier */ 429 struct drm_mode_fb_cmd2 cmd = { 430 .width = MAX_WIDTH, .height = MAX_HEIGHT, 431 .pixel_format = DRM_FORMAT_ABGR8888, .handles = { 1, 0, 0 }, 432 .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 433 .flags = DRM_MODE_FB_MODIFIERS, 434 }; 435 436 priv->buffer_created = false; 437 dev->mode_config.fb_modifiers_not_supported = 1; 438 439 fb = drm_internal_framebuffer_create(dev, &cmd, NULL); 440 KUNIT_EXPECT_EQ(test, priv->buffer_created, false); 441 KUNIT_EXPECT_EQ(test, PTR_ERR(fb), -EINVAL); 442} 443 444/* Parameters for testing drm_framebuffer_check_src_coords function */ 445struct drm_framebuffer_check_src_coords_case { 446 const char *name; 447 const int expect; 448 const unsigned int fb_size; 449 const uint32_t src_x; 450 const uint32_t src_y; 451 452 /* Deltas to be applied on source */ 453 const uint32_t dsrc_w; 454 const uint32_t dsrc_h; 455}; 456 457static const struct drm_framebuffer_check_src_coords_case 458drm_framebuffer_check_src_coords_cases[] = { 459 { .name = "Success: source fits into fb", 460 .expect = 0, 461 }, 462 { .name = "Fail: overflowing fb with x-axis coordinate", 463 .expect = -ENOSPC, .src_x = 1, .fb_size = UINT_MAX, 464 }, 465 { .name = "Fail: overflowing fb with y-axis coordinate", 466 .expect = -ENOSPC, .src_y = 1, .fb_size = UINT_MAX, 467 }, 468 { .name = "Fail: overflowing fb with source width", 469 .expect = -ENOSPC, .dsrc_w = 1, .fb_size = UINT_MAX - 1, 470 }, 471 { .name = "Fail: overflowing fb with source height", 472 .expect = -ENOSPC, .dsrc_h = 1, .fb_size = UINT_MAX - 1, 473 }, 474}; 475 476static void drm_test_framebuffer_check_src_coords(struct kunit *test) 477{ 478 const struct drm_framebuffer_check_src_coords_case *params = test->param_value; 479 const uint32_t src_x = params->src_x; 480 const uint32_t src_y = params->src_y; 481 const uint32_t src_w = (params->fb_size << 16) + params->dsrc_w; 482 const uint32_t src_h = (params->fb_size << 16) + params->dsrc_h; 483 const struct drm_framebuffer fb = { 484 .width = params->fb_size, 485 .height = params->fb_size 486 }; 487 int ret; 488 489 ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, &fb); 490 KUNIT_EXPECT_EQ(test, ret, params->expect); 491} 492 493static void 494check_src_coords_test_to_desc(const struct drm_framebuffer_check_src_coords_case *t, 495 char *desc) 496{ 497 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 498} 499 500KUNIT_ARRAY_PARAM(check_src_coords, drm_framebuffer_check_src_coords_cases, 501 check_src_coords_test_to_desc); 502 503/* 504 * Test if drm_framebuffer_cleanup() really pops out the framebuffer object 505 * from device's fb_list and decrement the number of framebuffers for that 506 * device, which is the only things it does. 507 */ 508static void drm_test_framebuffer_cleanup(struct kunit *test) 509{ 510 struct drm_framebuffer_test_priv *priv = test->priv; 511 struct drm_device *dev = &priv->dev; 512 struct list_head *fb_list = &dev->mode_config.fb_list; 513 struct drm_format_info format = { }; 514 struct drm_framebuffer fb1 = { .dev = dev, .format = &format }; 515 struct drm_framebuffer fb2 = { .dev = dev, .format = &format }; 516 517 /* This will result on [fb_list] -> fb2 -> fb1 */ 518 drm_framebuffer_init(dev, &fb1, NULL); 519 drm_framebuffer_init(dev, &fb2, NULL); 520 521 drm_framebuffer_cleanup(&fb1); 522 523 /* Now fb2 is the only one element on fb_list */ 524 KUNIT_ASSERT_TRUE(test, list_is_singular(&fb2.head)); 525 KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 1); 526 527 drm_framebuffer_cleanup(&fb2); 528 529 /* Now fb_list is empty */ 530 KUNIT_ASSERT_TRUE(test, list_empty(fb_list)); 531 KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 0); 532} 533 534/* 535 * Initialize a framebuffer, lookup its id and test if the returned reference 536 * matches. 537 */ 538static void drm_test_framebuffer_lookup(struct kunit *test) 539{ 540 struct drm_framebuffer_test_priv *priv = test->priv; 541 struct drm_device *dev = &priv->dev; 542 struct drm_format_info format = { }; 543 struct drm_framebuffer expected_fb = { .dev = dev, .format = &format }; 544 struct drm_framebuffer *returned_fb; 545 uint32_t id = 0; 546 int ret; 547 548 ret = drm_framebuffer_init(dev, &expected_fb, NULL); 549 KUNIT_ASSERT_EQ(test, ret, 0); 550 id = expected_fb.base.id; 551 552 /* Looking for expected_fb */ 553 returned_fb = drm_framebuffer_lookup(dev, NULL, id); 554 KUNIT_EXPECT_PTR_EQ(test, returned_fb, &expected_fb); 555 drm_framebuffer_put(returned_fb); 556 557 drm_framebuffer_cleanup(&expected_fb); 558} 559 560/* Try to lookup an id that is not linked to a framebuffer */ 561static void drm_test_framebuffer_lookup_inexistent(struct kunit *test) 562{ 563 struct drm_framebuffer_test_priv *priv = test->priv; 564 struct drm_device *dev = &priv->dev; 565 struct drm_framebuffer *fb; 566 uint32_t id = 0; 567 568 /* Looking for an inexistent framebuffer */ 569 fb = drm_framebuffer_lookup(dev, NULL, id); 570 KUNIT_EXPECT_NULL(test, fb); 571} 572 573/* 574 * Test if drm_framebuffer_init initializes the framebuffer successfully, 575 * asserting that its modeset object struct and its refcount are correctly 576 * set and that strictly one framebuffer is initialized. 577 */ 578static void drm_test_framebuffer_init(struct kunit *test) 579{ 580 struct drm_framebuffer_test_priv *priv = test->priv; 581 struct drm_device *dev = &priv->dev; 582 struct drm_format_info format = { }; 583 struct drm_framebuffer fb1 = { .dev = dev, .format = &format }; 584 struct drm_framebuffer_funcs funcs = { }; 585 int ret; 586 587 ret = drm_framebuffer_init(dev, &fb1, &funcs); 588 KUNIT_ASSERT_EQ(test, ret, 0); 589 590 /* Check if fb->funcs is actually set to the drm_framebuffer_funcs passed on */ 591 KUNIT_EXPECT_PTR_EQ(test, fb1.funcs, &funcs); 592 593 /* The fb->comm must be set to the current running process */ 594 KUNIT_EXPECT_STREQ(test, fb1.comm, current->comm); 595 596 /* The fb->base must be successfully initialized */ 597 KUNIT_EXPECT_NE(test, fb1.base.id, 0); 598 KUNIT_EXPECT_EQ(test, fb1.base.type, DRM_MODE_OBJECT_FB); 599 KUNIT_EXPECT_EQ(test, kref_read(&fb1.base.refcount), 1); 600 KUNIT_EXPECT_PTR_EQ(test, fb1.base.free_cb, &drm_framebuffer_free); 601 602 /* There must be just that one fb initialized */ 603 KUNIT_EXPECT_EQ(test, dev->mode_config.num_fb, 1); 604 KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.prev, &fb1.head); 605 KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.next, &fb1.head); 606 607 drm_framebuffer_cleanup(&fb1); 608} 609 610/* Try to init a framebuffer without setting its format */ 611static void drm_test_framebuffer_init_bad_format(struct kunit *test) 612{ 613 struct drm_framebuffer_test_priv *priv = test->priv; 614 struct drm_device *dev = &priv->dev; 615 struct drm_framebuffer fb1 = { .dev = dev, .format = NULL }; 616 struct drm_framebuffer_funcs funcs = { }; 617 int ret; 618 619 /* Fails if fb.format isn't set */ 620 ret = drm_framebuffer_init(dev, &fb1, &funcs); 621 KUNIT_EXPECT_EQ(test, ret, -EINVAL); 622} 623 624/* 625 * Test calling drm_framebuffer_init() passing a framebuffer linked to a 626 * different drm_device parent from the one passed on the first argument, which 627 * must fail. 628 */ 629static void drm_test_framebuffer_init_dev_mismatch(struct kunit *test) 630{ 631 struct drm_framebuffer_test_priv *priv = test->priv; 632 struct drm_device *right_dev = &priv->dev; 633 struct drm_device *wrong_dev; 634 struct device *wrong_dev_parent; 635 struct drm_format_info format = { }; 636 struct drm_framebuffer fb1 = { .dev = right_dev, .format = &format }; 637 struct drm_framebuffer_funcs funcs = { }; 638 int ret; 639 640 wrong_dev_parent = kunit_device_register(test, "drm-kunit-wrong-device-mock"); 641 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev_parent); 642 643 wrong_dev = __drm_kunit_helper_alloc_drm_device(test, wrong_dev_parent, 644 sizeof(struct drm_device), 645 0, 0); 646 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev); 647 648 /* Fails if fb->dev doesn't point to the drm_device passed on first arg */ 649 ret = drm_framebuffer_init(wrong_dev, &fb1, &funcs); 650 KUNIT_EXPECT_EQ(test, ret, -EINVAL); 651} 652 653static void destroy_free_mock(struct drm_framebuffer *fb) 654{ 655 struct drm_framebuffer_test_priv *priv = container_of(fb->dev, typeof(*priv), dev); 656 657 priv->buffer_freed = true; 658} 659 660static struct drm_framebuffer_funcs framebuffer_funcs_free_mock = { 661 .destroy = destroy_free_mock, 662}; 663 664/* 665 * In summary, the drm_framebuffer_free() function must implicitly call 666 * fb->funcs->destroy() and garantee that the framebufer object is unregistered 667 * from the drm_device idr pool. 668 */ 669static void drm_test_framebuffer_free(struct kunit *test) 670{ 671 struct drm_framebuffer_test_priv *priv = test->priv; 672 struct drm_device *dev = &priv->dev; 673 struct drm_mode_object *obj; 674 struct drm_framebuffer fb = { 675 .dev = dev, 676 .funcs = &framebuffer_funcs_free_mock, 677 }; 678 int id, ret; 679 680 priv->buffer_freed = false; 681 682 /* 683 * Mock a framebuffer that was not unregistered at the moment of the 684 * drm_framebuffer_free() call. 685 */ 686 ret = drm_mode_object_add(dev, &fb.base, DRM_MODE_OBJECT_FB); 687 KUNIT_ASSERT_EQ(test, ret, 0); 688 id = fb.base.id; 689 690 drm_framebuffer_free(&fb.base.refcount); 691 692 /* The framebuffer object must be unregistered */ 693 obj = drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_FB); 694 KUNIT_EXPECT_PTR_EQ(test, obj, NULL); 695 KUNIT_EXPECT_EQ(test, fb.base.id, 0); 696 697 /* Test if fb->funcs->destroy() was called */ 698 KUNIT_EXPECT_EQ(test, priv->buffer_freed, true); 699} 700 701static struct kunit_case drm_framebuffer_tests[] = { 702 KUNIT_CASE_PARAM(drm_test_framebuffer_check_src_coords, check_src_coords_gen_params), 703 KUNIT_CASE(drm_test_framebuffer_cleanup), 704 KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params), 705 KUNIT_CASE(drm_test_framebuffer_free), 706 KUNIT_CASE(drm_test_framebuffer_init), 707 KUNIT_CASE(drm_test_framebuffer_init_bad_format), 708 KUNIT_CASE(drm_test_framebuffer_init_dev_mismatch), 709 KUNIT_CASE(drm_test_framebuffer_lookup), 710 KUNIT_CASE(drm_test_framebuffer_lookup_inexistent), 711 KUNIT_CASE(drm_test_framebuffer_modifiers_not_supported), 712 { } 713}; 714 715static struct kunit_suite drm_framebuffer_test_suite = { 716 .name = "drm_framebuffer", 717 .init = drm_framebuffer_test_init, 718 .test_cases = drm_framebuffer_tests, 719}; 720 721kunit_test_suite(drm_framebuffer_test_suite); 722 723MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions"); 724MODULE_LICENSE("GPL");