kms_cursor_crc.c 26 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Copyright © 2013 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 */

Thomas Wood's avatar
Thomas Wood committed
25
#include "igt.h"
26
#include <errno.h>
27
#include <limits.h>
28 29 30 31 32
#include <stdbool.h>
#include <stdio.h>
#include <string.h>


Thomas Wood's avatar
Thomas Wood committed
33 34 35 36
IGT_TEST_DESCRIPTION(
   "Use the display CRC support to validate cursor plane functionality. "
   "The test will position the cursor plane either fully onscreen, "
   "partially onscreen, or fully offscreen, using either a fully opaque "
37 38 39 40
   "or fully transparent surface. In each case, it enables the cursor plane "
   "and then reads the PF CRC (hardware test) and compares it with the CRC "
   "value obtained when the cursor plane was disabled and its drawing is "
   "directly inserted on the PF by software.");
Thomas Wood's avatar
Thomas Wood committed
41

42 43 44 45 46 47 48
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
#ifndef DRM_CAP_CURSOR_HEIGHT
#define DRM_CAP_CURSOR_HEIGHT 0x9
#endif

49 50
typedef struct {
	int drm_fd;
51
	igt_display_t display;
52
	struct igt_fb primary_fb[2];
53
	struct igt_fb fb;
54 55
	igt_output_t *output;
	enum pipe pipe;
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 64 65 66 67 68 69 70 71 72
	igt_plane_t *primary;
	igt_plane_t *cursor;
	cairo_surface_t *surface;
	uint32_t devid;
	drm_intel_bufmgr *bufmgr;
	igt_render_copyfunc_t rendercopy;
	drm_intel_bo * drmibo[2];
	struct intel_batchbuffer *batch;
	struct igt_buf igtbo[2];

73
} data_t;
74

75 76 77
#define TEST_DPMS (1<<0)
#define TEST_SUSPEND (1<<1)

78 79 80
#define FRONTBUFFER 0
#define RESTOREBUFFER 1

81
static void draw_cursor(cairo_t *cr, int x, int y, int cw, int ch, double a)
82
{
83 84 85 86 87 88 89 90
	int wl, wr, ht, hb;

	/* deal with odd cursor width/height */
	wl = cw / 2;
	wr = (cw + 1) / 2;
	ht = ch / 2;
	hb = (ch + 1) / 2;

91 92 93
	/* Cairo doesn't like to be fed numbers that are too wild */
	if ((x < SHRT_MIN) || (x > SHRT_MAX) || (y < SHRT_MIN) || (y > SHRT_MAX))
		return;
94
	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
95 96
	cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
	/* 4 color rectangles in the corners, RGBY */
97 98 99
	igt_paint_color_alpha(cr, x,      y,      wl, ht, 1.0, 0.0, 0.0, a);
	igt_paint_color_alpha(cr, x + wl, y,      wr, ht, 0.0, 1.0, 0.0, a);
	igt_paint_color_alpha(cr, x,      y + ht, wl, hb, 0.0, 0.0, 1.0, a);
100
	igt_paint_color_alpha(cr, x + wl, y + ht, wr, hb, 1.0, 1.0, 1.0, a);
101
}
102

103
static void cursor_enable(data_t *data)
104
{
105 106 107
	igt_plane_set_fb(data->cursor, &data->fb);
	igt_plane_set_size(data->cursor, data->curw, data->curh);
	igt_fb_set_size(&data->fb, data->cursor, data->curw, data->curh);
108 109
}

110
static void cursor_disable(data_t *data)
111
{
112 113
	igt_plane_set_fb(data->cursor, NULL);
	igt_plane_set_position(data->cursor, 0, 0);
114 115
}

116 117
static bool chv_cursor_broken(data_t *data, int x)
{
118 119 120 121 122 123 124
	uint32_t devid;

	if (!is_i915_device(data->drm_fd))
		return false;

	devid = intel_get_drm_devid(data->drm_fd);

125 126 127 128 129 130 131 132 133 134 135 136 137
	/*
	 * CHV gets a FIFO underrun on pipe C when cursor x coordinate
	 * is negative and the cursor visible.
	 *
	 * i915 is fixed to return -EINVAL on cursor updates with those
	 * negative coordinates, so require cursor update to fail with
	 * -EINVAL in that case.
	 *
	 * See also kms_chv_cursor_fail.c
	 */
	if (x >= 0)
		return false;

138
	return IS_CHERRYVIEW(devid) && data->pipe == PIPE_C;
139 140 141 142 143 144 145 146 147 148 149 150
}

