xref: /aosp_15_r20/external/igt-gpu-tools/tests/kms_cursor_crc.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2013 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 
25 #include "igt.h"
26 #include <errno.h>
27 #include <limits.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 
33 IGT_TEST_DESCRIPTION(
34    "Use the display CRC support to validate cursor plane functionality. "
35    "The test will position the cursor plane either fully onscreen, "
36    "partially onscreen, or fully offscreen, using either a fully opaque "
37    "or fully transparent surface. In each case it then reads the PF CRC "
38    "and compares it with the CRC value obtained when the cursor plane "
39    "was disabled.");
40 
41 #ifndef DRM_CAP_CURSOR_WIDTH
42 #define DRM_CAP_CURSOR_WIDTH 0x8
43 #endif
44 #ifndef DRM_CAP_CURSOR_HEIGHT
45 #define DRM_CAP_CURSOR_HEIGHT 0x9
46 #endif
47 
48 typedef struct {
49 	int drm_fd;
50 	igt_display_t display;
51 	struct igt_fb primary_fb;
52 	struct igt_fb fb;
53 	igt_output_t *output;
54 	enum pipe pipe;
55 	igt_crc_t ref_crc;
56 	int left, right, top, bottom;
57 	int screenw, screenh;
58 	int refresh;
59 	int curw, curh; /* cursor size */
60 	int cursor_max_w, cursor_max_h;
61 	igt_pipe_crc_t *pipe_crc;
62 	unsigned flags;
63 } data_t;
64 
65 #define TEST_DPMS (1<<0)
66 #define TEST_SUSPEND (1<<1)
67 
draw_cursor(cairo_t * cr,int x,int y,int cw,int ch,double a)68 static void draw_cursor(cairo_t *cr, int x, int y, int cw, int ch, double a)
69 {
70 	int wl, wr, ht, hb;
71 
72 	/* deal with odd cursor width/height */
73 	wl = cw / 2;
74 	wr = (cw + 1) / 2;
75 	ht = ch / 2;
76 	hb = (ch + 1) / 2;
77 
78 	/* Cairo doesn't like to be fed numbers that are too wild */
79 	if ((x < SHRT_MIN) || (x > SHRT_MAX) || (y < SHRT_MIN) || (y > SHRT_MAX))
80 		return;
81 	cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
82 	/* 4 color rectangles in the corners, RGBY */
83 	igt_paint_color_alpha(cr, x,      y,      wl, ht, 1.0, 0.0, 0.0, a);
84 	igt_paint_color_alpha(cr, x + wl, y,      wr, ht, 0.0, 1.0, 0.0, a);
85 	igt_paint_color_alpha(cr, x,      y + ht, wl, hb, 0.0, 0.0, 1.0, a);
86 	igt_paint_color_alpha(cr, x + wl, y + ht, wr, hb, 0.5, 0.5, 0.5, a);
87 }
88 
cursor_enable(data_t * data)89 static void cursor_enable(data_t *data)
90 {
91 	igt_output_t *output = data->output;
92 	igt_plane_t *cursor =
93 		igt_output_get_plane_type(output, DRM_PLANE_TYPE_CURSOR);
94 
95 	igt_plane_set_fb(cursor, &data->fb);
96 	igt_plane_set_size(cursor, data->curw, data->curh);
97 	igt_fb_set_size(&data->fb, cursor, data->curw, data->curh);
98 }
99 
cursor_disable(data_t * data)100 static void cursor_disable(data_t *data)
101 {
102 	igt_output_t *output = data->output;
103 	igt_plane_t *cursor =
104 		igt_output_get_plane_type(output, DRM_PLANE_TYPE_CURSOR);
105 
106 	igt_plane_set_fb(cursor, NULL);
107 	igt_plane_set_position(cursor, 0, 0);
108 }
109 
chv_cursor_broken(data_t * data,int x)110 static bool chv_cursor_broken(data_t *data, int x)
111 {
112 	uint32_t devid;
113 
114 	if (!is_i915_device(data->drm_fd))
115 		return false;
116 
117 	devid = intel_get_drm_devid(data->drm_fd);
118 
119 	/*
120 	 * CHV gets a FIFO underrun on pipe C when cursor x coordinate
121 	 * is negative and the cursor visible.
122 	 *
123 	 * i915 is fixed to return -EINVAL on cursor updates with those
124 	 * negative coordinates, so require cursor update to fail with
125 	 * -EINVAL in that case.
126 	 *
127 	 * See also kms_chv_cursor_fail.c
128 	 */
129 	if (x >= 0)
130 		return false;
131 
132 	return IS_CHERRYVIEW(devid) && data->pipe == PIPE_C;
133 }
134 
cursor_visible(data_t * data,int x,int y)135 static bool cursor_visible(data_t *data, int x, int y)
136 {
137 	if (x + data->curw <= 0 || y + data->curh <= 0)
138 		return false;
139 
140 	if (x >= data->screenw || y >= data->screenh)
141 		return false;
142 
143 	return true;
144 }
145 
do_single_test(data_t * data,int x,int y)146 static void do_single_test(data_t *data, int x, int y)
147 {
148 	igt_display_t *display = &data->display;
149 	igt_pipe_crc_t *pipe_crc = data->pipe_crc;
150 	igt_crc_t crc, ref_crc;
151 	igt_plane_t *cursor =
152 		igt_output_get_plane_type(data->output, DRM_PLANE_TYPE_CURSOR);
153 	cairo_t *cr;
154 	int ret = 0;
155 
156 	igt_print_activity();
157 
158 	/* Hardware test */
159 	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
160 	igt_paint_test_pattern(cr, data->screenw, data->screenh);
161 	igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
162 
163 	cursor_enable(data);
164 	igt_plane_set_position(cursor, x, y);
165 
166 	if (chv_cursor_broken(data, x) && cursor_visible(data, x, y)) {
167 		ret = igt_display_try_commit2(display, COMMIT_LEGACY);
168 		igt_assert_eq(ret, -EINVAL);
169 		igt_plane_set_position(cursor, 0, y);
170 
171 		return;
172 	}
173 
174 	igt_display_commit(display);
175 
176 	igt_wait_for_vblank(data->drm_fd, data->pipe);
177 	igt_pipe_crc_collect_crc(pipe_crc, &crc);
178 
179 	if (data->flags & (TEST_DPMS | TEST_SUSPEND)) {
180 		igt_crc_t crc_after;
181 
182 		if (data->flags & TEST_DPMS) {
183 			igt_debug("dpms off/on cycle\n");
184 			kmstest_set_connector_dpms(data->drm_fd,
185 						   data->output->config.connector,
186 						   DRM_MODE_DPMS_OFF);
187 			kmstest_set_connector_dpms(data->drm_fd,
188 						   data->output->config.connector,
189 						   DRM_MODE_DPMS_ON);
190 		}
191 
192 		if (data->flags & TEST_SUSPEND)
193 			igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
194 						      SUSPEND_TEST_NONE);
195 
196 		igt_pipe_crc_collect_crc(pipe_crc, &crc_after);
197 		igt_assert_crc_equal(&crc, &crc_after);
198 	}
199 
200 	cursor_disable(data);
201 	igt_display_commit(display);
202 
203 	/* Now render the same in software and collect crc */
204 	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
205 	draw_cursor(cr, x, y, data->curw, data->curh, 1.0);
206 	igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
207 	igt_display_commit(display);
208 
209 	igt_wait_for_vblank(data->drm_fd, data->pipe);
210 	igt_pipe_crc_collect_crc(pipe_crc, &ref_crc);
211 	igt_assert_crc_equal(&crc, &ref_crc);
212 
213 	/* Clear screen afterwards */
214 	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
215 	igt_paint_color(cr, 0, 0, data->screenw, data->screenh, 0.0, 0.0, 0.0);
216 	igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
217 }
218 
do_fail_test(data_t * data,int x,int y,int expect)219 static void do_fail_test(data_t *data, int x, int y, int expect)
220 {
221 	igt_display_t *display = &data->display;
222 	igt_plane_t *cursor =
223 		igt_output_get_plane_type(data->output, DRM_PLANE_TYPE_CURSOR);
224 	cairo_t *cr;
225 	int ret;
226 
227 	igt_print_activity();
228 
229 	/* Hardware test */
230 	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
231 	igt_paint_test_pattern(cr, data->screenw, data->screenh);
232 	igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
233 
234 	cursor_enable(data);
235 	igt_plane_set_position(cursor, x, y);
236 	ret = igt_display_try_commit2(display, COMMIT_LEGACY);
237 
238 	igt_plane_set_position(cursor, 0, 0);
239 	cursor_disable(data);
240 	igt_display_commit(display);
241 
242 	igt_assert_eq(ret, expect);
243 }
244 
do_test(data_t * data,int left,int right,int top,int bottom)245 static void do_test(data_t *data,
246 		    int left, int right, int top, int bottom)
247 {
248 	do_single_test(data, left, top);
249 	do_single_test(data, right, top);
250 	do_single_test(data, right, bottom);
251 	do_single_test(data, left, bottom);
252 }
253 
test_crc_onscreen(data_t * data)254 static void test_crc_onscreen(data_t *data)
255 {
256 	int left = data->left;
257 	int right = data->right;
258 	int top = data->top;
259 	int bottom = data->bottom;
260 	int cursor_w = data->curw;
261 	int cursor_h = data->curh;
262 
263 	/* fully inside  */
264 	do_test(data, left, right, top, bottom);
265 
266 	/* 2 pixels inside */
267 	do_test(data, left - (cursor_w-2), right + (cursor_w-2), top               , bottom               );
268 	do_test(data, left               , right               , top - (cursor_h-2), bottom + (cursor_h-2));
269 	do_test(data, left - (cursor_w-2), right + (cursor_w-2), top - (cursor_h-2), bottom + (cursor_h-2));
270 
271 	/* 1 pixel inside */
272 	do_test(data, left - (cursor_w-1), right + (cursor_w-1), top               , bottom               );
273 	do_test(data, left               , right               , top - (cursor_h-1), bottom + (cursor_h-1));
274 	do_test(data, left - (cursor_w-1), right + (cursor_w-1), top - (cursor_h-1), bottom + (cursor_h-1));
275 }
276 
test_crc_offscreen(data_t * data)277 static void test_crc_offscreen(data_t *data)
278 {
279 	int left = data->left;
280 	int right = data->right;
281 	int top = data->top;
282 	int bottom = data->bottom;
283 	int cursor_w = data->curw;
284 	int cursor_h = data->curh;
285 
286 	/* fully outside */
287 	do_test(data, left - (cursor_w), right + (cursor_w), top             , bottom             );
288 	do_test(data, left             , right             , top - (cursor_h), bottom + (cursor_h));
289 	do_test(data, left - (cursor_w), right + (cursor_w), top - (cursor_h), bottom + (cursor_h));
290 
291 	/* fully outside by 1 extra pixels */
292 	do_test(data, left - (cursor_w+1), right + (cursor_w+1), top               , bottom               );
293 	do_test(data, left               , right               , top - (cursor_h+1), bottom + (cursor_h+1));
294 	do_test(data, left - (cursor_w+1), right + (cursor_w+1), top - (cursor_h+1), bottom + (cursor_h+1));
295 
296 	/* fully outside by 2 extra pixels */
297 	do_test(data, left - (cursor_w+2), right + (cursor_w+2), top               , bottom               );
298 	do_test(data, left               , right               , top - (cursor_h+2), bottom + (cursor_h+2));
299 	do_test(data, left - (cursor_w+2), right + (cursor_w+2), top - (cursor_h+2), bottom + (cursor_h+2));
300 
301 	/* fully outside by a lot of extra pixels */
302 	do_test(data, left - (cursor_w+512), right + (cursor_w+512), top                 , bottom                 );
303 	do_test(data, left                 , right                 , top - (cursor_h+512), bottom + (cursor_h+512));
304 	do_test(data, left - (cursor_w+512), right + (cursor_w+512), top - (cursor_h+512), bottom + (cursor_h+512));
305 
306 	/* go nuts */
307 	do_test(data, INT_MIN, INT_MAX - cursor_w, INT_MIN, INT_MAX - cursor_h);
308 	do_test(data, SHRT_MIN, SHRT_MAX, SHRT_MIN, SHRT_MAX);
309 
310 	/* Make sure we get -ERANGE on integer overflow */
311 	do_fail_test(data, INT_MAX - cursor_w + 1, INT_MAX - cursor_h + 1, -ERANGE);
312 }
313 
test_crc_sliding(data_t * data)314 static void test_crc_sliding(data_t *data)
315 {
316 	int i;
317 
318 	/* Make sure cursor moves smoothly and pixel-by-pixel, and that there are
319 	 * no alignment issues. Horizontal, vertical and diagonal test.
320 	 */
321 	for (i = 0; i < 16; i++) {
322 		do_single_test(data, i, 0);
323 		do_single_test(data, 0, i);
324 		do_single_test(data, i, i);
325 	}
326 }
327 
test_crc_random(data_t * data)328 static void test_crc_random(data_t *data)
329 {
330 	int i, max;
331 
332 	max = data->flags & (TEST_DPMS | TEST_SUSPEND) ? 2 : 50;
333 
334 	/* Random cursor placement */
335 	for (i = 0; i < max; i++) {
336 		int x = rand() % (data->screenw + data->curw * 2) - data->curw;
337 		int y = rand() % (data->screenh + data->curh * 2) - data->curh;
338 		do_single_test(data, x, y);
339 	}
340 }
341 
cleanup_crtc(data_t * data)342 static void cleanup_crtc(data_t *data)
343 {
344 	igt_display_t *display = &data->display;
345 
346 	igt_pipe_crc_free(data->pipe_crc);
347 	data->pipe_crc = NULL;
348 
349 	igt_remove_fb(data->drm_fd, &data->primary_fb);
350 
351 	igt_display_reset(display);
352 }
353 
prepare_crtc(data_t * data,igt_output_t * output,int cursor_w,int cursor_h)354 static void prepare_crtc(data_t *data, igt_output_t *output,
355 			 int cursor_w, int cursor_h)
356 {
357 	drmModeModeInfo *mode;
358 	igt_display_t *display = &data->display;
359 	igt_plane_t *primary;
360 
361 	cleanup_crtc(data);
362 
363 	/* select the pipe we want to use */
364 	igt_output_set_pipe(output, data->pipe);
365 
366 	/* create and set the primary plane fb */
367 	mode = igt_output_get_mode(output);
368 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
369 			    DRM_FORMAT_XRGB8888,
370 			    LOCAL_DRM_FORMAT_MOD_NONE,
371 			    0.0, 0.0, 0.0,
372 			    &data->primary_fb);
373 
374 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
375 	igt_plane_set_fb(primary, &data->primary_fb);
376 
377 	igt_display_commit(display);
378 
379 	/* create the pipe_crc object for this pipe */
380 	data->pipe_crc = igt_pipe_crc_new(data->drm_fd, data->pipe,
381 					  INTEL_PIPE_CRC_SOURCE_AUTO);
382 
383 	/* x/y position where the cursor is still fully visible */
384 	data->left = 0;
385 	data->right = mode->hdisplay - cursor_w;
386 	data->top = 0;
387 	data->bottom = mode->vdisplay - cursor_h;
388 	data->screenw = mode->hdisplay;
389 	data->screenh = mode->vdisplay;
390 	data->curw = cursor_w;
391 	data->curh = cursor_h;
392 	data->refresh = mode->vrefresh;
393 
394 	/* get reference crc w/o cursor */
395 	igt_pipe_crc_collect_crc(data->pipe_crc, &data->ref_crc);
396 }
397 
test_cursor_alpha(data_t * data,double a)398 static void test_cursor_alpha(data_t *data, double a)
399 {
400 	igt_display_t *display = &data->display;
401 	igt_pipe_crc_t *pipe_crc = data->pipe_crc;
402 	igt_crc_t crc, ref_crc;
403 	cairo_t *cr;
404 	uint32_t fb_id;
405 	int curw = data->curw;
406 	int curh = data->curh;
407 
408 	/*alpha cursor fb*/
409 	fb_id = igt_create_fb(data->drm_fd, curw, curh,
410 				    DRM_FORMAT_ARGB8888,
411 				    LOCAL_DRM_FORMAT_MOD_NONE,
412 				    &data->fb);
413 	igt_assert(fb_id);
414 	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb);
415 	igt_paint_color_alpha(cr, 0, 0, curw, curh, 1.0, 1.0, 1.0, a);
416 	igt_put_cairo_ctx(data->drm_fd, &data->fb, cr);
417 
418 	/*Hardware Test*/
419 	cursor_enable(data);
420 	igt_display_commit(display);
421 	igt_wait_for_vblank(data->drm_fd, data->pipe);
422 	igt_pipe_crc_collect_crc(pipe_crc, &crc);
423 	cursor_disable(data);
424 	igt_remove_fb(data->drm_fd, &data->fb);
425 
426 	/*Software Test*/
427 	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
428 	igt_paint_color_alpha(cr, 0, 0, curw, curh, 1.0, 1.0, 1.0, a);
429 	igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
430 
431 	igt_display_commit(display);
432 	igt_wait_for_vblank(data->drm_fd, data->pipe);
433 	igt_pipe_crc_collect_crc(pipe_crc, &ref_crc);
434 	igt_assert_crc_equal(&crc, &ref_crc);
435 
436 	/*Clear Screen*/
437 	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
438 	igt_paint_color(cr, 0, 0, data->screenw, data->screenh,
439 			0.0, 0.0, 0.0);
440 	igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
441 }
442 
test_cursor_transparent(data_t * data)443 static void test_cursor_transparent(data_t *data)
444 {
445 	test_cursor_alpha(data, 0.0);
446 
447 }
448 
test_cursor_opaque(data_t * data)449 static void test_cursor_opaque(data_t *data)
450 {
451 	test_cursor_alpha(data, 1.0);
452 }
453 
run_test(data_t * data,void (* testfunc)(data_t *),int cursor_w,int cursor_h)454 static void run_test(data_t *data, void (*testfunc)(data_t *), int cursor_w, int cursor_h)
455 {
456 	prepare_crtc(data, data->output, cursor_w, cursor_h);
457 	testfunc(data);
458 }
459 
create_cursor_fb(data_t * data,int cur_w,int cur_h)460 static void create_cursor_fb(data_t *data, int cur_w, int cur_h)
461 {
462 	cairo_t *cr;
463 	uint32_t fb_id;
464 
465 	/*
466 	 * Make the FB slightly taller and leave the extra
467 	 * line opaque white, so that we can see that the
468 	 * hardware won't scan beyond what it should (esp.
469 	 * with non-square cursors).
470 	 */
471 	fb_id = igt_create_color_fb(data->drm_fd, cur_w, cur_h + 1,
472 				    DRM_FORMAT_ARGB8888,
473 				    LOCAL_DRM_FORMAT_MOD_NONE,
474 				    1.0, 1.0, 1.0,
475 				    &data->fb);
476 
477 	igt_assert(fb_id);
478 
479 	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb);
480 	draw_cursor(cr, 0, 0, cur_w, cur_h, 1.0);
481 	igt_put_cairo_ctx(data->drm_fd, &data->fb, cr);
482 }
483 
has_nonsquare_cursors(data_t * data)484 static bool has_nonsquare_cursors(data_t *data)
485 {
486 	uint32_t devid;
487 
488 	if (!is_i915_device(data->drm_fd))
489 		return false;
490 
491 	devid = intel_get_drm_devid(data->drm_fd);
492 
493 	/*
494 	 * Test non-square cursors a bit on the platforms
495 	 * that support such things.
496 	 */
497 	if (devid == PCI_CHIP_845_G || devid == PCI_CHIP_I865_G)
498 		return true;
499 
500 	if (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid))
501 		return false;
502 
503 	return intel_gen(devid) >= 7;
504 }
505 
test_cursor_size(data_t * data)506 static void test_cursor_size(data_t *data)
507 {
508 	igt_display_t *display = &data->display;
509 	igt_pipe_crc_t *pipe_crc = data->pipe_crc;
510 	igt_crc_t crc[10], ref_crc;
511 	cairo_t *cr;
512 	uint32_t fb_id;
513 	int i, size;
514 	int cursor_max_size = data->cursor_max_w;
515 	igt_plane_t *cursor =
516 		igt_output_get_plane_type(data->output, DRM_PLANE_TYPE_CURSOR);
517 
518 	/* Create a maximum size cursor, then change the size in flight to
519 	 * smaller ones to see that the size is applied correctly
520 	 */
521 	fb_id = igt_create_fb(data->drm_fd, cursor_max_size, cursor_max_size,
522 			      DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
523 			      &data->fb);
524 	igt_assert(fb_id);
525 
526 	/* Use a solid white rectangle as the cursor */
527 	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb);
528 	igt_paint_color_alpha(cr, 0, 0, cursor_max_size, cursor_max_size, 1.0, 1.0, 1.0, 1.0);
529 	igt_put_cairo_ctx(data->drm_fd, &data->fb, cr);
530 
531 	/* Hardware test loop */
532 	cursor_enable(data);
533 	for (i = 0, size = cursor_max_size; size >= 64; size /= 2, i++) {
534 		/* Change size in flight: */
535 		igt_plane_set_size(cursor, size, size);
536 		igt_fb_set_size(&data->fb, cursor, size, size);
537 		igt_display_commit(display);
538 		igt_wait_for_vblank(data->drm_fd, data->pipe);
539 		igt_pipe_crc_collect_crc(pipe_crc, &crc[i]);
540 	}
541 	cursor_disable(data);
542 	igt_display_commit(display);
543 	igt_remove_fb(data->drm_fd, &data->fb);
544 	/* Software test loop */
545 	for (i = 0, size = cursor_max_size; size >= 64; size /= 2, i++) {
546 		/* Now render the same in software and collect crc */
547 		cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
548 		igt_paint_color_alpha(cr, 0, 0, size, size, 1.0, 1.0, 1.0, 1.0);
549 		igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
550 
551 		igt_display_commit(display);
552 		igt_wait_for_vblank(data->drm_fd, data->pipe);
553 		igt_pipe_crc_collect_crc(pipe_crc, &ref_crc);
554 		/* Clear screen afterwards */
555 		cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb);
556 		igt_paint_color(cr, 0, 0, data->screenw, data->screenh,
557 				0.0, 0.0, 0.0);
558 		igt_put_cairo_ctx(data->drm_fd, &data->primary_fb, cr);
559 		igt_assert_crc_equal(&crc[i], &ref_crc);
560 	}
561 }
562 
test_rapid_movement(data_t * data)563 static void test_rapid_movement(data_t *data)
564 {
565 	struct timeval start, end, delta;
566 	int x = 0, y = 0;
567 	long usec;
568 	igt_display_t *display = &data->display;
569 	igt_plane_t *cursor =
570 		igt_output_get_plane_type(data->output, DRM_PLANE_TYPE_CURSOR);
571 
572 	cursor_enable(data);
573 
574 	gettimeofday(&start, NULL);
575 	for ( ; x < 100; x++) {
576 		igt_plane_set_position(cursor, x, y);
577 		igt_display_commit(display);
578 	}
579 	for ( ; y < 100; y++) {
580 		igt_plane_set_position(cursor, x, y);
581 		igt_display_commit(display);
582 	}
583 	for ( ; x > 0; x--) {
584 		igt_plane_set_position(cursor, x, y);
585 		igt_display_commit(display);
586 	}
587 	for ( ; y > 0; y--) {
588 		igt_plane_set_position(cursor, x, y);
589 		igt_display_commit(display);
590 	}
591 	gettimeofday(&end, NULL);
592 
593 	/*
594 	 * We've done 400 cursor updates now.  If we're being throttled to
595 	 * vblank, then that would take roughly 400/refresh seconds.  If the
596 	 * elapsed time is greater than 90% of that value, we'll consider it
597 	 * a failure (since cursor updates shouldn't be throttled).
598 	 */
599 	timersub(&end, &start, &delta);
600 	usec = delta.tv_usec + 1000000 * delta.tv_sec;
601 	igt_assert_lt(usec, 0.9 * 400 * 1000000 / data->refresh);
602 }
603 
run_tests_on_pipe(data_t * data,enum pipe pipe)604 static void run_tests_on_pipe(data_t *data, enum pipe pipe)
605 {
606 	int cursor_size;
607 
608 	igt_fixture {
609 		data->pipe = pipe;
610 		data->output = igt_get_single_output_for_pipe(&data->display, pipe);
611 		igt_require(data->output);
612 	}
613 
614 	igt_subtest_f("pipe-%s-cursor-size-change", kmstest_pipe_name(pipe))
615 		run_test(data, test_cursor_size,
616 			 data->cursor_max_w, data->cursor_max_h);
617 
618 	igt_subtest_f("pipe-%s-cursor-alpha-opaque", kmstest_pipe_name(pipe))
619 		run_test(data, test_cursor_opaque, data->cursor_max_w, data->cursor_max_h);
620 
621 	igt_subtest_f("pipe-%s-cursor-alpha-transparent", kmstest_pipe_name(pipe))
622 		run_test(data, test_cursor_transparent, data->cursor_max_w, data->cursor_max_h);
623 
624 	igt_fixture
625 		create_cursor_fb(data, data->cursor_max_w, data->cursor_max_h);
626 
627 	igt_subtest_f("pipe-%s-cursor-dpms", kmstest_pipe_name(pipe)) {
628 		data->flags = TEST_DPMS;
629 		run_test(data, test_crc_random, data->cursor_max_w, data->cursor_max_h);
630 	}
631 	data->flags = 0;
632 
633 	igt_subtest_f("pipe-%s-cursor-suspend", kmstest_pipe_name(pipe)) {
634 		data->flags = TEST_SUSPEND;
635 		run_test(data, test_crc_random, data->cursor_max_w, data->cursor_max_h);
636 	}
637 	data->flags = 0;
638 
639 	igt_fixture
640 		igt_remove_fb(data->drm_fd, &data->fb);
641 
642 	for (cursor_size = 64; cursor_size <= 512; cursor_size *= 2) {
643 		int w = cursor_size;
644 		int h = cursor_size;
645 
646 		igt_fixture {
647 			igt_require(w <= data->cursor_max_w &&
648 				    h <= data->cursor_max_h);
649 
650 			create_cursor_fb(data, w, h);
651 		}
652 
653 		/* Using created cursor FBs to test cursor support */
654 		igt_subtest_f("pipe-%s-cursor-%dx%d-onscreen", kmstest_pipe_name(pipe), w, h)
655 			run_test(data, test_crc_onscreen, w, h);
656 		igt_subtest_f("pipe-%s-cursor-%dx%d-offscreen", kmstest_pipe_name(pipe), w, h)
657 			run_test(data, test_crc_offscreen, w, h);
658 		igt_subtest_f("pipe-%s-cursor-%dx%d-sliding", kmstest_pipe_name(pipe), w, h)
659 			run_test(data, test_crc_sliding, w, h);
660 		igt_subtest_f("pipe-%s-cursor-%dx%d-random", kmstest_pipe_name(pipe), w, h)
661 			run_test(data, test_crc_random, w, h);
662 
663 		igt_subtest_f("pipe-%s-cursor-%dx%d-rapid-movement", kmstest_pipe_name(pipe), w, h) {
664 			run_test(data, test_rapid_movement, w, h);
665 		}
666 
667 		igt_fixture
668 			igt_remove_fb(data->drm_fd, &data->fb);
669 
670 		/*
671 		 * Test non-square cursors a bit on the platforms
672 		 * that support such things. And make it a bit more
673 		 * interesting by using a non-pot height.
674 		 */
675 		h /= 3;
676 
677 		igt_fixture {
678 			if (has_nonsquare_cursors(data))
679 				create_cursor_fb(data, w, h);
680 		}
681 
682 		/* Using created cursor FBs to test cursor support */
683 		igt_subtest_f("pipe-%s-cursor-%dx%d-onscreen", kmstest_pipe_name(pipe), w, h) {
684 			igt_require(has_nonsquare_cursors(data));
685 			run_test(data, test_crc_onscreen, w, h);
686 		}
687 		igt_subtest_f("pipe-%s-cursor-%dx%d-offscreen", kmstest_pipe_name(pipe), w, h) {
688 			igt_require(has_nonsquare_cursors(data));
689 			run_test(data, test_crc_offscreen, w, h);
690 		}
691 		igt_subtest_f("pipe-%s-cursor-%dx%d-sliding", kmstest_pipe_name(pipe), w, h) {
692 			igt_require(has_nonsquare_cursors(data));
693 			run_test(data, test_crc_sliding, w, h);
694 		}
695 		igt_subtest_f("pipe-%s-cursor-%dx%d-random", kmstest_pipe_name(pipe), w, h) {
696 			igt_require(has_nonsquare_cursors(data));
697 			run_test(data, test_crc_random, w, h);
698 		}
699 
700 		igt_fixture
701 			igt_remove_fb(data->drm_fd, &data->fb);
702 	}
703 }
704 
705 static data_t data;
706 
707 igt_main
708 {
709 	uint64_t cursor_width = 64, cursor_height = 64;
710 	int ret;
711 	enum pipe pipe;
712 
713 	igt_skip_on_simulation();
714 
715 	igt_fixture {
716 		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
717 
718 		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_WIDTH, &cursor_width);
719 		igt_assert(ret == 0 || errno == EINVAL);
720 		/* Not making use of cursor_height since it is same as width, still reading */
721 		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height);
722 		igt_assert(ret == 0 || errno == EINVAL);
723 
724 		/* We assume width and height are same so max is assigned width */
725 		igt_assert_eq(cursor_width, cursor_height);
726 
727 		kmstest_set_vt_graphics_mode();
728 
729 		igt_require_pipe_crc(data.drm_fd);
730 
731 		igt_display_require(&data.display, data.drm_fd);
732 	}
733 
734 	data.cursor_max_w = cursor_width;
735 	data.cursor_max_h = cursor_height;
736 
737 	for_each_pipe_static(pipe)
738 		igt_subtest_group
739 			run_tests_on_pipe(&data, pipe);
740 
741 	igt_fixture {
742 		igt_display_fini(&data.display);
743 	}
744 }
745