]> Git Repo - linux.git/blob - drivers/gpu/drm/tests/drm_framebuffer_test.c
Merge tag 'hyperv-next-signed-20250123' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / gpu / drm / tests / drm_framebuffer_test.c
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/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
28 struct drm_framebuffer_test {
29         int buffer_created;
30         struct drm_mode_fb_cmd2 cmd;
31         const char *name;
32 };
33
34 static 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  */
358 struct drm_framebuffer_test_priv {
359         struct drm_device dev;
360         bool buffer_created;
361         bool buffer_freed;
362 };
363
364 static 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
374 static struct drm_mode_config_funcs mock_config_funcs = {
375         .fb_create = fb_create_mock,
376 };
377
378 static 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
402 static 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
413 static 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
418 KUNIT_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 */
422 static 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 */
445 struct 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
457 static const struct drm_framebuffer_check_src_coords_case
458 drm_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
476 static 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
493 static void
494 check_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
500 KUNIT_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  */
508 static 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  */
538 static 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 */
561 static 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  */
578 static 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 */
611 static 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  */
629 static 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
653 static 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
660 static 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  */
669 static 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
701 static 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
715 static 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
721 kunit_test_suite(drm_framebuffer_test_suite);
722
723 MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions");
724 MODULE_LICENSE("GPL");
This page took 0.076818 seconds and 4 git commands to generate.