static bool cursor_visible(data_t *data, int x, int y)
{
	if (x + data->curw <= 0 || y + data->curh <= 0)
		return false;

	if (x >= data->screenw || y >= data->screenh)
		return false;

	return true;
}
151

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
static void restore_image(data_t *data)
{
	cairo_t *cr;

	if (data->rendercopy != NULL) {
		/* use rendercopy if available */
		data->rendercopy(data->batch, NULL,
				 &data->igtbo[RESTOREBUFFER], 0, 0,
				 data->primary_fb[RESTOREBUFFER].width,
				 data->primary_fb[RESTOREBUFFER].height,
				 &data->igtbo[FRONTBUFFER], 0, 0);
	} else {
		/* if rendercopy not implemented in igt use cairo */
		cr = igt_get_cairo_ctx(data->drm_fd,
				       &data->primary_fb[FRONTBUFFER]);
		cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
		cairo_set_source_surface(cr, data->surface, 0, 0);
		cairo_rectangle(cr, 0, 0, data->screenw, data->screenh);
		cairo_fill(cr);
171
		igt_put_cairo_ctx(cr);
172 173 174 175
	}
	igt_dirty_fb(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
}

176
static void do_single_test(data_t *data, int x, int y)
177
{
178
	igt_display_t *display = &data->display;
179
	igt_pipe_crc_t *pipe_crc = data->pipe_crc;
180
	igt_crc_t crc, ref_crc;
181
	cairo_t *cr;
182
	int ret = 0;
183

184
	igt_print_activity();
185

186
	/* Hardware test */
187
	restore_image(data);
188

189
	igt_plane_set_position(data->cursor, x, y);
190
	cursor_enable(data);
191 192 193 194

	if (chv_cursor_broken(data, x) && cursor_visible(data, x, y)) {
		ret = igt_display_try_commit2(display, COMMIT_LEGACY);
		igt_assert_eq(ret, -EINVAL);
195
		igt_plane_set_position(data->cursor, 0, y);
196 197 198 199

		return;
	}

200
	igt_display_commit(display);
201

202
	/* Extra vblank wait is because nonblocking cursor ioctl */
203
	igt_wait_for_vblank(data->drm_fd, data->pipe);
204
	igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &crc);
205 206 207

	if (data->flags & (TEST_DPMS | TEST_SUSPEND)) {
		igt_crc_t crc_after;
208 209 210 211 212
		/*
		 * stop/start crc to avoid dmesg notifications about userspace
		 * reading too slow.
		 */
		igt_pipe_crc_stop(pipe_crc);
213 214 215 216 217 218 219 220 221 222 223 224

		if (data->flags & TEST_DPMS) {
			igt_debug("dpms off/on cycle\n");
			kmstest_set_connector_dpms(data->drm_fd,
						   data->output->config.connector,
						   DRM_MODE_DPMS_OFF);
			kmstest_set_connector_dpms(data->drm_fd,
						   data->output->config.connector,
						   DRM_MODE_DPMS_ON);
		}

		if (data->flags & TEST_SUSPEND)
225 226
			igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
						      SUSPEND_TEST_NONE);
227

228 229
		igt_pipe_crc_start(pipe_crc);
		igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &crc_after);
230 231 232
		igt_assert_crc_equal(&crc, &crc_after);
	}

233
	cursor_disable(data);
234

235
	/* Now render the same in software and collect crc */
236
	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
237
	draw_cursor(cr, x, y, data->curw, data->curh, 1.0);
238
	igt_put_cairo_ctx(cr);
239
	igt_display_commit(display);
240 241
	igt_dirty_fb(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
	/* Extra vblank wait is because nonblocking cursor ioctl */
242
	igt_wait_for_vblank(data->drm_fd, data->pipe);
243

244
	igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &ref_crc);
245
	igt_assert_crc_equal(&crc, &ref_crc);
246 247
}

