igt_fb.c 35.9 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 25 26 27 28 29 30 31 32
/*
 * Copyright © 2013,2014 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.
 *
 * Authors:
 * 	Daniel Vetter <daniel.vetter@ffwll.ch>
 * 	Damien Lespiau <damien.lespiau@intel.com>
 */

#include <stdio.h>
#include <math.h>

#include "drmtest.h"
#include "igt_fb.h"
33
#include "igt_kms.h"
34
#include "ioctl_wrappers.h"
35
#include "intel_chipset.h"
36

37 38 39
/**
 * SECTION:igt_fb
 * @short_description: Framebuffer handling and drawing library
40
 * @title: Framebuffer
41
 * @include: igt.h
42 43 44
 *
 * This library contains helper functions for handling kms framebuffer objects
 * using #igt_fb structures to track all the metadata.  igt_create_fb() creates
45
 * a basic framebuffer and igt_remove_fb() cleans everything up again.
46 47 48 49 50 51 52
 *
 * It also supports drawing using the cairo library and provides some simplified
 * helper functions to easily draw test patterns. The main function to create a
 * cairo drawing context for a framebuffer object is igt_get_cairo_ctx().
 *
 * Finally it also pulls in the drm fourcc headers and provides some helper
 * functions to work with these pixel format codes.
53 54
 */

