xref: /aosp_15_r20/external/igt-gpu-tools/tests/kms_universal_plane.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2014 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "igt.h"
25 #include <errno.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <fcntl.h>
30 
31 
32 typedef struct {
33 	int drm_fd;
34 	igt_display_t display;
35 	int gen;
36 } data_t;
37 
38 typedef struct {
39 	data_t *data;
40 	igt_pipe_crc_t *pipe_crc;
41 	igt_crc_t crc_1, crc_2, crc_3, crc_4, crc_5, crc_6, crc_7, crc_8,
42 		  crc_9, crc_10;
43 	struct igt_fb red_fb, blue_fb, black_fb, yellow_fb;
44 	drmModeModeInfo *mode;
45 } functional_test_t;
46 
47 typedef struct {
48 	data_t *data;
49 	drmModeResPtr moderes;
50 	struct igt_fb blue_fb, oversized_fb, undersized_fb;
51 } sanity_test_t;
52 
53 typedef struct {
54 	data_t *data;
55 	struct igt_fb red_fb, blue_fb;
56 } pageflip_test_t;
57 
58 typedef struct {
59 	data_t *data;
60 	int x, y;
61 	int w, h;
62 	struct igt_fb biggreen_fb, smallred_fb, smallblue_fb;
63 } gen9_test_t;
64 
65 static void
functional_test_init(functional_test_t * test,igt_output_t * output,enum pipe pipe)66 functional_test_init(functional_test_t *test, igt_output_t *output, enum pipe pipe)
67 {
68 	data_t *data = test->data;
69 	drmModeModeInfo *mode;
70 
71 	test->pipe_crc = igt_pipe_crc_new(data->drm_fd, pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
72 
73 	igt_output_set_pipe(output, pipe);
74 
75 	mode = igt_output_get_mode(output);
76 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
77 				DRM_FORMAT_XRGB8888,
78 				LOCAL_DRM_FORMAT_MOD_NONE,
79 				0.0, 0.0, 0.0,
80 				&test->black_fb);
81 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
82 				DRM_FORMAT_XRGB8888,
83 				LOCAL_DRM_FORMAT_MOD_NONE,
84 				0.0, 0.0, 1.0,
85 				&test->blue_fb);
86 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
87 				DRM_FORMAT_XRGB8888,
88 				LOCAL_DRM_FORMAT_MOD_NONE,
89 				1.0, 1.0, 0.0,
90 				&test->yellow_fb);
91 	igt_create_color_fb(data->drm_fd, 100, 100,
92 				DRM_FORMAT_XRGB8888,
93 				LOCAL_DRM_FORMAT_MOD_NONE,
94 				1.0, 0.0, 0.0,
95 				&test->red_fb);
96 
97 	test->mode = mode;
98 }
99 
100 static void
functional_test_fini(functional_test_t * test,igt_output_t * output)101 functional_test_fini(functional_test_t *test, igt_output_t *output)
102 {
103 	igt_pipe_crc_free(test->pipe_crc);
104 
105 	igt_remove_fb(test->data->drm_fd, &test->black_fb);
106 	igt_remove_fb(test->data->drm_fd, &test->blue_fb);
107 	igt_remove_fb(test->data->drm_fd, &test->red_fb);
108 	igt_remove_fb(test->data->drm_fd, &test->yellow_fb);
109 
110 	igt_output_set_pipe(output, PIPE_ANY);
111 	igt_display_commit2(&test->data->display, COMMIT_LEGACY);
112 }
113 
114 /*
115  * Universal plane functional testing.
116  *   - Black primary plane via traditional interfaces, red sprite, grab CRC:1.
117  *   - Blue primary plane via traditional interfaces, red sprite, grab CRC:2.
118  *   - Yellow primary via traditional interfaces
119  *   - Blue primary plane, red sprite via universal planes, grab CRC:3 and compare
120  *     with CRC:2 (should be the same)
121  *   - Disable primary plane, grab CRC:4 (should be same as CRC:1)
122  *   - Reenable primary, grab CRC:5 (should be same as CRC:2 and CRC:3)
123  *   - Yellow primary, no sprite
124  *   - Disable CRTC
125  *   - Program red sprite (while CRTC off)
126  *   - Program blue primary (while CRTC off)
127  *   - Enable CRTC, grab CRC:6 (should be same as CRC:2)
128  */
129 static void
functional_test_pipe(data_t * data,enum pipe pipe,igt_output_t * output)130 functional_test_pipe(data_t *data, enum pipe pipe, igt_output_t *output)
131 {
132 	functional_test_t test = { .data = data };
133 	igt_display_t *display = &data->display;
134 	igt_plane_t *primary, *sprite;
135 	int num_primary = 0, num_cursor = 0;
136 	int i;
137 
138 	igt_skip_on(pipe >= display->n_pipes);
139 
140 	igt_info("Testing connector %s using pipe %s\n", igt_output_name(output),
141 		 kmstest_pipe_name(pipe));
142 
143 	functional_test_init(&test, output, pipe);
144 
145 	/*
146 	 * Make sure we have no more than one primary or cursor plane per crtc.
147 	 * If the kernel accidentally calls drm_plane_init() rather than
148 	 * drm_universal_plane_init(), the type enum can get interpreted as a
149 	 * boolean and show up in userspace as the wrong type.
150 	 */
151 	for (i = 0; i < display->pipes[pipe].n_planes; i++)
152 		if (display->pipes[pipe].planes[i].type == DRM_PLANE_TYPE_PRIMARY)
153 			num_primary++;
154 		else if (display->pipes[pipe].planes[i].type == DRM_PLANE_TYPE_CURSOR)
155 			num_cursor++;
156 
157 	igt_assert_eq(num_primary, 1);
158 	igt_assert_lte(num_cursor, 1);
159 
160 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
161 	sprite = igt_output_get_plane_type(output, DRM_PLANE_TYPE_OVERLAY);
162 	if (!sprite) {
163 		functional_test_fini(&test, output);
164 		igt_skip("No sprite plane available\n");
165 	}
166 
167 	igt_plane_set_position(sprite, 100, 100);
168 
169 	/* Step 1: Legacy API's, black primary, red sprite (CRC 1) */
170 	igt_plane_set_fb(primary, &test.black_fb);
171 	igt_plane_set_fb(sprite, &test.red_fb);
172 	igt_display_commit2(display, COMMIT_LEGACY);
173 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_1);
174 
175 	/* Step 2: Legacy API', blue primary, red sprite (CRC 2) */
176 	igt_plane_set_fb(primary, &test.blue_fb);
177 	igt_plane_set_fb(sprite, &test.red_fb);
178 	igt_display_commit2(display, COMMIT_LEGACY);
179 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_2);
180 
181 	/* Step 3: Legacy API's, yellow primary (CRC 3) */
182 	igt_plane_set_fb(primary, &test.yellow_fb);
183 	igt_display_commit2(display, COMMIT_LEGACY);
184 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_3);
185 
186 	/* Step 4: Universal API's, blue primary, red sprite (CRC 4) */
187 	igt_plane_set_fb(primary, &test.blue_fb);
188 	igt_plane_set_fb(sprite, &test.red_fb);
189 	igt_display_commit2(display, COMMIT_UNIVERSAL);
190 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_4);
191 
192 	/* Step 5: Universal API's, disable primary plane (CRC 5) */
193 	igt_plane_set_fb(primary, NULL);
194 	igt_display_commit2(display, COMMIT_UNIVERSAL);
195 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_5);
196 
197 	/* Step 6: Universal API's, re-enable primary with blue (CRC 6) */
198 	igt_plane_set_fb(primary, &test.blue_fb);
199 	igt_display_commit2(display, COMMIT_UNIVERSAL);
200 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_6);
201 
202 	/* Step 7: Legacy API's, yellow primary, no sprite */
203 	igt_plane_set_fb(primary, &test.yellow_fb);
204 	igt_plane_set_fb(sprite, NULL);
205 	igt_display_commit2(display, COMMIT_LEGACY);
206 
207 	/* Step 8: Disable CRTC */
208 	igt_plane_set_fb(primary, NULL);
209 	igt_display_commit2(display, COMMIT_LEGACY);
210 
211 	/* Step 9: Universal API's with crtc off:
212 	 *  - red sprite
213 	 *  - multiple primary fb's, ending in blue
214 	 */
215 	igt_plane_set_fb(sprite, &test.red_fb);
216 	igt_display_commit2(display, COMMIT_UNIVERSAL);
217 	igt_plane_set_fb(primary, &test.yellow_fb);
218 	igt_display_commit2(display, COMMIT_UNIVERSAL);
219 	igt_plane_set_fb(primary, &test.black_fb);
220 	igt_display_commit2(display, COMMIT_UNIVERSAL);
221 	igt_plane_set_fb(primary, &test.blue_fb);
222 	igt_display_commit2(display, COMMIT_UNIVERSAL);
223 
224 	/* Step 10: Enable crtc (fb = -1), take CRC (CRC 7) */
225 	igt_assert(drmModeSetCrtc(data->drm_fd, output->config.crtc->crtc_id, -1,
226 				  0, 0, &output->config.connector->connector_id,
227 				  1, test.mode) == 0);
228 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_7);
229 
230 	/* Step 11: Disable primary plane */
231 	igt_plane_set_fb(primary, NULL);
232 	igt_display_commit2(display, COMMIT_UNIVERSAL);
233 
234 	/* Step 12: Legacy modeset to yellow FB (CRC 8) */
235 	igt_plane_set_fb(primary, &test.yellow_fb);
236 	igt_display_commit2(display, COMMIT_LEGACY);
237 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_8);
238 
239 	/* Step 13: Legacy API', blue primary, red sprite */
240 	igt_plane_set_fb(primary, &test.blue_fb);
241 	igt_plane_set_fb(sprite, &test.red_fb);
242 	igt_display_commit2(display, COMMIT_LEGACY);
243 
244 	/* Step 14: Universal API, set primary completely offscreen (CRC 9) */
245 	igt_assert(drmModeSetPlane(data->drm_fd, primary->drm_plane->plane_id,
246 				   output->config.crtc->crtc_id,
247 				   test.blue_fb.fb_id, 0,
248 				   9000, 9000,
249 				   test.mode->hdisplay,
250 				   test.mode->vdisplay,
251 				   IGT_FIXED(0,0), IGT_FIXED(0,0),
252 				   IGT_FIXED(test.mode->hdisplay,0),
253 				   IGT_FIXED(test.mode->vdisplay,0)) == 0);
254 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_9);
255 
256 	/*
257 	 * Step 15: Explicitly disable primary after it's already been
258 	 * implicitly disabled (CRC 10).
259 	 */
260 	igt_plane_set_fb(primary, NULL);
261 	igt_display_commit2(display, COMMIT_UNIVERSAL);
262 	igt_pipe_crc_collect_crc(test.pipe_crc, &test.crc_10);
263 
264 	/* Step 16: Legacy API's, blue primary, red sprite */
265 	igt_plane_set_fb(primary, &test.blue_fb);
266 	igt_plane_set_fb(sprite, &test.red_fb);
267 	igt_display_commit2(display, COMMIT_LEGACY);
268 
269 	/* Blue bg + red sprite should be same under both types of API's */
270 	igt_assert_crc_equal(&test.crc_2, &test.crc_4);
271 
272 	/* Disabling primary plane should be same as black primary */
273 	igt_assert_crc_equal(&test.crc_1, &test.crc_5);
274 
275 	/* Re-enabling primary should return to blue properly */
276 	igt_assert_crc_equal(&test.crc_2, &test.crc_6);
277 
278 	/*
279 	 * We should be able to setup plane FB's while CRTC is disabled and
280 	 * then have them pop up correctly when the CRTC is re-enabled.
281 	 */
282 	igt_assert_crc_equal(&test.crc_2, &test.crc_7);
283 
284 	/*
285 	 * We should be able to modeset with the primary plane off
286 	 * successfully
287 	 */
288 	igt_assert_crc_equal(&test.crc_3, &test.crc_8);
289 
290 	/*
291 	 * We should be able to move the primary plane completely offscreen
292 	 * and have it disable successfully.
293 	 */
294 	igt_assert_crc_equal(&test.crc_5, &test.crc_9);
295 
296 	/*
297 	 * We should be able to explicitly disable an already
298 	 * implicitly-disabled primary plane
299 	 */
300 	igt_assert_crc_equal(&test.crc_5, &test.crc_10);
301 
302 	igt_plane_set_fb(primary, NULL);
303 	igt_plane_set_fb(sprite, NULL);
304 
305 	functional_test_fini(&test, output);
306 }
307 
308 static void
sanity_test_init(sanity_test_t * test,igt_output_t * output,enum pipe pipe)309 sanity_test_init(sanity_test_t *test, igt_output_t *output, enum pipe pipe)
310 {
311 	data_t *data = test->data;
312 	drmModeModeInfo *mode;
313 
314 	igt_output_set_pipe(output, pipe);
315 
316 	mode = igt_output_get_mode(output);
317 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
318 			    DRM_FORMAT_XRGB8888,
319 			    LOCAL_DRM_FORMAT_MOD_NONE,
320 			    0.0, 0.0, 1.0,
321 			    &test->blue_fb);
322 	igt_create_color_fb(data->drm_fd,
323 			    mode->hdisplay + 100, mode->vdisplay + 100,
324 			    DRM_FORMAT_XRGB8888,
325 			    LOCAL_DRM_FORMAT_MOD_NONE,
326 			    0.0, 0.0, 1.0,
327 			    &test->oversized_fb);
328 	igt_create_color_fb(data->drm_fd,
329 			    mode->hdisplay - 100, mode->vdisplay - 100,
330 			    DRM_FORMAT_XRGB8888,
331 			    LOCAL_DRM_FORMAT_MOD_NONE,
332 			    0.0, 0.0, 1.0,
333 			    &test->undersized_fb);
334 
335 	test->moderes = drmModeGetResources(data->drm_fd);
336 	igt_assert(test->moderes);
337 }
338 
339 static void
sanity_test_fini(sanity_test_t * test,igt_output_t * output)340 sanity_test_fini(sanity_test_t *test, igt_output_t *output)
341 {
342 	drmModeFreeResources(test->moderes);
343 
344 	igt_remove_fb(test->data->drm_fd, &test->oversized_fb);
345 	igt_remove_fb(test->data->drm_fd, &test->undersized_fb);
346 	igt_remove_fb(test->data->drm_fd, &test->blue_fb);
347 
348 	igt_output_set_pipe(output, PIPE_ANY);
349 	igt_display_commit2(&test->data->display, COMMIT_LEGACY);
350 }
351 
352 /*
353  * Universal plane sanity testing.
354  *   - Primary doesn't cover CRTC
355  *   - Primary plane tries to scale down
356  *   - Primary plane tries to scale up
357  */
358 static void
sanity_test_pipe(data_t * data,enum pipe pipe,igt_output_t * output)359 sanity_test_pipe(data_t *data, enum pipe pipe, igt_output_t *output)
360 {
361 	sanity_test_t test = { .data = data };
362 	igt_plane_t *primary;
363 	drmModeModeInfo *mode;
364 	int i;
365 	int expect;
366 
367 	igt_skip_on(pipe >= data->display.n_pipes);
368 
369 	igt_output_set_pipe(output, pipe);
370 	mode = igt_output_get_mode(output);
371 
372 	sanity_test_init(&test, output, pipe);
373 
374 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
375 
376 	/* Use legacy API to set a mode with a blue FB */
377 	igt_plane_set_fb(primary, &test.blue_fb);
378 	igt_display_commit2(&data->display, COMMIT_LEGACY);
379 
380 	/*
381 	 * Try to use universal plane API to set primary plane that
382 	 * doesn't cover CRTC (should fail on pre-gen9 and succeed on
383 	 * gen9+).
384 	 */
385 	igt_plane_set_fb(primary, &test.undersized_fb);
386 	expect = (data->gen < 9) ? -EINVAL : 0;
387 	igt_assert(igt_display_try_commit2(&data->display, COMMIT_UNIVERSAL) == expect);
388 
389 	/* Same as above, but different plane positioning. */
390 	igt_plane_set_position(primary, 100, 100);
391 	igt_assert(igt_display_try_commit2(&data->display, COMMIT_UNIVERSAL) == expect);
392 
393 	igt_plane_set_position(primary, 0, 0);
394 
395 	/* Try to use universal plane API to scale down (should fail on pre-gen9) */
396 	expect = (data->gen < 9) ? -ERANGE : 0;
397 	igt_assert(drmModeSetPlane(data->drm_fd, primary->drm_plane->plane_id,
398 				   output->config.crtc->crtc_id,
399 				   test.oversized_fb.fb_id, 0,
400 				   0, 0,
401 				   mode->hdisplay + 100,
402 				   mode->vdisplay + 100,
403 				   IGT_FIXED(0,0), IGT_FIXED(0,0),
404 				   IGT_FIXED(mode->hdisplay,0),
405 				   IGT_FIXED(mode->vdisplay,0)) == expect);
406 
407 	/* Try to use universal plane API to scale up (should fail on pre-gen9) */
408 	igt_assert(drmModeSetPlane(data->drm_fd, primary->drm_plane->plane_id,
409 				   output->config.crtc->crtc_id,
410 				   test.oversized_fb.fb_id, 0,
411 				   0, 0,
412 				   mode->hdisplay,
413 				   mode->vdisplay,
414 				   IGT_FIXED(0,0), IGT_FIXED(0,0),
415 				   IGT_FIXED(mode->hdisplay - 100,0),
416 				   IGT_FIXED(mode->vdisplay - 100,0)) == expect);
417 
418 	/* Find other crtcs and try to program our primary plane on them */
419 	for (i = 0; i < test.moderes->count_crtcs; i++)
420 		if (test.moderes->crtcs[i] != output->config.crtc->crtc_id) {
421 			igt_assert(drmModeSetPlane(data->drm_fd,
422 						   primary->drm_plane->plane_id,
423 						   test.moderes->crtcs[i],
424 						   test.blue_fb.fb_id, 0,
425 						   0, 0,
426 						   mode->hdisplay,
427 						   mode->vdisplay,
428 						   IGT_FIXED(0,0), IGT_FIXED(0,0),
429 						   IGT_FIXED(mode->hdisplay,0),
430 						   IGT_FIXED(mode->vdisplay,0)) == -EINVAL);
431 		}
432 
433 	igt_plane_set_fb(primary, NULL);
434 	sanity_test_fini(&test, output);
435 }
436 
437 static void
pageflip_test_init(pageflip_test_t * test,igt_output_t * output,enum pipe pipe)438 pageflip_test_init(pageflip_test_t *test, igt_output_t *output, enum pipe pipe)
439 {
440 	data_t *data = test->data;
441 	drmModeModeInfo *mode;
442 
443 	igt_output_set_pipe(output, pipe);
444 
445 	mode = igt_output_get_mode(output);
446 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
447 			    DRM_FORMAT_XRGB8888,
448 			    LOCAL_DRM_FORMAT_MOD_NONE,
449 			    1.0, 0.0, 0.0,
450 			    &test->red_fb);
451 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
452 			    DRM_FORMAT_XRGB8888,
453 			    LOCAL_DRM_FORMAT_MOD_NONE,
454 			    0.0, 0.0, 1.0,
455 			    &test->blue_fb);
456 }
457 
458 static void
pageflip_test_fini(pageflip_test_t * test,igt_output_t * output)459 pageflip_test_fini(pageflip_test_t *test, igt_output_t *output)
460 {
461 	igt_remove_fb(test->data->drm_fd, &test->red_fb);
462 	igt_remove_fb(test->data->drm_fd, &test->blue_fb);
463 
464 	igt_output_set_pipe(output, PIPE_ANY);
465 	igt_display_commit2(&test->data->display, COMMIT_LEGACY);
466 }
467 
468 static void
pageflip_test_pipe(data_t * data,enum pipe pipe,igt_output_t * output)469 pageflip_test_pipe(data_t *data, enum pipe pipe, igt_output_t *output)
470 {
471 	pageflip_test_t test = { .data = data };
472 	igt_plane_t *primary;
473 	struct timeval timeout = { .tv_sec = 0, .tv_usec = 500 };
474 	drmEventContext evctx = { .version = 2 };
475 
476 	fd_set fds;
477 	int ret = 0;
478 
479 	igt_skip_on(pipe >= data->display.n_pipes);
480 
481 	igt_output_set_pipe(output, pipe);
482 
483 	pageflip_test_init(&test, output, pipe);
484 
485 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
486 
487 	/* Use legacy API to set a mode with a blue FB */
488 	igt_plane_set_fb(primary, &test.blue_fb);
489 	igt_display_commit2(&data->display, COMMIT_LEGACY);
490 
491 	/* Disable the primary plane */
492 	igt_plane_set_fb(primary, NULL);
493 	igt_display_commit2(&data->display, COMMIT_UNIVERSAL);
494 
495 	/*
496 	 * Issue a pageflip to red FB
497 	 *
498 	 * Note that crtc->primary->fb = NULL causes flip to return EBUSY for
499 	 * historical reasons...
500 	 */
501 	igt_assert(drmModePageFlip(data->drm_fd, output->config.crtc->crtc_id,
502 				   test.red_fb.fb_id, 0, NULL) == -EBUSY);
503 
504 	/* Turn primary plane back on */
505 	igt_plane_set_fb(primary, &test.blue_fb);
506 	igt_display_commit2(&data->display, COMMIT_UNIVERSAL);
507 
508 	/*
509 	 * Issue a pageflip to red, then immediately try to disable the primary
510 	 * plane, hopefully before the pageflip has a chance to complete.  The
511 	 * plane disable operation should wind up blocking while the pageflip
512 	 * completes, which we don't have a good way to specifically test for,
513 	 * but at least we can make sure that nothing blows up.
514 	 */
515 	igt_assert(drmModePageFlip(data->drm_fd, output->config.crtc->crtc_id,
516 				   test.red_fb.fb_id, DRM_MODE_PAGE_FLIP_EVENT,
517 				   &test) == 0);
518 	igt_plane_set_fb(primary, NULL);
519 	igt_display_commit2(&data->display, COMMIT_UNIVERSAL);
520 
521 	/* Wait for pageflip completion, then consume event on fd */
522 	FD_ZERO(&fds);
523 	FD_SET(data->drm_fd, &fds);
524 	do {
525 		ret = select(data->drm_fd + 1, &fds, NULL, NULL, &timeout);
526 	} while (ret < 0 && errno == EINTR);
527 	igt_assert_eq(ret, 1);
528 	igt_assert(drmHandleEvent(data->drm_fd, &evctx) == 0);
529 
530 	igt_plane_set_fb(primary, NULL);
531 	pageflip_test_fini(&test, output);
532 }
533 
534 static void
cursor_leak_test_fini(data_t * data,igt_output_t * output,struct igt_fb * bg,struct igt_fb * curs)535 cursor_leak_test_fini(data_t *data,
536 		      igt_output_t *output,
537 		      struct igt_fb *bg,
538 		      struct igt_fb *curs)
539 {
540 	int i;
541 
542 	igt_remove_fb(data->drm_fd, bg);
543 	for (i = 0; i < 10; i++)
544 		igt_remove_fb(data->drm_fd, &curs[i]);
545 
546 	igt_output_set_pipe(output, PIPE_ANY);
547 }
548 
549 static int
i915_gem_fb_count(data_t * data)550 i915_gem_fb_count(data_t *data)
551 {
552 	char buf[1024];
553 	FILE *fp;
554 	int fd;
555 	int count = 0;
556 
557 	fd = igt_debugfs_open(data->drm_fd, "i915_gem_framebuffer", O_RDONLY);
558 	fp = fdopen(fd, "r");
559 	igt_require(fp);
560 	while (fgets(buf, sizeof(buf), fp) != NULL)
561 		count++;
562 	fclose(fp);
563 	close(fd);
564 
565 	return count;
566 }
567 
568 static void
cursor_leak_test_pipe(data_t * data,enum pipe pipe,igt_output_t * output)569 cursor_leak_test_pipe(data_t *data, enum pipe pipe, igt_output_t *output)
570 {
571 	igt_display_t *display = &data->display;
572 	igt_plane_t *primary, *cursor;
573 	drmModeModeInfo *mode;
574 	struct igt_fb background_fb;
575 	struct igt_fb cursor_fb[10];
576 	int i;
577 	int r, g, b;
578 	int count1, count2;
579 
580 	igt_skip_on(pipe >= display->n_pipes);
581 	igt_require(display->has_cursor_plane);
582 
583 	igt_output_set_pipe(output, pipe);
584 	mode = igt_output_get_mode(output);
585 
586 	/* Count GEM framebuffers before creating our cursor FB's */
587 	count1 = i915_gem_fb_count(data);
588 
589 	/* Black background FB */
590 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
591 			    DRM_FORMAT_XRGB8888,
592 			    false,
593 			    0.0, 0.0, 0.0,
594 			    &background_fb);
595 
596 	/* Random color cursors */
597 	for (i = 0; i < 10; i++) {
598 		r = rand() % 0xFF;
599 		g = rand() % 0xFF;
600 		b = rand() % 0xFF;
601 		igt_create_color_fb(data->drm_fd, 64, 64,
602 				    DRM_FORMAT_ARGB8888,
603 				    false,
604 				    (double)r / 0xFF,
605 				    (double)g / 0xFF,
606 				    (double)b / 0xFF,
607 				    &cursor_fb[i]);
608 	}
609 
610 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
611 	cursor = igt_output_get_plane_type(output, DRM_PLANE_TYPE_CURSOR);
612 	if (!primary || !cursor) {
613 		cursor_leak_test_fini(data, output, &background_fb, cursor_fb);
614 		igt_skip("Primary and/or cursor are unavailable\n");
615 	}
616 
617 
618 	igt_plane_set_fb(primary, &background_fb);
619 	igt_display_commit2(display, COMMIT_LEGACY);
620 
621 	igt_plane_set_position(cursor, 100, 100);
622 
623 	/*
624 	 * Exercise both legacy and universal code paths.  Note that legacy
625 	 * handling in the kernel redirects through universal codepaths
626 	 * internally, so that redirection is where we're most worried about
627 	 * leaking.
628 	 */
629 	for (i = 0; i < 10; i++) {
630 		igt_plane_set_fb(cursor, &cursor_fb[i]);
631 		igt_display_commit2(display, COMMIT_UNIVERSAL);
632 	}
633 	for (i = 0; i < 10; i++) {
634 		igt_plane_set_fb(cursor, &cursor_fb[i]);
635 		igt_display_commit2(display, COMMIT_LEGACY);
636 	}
637 
638 	/* Release our framebuffer handles before we take a second count */
639 	igt_plane_set_fb(primary, NULL);
640 	igt_plane_set_fb(cursor, NULL);
641 	igt_display_commit2(display, COMMIT_LEGACY);
642 	cursor_leak_test_fini(data, output, &background_fb, cursor_fb);
643 
644 	/* We should be back to the same framebuffer count as when we started */
645 	count2 = i915_gem_fb_count(data);
646 
647 	igt_assert_eq(count1, count2);
648 }
649 
650 static void
gen9_test_init(gen9_test_t * test,igt_output_t * output,enum pipe pipe)651 gen9_test_init(gen9_test_t *test, igt_output_t *output, enum pipe pipe)
652 {
653 	data_t *data = test->data;
654 	drmModeModeInfo *mode;
655 
656 	igt_output_set_pipe(output, pipe);
657 
658 	mode = igt_output_get_mode(output);
659 	test->w = mode->hdisplay / 2;
660 	test->h = mode->vdisplay / 2;
661 	test->x = mode->hdisplay / 4;
662 	test->y = mode->vdisplay / 4;
663 
664 	/* Initial framebuffer of full CRTC size */
665 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
666 			    DRM_FORMAT_XRGB8888,
667 			    LOCAL_DRM_FORMAT_MOD_NONE,
668 			    0.0, 1.0, 0.0,
669 			    &test->biggreen_fb);
670 
671 	/* Framebuffers that only cover a quarter of the CRTC size */
672 	igt_create_color_fb(data->drm_fd, test->w, test->h,
673 			    DRM_FORMAT_XRGB8888,
674 			    LOCAL_DRM_FORMAT_MOD_NONE,
675 			    1.0, 0.0, 0.0,
676 			    &test->smallred_fb);
677 	igt_create_color_fb(data->drm_fd, test->w, test->h,
678 			    DRM_FORMAT_XRGB8888,
679 			    LOCAL_DRM_FORMAT_MOD_NONE,
680 			    0.0, 0.0, 1.0,
681 			    &test->smallblue_fb);
682 }
683 
684 static void
gen9_test_fini(gen9_test_t * test,igt_output_t * output)685 gen9_test_fini(gen9_test_t *test, igt_output_t *output)
686 {
687 	igt_remove_fb(test->data->drm_fd, &test->biggreen_fb);
688 	igt_remove_fb(test->data->drm_fd, &test->smallred_fb);
689 	igt_remove_fb(test->data->drm_fd, &test->smallblue_fb);
690 
691 	igt_output_set_pipe(output, PIPE_ANY);
692 	igt_display_commit2(&test->data->display, COMMIT_LEGACY);
693 }
694 
695 /*
696  * Test features specific to gen9+ platforms (i.e., primary plane
697  * windowing)
698  */
699 static void
gen9_test_pipe(data_t * data,enum pipe pipe,igt_output_t * output)700 gen9_test_pipe(data_t *data, enum pipe pipe, igt_output_t *output)
701 {
702 	gen9_test_t test = { .data = data };
703 	igt_plane_t *primary;
704 
705 	int ret = 0;
706 
707 	igt_skip_on(data->gen < 9);
708 	igt_skip_on(pipe >= data->display.n_pipes);
709 
710 	igt_output_set_pipe(output, pipe);
711 
712 	gen9_test_init(&test, output, pipe);
713 
714 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
715 
716 	/* Start with a full-screen primary plane */
717 	igt_plane_set_fb(primary, &test.biggreen_fb);
718 	igt_display_commit2(&data->display, COMMIT_LEGACY);
719 
720 	/* Set primary to windowed size/position */
721 	igt_plane_set_fb(primary, &test.smallblue_fb);
722 	igt_plane_set_position(primary, test.x, test.y);
723 	igt_plane_set_size(primary, test.w, test.h);
724 	igt_display_commit2(&data->display, COMMIT_UNIVERSAL);
725 
726 	/*
727 	 * SetPlane update to another framebuffer of the same size
728 	 * should succeed
729 	 */
730 	igt_plane_set_fb(primary, &test.smallred_fb);
731 	igt_plane_set_position(primary, test.x, test.y);
732 	igt_plane_set_size(primary, test.w, test.h);
733 	igt_display_commit2(&data->display, COMMIT_UNIVERSAL);
734 
735 	/* PageFlip should also succeed */
736 	ret = drmModePageFlip(data->drm_fd, output->config.crtc->crtc_id,
737 			      test.smallblue_fb.fb_id, 0, NULL);
738 	igt_assert_eq(ret, 0);
739 
740 	igt_plane_set_fb(primary, NULL);
741 	igt_plane_set_position(primary, 0, 0);
742 	gen9_test_fini(&test, output);
743 }
744 
745 static void
run_tests_for_pipe(data_t * data,enum pipe pipe)746 run_tests_for_pipe(data_t *data, enum pipe pipe)
747 {
748 	igt_output_t *output;
749 
750 	igt_fixture {
751 		int valid_tests = 0;
752 
753 		igt_skip_on(pipe >= data->display.n_pipes);
754 
755 		for_each_valid_output_on_pipe(&data->display, pipe, output)
756 			valid_tests++;
757 
758 		igt_require_f(valid_tests, "no valid crtc/connector combinations found\n");
759 	}
760 
761 	igt_subtest_f("universal-plane-pipe-%s-functional",
762 		      kmstest_pipe_name(pipe))
763 		for_each_valid_output_on_pipe(&data->display, pipe, output)
764 			functional_test_pipe(data, pipe, output);
765 
766 	igt_subtest_f("universal-plane-pipe-%s-sanity",
767 		      kmstest_pipe_name(pipe))
768 		for_each_valid_output_on_pipe(&data->display, pipe, output)
769 			sanity_test_pipe(data, pipe, output);
770 
771 	igt_subtest_f("disable-primary-vs-flip-pipe-%s",
772 		      kmstest_pipe_name(pipe))
773 		for_each_valid_output_on_pipe(&data->display, pipe, output)
774 			pageflip_test_pipe(data, pipe, output);
775 
776 	igt_subtest_f("cursor-fb-leak-pipe-%s",
777 		      kmstest_pipe_name(pipe))
778 		for_each_valid_output_on_pipe(&data->display, pipe, output)
779 			cursor_leak_test_pipe(data, pipe, output);
780 
781 	igt_subtest_f("universal-plane-gen9-features-pipe-%s",
782 		      kmstest_pipe_name(pipe))
783 		for_each_valid_output_on_pipe(&data->display, pipe, output)
784 			gen9_test_pipe(data, pipe, output);
785 }
786 
787 static data_t data;
788 
789 igt_main
790 {
791 	enum pipe pipe;
792 
793 	igt_skip_on_simulation();
794 
795 	igt_fixture {
796 		data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
797 		data.gen = intel_gen(intel_get_drm_devid(data.drm_fd));
798 
799 		kmstest_set_vt_graphics_mode();
800 
801 		igt_require_pipe_crc(data.drm_fd);
802 		igt_display_require(&data.display, data.drm_fd);
803 	}
804 
for_each_pipe_static(pipe)805 	for_each_pipe_static(pipe) {
806 		igt_subtest_group
807 			run_tests_for_pipe(&data, pipe);
808 	}
809 
810 	igt_fixture {
811 		igt_display_fini(&data.display);
812 	}
813 }
814