248 249 250 251 252 253 254 255
static void do_fail_test(data_t *data, int x, int y, int expect)
{
	igt_display_t *display = &data->display;
	int ret;

	igt_print_activity();

	/* Hardware test */
256
	restore_image(data);
257

258
	cursor_enable(data);
259
	igt_plane_set_position(data->cursor, x, y);
260 261
	ret = igt_display_try_commit2(display, COMMIT_LEGACY);

262
	igt_plane_set_position(data->cursor, 0, 0);
263 264 265
	cursor_disable(data);
	igt_display_commit(display);

266
	igt_assert_eq(ret, expect);
267 268
}

269
static void do_test(data_t *data,
270 271
		    int left, int right, int top, int bottom)
{
272 273 274 275
	do_single_test(data, left, top);
	do_single_test(data, right, top);
	do_single_test(data, right, bottom);
	do_single_test(data, left, bottom);
276 277
}

278
static void test_crc_onscreen(data_t *data)
279
{
280 281 282 283 284 285
	int left = data->left;
	int right = data->right;
	int top = data->top;
	int bottom = data->bottom;
	int cursor_w = data->curw;
	int cursor_h = data->curh;
286

287
	/* fully inside  */
288
	do_test(data, left, right, top, bottom);
289 290

	/* 2 pixels inside */
291 292 293
	do_test(data, left - (cursor_w-2), right + (cursor_w-2), top               , bottom               );
	do_test(data, left               , right               , top - (cursor_h-2), bottom + (cursor_h-2));
	do_test(data, left - (cursor_w-2), right + (cursor_w-2), top - (cursor_h-2), bottom + (cursor_h-2));
294 295

	/* 1 pixel inside */
296 297 298
	do_test(data, left - (cursor_w-1), right + (cursor_w-1), top               , bottom               );
	do_test(data, left               , right               , top - (cursor_h-1), bottom + (cursor_h-1));
	do_test(data, left - (cursor_w-1), right + (cursor_w-1), top - (cursor_h-1), bottom + (cursor_h-1));
299 300
}

301
static void test_crc_offscreen(data_t *data)
302
{
303 304 305 306 307 308
	int left = data->left;
	int right = data->right;
	int top = data->top;
	int bottom = data->bottom;
	int cursor_w = data->curw;
	int cursor_h = data->curh;
309 310

	/* fully outside */
311 312 313
	do_test(data, left - (cursor_w), right + (cursor_w), top             , bottom             );
	do_test(data, left             , right             , top - (cursor_h), bottom + (cursor_h));
	do_test(data, left - (cursor_w), right + (cursor_w), top - (cursor_h), bottom + (cursor_h));
314 315

	/* fully outside by 1 extra pixels */
316 317 318
	do_test(data, left - (cursor_w+1), right + (cursor_w+1), top               , bottom               );
	do_test(data, left               , right               , top - (cursor_h+1), bottom + (cursor_h+1));
	do_test(data, left - (cursor_w+1), right + (cursor_w+1), top - (cursor_h+1), bottom + (cursor_h+1));
319 320

	/* fully outside by 2 extra pixels */
321 322 323
	do_test(data, left - (cursor_w+2), right + (cursor_w+2), top               , bottom               );
	do_test(data, left               , right               , top - (cursor_h+2), bottom + (cursor_h+2));
	do_test(data, left - (cursor_w+2), right + (cursor_w+2), top - (cursor_h+2), bottom + (cursor_h+2));
324 325

	/* fully outside by a lot of extra pixels */
326 327 328
	do_test(data, left - (cursor_w+512), right + (cursor_w+512), top                 , bottom                 );
	do_test(data, left                 , right                 , top - (cursor_h+512), bottom + (cursor_h+512));
	do_test(data, left - (cursor_w+512), right + (cursor_w+512), top - (cursor_h+512), bottom + (cursor_h+512));
329 330

	/* go nuts */
331 332 333 334 335
	do_test(data, INT_MIN, INT_MAX - cursor_w, INT_MIN, INT_MAX - cursor_h);
	do_test(data, SHRT_MIN, SHRT_MAX, SHRT_MIN, SHRT_MAX);

	/* Make sure we get -ERANGE on integer overflow */
	do_fail_test(data, INT_MAX - cursor_w + 1, INT_MAX - cursor_h + 1, -ERANGE);
336 337
}