55 56 57 58 59 60 61 62 63 64 65
/* drm fourcc/cairo format maps */
#define DF(did, cid, _bpp, _depth)	\
	{ DRM_FORMAT_##did, CAIRO_FORMAT_##cid, # did, _bpp, _depth }
static struct format_desc_struct {
	uint32_t drm_id;
	cairo_format_t cairo_id;
	const char *name;
	int bpp;
	int depth;
} format_desc[] = {
	DF(RGB565,	RGB16_565,	16, 16),
Chris Wilson's avatar
Chris Wilson committed
66
	//DF(RGB888,	INVALID,	24, 24),
67 68 69 70 71 72 73 74 75
	DF(XRGB8888,	RGB24,		32, 24),
	DF(XRGB2101010,	RGB30,		32, 30),
	DF(ARGB8888,	ARGB32,		32, 32),
};
#undef DF

#define for_each_format(f)	\
	for (f = format_desc; f - format_desc < ARRAY_SIZE(format_desc); f++)

76 77 78 79 80 81 82 83 84 85 86 87 88
/**
 * igt_get_fb_tile_size:
 * @fd: the DRM file descriptor
 * @tiling: tiling layout of the framebuffer (as framebuffer modifier)
 * @fb_bpp: bits per pixel of the framebuffer
 * @width_ret: width of the tile in bytes
 * @height_ret: height of the tile in lines
 *
 * This function returns width and height of a tile based on the given tiling
 * format.
 */
void igt_get_fb_tile_size(int fd, uint64_t tiling, int fb_bpp,
			  unsigned *width_ret, unsigned *height_ret)
89 90 91 92 93 94 95
{
	switch (tiling) {
	case LOCAL_DRM_FORMAT_MOD_NONE:
		*width_ret = 64;
		*height_ret = 1;
		break;
	case LOCAL_I915_FORMAT_MOD_X_TILED:
96 97
		igt_require_intel(fd);
		if (intel_gen(intel_get_drm_devid(fd)) == 2) {
98 99 100 101 102 103 104 105
			*width_ret = 128;
			*height_ret = 16;
		} else {
			*width_ret = 512;
			*height_ret = 8;
		}
		break;
	case LOCAL_I915_FORMAT_MOD_Y_TILED:
106 107
		igt_require_intel(fd);
		if (intel_gen(intel_get_drm_devid(fd)) == 2) {
108 109
			*width_ret = 128;
			*height_ret = 16;
110
		} else if (IS_915(intel_get_drm_devid(fd))) {
111
			*width_ret = 512;
112 113
			*height_ret = 8;
		} else {
114
			*width_ret = 128;
115 116
			*height_ret = 32;
		}
117 118
		break;
	case LOCAL_I915_FORMAT_MOD_Yf_TILED:
119
		igt_require_intel(fd);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
		switch (fb_bpp) {
		case 8:
			*width_ret = 64;
			*height_ret = 64;
			break;
		case 16:
		case 32:
			*width_ret = 128;
			*height_ret = 32;
			break;
		case 64:
		case 128:
			*width_ret = 256;
			*height_ret = 16;
			break;
		default:
			igt_assert(false);
		}
		break;
	default:
		igt_assert(false);
	}
}
143

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/**
 * igt_calc_fb_size:
 * @fd: the DRM file descriptor
 * @width: width of the framebuffer in pixels
 * @height: height of the framebuffer in pixels
 * @bpp: bytes per pixel of the framebuffer
 * @tiling: tiling layout of the framebuffer (as framebuffer modifier)
 * @size_ret: returned size for the framebuffer
 * @stride_ret: returned stride for the framebuffer
 *
 * This function returns valid stride and size values for a framebuffer with the
 * specified parameters.
 */
void igt_calc_fb_size(int fd, int width, int height, int bpp, uint64_t tiling,
		      unsigned *size_ret, unsigned *stride_ret)
159
{
160 161
	unsigned int tile_width, tile_height, stride, size;
	int byte_width = width * (bpp / 8);
162

163 164
	igt_get_fb_tile_size(fd, tiling, bpp, &tile_width, &tile_height);

165 166
	if (tiling != LOCAL_DRM_FORMAT_MOD_NONE &&
	    intel_gen(intel_get_drm_devid(fd)) <= 3) {
167 168
		int v;

169 170 171
		/* Round the tiling up to the next power-of-two and the region
		 * up to the next pot fence size so that this works on all
		 * generations.
172
		 *
173 174
		 * This can still fail if the framebuffer is too large to be
		 * tiled. But then that failure is expected.
175 176 177 178 179 180 181 182 183 184
		 */

		v = width * bpp / 8;
		for (stride = 512; stride < v; stride *= 2)
			;

		v = stride * height;
		for (size = 1024*1024; size < v; size *= 2)
			;
	} else {
185 186
		stride = ALIGN(byte_width, tile_width);
		size = stride * ALIGN(height, tile_height);
187 188
	}

189 190 191 192
	*stride_ret = stride;
	*size_ret = size;
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
/**
 * igt_fb_mod_to_tiling:
 * @modifier: DRM framebuffer modifier
 *
 * This function converts a DRM framebuffer modifier to its corresponding
 * tiling constant.
 *
 * Returns:
 * A tiling constant
 */
uint64_t igt_fb_mod_to_tiling(uint64_t modifier)
{
	switch (modifier) {
	case LOCAL_DRM_FORMAT_MOD_NONE:
		return I915_TILING_NONE;
	case LOCAL_I915_FORMAT_MOD_X_TILED:
		return I915_TILING_X;
	case LOCAL_I915_FORMAT_MOD_Y_TILED:
		return I915_TILING_Y;
	case LOCAL_I915_FORMAT_MOD_Yf_TILED:
		return I915_TILING_Yf;
	default:
		igt_assert(0);
	}
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
/**
 * igt_fb_tiling_to_mod:
 * @tiling: DRM framebuffer tiling
 *
 * This function converts a DRM framebuffer tiling to its corresponding
 * modifier constant.
 *
 * Returns:
 * A modifier constant
 */
uint64_t igt_fb_tiling_to_mod(uint64_t tiling)
{
	switch (tiling) {
	case I915_TILING_NONE:
		return LOCAL_DRM_FORMAT_MOD_NONE;
	case I915_TILING_X:
		return LOCAL_I915_FORMAT_MOD_X_TILED;
	case I915_TILING_Y:
		return LOCAL_I915_FORMAT_MOD_Y_TILED;
	case I915_TILING_Yf:
		return LOCAL_I915_FORMAT_MOD_Yf_TILED;
	default:
		igt_assert(0);
	}
}

245 246 247 248 249
/* helpers to create nice-looking framebuffers */
static int create_bo_for_fb(int fd, int width, int height, uint32_t format,
			    uint64_t tiling, unsigned size, unsigned stride,
			    unsigned *size_ret, unsigned *stride_ret,
			    bool *is_dumb)
250 251 252 253
{
	int bpp = igt_drm_format_to_bpp(format);
	int bo;

254 255
	if (tiling || size || stride) {
		unsigned calculated_size, calculated_stride;
256

257 258
		igt_calc_fb_size(fd, width, height, bpp, tiling,
				 &calculated_size, &calculated_stride);
259 260
		if (stride == 0)
			stride = calculated_stride;
261 262
		if (size == 0)
			size = calculated_size;
263 264 265 266 267

		if (is_dumb)
			*is_dumb = false;

		if (is_i915_device(fd)) {
268
			uint32_t *ptr;
269 270

			bo = gem_create(fd, size);
271
			gem_set_tiling(fd, bo, igt_fb_mod_to_tiling(tiling), stride);
272

273 274 275
			/* Ensure the framebuffer is preallocated */
			ptr = gem_mmap__gtt(fd, bo, size, PROT_READ);
			igt_assert(*ptr == 0);
276
			gem_munmap(ptr, size);
277

278 279 280 281 282 283 284 285
			if (size_ret)
				*size_ret = size;

			if (stride_ret)
				*stride_ret = stride;

			return bo;
		} else {
286
			bool driver_has_gem_api = false;
287

288
			igt_require(driver_has_gem_api);
289 290 291 292 293 294 295 296 297 298 299
			return -EINVAL;
		}
	} else {
		if (is_dumb)
			*is_dumb = true;

		return kmstest_dumb_create(fd, width, height, bpp, stride_ret,
					   size_ret);
	}
}

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
/**
 * igt_create_bo_with_dimensions:
 * @fd: open drm file descriptor
 * @width: width of the buffer object in pixels
 * @height: height of the buffer object in pixels
 * @format: drm fourcc pixel format code
 * @modifier: modifier corresponding to the tiling layout of the buffer object
 * @stride: stride of the buffer object in bytes (0 for automatic stride)
 * @size_ret: size of the buffer object as created by the kernel
 * @stride_ret: stride of the buffer object as created by the kernel
 * @is_dumb: whether the created buffer object is a dumb buffer or not
 *
 * This function allocates a gem buffer object matching the requested
 * properties.
 *
 * Returns:
 * The kms id of the created buffer object.
 */
int igt_create_bo_with_dimensions(int fd, int width, int height,
				  uint32_t format, uint64_t modifier,
				  unsigned stride, unsigned *size_ret,
				  unsigned *stride_ret, bool *is_dumb)
322
{
323 324
	return create_bo_for_fb(fd, width, height, format, modifier, 0, stride,
			     size_ret, stride_ret, is_dumb);
325 326
}

327 328 329 330 331 332 333 334
/**
 * igt_paint_color:
 * @cr: cairo drawing context
 * @x: pixel x-coordination of the fill rectangle
 * @y: pixel y-coordination of the fill rectangle
 * @w: width of the fill rectangle
 * @h: height of the fill rectangle
 * @r: red value to use as fill color
335
 * @g: green value to use as fill color
336 337 338 339 340
 * @b: blue value to use as fill color
 *
 * This functions draws a solid rectangle with the given color using the drawing
 * context @cr.
 */
341
void igt_paint_color(cairo_t *cr, int x, int y, int w, int h,
342
		     double r, double g, double b)
343 344 345 346 347 348
{
	cairo_rectangle(cr, x, y, w, h);
	cairo_set_source_rgb(cr, r, g, b);
	cairo_fill(cr);
}

349 350 351 352 353 354 355 356
/**
 * igt_paint_color_alpha:
 * @cr: cairo drawing context
 * @x: pixel x-coordination of the fill rectangle
 * @y: pixel y-coordination of the fill rectangle
 * @w: width of the fill rectangle
 * @h: height of the fill rectangle
 * @r: red value to use as fill color
357
 * @g: green value to use as fill color
358 359 360 361 362 363
 * @b: blue value to use as fill color
 * @a: alpha value to use as fill color
 *
 * This functions draws a rectangle with the given color and alpha values using
 * the drawing context @cr.
 */
364
void igt_paint_color_alpha(cairo_t *cr, int x, int y, int w, int h,
365
			   double r, double g, double b, double a)
366 367 368 369 370 371
{
	cairo_rectangle(cr, x, y, w, h);
	cairo_set_source_rgba(cr, r, g, b, a);
	cairo_fill(cr);
}

372 373 374 375 376 377 378 379
/**
 * igt_paint_color_gradient:
 * @cr: cairo drawing context
 * @x: pixel x-coordination of the fill rectangle
 * @y: pixel y-coordination of the fill rectangle
 * @w: width of the fill rectangle
 * @h: height of the fill rectangle
 * @r: red value to use as fill color
380
 * @g: green value to use as fill color
381 382 383 384 385
 * @b: blue value to use as fill color
 *
 * This functions draws a gradient into the rectangle which fades in from black
 * to the given values using the drawing context @cr.
 */
386
void
387
igt_paint_color_gradient(cairo_t *cr, int x, int y, int w, int h,
388
			 int r, int g, int b)
389 390 391 392 393 394 395 396 397 398 399 400 401
{
	cairo_pattern_t *pat;

	pat = cairo_pattern_create_linear(x, y, x + w, y + h);
	cairo_pattern_add_color_stop_rgba(pat, 1, 0, 0, 0, 1);
	cairo_pattern_add_color_stop_rgba(pat, 0, r, g, b, 1);

	cairo_rectangle(cr, x, y, w, h);
	cairo_set_source(cr, pat);
	cairo_fill(cr);
	cairo_pattern_destroy(pat);
}

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
/**
 * igt_paint_color_gradient_range:
 * @cr: cairo drawing context
 * @x: pixel x-coordination of the fill rectangle
 * @y: pixel y-coordination of the fill rectangle
 * @w: width of the fill rectangle
 * @h: height of the fill rectangle
 * @sr: red value to use as start gradient color
 * @sg: green value to use as start gradient color
 * @sb: blue value to use as start gradient color
 * @er: red value to use as end gradient color
 * @eg: green value to use as end gradient color
 * @eb: blue value to use as end gradient color
 *
 * This functions draws a gradient into the rectangle which fades in
 * from one color to the other using the drawing context @cr.
 */
void
igt_paint_color_gradient_range(cairo_t *cr, int x, int y, int w, int h,
			       double sr, double sg, double sb,
			       double er, double eg, double eb)
{
	cairo_pattern_t *pat;

	pat = cairo_pattern_create_linear(x, y, x + w, y + h);
	cairo_pattern_add_color_stop_rgba(pat, 1, sr, sg, sb, 1);
	cairo_pattern_add_color_stop_rgba(pat, 0, er, eg, eb, 1);

	cairo_rectangle(cr, x, y, w, h);
	cairo_set_source(cr, pat);
	cairo_fill(cr);
	cairo_pattern_destroy(pat);
}

436 437 438 439 440 441 442 443 444 445 446
static void
paint_test_patterns(cairo_t *cr, int width, int height)
{
	double gr_height, gr_width;
	int x, y;

	y = height * 0.10;
	gr_width = width * 0.75;
	gr_height = height * 0.08;
	x = (width / 2) - (gr_width / 2);

447
	igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 1, 0, 0);
448 449

	y += gr_height;
450
	igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 0, 1, 0);
451 452

	y += gr_height;
453
	igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 0, 0, 1);
454 455

	y += gr_height;
456
	igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 1, 1, 1);
