1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test cases for the drm_framebuffer functions 4 * 5 * Copyright (c) 2022 Maíra Canal <[email protected]> 6 */ 7 8 #include <kunit/test.h> 9 10 #include <drm/drm_device.h> 11 #include <drm/drm_drv.h> 12 #include <drm/drm_mode.h> 13 #include <drm/drm_fourcc.h> 14 #include <drm/drm_kunit_helpers.h> 15 #include <drm/drm_print.h> 16 17 #include "../drm_crtc_internal.h" 18 19 #define MIN_WIDTH 4 20 #define MAX_WIDTH 4096 21 #define MIN_HEIGHT 4 22 #define MAX_HEIGHT 4096 23 24 #define DRM_MODE_FB_INVALID BIT(2) 25 26 struct drm_framebuffer_test { 27 int buffer_created; 28 struct drm_mode_fb_cmd2 cmd; 29 const char *name; 30 }; 31 32 static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = { 33 { .buffer_created = 1, .name = "ABGR8888 normal sizes", 34 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888, 35 .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 }, 36 } 37 }, 38 { .buffer_created = 1, .name = "ABGR8888 max sizes", 39 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 40 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 41 } 42 }, 43 { .buffer_created = 1, .name = "ABGR8888 pitch greater than min required", 44 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 45 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 }, 46 } 47 }, 48 { .buffer_created = 0, .name = "ABGR8888 pitch less than min required", 49 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 50 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 }, 51 } 52 }, 53 { .buffer_created = 0, .name = "ABGR8888 Invalid width", 54 .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 55 .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 }, 56 } 57 }, 58 { .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle", 59 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 60 .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 61 } 62 }, 63 { .buffer_created = 0, .name = "No pixel format", 64 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0, 65 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 66 } 67 }, 68 { .buffer_created = 0, .name = "ABGR8888 Width 0", 69 .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 70 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 71 } 72 }, 73 { .buffer_created = 0, .name = "ABGR8888 Height 0", 74 .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888, 75 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 76 } 77 }, 78 { .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination", 79 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 80 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, 81 .pitches = { 4 * MAX_WIDTH, 0, 0 }, 82 } 83 }, 84 { .buffer_created = 1, .name = "ABGR8888 Large buffer offset", 85 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 86 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 87 .pitches = { 4 * MAX_WIDTH, 0, 0 }, 88 } 89 }, 90 91 /* 92 * All entries in members that represents per-plane values (@modifier, @handles, 93 * @pitches and @offsets) must be zero when unused. 94 */ 95 { .buffer_created = 0, .name = "ABGR8888 Buffer offset for inexistent plane", 96 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 97 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, UINT_MAX / 2, 0 }, 98 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 99 } 100 }, 101 102 { .buffer_created = 0, .name = "ABGR8888 Invalid flag", 103 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 104 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 105 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_INVALID, 106 } 107 }, 108 { .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers", 109 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 110 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 111 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 112 } 113 }, 114 { .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier", 115 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 116 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 117 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 118 .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 }, 119 } 120 }, 121 { .buffer_created = 0, 122 .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)", 123 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 124 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 125 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 126 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 127 } 128 }, 129 { .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS", 130 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 131 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 132 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, 133 } 134 }, 135 { .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS", 136 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 137 .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 138 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, 139 } 140 }, 141 { .buffer_created = 1, .name = "NV12 Normal sizes", 142 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, 143 .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 }, 144 } 145 }, 146 { .buffer_created = 1, .name = "NV12 Max sizes", 147 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 148 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 149 } 150 }, 151 { .buffer_created = 0, .name = "NV12 Invalid pitch", 152 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 153 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 }, 154 } 155 }, 156 { .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag", 157 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 158 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 159 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 160 } 161 }, 162 { .buffer_created = 0, .name = "NV12 different modifier per-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, 0, 0 }, 166 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 167 } 168 }, 169 { .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE", 170 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 171 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 172 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 173 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, 174 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 175 } 176 }, 177 { .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS", 178 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 179 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 180 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, 181 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 182 } 183 }, 184 { .buffer_created = 0, .name = "NV12 Modifier for inexistent plane", 185 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 186 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 187 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 188 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE }, 189 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 190 } 191 }, 192 { .buffer_created = 0, .name = "NV12 Handle for inexistent plane", 193 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 194 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 195 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 196 } 197 }, 198 { .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS", 199 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, 200 .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, 201 } 202 }, 203 { .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", 204 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, 205 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 206 .pitches = { 600, 300, 300 }, 207 } 208 }, 209 { .buffer_created = 1, .name = "YVU420 Normal sizes", 210 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, 211 .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, 212 } 213 }, 214 { .buffer_created = 1, .name = "YVU420 Max sizes", 215 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 216 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), 217 DIV_ROUND_UP(MAX_WIDTH, 2) }, 218 } 219 }, 220 { .buffer_created = 0, .name = "YVU420 Invalid pitch", 221 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 222 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1, 223 DIV_ROUND_UP(MAX_WIDTH, 2) }, 224 } 225 }, 226 { .buffer_created = 1, .name = "YVU420 Different pitches", 227 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 228 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, 229 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, 230 } 231 }, 232 { .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches", 233 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 234 .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH + 235 MAX_WIDTH * MAX_HEIGHT, MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT }, 236 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, 237 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, 238 } 239 }, 240 { .buffer_created = 0, 241 .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS", 242 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 243 .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, 244 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 245 } 246 }, 247 { .buffer_created = 0, 248 .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS", 249 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 250 .handles = { 1, 1, 1 }, 251 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 252 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 253 } 254 }, 255 { .buffer_created = 0, 256 .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS", 257 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 258 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 259 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 260 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 261 } 262 }, 263 { .buffer_created = 1, .name = "YVU420 Valid modifier", 264 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 265 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 266 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 267 AFBC_FORMAT_MOD_SPARSE }, 268 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 269 } 270 }, 271 { .buffer_created = 0, .name = "YVU420 Different modifiers per plane", 272 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 273 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 274 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR, 275 AFBC_FORMAT_MOD_SPARSE }, 276 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 277 } 278 }, 279 { .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane", 280 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 281 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 282 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 283 AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE }, 284 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 285 } 286 }, 287 { .buffer_created = 0, .name = "YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)", 288 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YUV420_10BIT, 289 .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 290 .modifier = { DRM_FORMAT_MOD_LINEAR, 0, 0 }, 291 .pitches = { MAX_WIDTH, 0, 0 }, 292 } 293 }, 294 { .buffer_created = 1, .name = "X0L2 Normal sizes", 295 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2, 296 .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 } 297 } 298 }, 299 { .buffer_created = 1, .name = "X0L2 Max sizes", 300 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 301 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 } 302 } 303 }, 304 { .buffer_created = 0, .name = "X0L2 Invalid pitch", 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 } 308 }, 309 { .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required", 310 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 311 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 312 } 313 }, 314 { .buffer_created = 0, .name = "X0L2 Handle for inexistent plane", 315 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 316 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 317 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 318 } 319 }, 320 { .buffer_created = 1, 321 .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set", 322 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 323 .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 }, 324 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 325 } 326 }, 327 { .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set", 328 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 329 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 330 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, 331 } 332 }, 333 { .buffer_created = 1, .name = "X0L2 Valid modifier", 334 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 335 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 336 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 337 } 338 }, 339 { .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane", 340 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, 341 .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 }, 342 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 343 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 344 .flags = DRM_MODE_FB_MODIFIERS, 345 } 346 }, 347 }; 348 349 /* 350 * This struct is intended to provide a way to mocked functions communicate 351 * with the outer test when it can't be achieved by using its return value. In 352 * this way, the functions that receive the mocked drm_device, for example, can 353 * grab a reference to this and actually return something to be used on some 354 * expectation. 355 */ 356 struct drm_framebuffer_test_priv { 357 struct drm_device dev; 358 bool buffer_created; 359 }; 360 361 static struct drm_framebuffer *fb_create_mock(struct drm_device *dev, 362 struct drm_file *file_priv, 363 const struct drm_mode_fb_cmd2 *mode_cmd) 364 { 365 struct drm_framebuffer_test_priv *priv = container_of(dev, typeof(*priv), dev); 366 367 priv->buffer_created = true; 368 return ERR_PTR(-EINVAL); 369 } 370 371 static struct drm_mode_config_funcs mock_config_funcs = { 372 .fb_create = fb_create_mock, 373 }; 374 375 static int drm_framebuffer_test_init(struct kunit *test) 376 { 377 struct device *parent; 378 struct drm_framebuffer_test_priv *priv; 379 struct drm_device *dev; 380 381 parent = drm_kunit_helper_alloc_device(test); 382 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent); 383 384 priv = drm_kunit_helper_alloc_drm_device(test, parent, typeof(*priv), 385 dev, 0); 386 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); 387 dev = &priv->dev; 388 389 dev->mode_config.min_width = MIN_WIDTH; 390 dev->mode_config.max_width = MAX_WIDTH; 391 dev->mode_config.min_height = MIN_HEIGHT; 392 dev->mode_config.max_height = MAX_HEIGHT; 393 dev->mode_config.funcs = &mock_config_funcs; 394 395 test->priv = priv; 396 return 0; 397 } 398 399 static void drm_test_framebuffer_create(struct kunit *test) 400 { 401 const struct drm_framebuffer_test *params = test->param_value; 402 struct drm_framebuffer_test_priv *priv = test->priv; 403 struct drm_device *dev = &priv->dev; 404 405 priv->buffer_created = false; 406 drm_internal_framebuffer_create(dev, ¶ms->cmd, NULL); 407 KUNIT_EXPECT_EQ(test, params->buffer_created, priv->buffer_created); 408 } 409 410 static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc) 411 { 412 strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); 413 } 414 415 KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases, 416 drm_framebuffer_test_to_desc); 417 418 /* Tries to create a framebuffer with modifiers without drm_device supporting it */ 419 static void drm_test_framebuffer_modifiers_not_supported(struct kunit *test) 420 { 421 struct drm_framebuffer_test_priv *priv = test->priv; 422 struct drm_device *dev = &priv->dev; 423 struct drm_framebuffer *fb; 424 425 /* A valid cmd with modifier */ 426 struct drm_mode_fb_cmd2 cmd = { 427 .width = MAX_WIDTH, .height = MAX_HEIGHT, 428 .pixel_format = DRM_FORMAT_ABGR8888, .handles = { 1, 0, 0 }, 429 .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 430 .flags = DRM_MODE_FB_MODIFIERS, 431 }; 432 433 priv->buffer_created = false; 434 dev->mode_config.fb_modifiers_not_supported = 1; 435 436 fb = drm_internal_framebuffer_create(dev, &cmd, NULL); 437 KUNIT_EXPECT_EQ(test, priv->buffer_created, false); 438 KUNIT_EXPECT_EQ(test, PTR_ERR(fb), -EINVAL); 439 } 440 441 static struct kunit_case drm_framebuffer_tests[] = { 442 KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params), 443 KUNIT_CASE(drm_test_framebuffer_modifiers_not_supported), 444 { } 445 }; 446 447 static struct kunit_suite drm_framebuffer_test_suite = { 448 .name = "drm_framebuffer", 449 .init = drm_framebuffer_test_init, 450 .test_cases = drm_framebuffer_tests, 451 }; 452 453 kunit_test_suite(drm_framebuffer_test_suite); 454 455 MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions"); 456 MODULE_LICENSE("GPL"); 457