338
static void test_crc_sliding(data_t *data)
339 340 341 342 343 344 345
{
	int i;

	/* Make sure cursor moves smoothly and pixel-by-pixel, and that there are
	 * no alignment issues. Horizontal, vertical and diagonal test.
	 */
	for (i = 0; i < 16; i++) {
346 347 348
		do_single_test(data, i, 0);
		do_single_test(data, 0, i);
		do_single_test(data, i, i);
349 350 351
	}
}

352
static void test_crc_random(data_t *data)
353
{
354 355 356
	int i, max;

	max = data->flags & (TEST_DPMS | TEST_SUSPEND) ? 2 : 50;
357 358

	/* Random cursor placement */
359
	for (i = 0; i < max; i++) {
360 361 362
		int x = rand() % (data->screenw + data->curw * 2) - data->curw;
		int y = rand() % (data->screenh + data->curh * 2) - data->curh;
		do_single_test(data, x, y);
363 364 365
	}
}

366 367 368 369
static void cleanup_crtc(data_t *data)
{
	igt_display_t *display = &data->display;

370
	igt_pipe_crc_stop(data->pipe_crc);
371 372 373
	igt_pipe_crc_free(data->pipe_crc);
	data->pipe_crc = NULL;

374 375 376 377 378 379 380
	cairo_surface_destroy(data->surface);

	igt_plane_set_fb(data->primary, NULL);
	igt_display_commit(display);

	igt_remove_fb(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
	igt_remove_fb(data->drm_fd, &data->primary_fb[RESTOREBUFFER]);
381 382 383 384

	igt_display_reset(display);
}

385 386 387
static void scratch_buf_init(data_t *data, int buffer)
{
	data->igtbo[buffer].bo = data->drmibo[buffer];
388
	data->igtbo[buffer].surface[0].stride = data->primary_fb[buffer].strides[0];
389
	data->igtbo[buffer].tiling = data->primary_fb[buffer].modifier;
390
	data->igtbo[buffer].surface[0].size = data->primary_fb[buffer].size;
391 392 393
	data->igtbo[buffer].bpp = data->primary_fb[buffer].plane_bpp[0];
}

394
static void prepare_crtc(data_t *data, igt_output_t *output,
395
			 int cursor_w, int cursor_h)
396
{
397 398
	drmModeModeInfo *mode;
	igt_display_t *display = &data->display;
399
	cairo_t *cr;
400

401
	/* select the pipe we want to use */
402
	igt_output_set_pipe(output, data->pipe);
403

404
	/* create and set the primary plane fbs */
405
	mode = igt_output_get_mode(output);
406
	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
407
			    DRM_FORMAT_XRGB8888,
408
			    LOCAL_DRM_FORMAT_MOD_NONE,
409
			    0.0, 0.0, 0.0,
410 411 412 413 414 415 416
			    &data->primary_fb[FRONTBUFFER]);

	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
			    DRM_FORMAT_XRGB8888,
			    LOCAL_DRM_FORMAT_MOD_NONE,
			    0.0, 0.0, 0.0,
			    &data->primary_fb[RESTOREBUFFER]);
417

418 419 420 421
	data->primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
	data->cursor = igt_output_get_plane_type(output, DRM_PLANE_TYPE_CURSOR);

	igt_plane_set_fb(data->primary, &data->primary_fb[FRONTBUFFER]);
422

423 424 425
	igt_display_commit(display);

	/* create the pipe_crc object for this pipe */
426
	data->pipe_crc = igt_pipe_crc_new(data->drm_fd, data->pipe,
427
					  INTEL_PIPE_CRC_SOURCE_AUTO);
428

429
	/* x/y position where the cursor is still fully visible */
430 431 432 433 434 435 436 437
	data->left = 0;
	data->right = mode->hdisplay - cursor_w;
	data->top = 0;
	data->bottom = mode->vdisplay - cursor_h;
	data->screenw = mode->hdisplay;
	data->screenh = mode->vdisplay;
	data->curw = cursor_w;
	data->curh = cursor_h;
438
	data->refresh = mode->vrefresh;
439

