1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Test cases for the drm_framebuffer functions 4 */ 5 6#include <linux/kernel.h> 7 8#include <drm/drm_device.h> 9#include <drm/drm_mode.h> 10#include <drm/drm_fourcc.h> 11 12#include "../drm_crtc_internal.h" 13 14#include "test-drm_modeset_common.h" 15 16#define MIN_WIDTH 4 17#define MAX_WIDTH 4096 18#define MIN_HEIGHT 4 19#define MAX_HEIGHT 4096 20 21struct drm_framebuffer_test { 22 int buffer_created; 23 struct drm_mode_fb_cmd2 cmd; 24 const char *name; 25}; 26 27static struct drm_framebuffer_test createbuffer_tests[] = { 28{ .buffer_created = 1, .name = "ABGR8888 normal sizes", 29 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888, 30 .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 }, 31 } 32}, 33{ .buffer_created = 1, .name = "ABGR8888 max sizes", 34 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 35 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 36 } 37}, 38{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required", 39 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 40 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 }, 41 } 42}, 43{ .buffer_created = 0, .name = "ABGR8888 pitch less 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 Invalid width", 49 .cmd = { .width = MAX_WIDTH + 1, .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 buffer handle", 54 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 55 .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 56 } 57}, 58{ .buffer_created = 0, .name = "No pixel format", 59 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0, 60 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 61 } 62}, 63{ .buffer_created = 0, .name = "ABGR8888 Width 0", 64 .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 65 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 66 } 67}, 68{ .buffer_created = 0, .name = "ABGR8888 Height 0", 69 .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888, 70 .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 71 } 72}, 73{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination", 74 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 75 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 76 } 77}, 78{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset", 79 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 80 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 81 } 82}, 83{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers", 84 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 85 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 86 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 87 } 88}, 89{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier", 90 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 91 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, 92 .flags = DRM_MODE_FB_MODIFIERS, .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 }, 93 } 94}, 95{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)", 96 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 97 .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, 98 .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 99 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 100 } 101}, 102{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS", 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, 4 * MAX_WIDTH, 0 }, 106 } 107}, 108{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS", 109 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, 110 .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 111 .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, 112 } 113}, 114{ .buffer_created = 1, .name = "NV12 Normal sizes", 115 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, 116 .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 }, 117 } 118}, 119{ .buffer_created = 1, .name = "NV12 Max sizes", 120 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 121 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 122 } 123}, 124{ .buffer_created = 0, .name = "NV12 Invalid pitch", 125 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 126 .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 }, 127 } 128}, 129{ .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag", 130 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 131 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 132 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 133 } 134}, 135{ .buffer_created = 0, .name = "NV12 different modifier per-plane", 136 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 137 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 138 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, 139 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 140 } 141}, 142{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE", 143 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 144 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 145 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, 146 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 147 } 148}, 149{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS", 150 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 151 .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 152 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, 153 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 154 } 155}, 156{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane", 157 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 158 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 159 .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 160 DRM_FORMAT_MOD_SAMSUNG_64_32_TILE }, 161 .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 162 } 163}, 164{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane", 165 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, 166 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, 167 } 168}, 169{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS", 170 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, 171 .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, 172 } 173}, 174{ .buffer_created = 1, .name = "YVU420 Normal sizes", 175 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, 176 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 177 .pitches = { 600, 300, 300 }, 178 } 179}, 180{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", 181 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, 182 .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, 183 } 184}, 185{ .buffer_created = 1, .name = "YVU420 Max sizes", 186 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 187 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), 188 DIV_ROUND_UP(MAX_WIDTH, 2) }, 189 } 190}, 191{ .buffer_created = 0, .name = "YVU420 Invalid pitch", 192 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 193 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1, 194 DIV_ROUND_UP(MAX_WIDTH, 2) }, 195 } 196}, 197{ .buffer_created = 1, .name = "YVU420 Different pitches", 198 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 199 .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, 200 DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, 201 } 202}, 203{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches", 204 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 205 .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH + MAX_WIDTH * MAX_HEIGHT, 206 MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT }, 207 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, 208 } 209}, 210{ .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS", 211 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 212 .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, 213 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 214 } 215}, 216{ .buffer_created = 0, .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS", 217 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 218 .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 219 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 220 } 221}, 222{ .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS", 223 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 224 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 225 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 226 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 227 } 228}, 229{ .buffer_created = 1, .name = "YVU420 Valid modifier", 230 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 231 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 232 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE }, 233 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 234 } 235}, 236{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane", 237 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 238 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 239 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR, 240 AFBC_FORMAT_MOD_SPARSE }, 241 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 242 } 243}, 244{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane", 245 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, 246 .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, 247 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 248 AFBC_FORMAT_MOD_SPARSE }, 249 .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, 250 } 251}, 252{ .buffer_created = 1, .name = "X0L2 Normal sizes", 253 .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2, 254 .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 } 255 } 256}, 257{ .buffer_created = 1, .name = "X0L2 Max sizes", 258 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 259 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 } 260 } 261}, 262{ .buffer_created = 0, .name = "X0L2 Invalid pitch", 263 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 264 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 } 265 } 266}, 267{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required", 268 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 269 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 270 } 271}, 272{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane", 273 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 274 .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 275 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 276 } 277}, 278{ .buffer_created = 1, .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set", 279 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 280 .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 }, 281 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } 282 } 283}, 284{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set", 285 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 286 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 287 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, 288 } 289}, 290{ .buffer_created = 1, .name = "X0L2 Valid modifier", 291 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, 292 .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 293 .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, 294 } 295}, 296{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane", 297 .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, 298 .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 }, 299 .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, 300 .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, 301 .flags = DRM_MODE_FB_MODIFIERS, 302 } 303}, 304}; 305 306static struct drm_framebuffer *fb_create_mock(struct drm_device *dev, 307 struct drm_file *file_priv, 308 const struct drm_mode_fb_cmd2 *mode_cmd) 309{ 310 int *buffer_created = dev->dev_private; 311 *buffer_created = 1; 312 return ERR_PTR(-EINVAL); 313} 314 315static struct drm_mode_config_funcs mock_config_funcs = { 316 .fb_create = fb_create_mock, 317}; 318 319static struct drm_device mock_drm_device = { 320 .mode_config = { 321 .min_width = MIN_WIDTH, 322 .max_width = MAX_WIDTH, 323 .min_height = MIN_HEIGHT, 324 .max_height = MAX_HEIGHT, 325 .allow_fb_modifiers = true, 326 .funcs = &mock_config_funcs, 327 }, 328}; 329 330static int execute_drm_mode_fb_cmd2(struct drm_mode_fb_cmd2 *r) 331{ 332 int buffer_created = 0; 333 struct drm_framebuffer *fb; 334 335 mock_drm_device.dev_private = &buffer_created; 336 fb = drm_internal_framebuffer_create(&mock_drm_device, r, NULL); 337 return buffer_created; 338} 339 340int igt_check_drm_framebuffer_create(void *ignored) 341{ 342 int i = 0; 343 344 for (i = 0; i < ARRAY_SIZE(createbuffer_tests); i++) { 345 FAIL(createbuffer_tests[i].buffer_created != 346 execute_drm_mode_fb_cmd2(&createbuffer_tests[i].cmd), 347 "Test %d: \"%s\" failed\n", i, createbuffer_tests[i].name); 348 } 349 350 return 0; 351} 352