457 458
}

459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
/**
 * igt_cairo_printf_line:
 * @cr: cairo drawing context
 * @align: text alignment
 * @yspacing: additional y-direction feed after this line
 * @fmt: format string
 * @...: optional arguments used in the format string
 *
 * This is a little helper to draw text onto framebuffers. All the initial setup
 * (like setting the font size and the moving to the starting position) still
 * needs to be done manually with explicit cairo calls on @cr.
 *
 * Returns:
 * The width of the drawn text.
 */
474
int igt_cairo_printf_line(cairo_t *cr, enum igt_text_align align,
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
				double yspacing, const char *fmt, ...)
{
	double x, y, xofs, yofs;
	cairo_text_extents_t extents;
	char *text;
	va_list ap;
	int ret;

	va_start(ap, fmt);
	ret = vasprintf(&text, fmt, ap);
	igt_assert(ret >= 0);
	va_end(ap);

	cairo_text_extents(cr, text, &extents);

	xofs = yofs = 0;
	if (align & align_right)
		xofs = -extents.width;
	else if (align & align_hcenter)
		xofs = -extents.width / 2;

	if (align & align_top)
		yofs = extents.height;
	else if (align & align_vcenter)
		yofs = extents.height / 2;

	cairo_get_current_point(cr, &x, &y);
	if (xofs || yofs)
		cairo_rel_move_to(cr, xofs, yofs);

	cairo_text_path(cr, text);
	cairo_set_source_rgb(cr, 0, 0, 0);
	cairo_stroke_preserve(cr);
	cairo_set_source_rgb(cr, 1, 1, 1);
	cairo_fill(cr);

	cairo_move_to(cr, x, y + extents.height + yspacing);

	free(text);

	return extents.width;
}