440 441 442 443 444 445 446 447 448 449 450 451 452 453
	data->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, data->screenw, data->screenh);

	if (data->rendercopy == NULL) {
		/* store test image as cairo surface */
		cr = cairo_create(data->surface);
		cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
		igt_paint_test_pattern(cr, data->screenw, data->screenh);
		cairo_destroy(cr);
	} else {
		/* store test image as fb if rendercopy is available */
		cr = igt_get_cairo_ctx(data->drm_fd,
		                       &data->primary_fb[RESTOREBUFFER]);
		cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
		igt_paint_test_pattern(cr, data->screenw, data->screenh);
454
		igt_put_cairo_ctx(cr);
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

		data->drmibo[FRONTBUFFER] = gem_handle_to_libdrm_bo(data->bufmgr,
								    data->drm_fd,
								    "", data->primary_fb[FRONTBUFFER].gem_handle);
		igt_assert(data->drmibo[FRONTBUFFER]);

		data->drmibo[RESTOREBUFFER] = gem_handle_to_libdrm_bo(data->bufmgr,
								      data->drm_fd,
								      "", data->primary_fb[RESTOREBUFFER].gem_handle);
		igt_assert(data->drmibo[RESTOREBUFFER]);

		scratch_buf_init(data, RESTOREBUFFER);
		scratch_buf_init(data, FRONTBUFFER);

		data->batch = intel_batchbuffer_alloc(data->bufmgr,
						      data->devid);
		igt_assert(data->batch);
	}

474
	igt_pipe_crc_start(data->pipe_crc);
475 476
}

477 478 479 480 481 482 483
static void test_cursor_alpha(data_t *data, double a)
{
	igt_display_t *display = &data->display;
	igt_pipe_crc_t *pipe_crc = data->pipe_crc;
	igt_crc_t crc, ref_crc;
	cairo_t *cr;
	uint32_t fb_id;
484 485
	int curw = data->curw;
	int curh = data->curh;
486

487
	/* Alpha cursor fb with white color */
488
	fb_id = igt_create_fb(data->drm_fd, curw, curh,
489 490 491 492 493
				    DRM_FORMAT_ARGB8888,
				    LOCAL_DRM_FORMAT_MOD_NONE,
				    &data->fb);
	igt_assert(fb_id);
	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb);
494
	igt_paint_color_alpha(cr, 0, 0, curw, curh, 1.0, 1.0, 1.0, a);
495
	igt_put_cairo_ctx(cr);
496

497
	/* Hardware Test - enable cursor and get PF CRC */
498
	cursor_enable(data);
499
	igt_display_commit(display);
500
	igt_wait_for_vblank(data->drm_fd, data->pipe);
501
	igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &crc);
502

503
	cursor_disable(data);
504
	igt_remove_fb(data->drm_fd, &data->fb);
505

506
	/* Software Test - render cursor in software, drawn it directly on PF */
507
	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
508
	igt_paint_color_alpha(cr, 0, 0, curw, curh, 1.0, 1.0, 1.0, a);
509
	igt_put_cairo_ctx(cr);
510 511 512

	igt_display_commit(display);
	igt_wait_for_vblank(data->drm_fd, data->pipe);
513
	igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &ref_crc);
514 515

	/* Compare CRC from Hardware/Software tests */
516 517 518
	igt_assert_crc_equal(&crc, &ref_crc);

	/*Clear Screen*/
519
	cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
520 521
	igt_paint_color(cr, 0, 0, data->screenw, data->screenh,
			0.0, 0.0, 0.0);
522
	igt_put_cairo_ctx(cr);
523 524 525 526 527 528 529 530 531 532 533 534 535
}

static void test_cursor_transparent(data_t *data)
{
	test_cursor_alpha(data, 0.0);

}

static void test_cursor_opaque(data_t *data)
{
	test_cursor_alpha(data, 1.0);
}

536
static void run_test(data_t *data, void (*testfunc)(data_t *), int cursor_w, int cursor_h)
537
{
538 539
	prepare_crtc(data, data->output, cursor_w, cursor_h);
	testfunc(data);
540
	cleanup_crtc(data);
541 542
}

543
static void create_cursor_fb(data_t *data, int cur_w, int cur_h)
544 545
{
	cairo_t *cr;
546
	uint32_t fb_id;
547

548 549 550 551 552 553 554
	/*
	 * Make the FB slightly taller and leave the extra
	 * line opaque white, so that we can see that the
	 * hardware won't scan beyond what it should (esp.
	 * with non-square cursors).
	 */
	fb_id = igt_create_color_fb(data->drm_fd, cur_w, cur_h + 1,
555 556
				    DRM_FORMAT_ARGB8888,
				    LOCAL_DRM_FORMAT_MOD_NONE,
557 558 559
				    1.0, 1.0, 1.0,
				    &data->fb);

560
	igt_assert(fb_id);
561

562
	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb);
