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