static void
paint_marker(cairo_t *cr, int x, int y)
{
521
	enum igt_text_align align;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
	int xoff, yoff;

	cairo_move_to(cr, x, y - 20);
	cairo_line_to(cr, x, y + 20);
	cairo_move_to(cr, x - 20, y);
	cairo_line_to(cr, x + 20, y);
	cairo_new_sub_path(cr);
	cairo_arc(cr, x, y, 10, 0, M_PI * 2);
	cairo_set_line_width(cr, 4);
	cairo_set_source_rgb(cr, 0, 0, 0);
	cairo_stroke_preserve(cr);
	cairo_set_source_rgb(cr, 1, 1, 1);
	cairo_set_line_width(cr, 2);
	cairo_stroke(cr);

	xoff = x ? -20 : 20;
	align = x ? align_right : align_left;

	yoff = y ? -20 : 20;
	align |= y ? align_bottom : align_top;

	cairo_move_to(cr, x + xoff, y + yoff);
	cairo_set_font_size(cr, 18);
545
	igt_cairo_printf_line(cr, align, 0, "(%d, %d)", x, y);
546 547
}

548 549 550 551 552 553 554 555 556 557 558 559 560 561
/**
 * igt_paint_test_pattern:
 * @cr: cairo drawing context
 * @width: width of the visible area
 * @height: height of the visible area
 *
 * This functions draws an entire set of test patterns for the given visible
 * area using the drawing context @cr. This is useful for manual visual
 * inspection of displayed framebuffers.
 *
 * The test patterns include
 *  - corner markers to check for over/underscan and
 *  - a set of color and b/w gradients.
 */
562
void igt_paint_test_pattern(cairo_t *cr, int width, int height)
563 564 565 566 567 568 569 570 571 572 573 574 575 576
{
	paint_test_patterns(cr, width, height);

	cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);

	/* Paint corner markers */
	paint_marker(cr, 0, 0);
	paint_marker(cr, width, 0);
	paint_marker(cr, 0, height);
	paint_marker(cr, width, height);

	igt_assert(!cairo_status(cr));
}

577 578 579 580 581 582 583 584 585
static cairo_status_t
stdio_read_func(void *closure, unsigned char* data, unsigned int size)
{
	if (fread(data, 1, size, (FILE*)closure) != size)
		return CAIRO_STATUS_READ_ERROR;

	return CAIRO_STATUS_SUCCESS;
}

586 587 588 589 590 591 592 593 594 595 596 597
cairo_surface_t *igt_cairo_image_surface_create_from_png(const char *filename)
{
	cairo_surface_t *image;
	FILE *f;

	f = igt_fopen_data(filename);
	image = cairo_image_surface_create_from_png_stream(&stdio_read_func, f);
	fclose(f);

	return image;
}

598 599 600 601 602 603 604 605 606
/**
 * igt_paint_image:
 * @cr: cairo drawing context
 * @filename: filename of the png image to draw
 * @dst_x: pixel x-coordination of the destination rectangle
 * @dst_y: pixel y-coordination of the destination rectangle
 * @dst_width: width of the destination rectangle
 * @dst_height: height of the destination rectangle
 *
607 608
 * This function can be used to draw a scaled version of the supplied png image,
 * which is loaded from the package data directory.
609
 */
610
void igt_paint_image(cairo_t *cr, const char *filename,
611
		     int dst_x, int dst_y, int dst_width, int dst_height)
612 613 614 615 616
{
	cairo_surface_t *image;
	int img_width, img_height;
	double scale_x, scale_y;

617
	image = igt_cairo_image_surface_create_from_png(filename);
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
	igt_assert(cairo_surface_status(image) == CAIRO_STATUS_SUCCESS);

	img_width = cairo_image_surface_get_width(image);
	img_height = cairo_image_surface_get_height(image);

	scale_x = (double)dst_width / img_width;
	scale_y = (double)dst_height / img_height;

	cairo_save(cr);

	cairo_translate(cr, dst_x, dst_y);
	cairo_scale(cr, scale_x, scale_y);
	cairo_set_source_surface(cr, image, 0, 0);
	cairo_paint(cr);

	cairo_surface_destroy(image);

	cairo_restore(cr);
}

638
/**
639
 * igt_create_fb_with_bo_size:
640 641 642 643
 * @fd: open i915 drm file descriptor
 * @width: width of the framebuffer in pixel
 * @height: height of the framebuffer in pixel
 * @format: drm fourcc pixel format code
644
 * @tiling: tiling layout of the framebuffer (as framebuffer modifier)
645
 * @fb: pointer to an #igt_fb structure
646
 * @bo_size: size of the backing bo (0 for automatic size)
647
 * @bo_stride: stride of the backing bo (0 for automatic stride)
648 649 650
 *
 * This function allocates a gem buffer object suitable to back a framebuffer
 * with the requested properties and then wraps it up in a drm framebuffer
651
 * object of the requested size. All metadata is stored in @fb.
652 653 654 655 656
 *
 * The backing storage of the framebuffer is filled with all zeros, i.e. black
 * for rgb pixel formats.
 *
 * Returns:
657
 * The kms id of the created framebuffer.
658
 */
659 660
unsigned int
igt_create_fb_with_bo_size(int fd, int width, int height,
661
			   uint32_t format, uint64_t tiling,
662 663
			   struct igt_fb *fb, unsigned bo_size,
			   unsigned bo_stride)
664 665 666 667 668
{
	uint32_t fb_id;

	memset(fb, 0, sizeof(*fb));

669 670
	igt_debug("%s(width=%d, height=%d, format=0x%x, tiling=0x%"PRIx64", size=%d)\n",
		  __func__, width, height, format, tiling, bo_size);
671 672 673 674
	fb->gem_handle = create_bo_for_fb(fd, width, height, format, tiling,
					  bo_size, bo_stride, &fb->size,
					  &fb->stride, &fb->is_dumb);
	igt_assert(fb->gem_handle > 0);
675 676

	igt_debug("%s(handle=%d, pitch=%d)\n",
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
		  __func__, fb->gem_handle, fb->stride);

	if (tiling != LOCAL_DRM_FORMAT_MOD_NONE &&
	    tiling != LOCAL_I915_FORMAT_MOD_X_TILED) {
		do_or_die(__kms_addfb(fd, fb->gem_handle, width, height,
				      fb->stride, format, tiling,
				      LOCAL_DRM_MODE_FB_MODIFIERS, &fb_id));
	} else {
		uint32_t handles[4];
		uint32_t pitches[4];
		uint32_t offsets[4];

		memset(handles, 0, sizeof(handles));
		memset(pitches, 0, sizeof(pitches));
		memset(offsets, 0, sizeof(offsets));

		handles[0] = fb->gem_handle;
		pitches[0] = fb->stride;

		do_or_die(drmModeAddFB2(fd, width, height, format,
					handles, pitches, offsets,
					&fb_id, 0));
	}
700 701 702

	fb->width = width;
	fb->height = height;
703
	fb->tiling = tiling;
704 705
	fb->drm_format = format;
	fb->fb_id = fb_id;
Tomeu Vizoso's avatar
Tomeu Vizoso committed
706
	fb->fd = fd;
707 708 709 710

	return fb_id;
}