563
	draw_cursor(cr, 0, 0, cur_w, cur_h, 1.0);
564
	igt_put_cairo_ctx(cr);
565 566
}

567
static bool has_nonsquare_cursors(data_t *data)
568
{
569 570 571 572 573 574 575
	uint32_t devid;

	if (!is_i915_device(data->drm_fd))
		return false;

	devid = intel_get_drm_devid(data->drm_fd);

576 577 578 579
	/*
	 * Test non-square cursors a bit on the platforms
	 * that support such things.
	 */
580 581 582 583 584 585 586
	if (devid == PCI_CHIP_845_G || devid == PCI_CHIP_I865_G)
		return true;

	if (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid))
		return false;

	return intel_gen(devid) >= 7;
587 588
}

589
static void test_cursor_size(data_t *data)
590 591
{
	igt_display_t *display = &data->display;
592
	igt_pipe_crc_t *pipe_crc = data->pipe_crc;
593 594 595
	igt_crc_t crc[10], ref_crc;
	cairo_t *cr;
	uint32_t fb_id;
596 597
	int i, size;
	int cursor_max_size = data->cursor_max_w;
598 599 600 601 602

	/* Create a maximum size cursor, then change the size in flight to
	 * smaller ones to see that the size is applied correctly
	 */
	fb_id = igt_create_fb(data->drm_fd, cursor_max_size, cursor_max_size,
603 604
			      DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
			      &data->fb);
605 606 607 608 609
	igt_assert(fb_id);

	/* Use a solid white rectangle as the cursor */
	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb);
	igt_paint_color_alpha(cr, 0, 0, cursor_max_size, cursor_max_size, 1.0, 1.0, 1.0, 1.0);
610
	igt_put_cairo_ctx(cr);
611 612

	/* Hardware test loop */
613
	cursor_enable(data);
614 615
	for (i = 0, size = cursor_max_size; size >= 64; size /= 2, i++) {
		/* Change size in flight: */
616 617
		igt_plane_set_size(data->cursor, size, size);
		igt_fb_set_size(&data->fb, data->cursor, size, size);
618
		igt_display_commit(display);
619
		igt_wait_for_vblank(data->drm_fd, data->pipe);
620
		igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &crc[i]);
621
	}
622
	cursor_disable(data);
623
	igt_display_commit(display);
624
	igt_remove_fb(data->drm_fd, &data->fb);
625 626 627
	/* Software test loop */
	for (i = 0, size = cursor_max_size; size >= 64; size /= 2, i++) {
		/* Now render the same in software and collect crc */
628
		cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
629
		igt_paint_color_alpha(cr, 0, 0, size, size, 1.0, 1.0, 1.0, 1.0);
630
		igt_put_cairo_ctx(cr);
631

632
		igt_display_commit(display);
633
		igt_wait_for_vblank(data->drm_fd, data->pipe);
634
		igt_pipe_crc_get_current(data->drm_fd, pipe_crc, &ref_crc);
635
		/* Clear screen afterwards */
636
		cr = igt_get_cairo_ctx(data->drm_fd, &data->primary_fb[FRONTBUFFER]);
637
		igt_paint_color(cr, 0, 0, data->screenw, data->screenh,
638
				0.0, 0.0, 0.0);
639
		igt_put_cairo_ctx(cr);
640
		igt_assert_crc_equal(&crc[i], &ref_crc);
641 642 643
	}
}

