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