711 712 713 714 715 716
/**
 * igt_create_fb:
 * @fd: open i915 drm file descriptor
 * @width: width of the framebuffer in pixel
 * @height: height of the framebuffer in pixel
 * @format: drm fourcc pixel format code
717
 * @tiling: tiling layout of the framebuffer
718 719 720 721 722 723 724 725 726 727
 * @fb: pointer to an #igt_fb structure
 *
 * This function allocates a gem buffer object suitable to back a framebuffer
 * with the requested properties and then wraps it up in a drm framebuffer
 * object. All metadata is stored in @fb.
 *
 * The backing storage of the framebuffer is filled with all zeros, i.e. black
 * for rgb pixel formats.
 *
 * Returns:
728
 * The kms id of the created framebuffer.
729 730
 */
unsigned int igt_create_fb(int fd, int width, int height, uint32_t format,
731
			   uint64_t tiling, struct igt_fb *fb)
732
{
733 734
	return igt_create_fb_with_bo_size(fd, width, height, format, tiling, fb,
					  0, 0);
735 736
}

737 738 739 740 741 742
/**
 * igt_create_color_fb:
 * @fd: open i915 drm file descriptor
 * @width: width of the framebuffer in pixel
 * @height: height of the framebuffer in pixel
 * @format: drm fourcc pixel format code
743
 * @tiling: tiling layout of the framebuffer
744
 * @r: red value to use as fill color
745
 * @g: green value to use as fill color
746 747 748 749 750 751 752 753 754 755 756 757 758 759
 * @b: blue value to use as fill color
 * @fb: pointer to an #igt_fb structure
 *
 * This function allocates a gem buffer object suitable to back a framebuffer
 * with the requested properties and then wraps it up in a drm framebuffer
 * object. All metadata is stored in @fb.
 *
 * Compared to igt_create_fb() this function also fills the entire framebuffer
 * with the given color, which is useful for some simple pipe crc based tests.
 *
 * Returns:
 * The kms id of the created framebuffer on success or a negative error code on
 * failure.
 */
760
unsigned int igt_create_color_fb(int fd, int width, int height,
761
				 uint32_t format, uint64_t tiling,
762 763
				 double r, double g, double b,
				 struct igt_fb *fb /* out */)
764 765 766 767
{
	unsigned int fb_id;
	cairo_t *cr;

768
	fb_id = igt_create_fb(fd, width, height, format, tiling, fb);
769 770
	igt_assert(fb_id);

771 772
	cr = igt_get_cairo_ctx(fd, fb);
	igt_paint_color(cr, 0, 0, width, height, r, g, b);
773 774 775 776 777 778
	igt_assert(cairo_status(cr) == 0);
	cairo_destroy(cr);

	return fb_id;
}

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
/**
 * igt_create_pattern_fb:
 * @fd: open i915 drm file descriptor
 * @width: width of the framebuffer in pixel
 * @height: height of the framebuffer in pixel
 * @format: drm fourcc pixel format code
 * @tiling: tiling layout of the framebuffer
 * @fb: pointer to an #igt_fb structure
 *
 * This function allocates a gem buffer object suitable to back a framebuffer
 * with the requested properties and then wraps it up in a drm framebuffer
 * object. All metadata is stored in @fb.
 *
 * Compared to igt_create_fb() this function also draws the standard test pattern
 * into the framebuffer.
 *
 * Returns:
 * The kms id of the created framebuffer on success or a negative error code on
 * failure.
 */
unsigned int igt_create_pattern_fb(int fd, int width, int height,
				   uint32_t format, uint64_t tiling,
				   struct igt_fb *fb /* out */)
{
	unsigned int fb_id;
	cairo_t *cr;

	fb_id = igt_create_fb(fd, width, height, format, tiling, fb);
	igt_assert(fb_id);

	cr = igt_get_cairo_ctx(fd, fb);
	igt_paint_test_pattern(cr, width, height);
	igt_assert(cairo_status(cr) == 0);
	cairo_destroy(cr);

	return fb_id;
}

/**
 * igt_create_color_pattern_fb:
 * @fd: open i915 drm file descriptor
 * @width: width of the framebuffer in pixel
 * @height: height of the framebuffer in pixel
 * @format: drm fourcc pixel format code
 * @tiling: tiling layout of the framebuffer
 * @r: red value to use as fill color
 * @g: green value to use as fill color
 * @b: blue value to use as fill color
 * @fb: pointer to an #igt_fb structure
 *
 * This function allocates a gem buffer object suitable to back a framebuffer
 * with the requested properties and then wraps it up in a drm framebuffer
 * object. All metadata is stored in @fb.
 *
 * Compared to igt_create_fb() this function also fills the entire framebuffer
 * with the given color, and then draws the standard test pattern into the
 * framebuffer.
 *
 * Returns:
 * The kms id of the created framebuffer on success or a negative error code on
 * failure.
 */
unsigned int igt_create_color_pattern_fb(int fd, int width, int height,
					 uint32_t format, uint64_t tiling,
					 double r, double g, double b,
					 struct igt_fb *fb /* out */)
{
	unsigned int fb_id;
	cairo_t *cr;