644 645 646 647 648
static void test_rapid_movement(data_t *data)
{
	struct timeval start, end, delta;
	int x = 0, y = 0;
	long usec;
649
	igt_display_t *display = &data->display;
650

651
	cursor_enable(data);
652 653

	gettimeofday(&start, NULL);
654
	for ( ; x < 100; x++) {
655
		igt_plane_set_position(data->cursor, x, y);
656 657 658
		igt_display_commit(display);
	}
	for ( ; y < 100; y++) {
659
		igt_plane_set_position(data->cursor, x, y);
660 661 662
		igt_display_commit(display);
	}
	for ( ; x > 0; x--) {
663
		igt_plane_set_position(data->cursor, x, y);
664 665 666
		igt_display_commit(display);
	}
	for ( ; y > 0; y--) {
667
		igt_plane_set_position(data->cursor, x, y);
668 669
		igt_display_commit(display);
	}
670 671 672 673 674 675 676 677 678 679 680 681 682
	gettimeofday(&end, NULL);

	/*
	 * We've done 400 cursor updates now.  If we're being throttled to
	 * vblank, then that would take roughly 400/refresh seconds.  If the
	 * elapsed time is greater than 90% of that value, we'll consider it
	 * a failure (since cursor updates shouldn't be throttled).
	 */
	timersub(&end, &start, &delta);
	usec = delta.tv_usec + 1000000 * delta.tv_sec;
	igt_assert_lt(usec, 0.9 * 400 * 1000000 / data->refresh);
}

683
static void run_tests_on_pipe(data_t *data, enum pipe pipe)
684 685
{
	int cursor_size;
686 687 688 689 690 691 692

	igt_fixture {
		data->pipe = pipe;
		data->output = igt_get_single_output_for_pipe(&data->display, pipe);
		igt_require(data->output);
	}

693 694 695
	igt_describe("Create a maximum size cursor, then change the size in "
		     "flight to smaller ones to see that the size is applied "
		     "correctly.");
696 697 698 699
	igt_subtest_f("pipe-%s-cursor-size-change", kmstest_pipe_name(pipe))
		run_test(data, test_cursor_size,
			 data->cursor_max_w, data->cursor_max_h);

700 701
	igt_describe("Validates the composition of a fully opaque cursor "
		     "plane, i.e., alpha channel equal to 1.0.");
702 703 704
	igt_subtest_f("pipe-%s-cursor-alpha-opaque", kmstest_pipe_name(pipe))
		run_test(data, test_cursor_opaque, data->cursor_max_w, data->cursor_max_h);

705 706
	igt_describe("Validates the composition of a fully transparent cursor "
		     "plane, i.e., alpha channel equal to 0.0.");
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
	igt_subtest_f("pipe-%s-cursor-alpha-transparent", kmstest_pipe_name(pipe))
		run_test(data, test_cursor_transparent, data->cursor_max_w, data->cursor_max_h);

	igt_fixture
		create_cursor_fb(data, data->cursor_max_w, data->cursor_max_h);

	igt_subtest_f("pipe-%s-cursor-dpms", kmstest_pipe_name(pipe)) {
		data->flags = TEST_DPMS;
		run_test(data, test_crc_random, data->cursor_max_w, data->cursor_max_h);
	}
	data->flags = 0;

	igt_subtest_f("pipe-%s-cursor-suspend", kmstest_pipe_name(pipe)) {
		data->flags = TEST_SUSPEND;
		run_test(data, test_crc_random, data->cursor_max_w, data->cursor_max_h);
	}
	data->flags = 0;

	igt_fixture
		igt_remove_fb(data->drm_fd, &data->fb);

728 729 730 731
	for (cursor_size = 64; cursor_size <= 512; cursor_size *= 2) {
		int w = cursor_size;
		int h = cursor_size;

732 733 734 735
		igt_fixture {
			igt_require(w <= data->cursor_max_w &&
				    h <= data->cursor_max_h);

736
			create_cursor_fb(data, w, h);
737
		}
738 739

		/* Using created cursor FBs to test cursor support */
740
		igt_describe("Check if a given-size cursor is well-positioned inside the screen.");
741
		igt_subtest_f("pipe-%s-cursor-%dx%d-onscreen", kmstest_pipe_name(pipe), w, h)
742
			run_test(data, test_crc_onscreen, w, h);
743 744

		igt_describe("Check if a given-size cursor is well-positioned outside the screen.");
745
		igt_subtest_f("pipe-%s-cursor-%dx%d-offscreen", kmstest_pipe_name(pipe), w, h)
746
			run_test(data, test_crc_offscreen, w, h);
747 748 749

		igt_describe("Check the smooth and pixel-by-pixel given-size cursor movements on"
		             "horizontal, vertical and diagonal.");
750
		igt_subtest_f("pipe-%s-cursor-%dx%d-sliding", kmstest_pipe_name(pipe), w, h)
751
			run_test(data, test_crc_sliding, w, h);
752 753

		igt_describe("Check random placement of a cursor with given size.");
754
		igt_subtest_f("pipe-%s-cursor-%dx%d-random", kmstest_pipe_name(pipe), w, h)
755
			run_test(data, test_crc_random, w, h);
756

757
		igt_describe("Check the rapid update of given-size cursor movements.");
758
		igt_subtest_f("pipe-%s-cursor-%dx%d-rapid-movement", kmstest_pipe_name(pipe), w, h) {
759 760 761
			run_test(data, test_rapid_movement, w, h);
		}

762
		igt_fixture
763 764 765 766 767 768 769 770 771
			igt_remove_fb(data->drm_fd, &data->fb);

		/*
		 * Test non-square cursors a bit on the platforms
		 * that support such things. And make it a bit more
		 * interesting by using a non-pot height.
		 */
		h /= 3;

772 773 774 775
		igt_fixture {
			if (has_nonsquare_cursors(data))
				create_cursor_fb(data, w, h);
		}
776 777

		/* Using created cursor FBs to test cursor support */
778
		igt_subtest_f("pipe-%s-cursor-%dx%d-onscreen", kmstest_pipe_name(pipe), w, h) {
779
			igt_require(has_nonsquare_cursors(data));
780 781
			run_test(data, test_crc_onscreen, w, h);
		}
782
		igt_subtest_f("pipe-%s-cursor-%dx%d-offscreen", kmstest_pipe_name(pipe), w, h) {
783
			igt_require(has_nonsquare_cursors(data));
784 785
			run_test(data, test_crc_offscreen, w, h);
		}
786
		igt_subtest_f("pipe-%s-cursor-%dx%d-sliding", kmstest_pipe_name(pipe), w, h) {
787
			igt_require(has_nonsquare_cursors(data));
788 789
			run_test(data, test_crc_sliding, w, h);
		}
790
		igt_subtest_f("pipe-%s-cursor-%dx%d-random", kmstest_pipe_name(pipe), w, h) {
791
			igt_require(has_nonsquare_cursors(data));
792 793
			run_test(data, test_crc_random, w, h);
		}
794

795 796 797
		igt_fixture
			igt_remove_fb(data->drm_fd, &data->fb);
	}
798 799
}

800 801
static data_t data;

Daniel Vetter's avatar
Daniel Vetter committed
802
igt_main
803
{
804
	uint64_t cursor_width = 64, cursor_height = 64;
805
	int ret;
806
	enum pipe pipe;
807 808

	igt_fixture {
809
		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
810

811
		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_WIDTH, &cursor_width);
812
		igt_assert(ret == 0 || errno == EINVAL);
813 814
		/* Not making use of cursor_height since it is same as width, still reading */
		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height);
815
		igt_assert(ret == 0 || errno == EINVAL);
816 817

		/* We assume width and height are same so max is assigned width */
818
		igt_assert_eq(cursor_width, cursor_height);
819

820
		kmstest_set_vt_graphics_mode();
821

822
		igt_require_pipe_crc(data.drm_fd);
823

824
		igt_display_require(&data.display, data.drm_fd);
825 826 827 828 829 830 831 832 833 834

		if (is_i915_device(data.drm_fd)) {
			data.bufmgr = drm_intel_bufmgr_gem_init(data.drm_fd, 4096);
			igt_assert(data.bufmgr);
			drm_intel_bufmgr_gem_enable_reuse(data.bufmgr);

			data.devid = intel_get_drm_devid(data.drm_fd);
			data.rendercopy = igt_get_render_copyfunc(data.devid);
		}
		igt_debug("Using %s for restoring test image\n", (data.rendercopy == NULL)?"Cairo":"rendercopy");
835 836
	}

837 838 839
	data.cursor_max_w = cursor_width;
	data.cursor_max_h = cursor_height;

840 841 842
	for_each_pipe_static(pipe)
		igt_subtest_group
			run_tests_on_pipe(&data, pipe);
843

844
	igt_fixture {
845 846 847 848 849 850 851 852 853 854
		if (data.pipe_crc != NULL) {
			igt_pipe_crc_stop(data.pipe_crc);
			igt_pipe_crc_free(data.pipe_crc);
		}

		if (data.bufmgr != NULL) {
			intel_batchbuffer_free(data.batch);
			drm_intel_bufmgr_destroy(data.bufmgr);
		}

855 856
		igt_display_fini(&data.display);
	}
857
}