	fb_id = igt_create_fb(fd, width, height, format, tiling, fb);
	igt_assert(fb_id);

	cr = igt_get_cairo_ctx(fd, fb);
	igt_paint_color(cr, 0, 0, width, height, r, g, b);
	igt_paint_test_pattern(cr, width, height);
	igt_assert(cairo_status(cr) == 0);
	cairo_destroy(cr);

	return fb_id;
}

/**
 * igt_create_image_fb:
 * @drm_fd: open i915 drm file descriptor
 * @width: width of the framebuffer in pixel or 0
 * @height: height of the framebuffer in pixel or 0
 * @format: drm fourcc pixel format code
 * @tiling: tiling layout of the framebuffer
 * @filename: filename of the png image to draw
 * @fb: pointer to an #igt_fb structure
 *
 * Create a framebuffer with the specified image. If @width is zero the
 * image width will be used. If @height is zero the image height will be used.
 *
 * Returns:
 * The kms id of the created framebuffer on success or a negative error code on
 * failure.
 */
unsigned int igt_create_image_fb(int fd, int width, int height,
				 uint32_t format, uint64_t tiling,
				 const char *filename,
				 struct igt_fb *fb /* out */)
{
	cairo_surface_t *image;
	uint32_t fb_id;
	cairo_t *cr;

887
	image = igt_cairo_image_surface_create_from_png(filename);
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
	igt_assert(cairo_surface_status(image) == CAIRO_STATUS_SUCCESS);
	if (width == 0)
		width = cairo_image_surface_get_width(image);
	if (height == 0)
		height = cairo_image_surface_get_height(image);
	cairo_surface_destroy(image);

	fb_id = igt_create_fb(fd, width, height, format, tiling, fb);

	cr = igt_get_cairo_ctx(fd, fb);
	igt_paint_image(cr, filename, 0, 0, width, height);
	igt_assert(cairo_status(cr) == 0);
	cairo_destroy(cr);

	return fb_id;
}

905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
struct box {
	int x, y, width, height;
};

struct stereo_fb_layout {
	int fb_width, fb_height;
	struct box left, right;
};

static void box_init(struct box *box, int x, int y, int bwidth, int bheight)
{
	box->x = x;
	box->y = y;
	box->width = bwidth;
	box->height = bheight;
}


static void stereo_fb_layout_from_mode(struct stereo_fb_layout *layout,
				       drmModeModeInfo *mode)
{
	unsigned int format = mode->flags & DRM_MODE_FLAG_3D_MASK;
	const int hdisplay = mode->hdisplay, vdisplay = mode->vdisplay;
	int middle;

	switch (format) {
	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
		layout->fb_width = hdisplay;
		layout->fb_height = vdisplay;

		middle = vdisplay / 2;
		box_init(&layout->left, 0, 0, hdisplay, middle);
		box_init(&layout->right,
			 0, middle, hdisplay, vdisplay - middle);
		break;
	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
		layout->fb_width = hdisplay;
		layout->fb_height = vdisplay;

		middle = hdisplay / 2;
		box_init(&layout->left, 0, 0, middle, vdisplay);
		box_init(&layout->right,
			 middle, 0, hdisplay - middle, vdisplay);
		break;
	case DRM_MODE_FLAG_3D_FRAME_PACKING:
	{
		int vactive_space = mode->vtotal - vdisplay;

		layout->fb_width = hdisplay;
		layout->fb_height = 2 * vdisplay + vactive_space;

		box_init(&layout->left,
			 0, 0, hdisplay, vdisplay);
		box_init(&layout->right,
			 0, vdisplay + vactive_space, hdisplay, vdisplay);
		break;
	}
	default:
		igt_assert(0);
	}
}

/**
 * igt_create_stereo_fb:
 * @drm_fd: open i915 drm file descriptor
 * @mode: A stereo 3D mode.
 * @format: drm fourcc pixel format code
 * @tiling: tiling layout of the framebuffer
 *
 * Create a framebuffer for use with the stereo 3D mode specified by @mode.
 *
 * Returns:
 * The kms id of the created framebuffer on success or a negative error code on
 * failure.
 */
unsigned int igt_create_stereo_fb(int drm_fd, drmModeModeInfo *mode,
981
				  uint32_t format, uint64_t tiling)
982 983 984 985 986 987 988 989 990 991 992
{
	struct stereo_fb_layout layout;
	cairo_t *cr;
	uint32_t fb_id;
	struct igt_fb fb;

	stereo_fb_layout_from_mode(&layout, mode);
	fb_id = igt_create_fb(drm_fd, layout.fb_width, layout.fb_height, format,
			      tiling, &fb);
	cr = igt_get_cairo_ctx(drm_fd, &fb);

993
	igt_paint_image(cr, "1080p-left.png",
994 995
			layout.left.x, layout.left.y,
			layout.left.width, layout.left.height);
996
	igt_paint_image(cr, "1080p-right.png",
997 998 999 1000 1001 1002 1003 1004
			layout.right.x, layout.right.y,
			layout.right.width, layout.right.height);

	cairo_destroy(cr);

	return fb_id;
}

1005 1006 1007 1008 1009 1010 1011 1012
static cairo_format_t drm_format_to_cairo(uint32_t drm_format)
{
	struct format_desc_struct *f;

	for_each_format(f)
		if (f->drm_id == drm_format)
			return f->cairo_id;

1013 1014
	igt_assert_f(0, "can't find a cairo format for %08x (%s)\n",
		     drm_format, igt_format_str(drm_format));
1015 1016
}

1017 1018 1019 1020 1021 1022 1023
struct fb_blit_upload {
	int fd;
	struct igt_fb *fb;
	struct {
		uint32_t handle;
		unsigned size, stride;
		uint8_t *map;
1024
		bool is_dumb;
1025 1026 1027 1028 1029 1030 1031
	} linear;
};

static void destroy_cairo_surface__blit(void *arg)
{
	struct fb_blit_upload *blit = arg;
	struct igt_fb *fb = blit->fb;
1032
	unsigned int obj_tiling = igt_fb_mod_to_tiling(fb->tiling);
1033

1034
	gem_munmap(blit->linear.map, blit->linear.size);
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
	fb->cairo_surface = NULL;

	gem_set_domain(blit->fd, blit->linear.handle,
			I915_GEM_DOMAIN_GTT, 0);

	igt_blitter_fast_copy__raw(blit->fd,
				   blit->linear.handle,
				   blit->linear.stride,
				   I915_TILING_NONE,
				   0, 0, /* src_x, src_y */
				   fb->width, fb->height,
1046
				   igt_drm_format_to_bpp(fb->drm_format),
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
				   fb->gem_handle,
				   fb->stride,
				   obj_tiling,
				   0, 0 /* dst_x, dst_y */);

	gem_sync(blit->fd, blit->linear.handle);
	gem_close(blit->fd, blit->linear.handle);

	free(blit);
}

static void create_cairo_surface__blit(int fd, struct igt_fb *fb)
{
	struct fb_blit_upload *blit;
	cairo_format_t cairo_format;
1062
	unsigned int obj_tiling = igt_fb_mod_to_tiling(fb->tiling);
1063 1064 1065 1066 1067 1068 1069 1070 1071

	blit = malloc(sizeof(*blit));
	igt_assert(blit);

	/*
	 * We create a linear BO that we'll map for the CPU to write to (using
	 * cairo). This linear bo will be then blitted to its final
	 * destination, tiling it at the same time.
	 */
1072 1073 1074 1075 1076 1077 1078 1079
	blit->linear.handle = create_bo_for_fb(fd, fb->width, fb->height,
					       fb->drm_format,
					       LOCAL_DRM_FORMAT_MOD_NONE, 0,
					       0, &blit->linear.size,
					       &blit->linear.stride,
					       &blit->linear.is_dumb);

	igt_assert(blit->linear.handle > 0);
1080 1081 1082

	blit->fd = fd;
	blit->fb = fb;
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093

	/* Copy fb content to linear BO */
	gem_set_domain(fd, blit->linear.handle,
			I915_GEM_DOMAIN_GTT, 0);

	igt_blitter_fast_copy__raw(fd,
				   fb->gem_handle,
				   fb->stride,
				   obj_tiling,
				   0, 0, /* src_x, src_y */
				   fb->width, fb->height,
1094
				   igt_drm_format_to_bpp(fb->drm_format),
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
				   blit->linear.handle,
				   blit->linear.stride,
				   I915_TILING_NONE,
				   0, 0 /* dst_x, dst_y */);

	gem_sync(fd, blit->linear.handle);

	gem_set_domain(fd, blit->linear.handle,
		       I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);

	/* Setup cairo context */
1106
	blit->linear.map = gem_mmap__cpu(fd,
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
					 blit->linear.handle,
					 0,
					 blit->linear.size,
					 PROT_READ | PROT_WRITE);

	cairo_format = drm_format_to_cairo(fb->drm_format);
	fb->cairo_surface =
		cairo_image_surface_create_for_data(blit->linear.map,
						    cairo_format,
						    fb->width, fb->height,
						    blit->linear.stride);
1118
	fb->domain = I915_GEM_DOMAIN_GTT;
1119 1120 1121 1122 1123 1124

	cairo_surface_set_user_data(fb->cairo_surface,
				    (cairo_user_data_key_t *)create_cairo_surface__blit,
				    blit, destroy_cairo_surface__blit);
}

Tomeu Vizoso's avatar
Tomeu Vizoso committed
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
/**
 * igt_dirty_fb:
 * @fd: open drm file descriptor
 * @fb: pointer to an #igt_fb structure
 *
 * Flushes out the whole framebuffer.
 *
 * Returns: 0 upon success.
 */
int igt_dirty_fb(int fd, struct igt_fb *fb)
{
	return drmModeDirtyFB(fb->fd, fb->fb_id, NULL, 0);
}

1139
static void destroy_cairo_surface__gtt(void *arg)
1140
{
1141
	struct igt_fb *fb = arg;
1142

1143
	gem_munmap(cairo_image_surface_get_data(fb->cairo_surface), fb->size);
1144
	fb->cairo_surface = NULL;
Tomeu Vizoso's avatar
Tomeu Vizoso committed
1145 1146 1147

	if (fb->is_dumb)
		igt_dirty_fb(fb->fd, fb);
1148 1149
}

1150 1151
static void create_cairo_surface__gtt(int fd, struct igt_fb *fb)
{
Tomeu Vizoso's avatar
Tomeu Vizoso committed
1152 1153 1154 1155 1156 1157 1158 1159
	void *ptr;

	if (fb->is_dumb)
		ptr = kmstest_dumb_map_buffer(fd, fb->gem_handle, fb->size,
					      PROT_READ | PROT_WRITE);
	else
		ptr = gem_mmap__gtt(fd, fb->gem_handle, fb->size,
				    PROT_READ | PROT_WRITE);
1160

1161
	fb->cairo_surface =
1162
		cairo_image_surface_create_for_data(ptr,
1163 1164
						    drm_format_to_cairo(fb->drm_format),
						    fb->width, fb->height, fb->stride);
1165
	fb->domain = I915_GEM_DOMAIN_GTT;
1166 1167 1168 1169 1170 1171

	cairo_surface_set_user_data(fb->cairo_surface,
				    (cairo_user_data_key_t *)create_cairo_surface__gtt,
				    fb, destroy_cairo_surface__gtt);
}

1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
/**
 * igt_get_cairo_surface:
 * @fd: open drm file descriptor
 * @fb: pointer to an #igt_fb structure
 *
 * This function stores the contents of the supplied framebuffer into a cairo
 * surface and returns it.
 *
 * Returns:
 * A pointer to a cairo surface with the contents of the framebuffer.
 */
cairo_surface_t *igt_get_cairo_surface(int fd, struct igt_fb *fb)
1184
{
1185 1186 1187 1188 1189 1190 1191
	if (fb->cairo_surface == NULL) {
		if (fb->tiling == LOCAL_I915_FORMAT_MOD_Y_TILED ||
		    fb->tiling == LOCAL_I915_FORMAT_MOD_Yf_TILED)
			create_cairo_surface__blit(fd, fb);
		else
			create_cairo_surface__gtt(fd, fb);
	}
1192

Tomeu Vizoso's avatar
Tomeu Vizoso committed
1193 1194 1195
	if (!fb->is_dumb)
		gem_set_domain(fd, fb->gem_handle, I915_GEM_DOMAIN_CPU,
			       I915_GEM_DOMAIN_CPU);
1196 1197

	igt_assert(cairo_surface_status(fb->cairo_surface) == CAIRO_STATUS_SUCCESS);
1198
	return fb->cairo_surface;
1199 1200
}

1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
/**
 * igt_get_cairo_ctx:
 * @fd: open i915 drm file descriptor
 * @fb: pointer to an #igt_fb structure
 *
 * This initializes a cairo surface for @fb and then allocates a drawing context
 * for it. The return cairo drawing context should be released by calling
 * cairo_destroy(). This also sets a default font for drawing text on
 * framebuffers.
 *
 * Returns:
 * The created cairo drawing context.
 */
1214
cairo_t *igt_get_cairo_ctx(int fd, struct igt_fb *fb)
1215 1216 1217 1218
{
	cairo_surface_t *surface;
	cairo_t *cr;

1219
	surface = igt_get_cairo_surface(fd, fb);
1220 1221
	cr = cairo_create(surface);
	cairo_surface_destroy(surface);
1222
	igt_assert(cairo_status(cr) == CAIRO_STATUS_SUCCESS);
1223

1224 1225
	cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
			       CAIRO_FONT_WEIGHT_NORMAL);
1226 1227 1228 1229 1230
	igt_assert(cairo_status(cr) == CAIRO_STATUS_SUCCESS);

	return cr;
}

1231 1232 1233 1234 1235 1236 1237 1238 1239
/**
 * igt_remove_fb:
 * @fd: open i915 drm file descriptor
 * @fb: pointer to an #igt_fb structure
 *
 * This function releases all resources allocated in igt_create_fb() for @fb.
 * Note that if this framebuffer is still in use on a primary plane the kernel
 * will disable the corresponding crtc.
 */
1240
void igt_remove_fb(int fd, struct igt_fb *fb)
1241 1242 1243 1244 1245 1246
{
	cairo_surface_destroy(fb->cairo_surface);
	do_or_die(drmModeRmFB(fd, fb->fb_id));
	gem_close(fd, fb->gem_handle);
}

1247 1248 1249 1250 1251 1252 1253 1254 1255
/**
 * igt_bpp_depth_to_drm_format:
 * @bpp: desired bits per pixel
 * @depth: desired depth
 *
 * Returns:
 * The rgb drm fourcc pixel format code corresponding to the given @bpp and
 * @depth values.  Fails hard if no match was found.
 */
1256
uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth)
1257 1258 1259 1260 1261 1262 1263
{
	struct format_desc_struct *f;

	for_each_format(f)
		if (f->bpp == bpp && f->depth == depth)
			return f->drm_id;

1264 1265 1266

	igt_assert_f(0, "can't find drm format with bpp=%d, depth=%d\n", bpp,
		     depth);
1267 1268
}

1269 1270 1271 1272 1273 1274 1275 1276
/**
 * igt_drm_format_to_bpp:
 * @drm_format: drm fourcc pixel format code
 *
 * Returns:
 * The bits per pixel for the given drm fourcc pixel format code. Fails hard if
 * no match was found.
 */
1277
uint32_t igt_drm_format_to_bpp(uint32_t drm_format)
1278 1279 1280 1281 1282 1283 1284
{
	struct format_desc_struct *f;

	for_each_format(f)
		if (f->drm_id == drm_format)
			return f->bpp;

1285 1286
	igt_assert_f(0, "can't find a bpp format for %08x (%s)\n",
		     drm_format, igt_format_str(drm_format));
1287 1288
}

1289 1290 1291 1292 1293 1294 1295 1296
/**
 * igt_format_str:
 * @drm_format: drm fourcc pixel format code
 *
 * Returns:
 * Human-readable fourcc pixel format code for @drm_format or "invalid" no match
 * was found.
 */
1297
const char *igt_format_str(uint32_t drm_format)
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
{
	struct format_desc_struct *f;

	for_each_format(f)
		if (f->drm_id == drm_format)
			return f->name;

	return "invalid";
}

1308
/**
1309
 * igt_get_all_cairo_formats:
1310 1311 1312
 * @formats: pointer to pointer to store the allocated formats array
 * @format_count: pointer to integer to store the size of the allocated array
 *
1313 1314
 * This functions returns an array of all the drm fourcc codes supported by
 * cairo and this library.
1315
 */
1316
void igt_get_all_cairo_formats(const uint32_t **formats, int *format_count)
1317 1318
{
	static uint32_t *drm_formats;
1319
	static int n_formats;
1320 1321 1322 1323 1324

	if (!drm_formats) {
		struct format_desc_struct *f;
		uint32_t *format;

1325 1326 1327 1328 1329 1330
		n_formats = 0;
		for_each_format(f)
			if (f->cairo_id != CAIRO_FORMAT_INVALID)
				n_formats++;

		drm_formats = calloc(n_formats, sizeof(*drm_formats));
1331 1332
		format = &drm_formats[0];
		for_each_format(f)
1333 1334
			if (f->cairo_id != CAIRO_FORMAT_INVALID)
				*format++ = f->drm_id;
1335 1336 1337
	}

	*formats = drm_formats;
1338
	*format_count = n_formats;
1339
}