drm: Branch 'master' - 20 commits

[email protected] (Emil Velikov)
Newsgroups gmane.comp.video.dri.patches
Message-ID <[email protected]>
 .gitignore                          |    2 
 CleanSpec.mk                        |    1 
 configure.ac                        |    2 
 exynos/exynos-symbol-check          |    3 
 exynos/exynos_drm.c                 |   76 ++
 exynos/exynos_drm.h                 |   12 
 exynos/exynos_drmif.h               |   26 
 exynos/exynos_fimg2d.c              |  174 ++++++
 exynos/exynos_fimg2d.h              |    4 
 exynos/libdrm_exynos.pc.in          |    2 
 tests/Makefile.am                   |    2 
 tests/exynos/Makefile.am            |   26 
 tests/exynos/exynos_fimg2d_event.c  |  326 ++++++++++++
 tests/exynos/exynos_fimg2d_perf.c   |  327 ++++++++++++
 tests/exynos/exynos_fimg2d_test.c   |  134 ++++-
 tests/kms/Makefile.am               |   36 +
 tests/kms/kms-steal-crtc.c          |  161 ++++++
 tests/kms/kms-universal-planes.c    |  358 +++++++++++++
 tests/kms/libkms-test-crtc.c        |   47 +
 tests/kms/libkms-test-device.c      |  218 ++++++++
 tests/kms/libkms-test-framebuffer.c |  157 +++++
 tests/kms/libkms-test-plane.c       |  139 +++++
 tests/kms/libkms-test-screen.c      |   92 +++
 tests/kms/libkms-test.h             |  120 ++++
 tests/modeprint/Makefile.am         |    1 
 tests/modeprint/modeprint.c         |    2 
 tests/modetest/Android.mk           |    1 
 tests/modetest/Makefile.am          |    8 
 tests/modetest/buffers.c            |  961 ------------------------------------
 tests/modetest/buffers.h            |   12 
 tests/modetest/cursor.c             |    4 
 tests/modetest/modetest.c           |  120 ----
 tests/proptest/Android.mk           |   13 
 tests/proptest/Makefile.am          |   10 
 tests/proptest/Makefile.sources     |    2 
 tests/proptest/proptest.c           |  104 +--
 tests/util/Android.mk               |   39 +
 tests/util/Makefile.am              |   13 
 tests/util/Makefile.sources         |    8 
 tests/util/common.h                 |   33 +
 tests/util/format.c                 |  120 ++++
 tests/util/format.h                 |   65 ++
 tests/util/kms.c                    |  177 ++++++
 tests/util/kms.h                    |   35 +
 tests/util/pattern.c                |  870 ++++++++++++++++++++++++++++++++
 tests/util/pattern.h                |   39 +
 tests/vbltest/Makefile.am           |    4 
 tests/vbltest/vbltest.c             |   55 --
 48 files changed, 3961 insertions(+), 1180 deletions(-)

New commits:
commit 0cfb6a39864c3ebc7802107a8e4ddac67195d4cd
Author: Stefan Agner <[email protected]>
Date:   Fri Dec 18 00:14:49 2015 -0800

    tests: remove missleading comments
    
    The comment has been copied from modetest and is not applicable
    for vbltest.
    
    Signed-off-by: Stefan Agner <[email protected]>
    Reviewed-by: Jesse Barnes <[email protected]>

diff --git a/tests/vbltest/vbltest.c b/tests/vbltest/vbltest.c
index 60badf1..1833321 100644
--- a/tests/vbltest/vbltest.c
+++ b/tests/vbltest/vbltest.c
@@ -1,5 +1,5 @@
 /*
- * DRM based mode setting test program
+ * DRM based vblank test program
  * Copyright 2008 Tungsten Graphics
  *   Jakob Bornecrantz <[email protected]>
  * Copyright 2008 Intel Corporation
@@ -24,19 +24,6 @@
  * IN THE SOFTWARE.
  */
 
-/*
- * This fairly simple test program dumps output in a similar format to the
- * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
- * since the kernel separates outputs into encoder and connector structures,
- * each with their own unique ID.  The program also allows test testing of the
- * memory management and mode setting APIs by allowing the user to specify a
- * connector and mode to use for mode setting.  If all works as expected, a
- * blue background should be painted on the monitor attached to the specified
- * connector after the selected mode is set.
- *
- * TODO: use cairo to write the mode info on the selected output once
- *       the mode has been programmed, along with possible test patterns.
- */
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
commit b020b02f841af6c04d97d484db0ecf1149b93abf
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:48 2015 +0100

    vbltest: Use util_open()
    
    Use the new util_open() helper instead of open-coding the method for
    finding a usable device. While at it, this adds -D and -M command-line
    options to vbltest to make its usage more consistent with its siblings
    modetest and proptest.
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/vbltest/Makefile.am b/tests/vbltest/Makefile.am
index 182e3b6..276afad 100644
--- a/tests/vbltest/Makefile.am
+++ b/tests/vbltest/Makefile.am
@@ -15,4 +15,5 @@ endif
 vbltest_SOURCES = \
 	vbltest.c
 vbltest_LDADD = \
-	$(top_builddir)/libdrm.la
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/tests/util/libutil.la
diff --git a/tests/vbltest/vbltest.c b/tests/vbltest/vbltest.c
index de93e77..60badf1 100644
--- a/tests/vbltest/vbltest.c
+++ b/tests/vbltest/vbltest.c
@@ -55,10 +55,11 @@
 #include "xf86drmMode.h"
 
 #include "util/common.h"
+#include "util/kms.h"
 
 extern char *optarg;
 extern int optind, opterr, optopt;
-static char optstr[] = "s";
+static char optstr[] = "D:M:s";
 
 int secondary = 0;
 
@@ -97,16 +98,19 @@ static void vblank_handler(int fd, unsigned int frame, unsigned int sec,
 
 static void usage(char *name)
 {
-	fprintf(stderr, "usage: %s [-s]\n", name);
-	fprintf(stderr, "\t-s\tuse secondary pipe\n");
+	fprintf(stderr, "usage: %s [-DMs]\n", name);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "options:\n");
+	fprintf(stderr, "  -D DEVICE  open the given device\n");
+	fprintf(stderr, "  -M MODULE  open the given module\n");
+	fprintf(stderr, "  -s         use secondary pipe\n");
 	exit(0);
 }
 
 int main(int argc, char **argv)
 {
-	unsigned i;
+	const char *device = NULL, *module = NULL;
 	int c, fd, ret;
-	const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "exynos", "omapdrm", "tilcdc", "msm", "tegra", "imx-drm" , "rockchip" };
 	drmVBlank vbl;
 	drmEventContext evctx;
 	struct vbl_info handler_info;
@@ -114,6 +118,12 @@ int main(int argc, char **argv)
 	opterr = 0;
 	while ((c = getopt(argc, argv, optstr)) != -1) {
 		switch (c) {
+		case 'D':
+			device = optarg;
+			break;
+		case 'M':
+			module = optarg;
+			break;
 		case 's':
 			secondary = 1;
 			break;
@@ -123,21 +133,9 @@ int main(int argc, char **argv)
 		}
 	}
 
-	for (i = 0; i < ARRAY_SIZE(modules); i++) {
-		printf("trying to load module %s...", modules[i]);
-		fd = drmOpen(modules[i], NULL);
-		if (fd < 0) {
-			printf("failed.\n");
-		} else {
-			printf("success.\n");
-			break;
-		}
-	}
-
-	if (i == ARRAY_SIZE(modules)) {
-		fprintf(stderr, "failed to load any modules, aborting.\n");
-		return -1;
-	}
+	fd = util_open(module, device);
+	if (fd < 0)
+		return 1;
 
 	/* Get current count first */
 	vbl.request.type = DRM_VBLANK_RELATIVE;
commit 6223481b8de901b5356afdef538726f1fb77bfd1
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:47 2015 +0100

    proptest: Use util_open()
    
    Use the new util_open() helper instead of open-coding the method for
    finding a usable device. While at it, make the command-line interface
    more consistent with that of modetest by adding the -D and -M options.
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/proptest/proptest.c b/tests/proptest/proptest.c
index 11eb6e8..24c6345 100644
--- a/tests/proptest/proptest.c
+++ b/tests/proptest/proptest.c
@@ -27,6 +27,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <getopt.h>
 #include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -232,28 +233,32 @@ static int setProperty(char *argv[])
 	uint32_t obj_id, obj_type, prop_id;
 	uint64_t value;
 
-	obj_id = atoi(argv[1]);
+	obj_id = atoi(argv[0]);
 
-	if (!strcmp(argv[2], "connector")) {
+	if (!strcmp(argv[1], "connector")) {
 		obj_type = DRM_MODE_OBJECT_CONNECTOR;
-	} else if (!strcmp(argv[2], "crtc")) {
+	} else if (!strcmp(argv[1], "crtc")) {
 		obj_type = DRM_MODE_OBJECT_CRTC;
 	} else {
 		fprintf(stderr, "Invalid object type.\n");
 		return 1;
 	}
 
-	prop_id = atoi(argv[3]);
-	value = atoll(argv[4]);
+	prop_id = atoi(argv[2]);
+	value = atoll(argv[3]);
 
 	return drmModeObjectSetProperty(fd, obj_id, obj_type, prop_id, value);
 }
 
-static void printUsage(void)
+static void usage(const char *program)
 {
 	printf("Usage:\n"
-"  proptest\n"
-"  proptest [obj id] [obj type] [prop id] [value]\n"
+"  %s [options]\n"
+"  %s [options] [obj id] [obj type] [prop id] [value]\n"
+"\n"
+"options:\n"
+"  -D DEVICE  use the given device\n"
+"  -M MODULE  use the given driver\n"
 "\n"
 "The first form just prints all the existing properties. The second one is\n"
 "used to set the value of a specified property. The object type can be one of\n"
@@ -262,26 +267,37 @@ static void printUsage(void)
 "\n"
 "Example:\n"
 "  proptest 7 connector 2 1\n"
-"will set property 2 of connector 7 to 1\n");
+"will set property 2 of connector 7 to 1\n", program, program);
 }
 
 int main(int argc, char *argv[])
 {
-	const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "msm", "rockchip" };
-	unsigned int i, ret = 0;
+	static const char optstr[] = "D:M:";
+	int c, args, ret = 0;
+	char *device = NULL;
+	char *module = NULL;
+
+	while ((c = getopt(argc, argv, optstr)) != -1) {
+		switch (c) {
+		case 'D':
+			device = optarg;
+			break;
+
+		case 'M':
+			module = optarg;
+			break;
 
-	for (i = 0; i < ARRAY_SIZE(modules); i++){
-		fd = drmOpen(modules[i], NULL);
-		if (fd >= 0) {
-			printf("Module %s loaded.\n", modules[i]);
+		default:
+			usage(argv[0]);
 			break;
 		}
 	}
 
-	if (i == ARRAY_SIZE(modules)) {
-		fprintf(stderr, "Failed to load drm modules.\n");
+	args = argc - optind;
+
+	fd = util_open(module, device);
+	if (fd < 0)
 		return 1;
-	}
 
 	res = drmModeGetResources(fd);
 	if (!res) {
@@ -291,12 +307,12 @@ int main(int argc, char *argv[])
 		goto done;
 	}
 
-	if (argc < 2) {
+	if (args < 1) {
 		listAllProperties();
-	} else if (argc == 5) {
-		ret = setProperty(argv);
+	} else if (args == 4) {
+		ret = setProperty(&argv[optind]);
 	} else {
-		printUsage();
+		usage(argv[0]);
 		ret = 1;
 	}
 
commit c26266fcd082a8b67d1f32f5e5351cfa82c7accb
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:46 2015 +0100

    modetest: Use util_open()
    
    Use the new util_open() helper instead of open-coding the method for
    finding a usable device.
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index 0e009d8..22e3e81 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -1503,7 +1503,6 @@ int main(int argc, char **argv)
 	int drop_master = 0;
 	int test_vsync = 0;
 	int test_cursor = 0;
-	const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tilcdc", "msm", "sti", "tegra", "imx-drm", "rockchip", "atmel-hlcdc" };
 	char *device = NULL;
 	char *module = NULL;
 	unsigned int i;
@@ -1604,29 +1603,9 @@ int main(int argc, char **argv)
 	if (!args)
 		encoders = connectors = crtcs = planes = framebuffers = 1;
 
-	if (module) {
-		dev.fd = drmOpen(module, device);
-		if (dev.fd < 0) {
-			fprintf(stderr, "failed to open device '%s'.\n", module);
-			return 1;
-		}
-	} else {
-		for (i = 0; i < ARRAY_SIZE(modules); i++) {
-			printf("trying to open device '%s'...", modules[i]);
-			dev.fd = drmOpen(modules[i], device);
-			if (dev.fd < 0) {
-				printf("failed.\n");
-			} else {
-				printf("success.\n");
-				break;
-			}
-		}
-
-		if (dev.fd < 0) {
-			fprintf(stderr, "no device found.\n");
-			return 1;
-		}
-	}
+	dev.fd = util_open(module, device);
+	if (dev.fd < 0)
+		return -1;
 
 	if (test_vsync && !page_flipping_supported()) {
 		fprintf(stderr, "page flipping not supported by drm.\n");
commit e744b02375e939e853b4c83979f170bfc89e482c
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:45 2015 +0100

    tests: Add helper to open a device/module
    
    The new function util_open() encapsulates the standard method employed
    by tests to open a device or module. There is a verbatim copy of this in
    almost all test programs, with slight variations in the list of modules.
    Moving this code into a common helper allows code reuse and makes tests
    more consistent.
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/util/kms.c b/tests/util/kms.c
index 687b3c3..57b0191 100644
--- a/tests/util/kms.c
+++ b/tests/util/kms.c
@@ -41,9 +41,13 @@
 #include "config.h"
 #endif
 
+#include <errno.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
+#include "xf86drm.h"
 #include "xf86drmMode.h"
 
 #include "common.h"
@@ -120,3 +124,54 @@ const char *util_lookup_connector_type_name(unsigned int type)
 	return util_lookup_type_name(type, connector_type_names,
 				     ARRAY_SIZE(connector_type_names));
 }
+
+static const char * const modules[] = {
+	"i915",
+	"radeon",
+	"nouveau",
+	"vmwgfx",
+	"omapdrm",
+	"exynos",
+	"tilcdc",
+	"msm",
+	"sti",
+	"tegra",
+	"imx-drm",
+	"rockchip",
+	"atmel-hlcdc",
+};
+
+int util_open(const char *device, const char *module)
+{
+	int fd;
+
+	if (module) {
+		fd = drmOpen(module, device);
+		if (fd < 0) {
+			fprintf(stderr, "failed to open device '%s': %s\n",
+				module, strerror(errno));
+			return -errno;
+		}
+	} else {
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(modules); i++) {
+			printf("trying to open device '%s'...", modules[i]);
+
+			fd = drmOpen(modules[i], device);
+			if (fd < 0) {
+				printf("failed\n");
+			} else {
+				printf("done\n");
+				break;
+			}
+		}
+
+		if (fd < 0) {
+			fprintf(stderr, "no device found\n");
+			return -ENODEV;
+		}
+	}
+
+	return fd;
+}
diff --git a/tests/util/kms.h b/tests/util/kms.h
index fa9ab69..dde2ed2 100644
--- a/tests/util/kms.h
+++ b/tests/util/kms.h
@@ -30,4 +30,6 @@ const char *util_lookup_encoder_type_name(unsigned int type);
 const char *util_lookup_connector_status_name(unsigned int type);
 const char *util_lookup_connector_type_name(unsigned int type);
 
+int util_open(const char *device, const char *module);
+
 #endif /* UTIL_KMS_H */
commit 89cca28dfbc0c96c13b1dfa3bc3c947876469159
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:44 2015 +0100

    tests: kms: Implement universal planes test
    
    This small program allows universal planes to be tested. Currently this
    isn't very flexible because it allows only the first plane of a given
    type to be tested on the first CRTC. However it should be simple to
    extend this with some additional command-line arguments.
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/kms/Makefile.am b/tests/kms/Makefile.am
index f28b729..6645af7 100644
--- a/tests/kms/Makefile.am
+++ b/tests/kms/Makefile.am
@@ -21,11 +21,16 @@ libkms_test_la_LIBADD = \
 
 if HAVE_INSTALL_TESTS
 bin_PROGRAMS = \
-	kms-steal-crtc
+	kms-steal-crtc \
+	kms-universal-planes
 else
 noinst_PROGRAMS = \
-	kms-steal-crtc
+	kms-steal-crtc \
+	kms-universal-planes
 endif
 
 kms_steal_crtc_SOURCES = kms-steal-crtc.c
 kms_steal_crtc_LDADD = libkms-test.la ../util/libutil.la $(CAIRO_LIBS)
+
+kms_universal_planes_SOURCES = kms-universal-planes.c
+kms_universal_planes_LDADD = libkms-test.la $(CAIRO_LIBS)
diff --git a/tests/kms/kms-universal-planes.c b/tests/kms/kms-universal-planes.c
new file mode 100644
index 0000000..9151231
--- /dev/null
+++ b/tests/kms/kms-universal-planes.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <drm_fourcc.h>
+#include "xf86drm.h"
+
+#include "util/common.h"
+#include "libkms-test.h"
+
+static const uint32_t formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBA8888,
+};
+
+static uint32_t choose_format(struct kms_plane *plane)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++)
+		if (kms_plane_supports_format(plane, formats[i]))
+			return formats[i];
+
+	return 0;
+}
+
+static void prepare_framebuffer(struct kms_framebuffer *fb, bool invert)
+{
+	const unsigned int block_size = 16;
+	uint32_t colors[2];
+	unsigned int i, j;
+	uint32_t *buf;
+	void *ptr;
+	int err;
+
+	switch (fb->format) {
+	case DRM_FORMAT_XRGB8888:
+		printf("using XRGB8888 format\n");
+		           /* XXRRGGBB */
+		colors[0] = 0xffff0000;
+		colors[1] = 0xff0000ff;
+		break;
+
+	case DRM_FORMAT_XBGR8888:
+		printf("using XBGR8888 format\n");
+		           /* XXBBGGRR */
+		colors[0] = 0xff0000ff;
+		colors[1] = 0xffff0000;
+		break;
+
+	case DRM_FORMAT_RGBA8888:
+		printf("using RGBA8888 format\n");
+		           /* RRGGBBAA */
+		colors[0] = 0xff0000ff;
+		colors[1] = 0x0000ffff;
+		break;
+
+	default:
+		colors[0] = 0xffffffff;
+		colors[1] = 0xffffffff;
+		break;
+	}
+
+	err = kms_framebuffer_map(fb, &ptr);
+	if (err < 0) {
+		fprintf(stderr, "kms_framebuffer_map() failed: %s\n",
+			strerror(-err));
+		return;
+	}
+
+	buf = ptr;
+
+	for (j = 0; j < fb->height; j++) {
+		for (i = 0; i < fb->width; i++) {
+			unsigned int color = (j / block_size) ^
+					     (i / block_size);
+
+			if (invert)
+				color ^= color;
+
+			*buf++ = colors[color & 1];
+		}
+	}
+
+	kms_framebuffer_unmap(fb);
+}
+
+int main(int argc, char *argv[])
+{
+	static const char opts[] = "chopv";
+	static struct option options[] = {
+		{ "cursor", 0, 0, 'c' },
+		{ "help", 0, 0, 'h' },
+		{ "overlay", 0, 0, 'o' },
+		{ "primary", 0, 0, 'p' },
+		{ "verbose", 0, 0, 'v' },
+		{ 0, 0, 0, 0 },
+	};
+	struct kms_framebuffer *cursor = NULL;
+	struct kms_framebuffer *root = NULL;
+	struct kms_framebuffer *fb = NULL;
+	struct kms_device *device;
+	bool use_overlay = false;
+	bool use_primary = false;
+	struct kms_plane *plane;
+	bool use_cursor = false;
+	bool verbose = false;
+	unsigned int i;
+	int opt, idx;
+	int fd, err;
+
+	while ((opt = getopt_long(argc, argv, opts, options, &idx)) != -1) {
+		switch (opt) {
+		case 'c':
+			use_cursor = true;
+			break;
+
+		case 'h':
+			break;
+
+		case 'o':
+			use_overlay = true;
+			break;
+
+		case 'p':
+			use_primary = true;
+			break;
+
+		case 'v':
+			verbose = true;
+			break;
+
+		default:
+			printf("unknown option \"%c\"\n", opt);
+			return 1;
+		}
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "usage: %s [options] DEVICE\n", argv[0]);
+		return 1;
+	}
+
+	fd = open(argv[optind], O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "open() failed: %m\n");
+		return 1;
+	}
+
+	err = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+	if (err < 0) {
+		fprintf(stderr, "drmSetClientCap() failed: %d\n", err);
+		return 1;
+	}
+
+	device = kms_device_open(fd);
+	if (!device)
+		return 1;
+
+	if (verbose) {
+		printf("Screens: %u\n", device->num_screens);
+
+		for (i = 0; i < device->num_screens; i++) {
+			struct kms_screen *screen = device->screens[i];
+			const char *status = "disconnected";
+
+			if (screen->connected)
+				status = "connected";
+
+			printf("  %u: %x\n", i, screen->id);
+			printf("    Status: %s\n", status);
+			printf("    Name: %s\n", screen->name);
+			printf("    Resolution: %ux%u\n", screen->width,
+			       screen->height);
+		}
+
+		printf("Planes: %u\n", device->num_planes);
+
+		for (i = 0; i < device->num_planes; i++) {
+			struct kms_plane *plane = device->planes[i];
+			const char *type = NULL;
+
+			switch (plane->type) {
+			case DRM_PLANE_TYPE_OVERLAY:
+				type = "overlay";
+				break;
+
+			case DRM_PLANE_TYPE_PRIMARY:
+				type = "primary";
+				break;
+
+			case DRM_PLANE_TYPE_CURSOR:
+				type = "cursor";
+				break;
+			}
+
+			printf("  %u: %p\n", i, plane);
+			printf("    ID: %x\n", plane->id);
+			printf("    CRTC: %x\n", plane->crtc->id);
+			printf("    Type: %x (%s)\n", plane->type, type);
+		}
+	}
+
+	if (use_cursor) {
+		unsigned int x, y;
+		uint32_t format;
+
+		plane = kms_device_find_plane_by_type(device,
+						      DRM_PLANE_TYPE_CURSOR,
+						      0);
+		if (!plane) {
+			fprintf(stderr, "no cursor plane found\n");
+			return 1;
+		}
+
+		format = choose_format(plane);
+		if (!format) {
+			fprintf(stderr, "no matching format found\n");
+			return 1;
+		}
+
+		cursor = kms_framebuffer_create(device, 32, 32, format);
+		if (!cursor) {
+			fprintf(stderr, "failed to create cursor buffer\n");
+			return 1;
+		}
+
+		prepare_framebuffer(cursor, false);
+
+		x = (device->screens[0]->width - cursor->width) / 2;
+		y = (device->screens[0]->height - cursor->height) / 2;
+
+		kms_plane_set(plane, cursor, x, y);
+	}
+
+	if (use_overlay) {
+		uint32_t format;
+
+		plane = kms_device_find_plane_by_type(device,
+						      DRM_PLANE_TYPE_OVERLAY,
+						      0);
+		if (!plane) {
+			fprintf(stderr, "no overlay plane found\n");
+			return 1;
+		}
+
+		format = choose_format(plane);
+		if (!format) {
+			fprintf(stderr, "no matching format found\n");
+			return 1;
+		}
+
+		fb = kms_framebuffer_create(device, 320, 240, format);
+		if (!fb)
+			return 1;
+
+		prepare_framebuffer(fb, false);
+
+		kms_plane_set(plane, fb, 0, 0);
+	}
+
+	if (use_primary) {
+		unsigned int x, y;
+		uint32_t format;
+
+		plane = kms_device_find_plane_by_type(device,
+						      DRM_PLANE_TYPE_PRIMARY,
+						      0);
+		if (!plane) {
+			fprintf(stderr, "no primary plane found\n");
+			return 1;
+		}
+
+		format = choose_format(plane);
+		if (!format) {
+			fprintf(stderr, "no matching format found\n");
+			return 1;
+		}
+
+		root = kms_framebuffer_create(device, 640, 480, format);
+		if (!root)
+			return 1;
+
+		prepare_framebuffer(root, true);
+
+		x = (device->screens[0]->width - root->width) / 2;
+		y = (device->screens[0]->height - root->height) / 2;
+
+		kms_plane_set(plane, root, x, y);
+	}
+
+	while (1) {
+		struct timeval timeout = { 1, 0 };
+		fd_set fds;
+
+		FD_ZERO(&fds);
+		FD_SET(STDIN_FILENO, &fds);
+
+		err = select(STDIN_FILENO + 1, &fds, NULL, NULL, &timeout);
+		if (err < 0) {
+			fprintf(stderr, "select() failed: %m\n");
+			break;
+		}
+
+		/* timeout */
+		if (err == 0)
+			continue;
+
+		if (FD_ISSET(STDIN_FILENO, &fds))
+			break;
+	}
+
+	if (cursor)
+		kms_framebuffer_free(cursor);
+
+	if (root)
+		kms_framebuffer_free(root);
+
+	if (fb)
+		kms_framebuffer_free(fb);
+
+	kms_device_close(device);
+	close(fd);
+
+	return 0;
+}
commit e0ec59efb12eb4e5a548f29df620c3c5e1cd6e4f
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:43 2015 +0100

    tests: kms: Implement CRTC stealing test
    
    This test program sets a mode and framebuffer on a connector and cycles
    through all CRTCs, moving the connector to each of them in turn. This is
    useful to verify that CRTC stealing is properly handled in the DRM core
    and drivers.
    
    Signed-off-by: Thierry Reding <[email protected]>

diff --git a/tests/kms/Makefile.am b/tests/kms/Makefile.am
index da46421..f28b729 100644
--- a/tests/kms/Makefile.am
+++ b/tests/kms/Makefile.am
@@ -18,3 +18,14 @@ libkms_test_la_SOURCES = \
 
 libkms_test_la_LIBADD = \
 	$(top_builddir)/libdrm.la
+
+if HAVE_INSTALL_TESTS
+bin_PROGRAMS = \
+	kms-steal-crtc
+else
+noinst_PROGRAMS = \
+	kms-steal-crtc
+endif
+
+kms_steal_crtc_SOURCES = kms-steal-crtc.c
+kms_steal_crtc_LDADD = libkms-test.la ../util/libutil.la $(CAIRO_LIBS)
diff --git a/tests/kms/kms-steal-crtc.c b/tests/kms/kms-steal-crtc.c
new file mode 100644
index 0000000..2f7f327
--- /dev/null
+++ b/tests/kms/kms-steal-crtc.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <drm_fourcc.h>
+
+#include "util/pattern.h"
+#include "libkms-test.h"
+
+static void signal_handler(int signum)
+{
+}
+
+int main(int argc, char *argv[])
+{
+	struct kms_framebuffer *fb;
+	struct kms_screen *screen;
+	struct kms_device *device;
+	unsigned int index = 0;
+	struct sigaction sa;
+	int fd, err;
+	void *ptr;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s DEVICE\n", argv[0]);
+		return 1;
+	}
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = signal_handler;
+
+	err = sigaction(SIGINT, &sa, NULL);
+	if (err < 0) {
+		fprintf(stderr, "sigaction() failed: %m\n");
+		return 1;
+	}
+
+	fd = open(argv[1], O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "open() failed: %m\n");
+		return 1;
+	}
+
+	device = kms_device_open(fd);
+	if (!device) {
+		fprintf(stderr, "kms_device_open() failed: %m\n");
+		return 1;
+	}
+
+	if (device->num_screens < 1) {
+		fprintf(stderr, "no screens found\n");
+		kms_device_close(device);
+		close(fd);
+		return 1;
+	}
+
+	/* TODO: allow command-line to override */
+	screen = device->screens[0];
+
+	printf("Using screen %s, resolution %ux%u\n", screen->name,
+	       screen->width, screen->height);
+
+	fb = kms_framebuffer_create(device, screen->width, screen->height,
+				    DRM_FORMAT_XRGB8888);
+	if (!fb) {
+		fprintf(stderr, "kms_framebuffer_create() failed\n");
+		return 1;
+	}
+
+	err = kms_framebuffer_map(fb, &ptr);
+	if (err < 0) {
+		fprintf(stderr, "kms_framebuffer_map() failed: %d\n", err);
+		return 1;
+	}
+
+	util_fill_pattern(fb->format, UTIL_PATTERN_SMPTE, &ptr, fb->width,
+			  fb->height, fb->pitch);
+
+	kms_framebuffer_unmap(fb);
+
+	err = kms_screen_set(screen, device->crtcs[index++], fb);
+	if (err < 0) {
+		fprintf(stderr, "kms_screen_set() failed: %d\n", err);
+		return 1;
+	}
+
+	while (true) {
+		int nfds = STDIN_FILENO + 1;
+		struct timeval timeout;
+		fd_set fds;
+
+		memset(&timeout, 0, sizeof(timeout));
+		timeout.tv_sec = 5;
+		timeout.tv_usec = 0;
+
+		FD_ZERO(&fds);
+		FD_SET(STDIN_FILENO, &fds);
+
+		err = select(nfds, &fds, NULL, NULL, &timeout);
+		if (err < 0) {
+			if (errno == EINTR)
+				break;
+
+			fprintf(stderr, "select() failed: %d\n", errno);
+			break;
+		}
+
+		if (err > 0) {
+			if (FD_ISSET(STDIN_FILENO, &fds))
+				break;
+		}
+
+		/* switch CRTC */
+		if (index >= device->num_crtcs)
+			index = 0;
+
+		err = kms_screen_set(screen, device->crtcs[index], fb);
+		if (err < 0) {
+			fprintf(stderr, "kms_screen_set() failed: %d\n", err);
+			break;
+		}
+
+		index++;
+	}
+
+	kms_framebuffer_free(fb);
+	kms_device_close(device);
+	close(fd);
+
+	return 0;
+}
commit c13c504ed2778fe5410d4883a7025abd75d0a6a1
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:42 2015 +0100

    tests: Add libkms-test library
    
    This library contains abstractions for KMS that help remove the need for
    a lot of boilerplate in KMS test programs.
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/configure.ac b/configure.ac
index ca6cbf4..0c66dfc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -499,6 +499,7 @@ AC_CONFIG_FILES([
 	tests/Makefile
 	tests/modeprint/Makefile
 	tests/modetest/Makefile
+	tests/kms/Makefile
 	tests/kmstest/Makefile
 	tests/proptest/Makefile
 	tests/radeon/Makefile
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7ffc076..58feb12 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = util modeprint proptest modetest vbltest
+SUBDIRS = util kms modeprint proptest modetest vbltest
 
 if HAVE_LIBKMS
 SUBDIRS += kmstest
diff --git a/tests/kms/Makefile.am b/tests/kms/Makefile.am
new file mode 100644
index 0000000..da46421
--- /dev/null
+++ b/tests/kms/Makefile.am
@@ -0,0 +1,20 @@
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/tests \
+	-I$(top_srcdir)
+
+AM_CFLAGS = \
+	$(WARN_CFLAGS)
+
+noinst_LTLIBRARIES = libkms-test.la
+
+libkms_test_la_SOURCES = \
+	libkms-test.h \
+	libkms-test-crtc.c \
+	libkms-test-device.c \
+	libkms-test-framebuffer.c \
+	libkms-test-plane.c \
+	libkms-test-screen.c
+
+libkms_test_la_LIBADD = \
+	$(top_builddir)/libdrm.la
diff --git a/tests/kms/libkms-test-crtc.c b/tests/kms/libkms-test-crtc.c
new file mode 100644
index 0000000..3adb490
--- /dev/null
+++ b/tests/kms/libkms-test-crtc.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "libkms-test.h"
+
+struct kms_crtc *kms_crtc_create(struct kms_device *device, uint32_t id)
+{
+	struct kms_crtc *crtc;
+
+	crtc = calloc(1, sizeof(*crtc));
+	if (!crtc)
+		return NULL;
+
+	crtc->device = device;
+	crtc->id = id;
+
+	return crtc;
+}
+
+void kms_crtc_free(struct kms_crtc *crtc)
+{
+	free(crtc);
+}
diff --git a/tests/kms/libkms-test-device.c b/tests/kms/libkms-test-device.c
new file mode 100644
index 0000000..53c7349
--- /dev/null
+++ b/tests/kms/libkms-test-device.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util/common.h"
+#include "libkms-test.h"
+
+static const char *const connector_names[] = {
+	"Unknown",
+	"VGA",
+	"DVI-I",
+	"DVI-D",
+	"DVI-A",
+	"Composite",
+	"SVIDEO",
+	"LVDS",
+	"Component",
+	"9PinDIN",
+	"DisplayPort",
+	"HDMI-A",
+	"HDMI-B",
+	"TV",
+	"eDP",
+	"Virtual",
+	"DSI",
+};
+
+static void kms_device_probe_screens(struct kms_device *device)
+{
+	unsigned int counts[ARRAY_SIZE(connector_names)];
+	struct kms_screen *screen;
+	drmModeRes *res;
+	int i;
+
+	memset(counts, 0, sizeof(counts));
+
+	res = drmModeGetResources(device->fd);
+	if (!res)
+		return;
+
+	device->screens = calloc(res->count_connectors, sizeof(screen));
+	if (!device->screens)
+		return;
+
+	for (i = 0; i < res->count_connectors; i++) {
+		unsigned int *count;
+		const char *type;
+		int len;
+
+		screen = kms_screen_create(device, res->connectors[i]);
+		if (!screen)
+			continue;
+
+		/* assign a unique name to this screen */
+		type = connector_names[screen->type];
+		count = &counts[screen->type];
+
+		len = snprintf(NULL, 0, "%s-%u", type, *count);
+
+		screen->name = malloc(len + 1);
+		if (!screen->name) {
+			free(screen);
+			continue;
+		}
+
+		snprintf(screen->name, len + 1, "%s-%u", type, *count);
+		(*count)++;
+
+		device->screens[i] = screen;
+		device->num_screens++;
+	}
+
+	drmModeFreeResources(res);
+}
+
+static void kms_device_probe_crtcs(struct kms_device *device)
+{
+	struct kms_crtc *crtc;
+	drmModeRes *res;
+	int i;
+
+	res = drmModeGetResources(device->fd);
+	if (!res)
+		return;
+
+	device->crtcs = calloc(res->count_crtcs, sizeof(crtc));
+	if (!device->crtcs)
+		return;
+
+	for (i = 0; i < res->count_crtcs; i++) {
+		crtc = kms_crtc_create(device, res->crtcs[i]);
+		if (!crtc)
+			continue;
+
+		device->crtcs[i] = crtc;
+		device->num_crtcs++;
+	}
+
+	drmModeFreeResources(res);
+}
+
+static void kms_device_probe_planes(struct kms_device *device)
+{
+	struct kms_plane *plane;
+	drmModePlaneRes *res;
+	unsigned int i;
+
+	res = drmModeGetPlaneResources(device->fd);
+	if (!res)
+		return;
+
+	device->planes = calloc(res->count_planes, sizeof(plane));
+	if (!device->planes)
+		return;
+
+	for (i = 0; i < res->count_planes; i++) {
+		plane = kms_plane_create(device, res->planes[i]);
+		if (!plane)
+			continue;
+
+		device->planes[i] = plane;
+		device->num_planes++;
+	}
+
+	drmModeFreePlaneResources(res);
+}
+
+static void kms_device_probe(struct kms_device *device)
+{
+	kms_device_probe_screens(device);
+	kms_device_probe_crtcs(device);
+	kms_device_probe_planes(device);
+}
+
+struct kms_device *kms_device_open(int fd)
+{
+	struct kms_device *device;
+
+	device = calloc(1, sizeof(*device));
+	if (!device)
+		return NULL;
+
+	device->fd = fd;
+
+	kms_device_probe(device);
+
+	return device;
+}
+
+void kms_device_close(struct kms_device *device)
+{
+	unsigned int i;
+
+	for (i = 0; i < device->num_planes; i++)
+		kms_plane_free(device->planes[i]);
+
+	free(device->planes);
+
+	for (i = 0; i < device->num_crtcs; i++)
+		kms_crtc_free(device->crtcs[i]);
+
+	free(device->crtcs);
+
+	for (i = 0; i < device->num_screens; i++)
+		kms_screen_free(device->screens[i]);
+
+	free(device->screens);
+
+	if (device->fd >= 0)
+		close(device->fd);
+
+	free(device);
+}
+
+struct kms_plane *kms_device_find_plane_by_type(struct kms_device *device,
+						uint32_t type,
+						unsigned int index)
+{
+	unsigned int i;
+
+	for (i = 0; i < device->num_planes; i++) {
+		if (device->planes[i]->type == type) {
+			if (index == 0)
+				return device->planes[i];
+
+			index--;
+		}
+	}
+
+	return NULL;
+}
diff --git a/tests/kms/libkms-test-framebuffer.c b/tests/kms/libkms-test-framebuffer.c
new file mode 100644
index 0000000..c9e5ad3
--- /dev/null
+++ b/tests/kms/libkms-test-framebuffer.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <sys/mman.h>
+
+#include <drm_fourcc.h>
+
+#include "xf86drm.h"
+
+#include "libkms-test.h"
+
+struct kms_framebuffer *kms_framebuffer_create(struct kms_device *device,
+					       unsigned int width,
+					       unsigned int height,
+					       uint32_t format)
+{
+	uint32_t handles[4], pitches[4], offsets[4];
+	struct drm_mode_create_dumb args;
+	struct kms_framebuffer *fb;
+	int err;
+
+	fb = calloc(1, sizeof(*fb));
+	if (!fb)
+		return NULL;
+
+	fb->device = device;
+	fb->width = width;
+	fb->height = height;
+	fb->format = format;
+
+	memset(&args, 0, sizeof(args));
+	args.width = width;
+	args.height = height;
+
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_RGBA8888:
+		args.bpp = 32;
+		break;
+
+	default:
+		free(fb);
+		return NULL;
+	}
+
+	err = drmIoctl(device->fd, DRM_IOCTL_MODE_CREATE_DUMB, &args);
+	if (err < 0) {
+		free(fb);
+		return NULL;
+	}
+
+	fb->handle = args.handle;
+	fb->pitch = args.pitch;
+	fb->size = args.size;
+
+	handles[0] = fb->handle;
+	pitches[0] = fb->pitch;
+	offsets[0] = 0;
+
+	err = drmModeAddFB2(device->fd, width, height, format, handles,
+			    pitches, offsets, &fb->id, 0);
+	if (err < 0) {
+		kms_framebuffer_free(fb);
+		return NULL;
+	}
+
+	return fb;
+}
+
+void kms_framebuffer_free(struct kms_framebuffer *fb)
+{
+	struct kms_device *device = fb->device;
+	struct drm_mode_destroy_dumb args;
+	int err;
+
+	if (fb->id) {
+		err = drmModeRmFB(device->fd, fb->id);
+		if (err < 0) {
+			/* not much we can do now */
+		}
+	}
+
+	memset(&args, 0, sizeof(args));
+	args.handle = fb->handle;
+
+	err = drmIoctl(device->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &args);
+	if (err < 0) {
+		/* not much we can do now */
+	}
+
+	free(fb);
+}
+
+int kms_framebuffer_map(struct kms_framebuffer *fb, void **ptrp)
+{
+	struct kms_device *device = fb->device;
+	struct drm_mode_map_dumb args;
+	void *ptr;
+	int err;
+
+	if (fb->ptr) {
+		*ptrp = fb->ptr;
+		return 0;
+	}
+
+	memset(&args, 0, sizeof(args));
+	args.handle = fb->handle;
+
+	err = drmIoctl(device->fd, DRM_IOCTL_MODE_MAP_DUMB, &args);
+	if (err < 0)
+		return -errno;
+
+	ptr = mmap(0, fb->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+		   device->fd, args.offset);
+	if (ptr == MAP_FAILED)
+		return -errno;
+
+	*ptrp = fb->ptr = ptr;
+
+	return 0;
+}
+
+void kms_framebuffer_unmap(struct kms_framebuffer *fb)
+{
+	if (fb->ptr) {
+		munmap(fb->ptr, fb->size);
+		fb->ptr = NULL;
+	}
+}
diff --git a/tests/kms/libkms-test-plane.c b/tests/kms/libkms-test-plane.c
new file mode 100644
index 0000000..8eb78af
--- /dev/null
+++ b/tests/kms/libkms-test-plane.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include "libkms-test.h"
+
+static int kms_plane_probe(struct kms_plane *plane)
+{
+	struct kms_device *device = plane->device;
+	drmModeObjectPropertiesPtr props;
+	drmModePlane *p;
+	unsigned int i;
+
+	p = drmModeGetPlane(device->fd, plane->id);
+	if (!p)
+		return -ENODEV;
+
+	/* TODO: allow dynamic assignment to CRTCs */
+	if (p->crtc_id == 0) {
+		for (i = 0; i < device->num_crtcs; i++) {
+			if (p->possible_crtcs & (1 << i)) {
+				p->crtc_id = device->crtcs[i]->id;
+				break;
+			}
+		}
+	}
+
+	for (i = 0; i < device->num_crtcs; i++) {
+		if (device->crtcs[i]->id == p->crtc_id) {
+			plane->crtc = device->crtcs[i];
+			break;
+		}
+	}
+
+	plane->formats = calloc(p->count_formats, sizeof(uint32_t));
+	if (!plane->formats)
+		return -ENOMEM;
+
+	for (i = 0; i < p->count_formats; i++)
+		plane->formats[i] = p->formats[i];
+
+	plane->num_formats = p->count_formats;
+
+	drmModeFreePlane(p);
+
+	props = drmModeObjectGetProperties(device->fd, plane->id,
+					   DRM_MODE_OBJECT_PLANE);
+	if (!props)
+		return -ENODEV;
+
+	for (i = 0; i < props->count_props; i++) {
+		drmModePropertyPtr prop;
+
+		prop = drmModeGetProperty(device->fd, props->props[i]);
+		if (prop) {
+			if (strcmp(prop->name, "type") == 0)
+				plane->type = props->prop_values[i];
+
+			drmModeFreeProperty(prop);
+		}
+	}
+
+	drmModeFreeObjectProperties(props);
+
+	return 0;
+}
+
+struct kms_plane *kms_plane_create(struct kms_device *device, uint32_t id)
+{
+	struct kms_plane *plane;
+
+	plane = calloc(1, sizeof(*plane));
+	if (!plane)
+		return NULL;
+
+	plane->device = device;
+	plane->id = id;
+
+	kms_plane_probe(plane);
+
+	return plane;
+}
+
+void kms_plane_free(struct kms_plane *plane)
+{
+	free(plane);
+}
+
+int kms_plane_set(struct kms_plane *plane, struct kms_framebuffer *fb,
+		  unsigned int x, unsigned int y)
+{
+	struct kms_device *device = plane->device;
+	int err;
+
+	err = drmModeSetPlane(device->fd, plane->id, plane->crtc->id, fb->id,
+			      0, x, y, fb->width, fb->height, 0 << 16,
+			      0 << 16, fb->width << 16, fb->height << 16);
+	if (err < 0)
+		return -errno;
+
+	return 0;
+}
+
+bool kms_plane_supports_format(struct kms_plane *plane, uint32_t format)
+{
+	unsigned int i;
+
+	for (i = 0; i < plane->num_formats; i++)
+		if (plane->formats[i] == format)
+			return true;
+
+	return false;
+}
diff --git a/tests/kms/libkms-test-screen.c b/tests/kms/libkms-test-screen.c
new file mode 100644
index 0000000..3369022
--- /dev/null
+++ b/tests/kms/libkms-test-screen.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include "libkms-test.h"
+
+static void kms_screen_probe(struct kms_screen *screen)
+{
+	struct kms_device *device = screen->device;
+	drmModeConnector *con;
+
+	con = drmModeGetConnector(device->fd, screen->id);
+	if (!con)
+		return;
+
+	screen->type = con->connector_type;
+
+	if (con->connection == DRM_MODE_CONNECTED)
+		screen->connected = true;
+	else
+		screen->connected = false;
+
+	memcpy(&screen->mode, &con->modes[0], sizeof(drmModeModeInfo));
+	screen->width = screen->mode.hdisplay;
+	screen->height = screen->mode.vdisplay;
+
+	drmModeFreeConnector(con);
+}
+
+struct kms_screen *kms_screen_create(struct kms_device *device, uint32_t id)
+{
+	struct kms_screen *screen;
+
+	screen = calloc(1, sizeof(*screen));
+	if (!screen)
+		return NULL;
+
+	screen->device = device;
+	screen->id = id;
+
+	kms_screen_probe(screen);
+
+	return screen;
+}
+
+void kms_screen_free(struct kms_screen *screen)
+{
+	if (screen)
+		free(screen->name);
+
+	free(screen);
+}
+
+int kms_screen_set(struct kms_screen *screen, struct kms_crtc *crtc,
+		   struct kms_framebuffer *fb)
+{
+	struct kms_device *device = screen->device;
+	int err;
+
+	err = drmModeSetCrtc(device->fd, crtc->id, fb->id, 0, 0, &screen->id,
+			     1, &screen->mode);
+	if (err < 0)
+		return -errno;
+
+	return 0;
+}
diff --git a/tests/kms/libkms-test.h b/tests/kms/libkms-test.h
new file mode 100644
index 0000000..7b1d02e
--- /dev/null
+++ b/tests/kms/libkms-test.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2014 NVIDIA 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.
+ */
+
+#ifndef LIBKMS_TEST_H
+#define LIBKMS_TEST_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <xf86drmMode.h>
+
+struct kms_device {
+	int fd;
+
+	struct kms_screen **screens;
+	unsigned int num_screens;
+
+	struct kms_crtc **crtcs;
+	unsigned int num_crtcs;
+
+	struct kms_plane **planes;
+	unsigned int num_planes;
+};
+
+struct kms_device *kms_device_open(int fd);
+void kms_device_close(struct kms_device *device);
+
+struct kms_plane *kms_device_find_plane_by_type(struct kms_device *device,
+						uint32_t type,
+						unsigned int index);
+
+struct kms_crtc {
+	struct kms_device *device;
+	uint32_t id;
+};
+
+struct kms_crtc *kms_crtc_create(struct kms_device *device, uint32_t id);
+void kms_crtc_free(struct kms_crtc *crtc);
+
+struct kms_framebuffer {
+	struct kms_device *device;
+
+	unsigned int width;
+	unsigned int height;
+	unsigned int pitch;
+	uint32_t format;
+	size_t size;
+
+	uint32_t handle;
+	uint32_t id;
+
+	void *ptr;
+};
+
+struct kms_framebuffer *kms_framebuffer_create(struct kms_device *device,
+					       unsigned int width,
+					       unsigned int height,
+					       uint32_t format);
+void kms_framebuffer_free(struct kms_framebuffer *fb);
+int kms_framebuffer_map(struct kms_framebuffer *fb, void **ptrp);
+void kms_framebuffer_unmap(struct kms_framebuffer *fb);
+
+struct kms_screen {
+	struct kms_device *device;
+	bool connected;
+	uint32_t type;
+	uint32_t id;
+
+	unsigned int width;
+	unsigned int height;
+	char *name;
+
+	drmModeModeInfo mode;
+};
+
+struct kms_screen *kms_screen_create(struct kms_device *device, uint32_t id);
+void kms_screen_free(struct kms_screen *screen);
+
+int kms_screen_set(struct kms_screen *screen, struct kms_crtc *crtc,
+		   struct kms_framebuffer *fb);
+
+struct kms_plane {
+	struct kms_device *device;
+	struct kms_crtc *crtc;
+	unsigned int type;
+	uint32_t id;
+
+	uint32_t *formats;
+	unsigned int num_formats;
+};
+
+struct kms_plane *kms_plane_create(struct kms_device *device, uint32_t id);
+void kms_plane_free(struct kms_plane *plane);
+
+int kms_plane_set(struct kms_plane *plane, struct kms_framebuffer *fb,
+		  unsigned int x, unsigned int y);
+bool kms_plane_supports_format(struct kms_plane *plane, uint32_t format);
+
+#endif
commit ca6c5f8c81e762b25c7b999fa5fd9d17fd67e4dc
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:41 2015 +0100

    proptest: Add Android support
    
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/proptest/Android.mk b/tests/proptest/Android.mk
new file mode 100644
index 0000000..d0ab5c9
--- /dev/null
+++ b/tests/proptest/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_SRC_FILES := $(PROPTEST_FILES)
+
+LOCAL_MODULE := proptest
+
+LOCAL_SHARED_LIBRARIES := libdrm
+LOCAL_STATIC_LIBRARIES := libdrm_util
+
+include $(BUILD_EXECUTABLE)
diff --git a/tests/proptest/Makefile.am b/tests/proptest/Makefile.am
index 1428820..3fde46b 100644
--- a/tests/proptest/Makefile.am
+++ b/tests/proptest/Makefile.am
@@ -1,3 +1,5 @@
+include Makefile.sources
+
 AM_CFLAGS = \
 	$(WARN_CFLAGS)\
 	-I$(top_srcdir)/include/drm \
@@ -12,8 +14,8 @@ noinst_PROGRAMS = \
 	proptest
 endif
 
-proptest_SOURCES = \
-	proptest.c
+proptest_SOURCES = $(PROPTEST_FILES)
+
 proptest_LDADD = \
 	$(top_builddir)/libdrm.la \
 	$(top_builddir)/tests/util/libutil.la
diff --git a/tests/proptest/Makefile.sources b/tests/proptest/Makefile.sources
new file mode 100644
index 0000000..446110d
--- /dev/null
+++ b/tests/proptest/Makefile.sources
@@ -0,0 +1,2 @@
+PROPTEST_FILES := \
+	proptest.c
commit 4664d657ead5ff176c92e8ac54e09ead2e6d94ca
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:40 2015 +0100

    tests: Move name tables to libutil
    
    These tables are duplicated in several places, so move them into libutil
    so that they can be shared.
    
    Acked-by: Laurent Pinchart <[email protected]>
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index 361c6da..0e009d8 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -62,6 +62,7 @@
 
 #include "util/common.h"
 #include "util/format.h"
+#include "util/kms.h"
 #include "util/pattern.h"
 
 #include "buffers.h"
@@ -126,64 +127,6 @@ static inline int64_t U642I64(uint64_t val)
 	return (int64_t)*((int64_t *)&val);
 }
 
-struct type_name {
-	int type;
-	const char *name;
-};
-
-#define type_name_fn(res) \
-const char * res##_str(int type) {			\
-	unsigned int i;					\
-	for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
-		if (res##_names[i].type == type)	\
-			return res##_names[i].name;	\
-	}						\
-	return "(invalid)";				\
-}
-
-struct type_name encoder_type_names[] = {
-	{ DRM_MODE_ENCODER_NONE, "none" },
-	{ DRM_MODE_ENCODER_DAC, "DAC" },
-	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
-	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
-	{ DRM_MODE_ENCODER_TVDAC, "TVDAC" },
-	{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
-	{ DRM_MODE_ENCODER_DSI, "DSI" },
-	{ DRM_MODE_ENCODER_DPMST, "DPMST" },
-};
-
-static type_name_fn(encoder_type)
-
-struct type_name connector_status_names[] = {
-	{ DRM_MODE_CONNECTED, "connected" },
-	{ DRM_MODE_DISCONNECTED, "disconnected" },
-	{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
-};
-
-static type_name_fn(connector_status)
-
-struct type_name connector_type_names[] = {
-	{ DRM_MODE_CONNECTOR_Unknown, "unknown" },
-	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
-	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
-	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
-	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
-	{ DRM_MODE_CONNECTOR_Composite, "composite" },
-	{ DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
-	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
-	{ DRM_MODE_CONNECTOR_Component, "component" },
-	{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
-	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
-	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
-	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
-	{ DRM_MODE_CONNECTOR_TV, "TV" },
-	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
-	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
-	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
-};
-
-static type_name_fn(connector_type)
-
 #define bit_name_fn(res)					\
 const char * res##_str(int type) {				\
 	unsigned int i;						\
@@ -243,7 +186,7 @@ static void dump_encoders(struct device *dev)
 		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
 		       encoder->encoder_id,
 		       encoder->crtc_id,
-		       encoder_type_str(encoder->encoder_type),
+		       util_lookup_encoder_type_name(encoder->encoder_type),
 		       encoder->possible_crtcs,
 		       encoder->possible_clones);
 	}
@@ -387,7 +330,7 @@ static void dump_connectors(struct device *dev)
 		printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
 		       connector->connector_id,
 		       connector->encoder_id,
-		       connector_status_str(connector->connection),
+		       util_lookup_connector_status_name(connector->connection),
 		       _connector->name,
 		       connector->mmWidth, connector->mmHeight,
 		       connector->count_modes);
@@ -611,10 +554,11 @@ static struct resources *get_resources(struct device *dev)
 	/* Set the name of all connectors based on the type name and the per-type ID. */
 	for (i = 0; i < res->res->count_connectors; i++) {
 		struct connector *connector = &res->connectors[i];
+		drmModeConnector *conn = connector->connector;
 
 		asprintf(&connector->name, "%s-%u",
-			 connector_type_str(connector->connector->connector_type),
-			 connector->connector->connector_type_id);
+			 util_lookup_connector_type_name(conn->connector_type),
+			 conn->connector_type_id);
 	}
 
 #define get_properties(_res, __res, type, Type)					\
diff --git a/tests/proptest/proptest.c b/tests/proptest/proptest.c
index b6e00cf..11eb6e8 100644
--- a/tests/proptest/proptest.c
+++ b/tests/proptest/proptest.c
@@ -36,6 +36,7 @@
 #include "xf86drmMode.h"
 
 #include "util/common.h"
+#include "util/kms.h"
 
 static inline int64_t U642I64(uint64_t val)
 {
@@ -45,44 +46,6 @@ static inline int64_t U642I64(uint64_t val)
 int fd;
 drmModeResPtr res = NULL;
 
-static const char *connector_type_str(uint32_t type)
-{
-	switch (type) {
-	case DRM_MODE_CONNECTOR_Unknown:
-		return "Unknown";
-	case DRM_MODE_CONNECTOR_VGA:
-		return "VGA";
-	case DRM_MODE_CONNECTOR_DVII:
-		return "DVI-I";
-	case DRM_MODE_CONNECTOR_DVID:
-		return "DVI-D";
-	case DRM_MODE_CONNECTOR_DVIA:
-		return "DVI-A";
-	case DRM_MODE_CONNECTOR_Composite:
-		return "Composite";
-	case DRM_MODE_CONNECTOR_SVIDEO:
-		return "SVIDEO";
-	case DRM_MODE_CONNECTOR_LVDS:
-		return "LVDS";
-	case DRM_MODE_CONNECTOR_Component:
-		return "Component";
-	case DRM_MODE_CONNECTOR_9PinDIN:
-		return "9PinDin";
-	case DRM_MODE_CONNECTOR_DisplayPort:
-		return "DisplayPort";
-	case DRM_MODE_CONNECTOR_HDMIA:
-		return "HDMI-A";
-	case DRM_MODE_CONNECTOR_HDMIB:
-		return "HDMI-B";
-	case DRM_MODE_CONNECTOR_TV:
-		return "TV";
-	case DRM_MODE_CONNECTOR_eDP:
-		return "eDP";
-	default:
-		return "Invalid";
-	}
-}
-
 /* dump_blob and dump_prop shamelessly copied from ../modetest/modetest.c */
 static void
 dump_blob(uint32_t blob_id)
@@ -226,7 +189,7 @@ static void listConnectorProperties(void)
 		}
 
 		printf("Connector %u (%s-%u)\n", c->connector_id,
-		       connector_type_str(c->connector_type),
+		       util_lookup_connector_type_name(c->connector_type),
 		       c->connector_type_id);
 
 		listObjectProperties(c->connector_id,
diff --git a/tests/util/Makefile.sources b/tests/util/Makefile.sources
index e91fa72..e5f8511 100644
--- a/tests/util/Makefile.sources
+++ b/tests/util/Makefile.sources
@@ -2,5 +2,7 @@ UTIL_FILES := \
 	common.h \
 	format.c \
 	format.h \
+	kms.c \
+	kms.h \
 	pattern.c \
 	pattern.h
diff --git a/tests/util/kms.c b/tests/util/kms.c
new file mode 100644
index 0000000..687b3c3
--- /dev/null
+++ b/tests/util/kms.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+/*
+ * This fairly simple test program dumps output in a similar format to the
+ * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
+ * since the kernel separates outputs into encoder and connector structures,
+ * each with their own unique ID.  The program also allows test testing of the
+ * memory management and mode setting APIs by allowing the user to specify a
+ * connector and mode to use for mode setting.  If all works as expected, a
+ * blue background should be painted on the monitor attached to the specified
+ * connector after the selected mode is set.
+ *
+ * TODO: use cairo to write the mode info on the selected output once
+ *       the mode has been programmed, along with possible test patterns.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "xf86drmMode.h"
+
+#include "common.h"
+
+struct type_name {
+	unsigned int type;
+	const char *name;
+};
+
+static const char *util_lookup_type_name(unsigned int type,
+					 const struct type_name *table,
+					 unsigned int count)
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++)
+		if (table[i].type == type)
+			return table[i].name;
+
+	return NULL;
+}
+
+static const struct type_name encoder_type_names[] = {
+	{ DRM_MODE_ENCODER_NONE, "none" },
+	{ DRM_MODE_ENCODER_DAC, "DAC" },
+	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
+	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
+	{ DRM_MODE_ENCODER_TVDAC, "TVDAC" },
+	{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
+	{ DRM_MODE_ENCODER_DSI, "DSI" },
+	{ DRM_MODE_ENCODER_DPMST, "DPMST" },
+};
+
+const char *util_lookup_encoder_type_name(unsigned int type)
+{
+	return util_lookup_type_name(type, encoder_type_names,
+				     ARRAY_SIZE(encoder_type_names));
+}
+
+static const struct type_name connector_status_names[] = {
+	{ DRM_MODE_CONNECTED, "connected" },
+	{ DRM_MODE_DISCONNECTED, "disconnected" },
+	{ DRM_MODE_UNKNOWNCONNECTION, "unknown" },
+};
+
+const char *util_lookup_connector_status_name(unsigned int status)
+{
+	return util_lookup_type_name(status, connector_status_names,
+				     ARRAY_SIZE(connector_status_names));
+}
+
+static const struct type_name connector_type_names[] = {
+	{ DRM_MODE_CONNECTOR_Unknown, "unknown" },
+	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
+	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+	{ DRM_MODE_CONNECTOR_Composite, "composite" },
+	{ DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
+	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+	{ DRM_MODE_CONNECTOR_Component, "component" },
+	{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
+	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+	{ DRM_MODE_CONNECTOR_TV, "TV" },
+	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
+	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
+	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
+};
+
+const char *util_lookup_connector_type_name(unsigned int type)
+{
+	return util_lookup_type_name(type, connector_type_names,
+				     ARRAY_SIZE(connector_type_names));
+}
diff --git a/tests/util/kms.h b/tests/util/kms.h
new file mode 100644
index 0000000..fa9ab69
--- /dev/null
+++ b/tests/util/kms.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+#ifndef UTIL_KMS_H
+#define UTIL_KMS_H
+
+const char *util_lookup_encoder_type_name(unsigned int type);
+const char *util_lookup_connector_status_name(unsigned int type);
+const char *util_lookup_connector_type_name(unsigned int type);
+
+#endif /* UTIL_KMS_H */
commit 1ec3c44bdd38051d870f64d0b2cc7dbd59760386
Author: Thierry Reding <[email protected]>
Date:   Wed Dec 9 18:37:39 2015 +0100

    tests: Split helpers into library
    
    Some of the helpers, such as the pattern drawing helpers or the format
    lookup helpers, have potential to be reused. Move them into a separate
    library to make it easier to share them.
    
    Acked-by: Laurent Pinchart <[email protected]>
    Signed-off-by: Thierry Reding <[email protected]>
    Reviewed-by: Emil Velikov <[email protected]>

diff --git a/CleanSpec.mk b/CleanSpec.mk
index 8e23cd2..28a11db 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -1,3 +1,4 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/include/libdrm)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/include/freedreno)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdrm_*intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libdrm_*intermediates)
diff --git a/configure.ac b/configure.ac
index b929d36..ca6cbf4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -507,6 +507,7 @@ AC_CONFIG_FILES([
 	tests/exynos/Makefile
 	tests/tegra/Makefile
 	tests/nouveau/Makefile
+	tests/util/Makefile
 	man/Makefile
 	libdrm.pc])
 AC_OUTPUT
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c53f4af..7ffc076 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = modeprint proptest modetest vbltest
+SUBDIRS = util modeprint proptest modetest vbltest
 
 if HAVE_LIBKMS
 SUBDIRS += kmstest
diff --git a/tests/modeprint/Makefile.am b/tests/modeprint/Makefile.am
index 895805f..601dbc9 100644
--- a/tests/modeprint/Makefile.am
+++ b/tests/modeprint/Makefile.am
@@ -1,6 +1,7 @@
 AM_CFLAGS = \
 	$(WARN_CFLAGS)\
 	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/tests \
 	-I$(top_srcdir)
 
 if HAVE_INSTALL_TESTS
diff --git a/tests/modeprint/modeprint.c b/tests/modeprint/modeprint.c
index 5e953f7..0d85410 100644
--- a/tests/modeprint/modeprint.c
+++ b/tests/modeprint/modeprint.c
@@ -41,7 +41,7 @@
 #include "xf86drm.h"
 #include "xf86drmMode.h"
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#include "util/common.h"
 
 int current;
 int connectors;
diff --git a/tests/modetest/Android.mk b/tests/modetest/Android.mk
index 0fdfe68..e616558 100644
--- a/tests/modetest/Android.mk
+++ b/tests/modetest/Android.mk
@@ -8,5 +8,6 @@ LOCAL_SRC_FILES := $(MODETEST_FILES)
 LOCAL_MODULE := modetest
 
 LOCAL_SHARED_LIBRARIES := libdrm
+LOCAL_STATIC_LIBRARIES := libdrm_util
 
 include $(BUILD_EXECUTABLE)
diff --git a/tests/modetest/Makefile.am b/tests/modetest/Makefile.am
index 93820fa..25ce372 100644
--- a/tests/modetest/Makefile.am
+++ b/tests/modetest/Makefile.am
@@ -4,6 +4,7 @@ AM_CFLAGS = $(filter-out -Wpointer-arith, $(WARN_CFLAGS))
 
 AM_CFLAGS += \
 	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/tests \
 	-I$(top_srcdir)
 
 if HAVE_INSTALL_TESTS
@@ -18,11 +19,8 @@ modetest_SOURCES = $(MODETEST_FILES)
 
 modetest_LDADD = \
 	$(top_builddir)/libdrm.la \
+	$(top_builddir)/tests/util/libutil.la \
+	$(CAIRO_LIBS) \
 	-lpthread
 
-if HAVE_CAIRO
-AM_CFLAGS += $(CAIRO_CFLAGS)
-modetest_LDADD += $(CAIRO_LIBS)
-endif
-
 EXTRA_DIST = Android.mk
diff --git a/tests/modetest/buffers.c b/tests/modetest/buffers.c
index ebb14ef..4fd310b 100644
--- a/tests/modetest/buffers.c
+++ b/tests/modetest/buffers.c
@@ -44,13 +44,6 @@
 
 #include "buffers.h"
 
-#ifdef HAVE_CAIRO
-#include <math.h>
-#include <cairo.h>
-#endif
-
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-
 struct bo
 {
 	int fd;
@@ -62,956 +55,6 @@ struct bo
 };
 
 /* -----------------------------------------------------------------------------
- * Formats
- */
-
-struct color_component {
-	unsigned int length;
-	unsigned int offset;
-};
-
-struct rgb_info {
-	struct color_component red;
-	struct color_component green;
-	struct color_component blue;
-	struct color_component alpha;
-};
-
-enum yuv_order {
-	YUV_YCbCr = 1,
-	YUV_YCrCb = 2,
-	YUV_YC = 4,
-	YUV_CY = 8,
-};
-
-struct yuv_info {
-	enum yuv_order order;
-	unsigned int xsub;
-	unsigned int ysub;
-	unsigned int chroma_stride;
-};
-
-struct format_info {
-	unsigned int format;
-	const char *name;
-	const struct rgb_info rgb;
-	const struct yuv_info yuv;
-};
-
-#define MAKE_RGB_INFO(rl, ro, gl, go, bl, bo, al, ao) \
-	.rgb = { { (rl), (ro) }, { (gl), (go) }, { (bl), (bo) }, { (al), (ao) } }
-
-#define MAKE_YUV_INFO(order, xsub, ysub, chroma_stride) \
-	.yuv = { (order), (xsub), (ysub), (chroma_stride) }
-
-static const struct format_info format_info[] = {
-	/* YUV packed */
-	{ DRM_FORMAT_UYVY, "UYVY", MAKE_YUV_INFO(YUV_YCbCr | YUV_CY, 2, 2, 2) },
-	{ DRM_FORMAT_VYUY, "VYUY", MAKE_YUV_INFO(YUV_YCrCb | YUV_CY, 2, 2, 2) },
-	{ DRM_FORMAT_YUYV, "YUYV", MAKE_YUV_INFO(YUV_YCbCr | YUV_YC, 2, 2, 2) },
-	{ DRM_FORMAT_YVYU, "YVYU", MAKE_YUV_INFO(YUV_YCrCb | YUV_YC, 2, 2, 2) },
-	/* YUV semi-planar */
-	{ DRM_FORMAT_NV12, "NV12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 2) },
-	{ DRM_FORMAT_NV21, "NV21", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 2) },
-	{ DRM_FORMAT_NV16, "NV16", MAKE_YUV_INFO(YUV_YCbCr, 2, 1, 2) },
-	{ DRM_FORMAT_NV61, "NV61", MAKE_YUV_INFO(YUV_YCrCb, 2, 1, 2) },
-	/* YUV planar */
-	{ DRM_FORMAT_YUV420, "YU12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 1) },
-	{ DRM_FORMAT_YVU420, "YV12", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 1) },
-	/* RGB16 */
-	{ DRM_FORMAT_ARGB4444, "AR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 4, 12) },
-	{ DRM_FORMAT_XRGB4444, "XR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 0, 0) },
-	{ DRM_FORMAT_ABGR4444, "AB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 4, 12) },
-	{ DRM_FORMAT_XBGR4444, "XB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 0, 0) },
-	{ DRM_FORMAT_RGBA4444, "RA12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 4, 0) },
-	{ DRM_FORMAT_RGBX4444, "RX12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 0, 0) },
-	{ DRM_FORMAT_BGRA4444, "BA12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 4, 0) },
-	{ DRM_FORMAT_BGRX4444, "BX12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 0, 0) },
-	{ DRM_FORMAT_ARGB1555, "AR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 1, 15) },
-	{ DRM_FORMAT_XRGB1555, "XR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 0, 0) },
-	{ DRM_FORMAT_ABGR1555, "AB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 1, 15) },
-	{ DRM_FORMAT_XBGR1555, "XB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 0, 0) },
-	{ DRM_FORMAT_RGBA5551, "RA15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 1, 0) },
-	{ DRM_FORMAT_RGBX5551, "RX15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 0, 0) },
-	{ DRM_FORMAT_BGRA5551, "BA15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 1, 0) },
-	{ DRM_FORMAT_BGRX5551, "BX15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 0, 0) },
-	{ DRM_FORMAT_RGB565, "RG16", MAKE_RGB_INFO(5, 11, 6, 5, 5, 0, 0, 0) },
-	{ DRM_FORMAT_BGR565, "BG16", MAKE_RGB_INFO(5, 0, 6, 5, 5, 11, 0, 0) },
-	/* RGB24 */
-	{ DRM_FORMAT_BGR888, "BG24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) },
-	{ DRM_FORMAT_RGB888, "RG24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) },
-	/* RGB32 */
-	{ DRM_FORMAT_ARGB8888, "AR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 8, 24) },
-	{ DRM_FORMAT_XRGB8888, "XR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) },
-	{ DRM_FORMAT_ABGR8888, "AB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 8, 24) },
-	{ DRM_FORMAT_XBGR8888, "XB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) },
-	{ DRM_FORMAT_RGBA8888, "RA24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 8, 0) },
-	{ DRM_FORMAT_RGBX8888, "RX24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 0, 0) },
-	{ DRM_FORMAT_BGRA8888, "BA24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 8, 0) },
-	{ DRM_FORMAT_BGRX8888, "BX24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 0, 0) },
-	{ DRM_FORMAT_ARGB2101010, "AR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 2, 30) },
-	{ DRM_FORMAT_XRGB2101010, "XR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 0, 0) },
-	{ DRM_FORMAT_ABGR2101010, "AB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 2, 30) },
-	{ DRM_FORMAT_XBGR2101010, "XB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 0, 0) },
-	{ DRM_FORMAT_RGBA1010102, "RA30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 2, 0) },
-	{ DRM_FORMAT_RGBX1010102, "RX30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 0, 0) },
-	{ DRM_FORMAT_BGRA1010102, "BA30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 2, 0) },
-	{ DRM_FORMAT_BGRX1010102, "BX30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 0, 0) },
-};
-
-unsigned int format_fourcc(const char *name)
-{
-	unsigned int i;
-	for (i = 0; i < ARRAY_SIZE(format_info); i++) {
-		if (!strcmp(format_info[i].name, name))
-			return format_info[i].format;
-	}
-	return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Test patterns
- */
-
-struct color_rgb24 {
-	unsigned int value:24;
-} __attribute__((__packed__));
-
-struct color_yuv {
-	unsigned char y;
-	unsigned char u;
-	unsigned char v;
-};
-
-#define MAKE_YUV_601_Y(r, g, b) \
-	((( 66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) + 16)
-#define MAKE_YUV_601_U(r, g, b) \
-	(((-38 * (r) -  74 * (g) + 112 * (b) + 128) >> 8) + 128)
-#define MAKE_YUV_601_V(r, g, b) \
-	(((112 * (r) -  94 * (g) -  18 * (b) + 128) >> 8) + 128)
-
-#define MAKE_YUV_601(r, g, b) \
-	{ .y = MAKE_YUV_601_Y(r, g, b), \
-	  .u = MAKE_YUV_601_U(r, g, b), \
-	  .v = MAKE_YUV_601_V(r, g, b) }
-
-#define MAKE_RGBA(rgb, r, g, b, a) \
-	((((r) >> (8 - (rgb)->red.length)) << (rgb)->red.offset) | \
-	 (((g) >> (8 - (rgb)->green.length)) << (rgb)->green.offset) | \
-	 (((b) >> (8 - (rgb)->blue.length)) << (rgb)->blue.offset) | \
-	 (((a) >> (8 - (rgb)->alpha.length)) << (rgb)->alpha.offset))
-
-#define MAKE_RGB24(rgb, r, g, b) \
-	{ .value = MAKE_RGBA(rgb, r, g, b, 0) }
-
-static void
-fill_smpte_yuv_planar(const struct yuv_info *yuv,
-		      unsigned char *y_mem, unsigned char *u_mem,
-		      unsigned char *v_mem, unsigned int width,
-		      unsigned int height, unsigned int stride)
-{
-	const struct color_yuv colors_top[] = {
-		MAKE_YUV_601(191, 192, 192),	/* grey */
-		MAKE_YUV_601(192, 192, 0),	/* yellow */
-		MAKE_YUV_601(0, 192, 192),	/* cyan */
-		MAKE_YUV_601(0, 192, 0),	/* green */
-		MAKE_YUV_601(192, 0, 192),	/* magenta */
-		MAKE_YUV_601(192, 0, 0),	/* red */
-		MAKE_YUV_601(0, 0, 192),	/* blue */
-	};
-	const struct color_yuv colors_middle[] = {
-		MAKE_YUV_601(0, 0, 192),	/* blue */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(192, 0, 192),	/* magenta */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(0, 192, 192),	/* cyan */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(192, 192, 192),	/* grey */
-	};
-	const struct color_yuv colors_bottom[] = {
-		MAKE_YUV_601(0, 33, 76),	/* in-phase */
-		MAKE_YUV_601(255, 255, 255),	/* super white */
-		MAKE_YUV_601(50, 0, 106),	/* quadrature */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(9, 9, 9),		/* 3.5% */
-		MAKE_YUV_601(19, 19, 19),	/* 7.5% */
-		MAKE_YUV_601(29, 29, 29),	/* 11.5% */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-	};
-	unsigned int cs = yuv->chroma_stride;
-	unsigned int xsub = yuv->xsub;
-	unsigned int ysub = yuv->ysub;
-	unsigned int x;
-	unsigned int y;
-
-	/* Luma */
-	for (y = 0; y < height * 6 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			y_mem[x] = colors_top[x * 7 / width].y;
-		y_mem += stride;
-	}
-
-	for (; y < height * 7 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			y_mem[x] = colors_middle[x * 7 / width].y;
-		y_mem += stride;
-	}
-
-	for (; y < height; ++y) {
-		for (x = 0; x < width * 5 / 7; ++x)
-			y_mem[x] = colors_bottom[x * 4 / (width * 5 / 7)].y;
-		for (; x < width * 6 / 7; ++x)
-			y_mem[x] = colors_bottom[(x - width * 5 / 7) * 3
-						 / (width / 7) + 4].y;
-		for (; x < width; ++x)
-			y_mem[x] = colors_bottom[7].y;
-		y_mem += stride;
-	}
-
-	/* Chroma */
-	for (y = 0; y < height / ysub * 6 / 9; ++y) {
-		for (x = 0; x < width; x += xsub) {
-			u_mem[x*cs/xsub] = colors_top[x * 7 / width].u;
-			v_mem[x*cs/xsub] = colors_top[x * 7 / width].v;
-		}
-		u_mem += stride * cs / xsub;
-		v_mem += stride * cs / xsub;
-	}
-
-	for (; y < height / ysub * 7 / 9; ++y) {
-		for (x = 0; x < width; x += xsub) {
-			u_mem[x*cs/xsub] = colors_middle[x * 7 / width].u;
-			v_mem[x*cs/xsub] = colors_middle[x * 7 / width].v;
-		}
-		u_mem += stride * cs / xsub;
-		v_mem += stride * cs / xsub;
-	}
-
-	for (; y < height / ysub; ++y) {
-		for (x = 0; x < width * 5 / 7; x += xsub) {
-			u_mem[x*cs/xsub] =
-				colors_bottom[x * 4 / (width * 5 / 7)].u;
-			v_mem[x*cs/xsub] =
-				colors_bottom[x * 4 / (width * 5 / 7)].v;
-		}
-		for (; x < width * 6 / 7; x += xsub) {
-			u_mem[x*cs/xsub] = colors_bottom[(x - width * 5 / 7) *
-							 3 / (width / 7) + 4].u;
-			v_mem[x*cs/xsub] = colors_bottom[(x - width * 5 / 7) *
-							 3 / (width / 7) + 4].v;
-		}
-		for (; x < width; x += xsub) {
-			u_mem[x*cs/xsub] = colors_bottom[7].u;
-			v_mem[x*cs/xsub] = colors_bottom[7].v;
-		}
-		u_mem += stride * cs / xsub;
-		v_mem += stride * cs / xsub;
-	}
-}
-
-static void
-fill_smpte_yuv_packed(const struct yuv_info *yuv, unsigned char *mem,
-		      unsigned int width, unsigned int height,
-		      unsigned int stride)
-{
-	const struct color_yuv colors_top[] = {
-		MAKE_YUV_601(191, 192, 192),	/* grey */
-		MAKE_YUV_601(192, 192, 0),	/* yellow */
-		MAKE_YUV_601(0, 192, 192),	/* cyan */
-		MAKE_YUV_601(0, 192, 0),	/* green */
-		MAKE_YUV_601(192, 0, 192),	/* magenta */
-		MAKE_YUV_601(192, 0, 0),	/* red */
-		MAKE_YUV_601(0, 0, 192),	/* blue */
-	};
-	const struct color_yuv colors_middle[] = {
-		MAKE_YUV_601(0, 0, 192),	/* blue */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(192, 0, 192),	/* magenta */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(0, 192, 192),	/* cyan */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(192, 192, 192),	/* grey */
-	};
-	const struct color_yuv colors_bottom[] = {
-		MAKE_YUV_601(0, 33, 76),	/* in-phase */
-		MAKE_YUV_601(255, 255, 255),	/* super white */
-		MAKE_YUV_601(50, 0, 106),	/* quadrature */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-		MAKE_YUV_601(9, 9, 9),		/* 3.5% */
-		MAKE_YUV_601(19, 19, 19),	/* 7.5% */
-		MAKE_YUV_601(29, 29, 29),	/* 11.5% */
-		MAKE_YUV_601(19, 19, 19),	/* black */
-	};
-	unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1;
-	unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1;
-	unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0;
-	unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0;
-	unsigned int x;
-	unsigned int y;
-
-	/* Luma */
-	for (y = 0; y < height * 6 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			y_mem[2*x] = colors_top[x * 7 / width].y;
-		y_mem += stride;
-	}
-
-	for (; y < height * 7 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			y_mem[2*x] = colors_middle[x * 7 / width].y;
-		y_mem += stride;
-	}
-
-	for (; y < height; ++y) {
-		for (x = 0; x < width * 5 / 7; ++x)
-			y_mem[2*x] = colors_bottom[x * 4 / (width * 5 / 7)].y;
-		for (; x < width * 6 / 7; ++x)
-			y_mem[2*x] = colors_bottom[(x - width * 5 / 7) * 3
-						   / (width / 7) + 4].y;
-		for (; x < width; ++x)
-			y_mem[2*x] = colors_bottom[7].y;
-		y_mem += stride;
-	}
-
-	/* Chroma */
-	for (y = 0; y < height * 6 / 9; ++y) {
-		for (x = 0; x < width; x += 2) {
-			c_mem[2*x+u] = colors_top[x * 7 / width].u;
-			c_mem[2*x+v] = colors_top[x * 7 / width].v;
-		}
-		c_mem += stride;
-	}
-
-	for (; y < height * 7 / 9; ++y) {
-		for (x = 0; x < width; x += 2) {
-			c_mem[2*x+u] = colors_middle[x * 7 / width].u;
-			c_mem[2*x+v] = colors_middle[x * 7 / width].v;
-		}
-		c_mem += stride;
-	}
-
-	for (; y < height; ++y) {
-		for (x = 0; x < width * 5 / 7; x += 2) {
-			c_mem[2*x+u] = colors_bottom[x * 4 / (width * 5 / 7)].u;
-			c_mem[2*x+v] = colors_bottom[x * 4 / (width * 5 / 7)].v;
-		}
-		for (; x < width * 6 / 7; x += 2) {
-			c_mem[2*x+u] = colors_bottom[(x - width * 5 / 7) *
-						     3 / (width / 7) + 4].u;
-			c_mem[2*x+v] = colors_bottom[(x - width * 5 / 7) *
-						     3 / (width / 7) + 4].v;
-		}
-		for (; x < width; x += 2) {
-			c_mem[2*x+u] = colors_bottom[7].u;
-			c_mem[2*x+v] = colors_bottom[7].v;
-		}
-		c_mem += stride;
-	}
-}
-
-static void
-fill_smpte_rgb16(const struct rgb_info *rgb, unsigned char *mem,
-		 unsigned int width, unsigned int height, unsigned int stride)
-{
-	const uint16_t colors_top[] = {
-		MAKE_RGBA(rgb, 192, 192, 192, 255),	/* grey */
-		MAKE_RGBA(rgb, 192, 192, 0, 255),	/* yellow */
-		MAKE_RGBA(rgb, 0, 192, 192, 255),	/* cyan */
-		MAKE_RGBA(rgb, 0, 192, 0, 255),		/* green */
-		MAKE_RGBA(rgb, 192, 0, 192, 255),	/* magenta */
-		MAKE_RGBA(rgb, 192, 0, 0, 255),		/* red */
-		MAKE_RGBA(rgb, 0, 0, 192, 255),		/* blue */
-	};
-	const uint16_t colors_middle[] = {
-		MAKE_RGBA(rgb, 0, 0, 192, 127),		/* blue */
-		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
-		MAKE_RGBA(rgb, 192, 0, 192, 127),	/* magenta */
-		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
-		MAKE_RGBA(rgb, 0, 192, 192, 127),	/* cyan */
-		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
-		MAKE_RGBA(rgb, 192, 192, 192, 127),	/* grey */
-	};
-	const uint16_t colors_bottom[] = {
-		MAKE_RGBA(rgb, 0, 33, 76, 255),		/* in-phase */
-		MAKE_RGBA(rgb, 255, 255, 255, 255),	/* super white */
-		MAKE_RGBA(rgb, 50, 0, 106, 255),	/* quadrature */
-		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
-		MAKE_RGBA(rgb, 9, 9, 9, 255),		/* 3.5% */
-		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* 7.5% */
-		MAKE_RGBA(rgb, 29, 29, 29, 255),	/* 11.5% */
-		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
-	};
-	unsigned int x;
-	unsigned int y;
-
-	for (y = 0; y < height * 6 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			((uint16_t *)mem)[x] = colors_top[x * 7 / width];
-		mem += stride;
-	}
-
-	for (; y < height * 7 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			((uint16_t *)mem)[x] = colors_middle[x * 7 / width];
-		mem += stride;
-	}
-
-	for (; y < height; ++y) {
-		for (x = 0; x < width * 5 / 7; ++x)
-			((uint16_t *)mem)[x] =
-				colors_bottom[x * 4 / (width * 5 / 7)];
-		for (; x < width * 6 / 7; ++x)
-			((uint16_t *)mem)[x] =
-				colors_bottom[(x - width * 5 / 7) * 3
-					      / (width / 7) + 4];
-		for (; x < width; ++x)
-			((uint16_t *)mem)[x] = colors_bottom[7];
-		mem += stride;
-	}
-}
-
-static void
-fill_smpte_rgb24(const struct rgb_info *rgb, void *mem,
-		 unsigned int width, unsigned int height, unsigned int stride)
-{
-	const struct color_rgb24 colors_top[] = {
-		MAKE_RGB24(rgb, 192, 192, 192),	/* grey */
-		MAKE_RGB24(rgb, 192, 192, 0),	/* yellow */
-		MAKE_RGB24(rgb, 0, 192, 192),	/* cyan */
-		MAKE_RGB24(rgb, 0, 192, 0),	/* green */
-		MAKE_RGB24(rgb, 192, 0, 192),	/* magenta */
-		MAKE_RGB24(rgb, 192, 0, 0),	/* red */
-		MAKE_RGB24(rgb, 0, 0, 192),	/* blue */
-	};
-	const struct color_rgb24 colors_middle[] = {
-		MAKE_RGB24(rgb, 0, 0, 192),	/* blue */
-		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
-		MAKE_RGB24(rgb, 192, 0, 192),	/* magenta */
-		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
-		MAKE_RGB24(rgb, 0, 192, 192),	/* cyan */
-		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
-		MAKE_RGB24(rgb, 192, 192, 192),	/* grey */
-	};
-	const struct color_rgb24 colors_bottom[] = {
-		MAKE_RGB24(rgb, 0, 33, 76),	/* in-phase */
-		MAKE_RGB24(rgb, 255, 255, 255),	/* super white */
-		MAKE_RGB24(rgb, 50, 0, 106),	/* quadrature */
-		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
-		MAKE_RGB24(rgb, 9, 9, 9),	/* 3.5% */
-		MAKE_RGB24(rgb, 19, 19, 19),	/* 7.5% */
-		MAKE_RGB24(rgb, 29, 29, 29),	/* 11.5% */
-		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
-	};
-	unsigned int x;
-	unsigned int y;
-
-	for (y = 0; y < height * 6 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			((struct color_rgb24 *)mem)[x] =
-				colors_top[x * 7 / width];
-		mem += stride;
-	}
-
-	for (; y < height * 7 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			((struct color_rgb24 *)mem)[x] =
-				colors_middle[x * 7 / width];
-		mem += stride;
-	}
-
-	for (; y < height; ++y) {
-		for (x = 0; x < width * 5 / 7; ++x)
-			((struct color_rgb24 *)mem)[x] =
-				colors_bottom[x * 4 / (width * 5 / 7)];
-		for (; x < width * 6 / 7; ++x)
-			((struct color_rgb24 *)mem)[x] =
-				colors_bottom[(x - width * 5 / 7) * 3
-					      / (width / 7) + 4];
-		for (; x < width; ++x)
-			((struct color_rgb24 *)mem)[x] = colors_bottom[7];
-		mem += stride;
-	}
-}
-
-static void
-fill_smpte_rgb32(const struct rgb_info *rgb, unsigned char *mem,
-		 unsigned int width, unsigned int height, unsigned int stride)
-{
-	const uint32_t colors_top[] = {
-		MAKE_RGBA(rgb, 192, 192, 192, 255),	/* grey */
-		MAKE_RGBA(rgb, 192, 192, 0, 255),	/* yellow */
-		MAKE_RGBA(rgb, 0, 192, 192, 255),	/* cyan */
-		MAKE_RGBA(rgb, 0, 192, 0, 255),		/* green */
-		MAKE_RGBA(rgb, 192, 0, 192, 255),	/* magenta */
-		MAKE_RGBA(rgb, 192, 0, 0, 255),		/* red */
-		MAKE_RGBA(rgb, 0, 0, 192, 255),		/* blue */
-	};
-	const uint32_t colors_middle[] = {
-		MAKE_RGBA(rgb, 0, 0, 192, 127),		/* blue */
-		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
-		MAKE_RGBA(rgb, 192, 0, 192, 127),	/* magenta */
-		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
-		MAKE_RGBA(rgb, 0, 192, 192, 127),	/* cyan */
-		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
-		MAKE_RGBA(rgb, 192, 192, 192, 127),	/* grey */
-	};
-	const uint32_t colors_bottom[] = {
-		MAKE_RGBA(rgb, 0, 33, 76, 255),		/* in-phase */
-		MAKE_RGBA(rgb, 255, 255, 255, 255),	/* super white */
-		MAKE_RGBA(rgb, 50, 0, 106, 255),	/* quadrature */
-		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
-		MAKE_RGBA(rgb, 9, 9, 9, 255),		/* 3.5% */
-		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* 7.5% */
-		MAKE_RGBA(rgb, 29, 29, 29, 255),	/* 11.5% */
-		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
-	};
-	unsigned int x;
-	unsigned int y;
-
-	for (y = 0; y < height * 6 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			((uint32_t *)mem)[x] = colors_top[x * 7 / width];
-		mem += stride;
-	}
-
-	for (; y < height * 7 / 9; ++y) {
-		for (x = 0; x < width; ++x)
-			((uint32_t *)mem)[x] = colors_middle[x * 7 / width];
-		mem += stride;
-	}
-
-	for (; y < height; ++y) {
-		for (x = 0; x < width * 5 / 7; ++x)
-			((uint32_t *)mem)[x] =
-				colors_bottom[x * 4 / (width * 5 / 7)];
-		for (; x < width * 6 / 7; ++x)
-			((uint32_t *)mem)[x] =
-				colors_bottom[(x - width * 5 / 7) * 3
-					      / (width / 7) + 4];
-		for (; x < width; ++x)
-			((uint32_t *)mem)[x] = colors_bottom[7];
-		mem += stride;
-	}
-}
-
-static void
-fill_smpte(const struct format_info *info, void *planes[3], unsigned int width,
-	   unsigned int height, unsigned int stride)
-{
-	unsigned char *u, *v;
-
-	switch (info->format) {
-	case DRM_FORMAT_UYVY:
-	case DRM_FORMAT_VYUY:
-	case DRM_FORMAT_YUYV:
-	case DRM_FORMAT_YVYU:
-		return fill_smpte_yuv_packed(&info->yuv, planes[0], width,
-					     height, stride);
-
-	case DRM_FORMAT_NV12:
-	case DRM_FORMAT_NV21:
-	case DRM_FORMAT_NV16:
-	case DRM_FORMAT_NV61:
-		u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1;
-		v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1;
-		return fill_smpte_yuv_planar(&info->yuv, planes[0], u, v,
-					     width, height, stride);
-
-	case DRM_FORMAT_YUV420:
-		return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[1],
-					     planes[2], width, height, stride);
-
-	case DRM_FORMAT_YVU420:
-		return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[2],
-					     planes[1], width, height, stride);
-
-	case DRM_FORMAT_ARGB4444:
-	case DRM_FORMAT_XRGB4444:
-	case DRM_FORMAT_ABGR4444:
-	case DRM_FORMAT_XBGR4444:
-	case DRM_FORMAT_RGBA4444:
-	case DRM_FORMAT_RGBX4444:
-	case DRM_FORMAT_BGRA4444:
-	case DRM_FORMAT_BGRX4444:
-	case DRM_FORMAT_RGB565:
-	case DRM_FORMAT_BGR565:
-	case DRM_FORMAT_ARGB1555:
-	case DRM_FORMAT_XRGB1555:
-	case DRM_FORMAT_ABGR1555:
-	case DRM_FORMAT_XBGR1555:
-	case DRM_FORMAT_RGBA5551:
-	case DRM_FORMAT_RGBX5551:
-	case DRM_FORMAT_BGRA5551:
-	case DRM_FORMAT_BGRX5551:
-		return fill_smpte_rgb16(&info->rgb, planes[0],
-					width, height, stride);
-
-	case DRM_FORMAT_BGR888:
-	case DRM_FORMAT_RGB888:
-		return fill_smpte_rgb24(&info->rgb, planes[0],
-					width, height, stride);
-	case DRM_FORMAT_ARGB8888:
-	case DRM_FORMAT_XRGB8888:
-	case DRM_FORMAT_ABGR8888:
-	case DRM_FORMAT_XBGR8888:
-	case DRM_FORMAT_RGBA8888:
-	case DRM_FORMAT_RGBX8888:
-	case DRM_FORMAT_BGRA8888:
-	case DRM_FORMAT_BGRX8888:
-	case DRM_FORMAT_ARGB2101010:
-	case DRM_FORMAT_XRGB2101010:
-	case DRM_FORMAT_ABGR2101010:
-	case DRM_FORMAT_XBGR2101010:
-	case DRM_FORMAT_RGBA1010102:
-	case DRM_FORMAT_RGBX1010102:
-	case DRM_FORMAT_BGRA1010102:
-	case DRM_FORMAT_BGRX1010102:
-		return fill_smpte_rgb32(&info->rgb, planes[0],
-					width, height, stride);
-	}
-}
-
-/* swap these for big endian.. */
-#define RED   2
-#define GREEN 1
-#define BLUE  0
-
-static void
-make_pwetty(void *data, int width, int height, int stride, uint32_t format)
-{
-#ifdef HAVE_CAIRO
-	cairo_surface_t *surface;
-	cairo_t *cr;
-	int x, y;
-	cairo_format_t cairo_format;
-
-	/* we can ignore the order of R,G,B channels */
-	switch (format) {
-	case DRM_FORMAT_XRGB8888:
-	case DRM_FORMAT_ARGB8888:
-	case DRM_FORMAT_XBGR8888:
-	case DRM_FORMAT_ABGR8888:
-		cairo_format = CAIRO_FORMAT_ARGB32;
-		break;
-	case DRM_FORMAT_RGB565:
-	case DRM_FORMAT_BGR565:
-		cairo_format = CAIRO_FORMAT_RGB16_565;
-		break;
-	default:
-		return;
-	}
-
-	surface = cairo_image_surface_create_for_data(data,
-						      cairo_format,
-						      width, height,
-						      stride);
-	cr = cairo_create(surface);
-	cairo_surface_destroy(surface);
-
-	cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
-	for (x = 0; x < width; x += 250)
-		for (y = 0; y < height; y += 250) {
-			char buf[64];
-
-			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);
-
-			snprintf(buf, sizeof buf, "%d, %d", x, y);
-			cairo_move_to(cr, x + 20, y + 20);
-			cairo_text_path(cr, buf);
-			cairo_set_source_rgb(cr, 0, 0, 0);
-			cairo_stroke_preserve(cr);
-			cairo_set_source_rgb(cr, 1, 1, 1);
-			cairo_fill(cr);
-		}
-
-	cairo_destroy(cr);
-#endif
-}
-
-static void
-fill_tiles_yuv_planar(const struct format_info *info,
-		      unsigned char *y_mem, unsigned char *u_mem,
-		      unsigned char *v_mem, unsigned int width,
-		      unsigned int height, unsigned int stride)
-{
-	const struct yuv_info *yuv = &info->yuv;
-	unsigned int cs = yuv->chroma_stride;
-	unsigned int xsub = yuv->xsub;
-	unsigned int ysub = yuv->ysub;
-	unsigned int x;
-	unsigned int y;
-
-	for (y = 0; y < height; ++y) {
-		for (x = 0; x < width; ++x) {
-			div_t d = div(x+y, width);
-			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
-				       + 0x000a1120 * (d.rem >> 6);
-			struct color_yuv color =
-				MAKE_YUV_601((rgb32 >> 16) & 0xff,
-					     (rgb32 >> 8) & 0xff, rgb32 & 0xff);
-
-			y_mem[x] = color.y;
-			u_mem[x/xsub*cs] = color.u;
-			v_mem[x/xsub*cs] = color.v;
-		}
-
-		y_mem += stride;
-		if ((y + 1) % ysub == 0) {
-			u_mem += stride * cs / xsub;
-			v_mem += stride * cs / xsub;
-		}
-	}
-}
-
-static void
-fill_tiles_yuv_packed(const struct format_info *info, unsigned char *mem,
-		      unsigned int width, unsigned int height,
-		      unsigned int stride)
-{
-	const struct yuv_info *yuv = &info->yuv;
-	unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1;
-	unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1;
-	unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0;
-	unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0;
-	unsigned int x;
-	unsigned int y;
-
-	for (y = 0; y < height; ++y) {
-		for (x = 0; x < width; x += 2) {
-			div_t d = div(x+y, width);
-			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
-				       + 0x000a1120 * (d.rem >> 6);
-			struct color_yuv color =
-				MAKE_YUV_601((rgb32 >> 16) & 0xff,
-					     (rgb32 >> 8) & 0xff, rgb32 & 0xff);
-
-			y_mem[2*x] = color.y;
-			c_mem[2*x+u] = color.u;
-			y_mem[2*x+2] = color.y;
-			c_mem[2*x+v] = color.v;
-		}
-
-		y_mem += stride;
-		c_mem += stride;
-	}
-}
-
-static void
-fill_tiles_rgb16(const struct format_info *info, unsigned char *mem,
-		 unsigned int width, unsigned int height, unsigned int stride)
-{
-	const struct rgb_info *rgb = &info->rgb;
-	unsigned char *mem_base = mem;
-	unsigned int x, y;
-
-	for (y = 0; y < height; ++y) {
-		for (x = 0; x < width; ++x) {
-			div_t d = div(x+y, width);
-			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
-				       + 0x000a1120 * (d.rem >> 6);
-			uint16_t color =
-				MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff,
-					  (rgb32 >> 8) & 0xff, rgb32 & 0xff,
-					  255);
-
-			((uint16_t *)mem)[x] = color;
-		}
-		mem += stride;
-	}
-
-	make_pwetty(mem_base, width, height, stride, info->format);
-}
-
-static void
-fill_tiles_rgb24(const struct format_info *info, unsigned char *mem,
-		 unsigned int width, unsigned int height, unsigned int stride)
-{
-	const struct rgb_info *rgb = &info->rgb;
-	unsigned int x, y;
-
-	for (y = 0; y < height; ++y) {
-		for (x = 0; x < width; ++x) {
-			div_t d = div(x+y, width);
-			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
-				       + 0x000a1120 * (d.rem >> 6);
-			struct color_rgb24 color =
-				MAKE_RGB24(rgb, (rgb32 >> 16) & 0xff,
-					   (rgb32 >> 8) & 0xff, rgb32 & 0xff);
-
-			((struct color_rgb24 *)mem)[x] = color;
-		}
-		mem += stride;
-	}
-}
-
-static void
-fill_tiles_rgb32(const struct format_info *info, unsigned char *mem,
-		 unsigned int width, unsigned int height, unsigned int stride)
-{
-	const struct rgb_info *rgb = &info->rgb;
-	unsigned char *mem_base = mem;
-	unsigned int x, y;
-
-	for (y = 0; y < height; ++y) {
-		for (x = 0; x < width; ++x) {
-			div_t d = div(x+y, width);
-			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
-				       + 0x000a1120 * (d.rem >> 6);
-			uint32_t alpha = ((y < height/2) && (x < width/2)) ? 127 : 255;
-			uint32_t color =
-				MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff,
-					  (rgb32 >> 8) & 0xff, rgb32 & 0xff,
-					  alpha);
-
-			((uint32_t *)mem)[x] = color;
-		}
-		mem += stride;
-	}
-
-	make_pwetty(mem_base, width, height, stride, info->format);
-}
-
-static void
-fill_tiles(const struct format_info *info, void *planes[3], unsigned int width,
-	   unsigned int height, unsigned int stride)
-{
-	unsigned char *u, *v;
-
-	switch (info->format) {
-	case DRM_FORMAT_UYVY:
-	case DRM_FORMAT_VYUY:
-	case DRM_FORMAT_YUYV:
-	case DRM_FORMAT_YVYU:
-		return fill_tiles_yuv_packed(info, planes[0],
-					     width, height, stride);
-
-	case DRM_FORMAT_NV12:
-	case DRM_FORMAT_NV21:
-	case DRM_FORMAT_NV16:
-	case DRM_FORMAT_NV61:
-		u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1;
-		v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1;
-		return fill_tiles_yuv_planar(info, planes[0], u, v,
-					     width, height, stride);
-
-	case DRM_FORMAT_YUV420:
-		return fill_tiles_yuv_planar(info, planes[0], planes[1],
-					     planes[2], width, height, stride);
-
-	case DRM_FORMAT_YVU420:
-		return fill_tiles_yuv_planar(info, planes[0], planes[2],
-					     planes[1], width, height, stride);
-
-	case DRM_FORMAT_ARGB4444:
-	case DRM_FORMAT_XRGB4444:
-	case DRM_FORMAT_ABGR4444:
-	case DRM_FORMAT_XBGR4444:
-	case DRM_FORMAT_RGBA4444:
-	case DRM_FORMAT_RGBX4444:
-	case DRM_FORMAT_BGRA4444:
-	case DRM_FORMAT_BGRX4444:
-	case DRM_FORMAT_RGB565:
-	case DRM_FORMAT_BGR565:
-	case DRM_FORMAT_ARGB1555:
-	case DRM_FORMAT_XRGB1555:
-	case DRM_FORMAT_ABGR1555:
-	case DRM_FORMAT_XBGR1555:
-	case DRM_FORMAT_RGBA5551:
-	case DRM_FORMAT_RGBX5551:
-	case DRM_FORMAT_BGRA5551:
-	case DRM_FORMAT_BGRX5551:
-		return fill_tiles_rgb16(info, planes[0],
-					width, height, stride);
-
-	case DRM_FORMAT_BGR888:
-	case DRM_FORMAT_RGB888:
-		return fill_tiles_rgb24(info, planes[0],
-					width, height, stride);
-	case DRM_FORMAT_ARGB8888:
-	case DRM_FORMAT_XRGB8888:
-	case DRM_FORMAT_ABGR8888:
-	case DRM_FORMAT_XBGR8888:
-	case DRM_FORMAT_RGBA8888:
-	case DRM_FORMAT_RGBX8888:
-	case DRM_FORMAT_BGRA8888:
-	case DRM_FORMAT_BGRX8888:
-	case DRM_FORMAT_ARGB2101010:
-	case DRM_FORMAT_XRGB2101010:
-	case DRM_FORMAT_ABGR2101010:
-	case DRM_FORMAT_XBGR2101010:
-	case DRM_FORMAT_RGBA1010102:
-	case DRM_FORMAT_RGBX1010102:
-	case DRM_FORMAT_BGRA1010102:
-	case DRM_FORMAT_BGRX1010102:
-		return fill_tiles_rgb32(info, planes[0],
-					width, height, stride);
-	}
-}
-
-static void
-fill_plain(const struct format_info *info, void *planes[3], unsigned int width,
-	   unsigned int height, unsigned int stride)
-{
-	memset(planes[0], 0x77, stride * height);
-}
-
-/*
- * fill_pattern - Fill a buffer with a test pattern
- * @format: Pixel format
- * @pattern: Test pattern
- * @buffer: Buffer memory
- * @width: Width in pixels
- * @height: Height in pixels
- * @stride: Line stride (pitch) in bytes
- *
- * Fill the buffer with the test pattern specified by the pattern parameter.
- * Supported formats vary depending on the selected pattern.
- */
-static void
-fill_pattern(unsigned int format, enum fill_pattern pattern, void *planes[3],
-	     unsigned int width, unsigned int height, unsigned int stride)
-{
-	const struct format_info *info = NULL;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(format_info); ++i) {
-		if (format_info[i].format == format) {
-			info = &format_info[i];
-			break;
-		}
-	}
-
-	if (info == NULL)
-		return;
-
-	switch (pattern) {
-	case PATTERN_TILES:
-		return fill_tiles(info, planes, width, height, stride);
-
-	case PATTERN_SMPTE:
-		return fill_smpte(info, planes, width, height, stride);
-
-	case PATTERN_PLAIN:
-		return fill_plain(info, planes, width, height, stride);
-
-	default:
-		printf("Error: unsupported test pattern %u.\n", pattern);
-		break;
-	}
-}
-
-/* -----------------------------------------------------------------------------
  * Buffers management
  */
 
@@ -1086,7 +129,7 @@ struct bo *
 bo_create(int fd, unsigned int format,
 	  unsigned int width, unsigned int height,
 	  unsigned int handles[4], unsigned int pitches[4],
-	  unsigned int offsets[4], enum fill_pattern pattern)
+	  unsigned int offsets[4], enum util_fill_pattern pattern)
 {
 	unsigned int virtual_height;
 	struct bo *bo;
@@ -1280,7 +323,7 @@ bo_create(int fd, unsigned int format,
 		break;
 	}
 
-	fill_pattern(format, pattern, planes, width, height, pitches[0]);
+	util_fill_pattern(format, pattern, planes, width, height, pitches[0]);
 	bo_unmap(bo);
 
 	return bo;
diff --git a/tests/modetest/buffers.h b/tests/modetest/buffers.h
index ad73d0e..7f95396 100644
--- a/tests/modetest/buffers.h
+++ b/tests/modetest/buffers.h
@@ -27,20 +27,14 @@
 #ifndef __BUFFERS_H__
 #define __BUFFERS_H__
 
-struct bo;
+#include "util/pattern.h"
 
-enum fill_pattern {
-	PATTERN_TILES = 0,
-	PATTERN_PLAIN = 1,
-	PATTERN_SMPTE = 2,
-};
+struct bo;
 
 struct bo *bo_create(int fd, unsigned int format,
 		   unsigned int width, unsigned int height,
 		   unsigned int handles[4], unsigned int pitches[4],
-		   unsigned int offsets[4], enum fill_pattern pattern);
+		   unsigned int offsets[4], enum util_fill_pattern pattern);
 void bo_destroy(struct bo *bo);
 
-unsigned int format_fourcc(const char *name);
-
 #endif
diff --git a/tests/modetest/cursor.c b/tests/modetest/cursor.c
index d8a19bd..6de82a4 100644
--- a/tests/modetest/cursor.c
+++ b/tests/modetest/cursor.c
@@ -40,11 +40,11 @@
 #include "xf86drm.h"
 #include "xf86drmMode.h"
 
+#include "util/common.h"
+
 #include "buffers.h"
 #include "cursor.h"
 
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-
 struct cursor {
 	int fd;
 	uint32_t bo_handle;
diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index 3b01918..361c6da 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -37,6 +37,7 @@
  * TODO: use cairo to write the mode info on the selected output once
  *       the mode has been programmed, along with possible test patterns.
  */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -59,6 +60,10 @@
 #include "xf86drmMode.h"
 #include "drm_fourcc.h"
 
+#include "util/common.h"
+#include "util/format.h"
+#include "util/pattern.h"
+
 #include "buffers.h"
 #include "cursor.h"
 
@@ -116,7 +121,6 @@ struct device {
 	} mode;
 };
 
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 static inline int64_t U642I64(uint64_t val)
 {
 	return (int64_t)*((int64_t *)&val);
@@ -1047,7 +1051,7 @@ static int set_plane(struct device *dev, struct plane_arg *p)
 		p->w, p->h, p->format_str, plane_id);
 
 	plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, handles,
-			     pitches, offsets, PATTERN_TILES);
+			     pitches, offsets, UTIL_PATTERN_TILES);
 	if (plane_bo == NULL)
 		return -1;
 
@@ -1123,8 +1127,9 @@ static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int co
 			dev->mode.height = pipe->mode->vdisplay;
 	}
 
-	bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width, dev->mode.height,
-		       handles, pitches, offsets, PATTERN_SMPTE);
+	bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
+		       dev->mode.height, handles, pitches, offsets,
+		       UTIL_PATTERN_SMPTE);
 	if (bo == NULL)
 		return;
 
@@ -1202,7 +1207,7 @@ static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int
 	 * translucent alpha
 	 */
 	bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
-		       offsets, PATTERN_PLAIN);
+		       offsets, UTIL_PATTERN_PLAIN);
 	if (bo == NULL)
 		return;
 
@@ -1241,9 +1246,9 @@ static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned
 	unsigned int i;
 	int ret;
 
-	other_bo = bo_create(dev->fd, pipes[0].fourcc,
-			     dev->mode.width, dev->mode.height,
-			     handles, pitches, offsets, PATTERN_PLAIN);
+	other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
+			     dev->mode.height, handles, pitches, offsets,
+			     UTIL_PATTERN_PLAIN);
 	if (other_bo == NULL)
 		return;
 
@@ -1391,7 +1396,7 @@ static int parse_connector(struct pipe_arg *pipe, const char *arg)
 		pipe->format_str[4] = '\0';
 	}
 
-	pipe->fourcc = format_fourcc(pipe->format_str);
+	pipe->fourcc = util_format_fourcc(pipe->format_str);
 	if (pipe->fourcc == 0)  {
 		fprintf(stderr, "unknown format %s\n", pipe->format_str);
 		return -1;
@@ -1444,7 +1449,7 @@ static int parse_plane(struct plane_arg *plane, const char *p)
 		strcpy(plane->format_str, "XR24");
 	}
 
-	plane->fourcc = format_fourcc(plane->format_str);
+	plane->fourcc = util_format_fourcc(plane->format_str);
 	if (plane->fourcc == 0) {
 		fprintf(stderr, "unknown format %s\n", plane->format_str);
 		return -EINVAL;
diff --git a/tests/proptest/Makefile.am b/tests/proptest/Makefile.am
index 0594e02..1428820 100644
--- a/tests/proptest/Makefile.am
+++ b/tests/proptest/Makefile.am
@@ -1,6 +1,7 @@
 AM_CFLAGS = \
 	$(WARN_CFLAGS)\
 	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/tests \
 	-I$(top_srcdir)
 
 if HAVE_INSTALL_TESTS
@@ -14,4 +15,5 @@ endif
 proptest_SOURCES = \
 	proptest.c
 proptest_LDADD = \
-	$(top_builddir)/libdrm.la
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/tests/util/libutil.la
diff --git a/tests/proptest/proptest.c b/tests/proptest/proptest.c
index b9296bd..b6e00cf 100644
--- a/tests/proptest/proptest.c
+++ b/tests/proptest/proptest.c
@@ -35,7 +35,8 @@
 #include "xf86drm.h"
 #include "xf86drmMode.h"
 
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#include "util/common.h"
+
 static inline int64_t U642I64(uint64_t val)
 {
 	return (int64_t)*((int64_t *)&val);
diff --git a/tests/util/Android.mk b/tests/util/Android.mk
new file mode 100644
index 0000000..73bc680
--- /dev/null
+++ b/tests/util/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright © 2015 NVIDIA 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_util
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(UTIL_FILES)
+
+# avoid name clashes by requiring users to include util/*.h
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(dir $(LOCAL_PATH))
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/tests/util/Makefile.am b/tests/util/Makefile.am
new file mode 100644
index 0000000..f8e0b17
--- /dev/null
+++ b/tests/util/Makefile.am
@@ -0,0 +1,13 @@
+include Makefile.sources
+
+noinst_LTLIBRARIES = \
+	libutil.la
+
+libutil_la_CPPFLAGS = \
+	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)
+
+libutil_la_CFLAGS = \
+	$(CAIRO_CFLAGS)
+
+libutil_la_SOURCES = $(UTIL_FILES)
diff --git a/tests/util/Makefile.sources b/tests/util/Makefile.sources
new file mode 100644
index 0000000..e91fa72
--- /dev/null
+++ b/tests/util/Makefile.sources
@@ -0,0 +1,6 @@
+UTIL_FILES := \
+	common.h \
+	format.c \
+	format.h \
+	pattern.c \
+	pattern.h
diff --git a/tests/util/common.h b/tests/util/common.h
new file mode 100644
index 0000000..5d572c2
--- /dev/null
+++ b/tests/util/common.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+#ifndef UTIL_COMMON_H
+#define UTIL_COMMON_H
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#endif /* UTIL_COMMON_H */
diff --git a/tests/util/format.c b/tests/util/format.c
new file mode 100644
index 0000000..043cfe7
--- /dev/null
+++ b/tests/util/format.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <drm_fourcc.h>
+
+#include "common.h"
+#include "format.h"
+
+#define MAKE_RGB_INFO(rl, ro, gl, go, bl, bo, al, ao) \
+	.rgb = { { (rl), (ro) }, { (gl), (go) }, { (bl), (bo) }, { (al), (ao) } }
+
+#define MAKE_YUV_INFO(order, xsub, ysub, chroma_stride) \
+	.yuv = { (order), (xsub), (ysub), (chroma_stride) }
+
+static const struct util_format_info format_info[] = {
+	/* YUV packed */
+	{ DRM_FORMAT_UYVY, "UYVY", MAKE_YUV_INFO(YUV_YCbCr | YUV_CY, 2, 2, 2) },
+	{ DRM_FORMAT_VYUY, "VYUY", MAKE_YUV_INFO(YUV_YCrCb | YUV_CY, 2, 2, 2) },
+	{ DRM_FORMAT_YUYV, "YUYV", MAKE_YUV_INFO(YUV_YCbCr | YUV_YC, 2, 2, 2) },
+	{ DRM_FORMAT_YVYU, "YVYU", MAKE_YUV_INFO(YUV_YCrCb | YUV_YC, 2, 2, 2) },
+	/* YUV semi-planar */
+	{ DRM_FORMAT_NV12, "NV12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 2) },
+	{ DRM_FORMAT_NV21, "NV21", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 2) },
+	{ DRM_FORMAT_NV16, "NV16", MAKE_YUV_INFO(YUV_YCbCr, 2, 1, 2) },
+	{ DRM_FORMAT_NV61, "NV61", MAKE_YUV_INFO(YUV_YCrCb, 2, 1, 2) },
+	/* YUV planar */
+	{ DRM_FORMAT_YUV420, "YU12", MAKE_YUV_INFO(YUV_YCbCr, 2, 2, 1) },
+	{ DRM_FORMAT_YVU420, "YV12", MAKE_YUV_INFO(YUV_YCrCb, 2, 2, 1) },
+	/* RGB16 */
+	{ DRM_FORMAT_ARGB4444, "AR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 4, 12) },
+	{ DRM_FORMAT_XRGB4444, "XR12", MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 0, 0) },
+	{ DRM_FORMAT_ABGR4444, "AB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 4, 12) },
+	{ DRM_FORMAT_XBGR4444, "XB12", MAKE_RGB_INFO(4, 0, 4, 4, 4, 8, 0, 0) },
+	{ DRM_FORMAT_RGBA4444, "RA12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 4, 0) },
+	{ DRM_FORMAT_RGBX4444, "RX12", MAKE_RGB_INFO(4, 12, 4, 8, 4, 4, 0, 0) },
+	{ DRM_FORMAT_BGRA4444, "BA12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 4, 0) },
+	{ DRM_FORMAT_BGRX4444, "BX12", MAKE_RGB_INFO(4, 4, 4, 8, 4, 12, 0, 0) },
+	{ DRM_FORMAT_ARGB1555, "AR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 1, 15) },
+	{ DRM_FORMAT_XRGB1555, "XR15", MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 0, 0) },
+	{ DRM_FORMAT_ABGR1555, "AB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 1, 15) },
+	{ DRM_FORMAT_XBGR1555, "XB15", MAKE_RGB_INFO(5, 0, 5, 5, 5, 10, 0, 0) },
+	{ DRM_FORMAT_RGBA5551, "RA15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 1, 0) },
+	{ DRM_FORMAT_RGBX5551, "RX15", MAKE_RGB_INFO(5, 11, 5, 6, 5, 1, 0, 0) },
+	{ DRM_FORMAT_BGRA5551, "BA15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 1, 0) },
+	{ DRM_FORMAT_BGRX5551, "BX15", MAKE_RGB_INFO(5, 1, 5, 6, 5, 11, 0, 0) },
+	{ DRM_FORMAT_RGB565, "RG16", MAKE_RGB_INFO(5, 11, 6, 5, 5, 0, 0, 0) },
+	{ DRM_FORMAT_BGR565, "BG16", MAKE_RGB_INFO(5, 0, 6, 5, 5, 11, 0, 0) },
+	/* RGB24 */
+	{ DRM_FORMAT_BGR888, "BG24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) },
+	{ DRM_FORMAT_RGB888, "RG24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) },
+	/* RGB32 */
+	{ DRM_FORMAT_ARGB8888, "AR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 8, 24) },
+	{ DRM_FORMAT_XRGB8888, "XR24", MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) },
+	{ DRM_FORMAT_ABGR8888, "AB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 8, 24) },
+	{ DRM_FORMAT_XBGR8888, "XB24", MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) },
+	{ DRM_FORMAT_RGBA8888, "RA24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 8, 0) },
+	{ DRM_FORMAT_RGBX8888, "RX24", MAKE_RGB_INFO(8, 24, 8, 16, 8, 8, 0, 0) },
+	{ DRM_FORMAT_BGRA8888, "BA24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 8, 0) },
+	{ DRM_FORMAT_BGRX8888, "BX24", MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 0, 0) },
+	{ DRM_FORMAT_ARGB2101010, "AR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 2, 30) },
+	{ DRM_FORMAT_XRGB2101010, "XR30", MAKE_RGB_INFO(10, 20, 10, 10, 10, 0, 0, 0) },
+	{ DRM_FORMAT_ABGR2101010, "AB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 2, 30) },
+	{ DRM_FORMAT_XBGR2101010, "XB30", MAKE_RGB_INFO(10, 0, 10, 10, 10, 20, 0, 0) },
+	{ DRM_FORMAT_RGBA1010102, "RA30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 2, 0) },
+	{ DRM_FORMAT_RGBX1010102, "RX30", MAKE_RGB_INFO(10, 22, 10, 12, 10, 2, 0, 0) },
+	{ DRM_FORMAT_BGRA1010102, "BA30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 2, 0) },
+	{ DRM_FORMAT_BGRX1010102, "BX30", MAKE_RGB_INFO(10, 2, 10, 12, 10, 22, 0, 0) },
+};
+
+uint32_t util_format_fourcc(const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(format_info); i++)
+		if (!strcmp(format_info[i].name, name))
+			return format_info[i].format;
+
+	return 0;
+}
+
+const struct util_format_info *util_format_info_find(uint32_t format)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(format_info); i++)
+		if (format_info[i].format == format)
+			return &format_info[i];
+
+	return NULL;
+}
diff --git a/tests/util/format.h b/tests/util/format.h
new file mode 100644
index 0000000..2ce1c02
--- /dev/null
+++ b/tests/util/format.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+#ifndef UTIL_FORMAT_H
+#define UTIL_FORMAT_H
+
+struct util_color_component {
+	unsigned int length;
+	unsigned int offset;
+};
+
+struct util_rgb_info {
+	struct util_color_component red;
+	struct util_color_component green;
+	struct util_color_component blue;
+	struct util_color_component alpha;
+};
+
+enum util_yuv_order {
+	YUV_YCbCr = 1,
+	YUV_YCrCb = 2,
+	YUV_YC = 4,
+	YUV_CY = 8,
+};
+
+struct util_yuv_info {
+	enum util_yuv_order order;
+	unsigned int xsub;
+	unsigned int ysub;
+	unsigned int chroma_stride;
+};
+
+struct util_format_info {
+	uint32_t format;
+	const char *name;
+	const struct util_rgb_info rgb;
+	const struct util_yuv_info yuv;
+};
+
+uint32_t util_format_fourcc(const char *name);
+const struct util_format_info *util_format_info_find(uint32_t format);
+
+#endif /* UTIL_FORMAT_H */
diff --git a/tests/util/pattern.c b/tests/util/pattern.c
new file mode 100644
index 0000000..00b08a8
--- /dev/null
+++ b/tests/util/pattern.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <drm_fourcc.h>
+
+#ifdef HAVE_CAIRO
+#include <cairo.h>
+#include <math.h>
+#endif
+
+#include "format.h"
+#include "pattern.h"
+
+struct color_rgb24 {
+	unsigned int value:24;
+} __attribute__((__packed__));
+
+struct color_yuv {
+	unsigned char y;
+	unsigned char u;
+	unsigned char v;
+};
+
+#define MAKE_YUV_601_Y(r, g, b) \
+	((( 66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) + 16)
+#define MAKE_YUV_601_U(r, g, b) \
+	(((-38 * (r) -  74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define MAKE_YUV_601_V(r, g, b) \
+	(((112 * (r) -  94 * (g) -  18 * (b) + 128) >> 8) + 128)
+
+#define MAKE_YUV_601(r, g, b) \
+	{ .y = MAKE_YUV_601_Y(r, g, b), \
+	  .u = MAKE_YUV_601_U(r, g, b), \
+	  .v = MAKE_YUV_601_V(r, g, b) }
+
+#define MAKE_RGBA(rgb, r, g, b, a) \
+	((((r) >> (8 - (rgb)->red.length)) << (rgb)->red.offset) | \
+	 (((g) >> (8 - (rgb)->green.length)) << (rgb)->green.offset) | \
+	 (((b) >> (8 - (rgb)->blue.length)) << (rgb)->blue.offset) | \
+	 (((a) >> (8 - (rgb)->alpha.length)) << (rgb)->alpha.offset))
+
+#define MAKE_RGB24(rgb, r, g, b) \
+	{ .value = MAKE_RGBA(rgb, r, g, b, 0) }
+
+static void fill_smpte_yuv_planar(const struct util_yuv_info *yuv,
+				  unsigned char *y_mem, unsigned char *u_mem,
+				  unsigned char *v_mem, unsigned int width,
+				  unsigned int height, unsigned int stride)
+{
+	const struct color_yuv colors_top[] = {
+		MAKE_YUV_601(191, 192, 192),	/* grey */
+		MAKE_YUV_601(192, 192, 0),	/* yellow */
+		MAKE_YUV_601(0, 192, 192),	/* cyan */
+		MAKE_YUV_601(0, 192, 0),	/* green */
+		MAKE_YUV_601(192, 0, 192),	/* magenta */
+		MAKE_YUV_601(192, 0, 0),	/* red */
+		MAKE_YUV_601(0, 0, 192),	/* blue */
+	};
+	const struct color_yuv colors_middle[] = {
+		MAKE_YUV_601(0, 0, 192),	/* blue */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(192, 0, 192),	/* magenta */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(0, 192, 192),	/* cyan */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(192, 192, 192),	/* grey */
+	};
+	const struct color_yuv colors_bottom[] = {
+		MAKE_YUV_601(0, 33, 76),	/* in-phase */
+		MAKE_YUV_601(255, 255, 255),	/* super white */
+		MAKE_YUV_601(50, 0, 106),	/* quadrature */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(9, 9, 9),		/* 3.5% */
+		MAKE_YUV_601(19, 19, 19),	/* 7.5% */
+		MAKE_YUV_601(29, 29, 29),	/* 11.5% */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+	};
+	unsigned int cs = yuv->chroma_stride;
+	unsigned int xsub = yuv->xsub;
+	unsigned int ysub = yuv->ysub;
+	unsigned int x;
+	unsigned int y;
+
+	/* Luma */
+	for (y = 0; y < height * 6 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			y_mem[x] = colors_top[x * 7 / width].y;
+		y_mem += stride;
+	}
+
+	for (; y < height * 7 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			y_mem[x] = colors_middle[x * 7 / width].y;
+		y_mem += stride;
+	}
+
+	for (; y < height; ++y) {
+		for (x = 0; x < width * 5 / 7; ++x)
+			y_mem[x] = colors_bottom[x * 4 / (width * 5 / 7)].y;
+		for (; x < width * 6 / 7; ++x)
+			y_mem[x] = colors_bottom[(x - width * 5 / 7) * 3
+						 / (width / 7) + 4].y;
+		for (; x < width; ++x)
+			y_mem[x] = colors_bottom[7].y;
+		y_mem += stride;
+	}
+
+	/* Chroma */
+	for (y = 0; y < height / ysub * 6 / 9; ++y) {
+		for (x = 0; x < width; x += xsub) {
+			u_mem[x*cs/xsub] = colors_top[x * 7 / width].u;
+			v_mem[x*cs/xsub] = colors_top[x * 7 / width].v;
+		}
+		u_mem += stride * cs / xsub;
+		v_mem += stride * cs / xsub;
+	}
+
+	for (; y < height / ysub * 7 / 9; ++y) {
+		for (x = 0; x < width; x += xsub) {
+			u_mem[x*cs/xsub] = colors_middle[x * 7 / width].u;
+			v_mem[x*cs/xsub] = colors_middle[x * 7 / width].v;
+		}
+		u_mem += stride * cs / xsub;
+		v_mem += stride * cs / xsub;
+	}
+
+	for (; y < height / ysub; ++y) {
+		for (x = 0; x < width * 5 / 7; x += xsub) {
+			u_mem[x*cs/xsub] =
+				colors_bottom[x * 4 / (width * 5 / 7)].u;
+			v_mem[x*cs/xsub] =
+				colors_bottom[x * 4 / (width * 5 / 7)].v;
+		}
+		for (; x < width * 6 / 7; x += xsub) {
+			u_mem[x*cs/xsub] = colors_bottom[(x - width * 5 / 7) *
+							 3 / (width / 7) + 4].u;
+			v_mem[x*cs/xsub] = colors_bottom[(x - width * 5 / 7) *
+							 3 / (width / 7) + 4].v;
+		}
+		for (; x < width; x += xsub) {
+			u_mem[x*cs/xsub] = colors_bottom[7].u;
+			v_mem[x*cs/xsub] = colors_bottom[7].v;
+		}
+		u_mem += stride * cs / xsub;
+		v_mem += stride * cs / xsub;
+	}
+}
+
+static void fill_smpte_yuv_packed(const struct util_yuv_info *yuv, void *mem,
+				  unsigned int width, unsigned int height,
+				  unsigned int stride)
+{
+	const struct color_yuv colors_top[] = {
+		MAKE_YUV_601(191, 192, 192),	/* grey */
+		MAKE_YUV_601(192, 192, 0),	/* yellow */
+		MAKE_YUV_601(0, 192, 192),	/* cyan */
+		MAKE_YUV_601(0, 192, 0),	/* green */
+		MAKE_YUV_601(192, 0, 192),	/* magenta */
+		MAKE_YUV_601(192, 0, 0),	/* red */
+		MAKE_YUV_601(0, 0, 192),	/* blue */
+	};
+	const struct color_yuv colors_middle[] = {
+		MAKE_YUV_601(0, 0, 192),	/* blue */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(192, 0, 192),	/* magenta */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(0, 192, 192),	/* cyan */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(192, 192, 192),	/* grey */
+	};
+	const struct color_yuv colors_bottom[] = {
+		MAKE_YUV_601(0, 33, 76),	/* in-phase */
+		MAKE_YUV_601(255, 255, 255),	/* super white */
+		MAKE_YUV_601(50, 0, 106),	/* quadrature */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+		MAKE_YUV_601(9, 9, 9),		/* 3.5% */
+		MAKE_YUV_601(19, 19, 19),	/* 7.5% */
+		MAKE_YUV_601(29, 29, 29),	/* 11.5% */
+		MAKE_YUV_601(19, 19, 19),	/* black */
+	};
+	unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1;
+	unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1;
+	unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0;
+	unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0;
+	unsigned int x;
+	unsigned int y;
+
+	/* Luma */
+	for (y = 0; y < height * 6 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			y_mem[2*x] = colors_top[x * 7 / width].y;
+		y_mem += stride;
+	}
+
+	for (; y < height * 7 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			y_mem[2*x] = colors_middle[x * 7 / width].y;
+		y_mem += stride;
+	}
+
+	for (; y < height; ++y) {
+		for (x = 0; x < width * 5 / 7; ++x)
+			y_mem[2*x] = colors_bottom[x * 4 / (width * 5 / 7)].y;
+		for (; x < width * 6 / 7; ++x)
+			y_mem[2*x] = colors_bottom[(x - width * 5 / 7) * 3
+						   / (width / 7) + 4].y;
+		for (; x < width; ++x)
+			y_mem[2*x] = colors_bottom[7].y;
+		y_mem += stride;
+	}
+
+	/* Chroma */
+	for (y = 0; y < height * 6 / 9; ++y) {
+		for (x = 0; x < width; x += 2) {
+			c_mem[2*x+u] = colors_top[x * 7 / width].u;
+			c_mem[2*x+v] = colors_top[x * 7 / width].v;
+		}
+		c_mem += stride;
+	}
+
+	for (; y < height * 7 / 9; ++y) {
+		for (x = 0; x < width; x += 2) {
+			c_mem[2*x+u] = colors_middle[x * 7 / width].u;
+			c_mem[2*x+v] = colors_middle[x * 7 / width].v;
+		}
+		c_mem += stride;
+	}
+
+	for (; y < height; ++y) {
+		for (x = 0; x < width * 5 / 7; x += 2) {
+			c_mem[2*x+u] = colors_bottom[x * 4 / (width * 5 / 7)].u;
+			c_mem[2*x+v] = colors_bottom[x * 4 / (width * 5 / 7)].v;
+		}
+		for (; x < width * 6 / 7; x += 2) {
+			c_mem[2*x+u] = colors_bottom[(x - width * 5 / 7) *
+						     3 / (width / 7) + 4].u;
+			c_mem[2*x+v] = colors_bottom[(x - width * 5 / 7) *
+						     3 / (width / 7) + 4].v;
+		}
+		for (; x < width; x += 2) {
+			c_mem[2*x+u] = colors_bottom[7].u;
+			c_mem[2*x+v] = colors_bottom[7].v;
+		}
+		c_mem += stride;
+	}
+}
+
+static void fill_smpte_rgb16(const struct util_rgb_info *rgb, void *mem,
+			     unsigned int width, unsigned int height,
+			     unsigned int stride)
+{
+	const uint16_t colors_top[] = {
+		MAKE_RGBA(rgb, 192, 192, 192, 255),	/* grey */
+		MAKE_RGBA(rgb, 192, 192, 0, 255),	/* yellow */
+		MAKE_RGBA(rgb, 0, 192, 192, 255),	/* cyan */
+		MAKE_RGBA(rgb, 0, 192, 0, 255),		/* green */
+		MAKE_RGBA(rgb, 192, 0, 192, 255),	/* magenta */
+		MAKE_RGBA(rgb, 192, 0, 0, 255),		/* red */
+		MAKE_RGBA(rgb, 0, 0, 192, 255),		/* blue */
+	};
+	const uint16_t colors_middle[] = {
+		MAKE_RGBA(rgb, 0, 0, 192, 127),		/* blue */
+		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
+		MAKE_RGBA(rgb, 192, 0, 192, 127),	/* magenta */
+		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
+		MAKE_RGBA(rgb, 0, 192, 192, 127),	/* cyan */
+		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
+		MAKE_RGBA(rgb, 192, 192, 192, 127),	/* grey */
+	};
+	const uint16_t colors_bottom[] = {
+		MAKE_RGBA(rgb, 0, 33, 76, 255),		/* in-phase */
+		MAKE_RGBA(rgb, 255, 255, 255, 255),	/* super white */
+		MAKE_RGBA(rgb, 50, 0, 106, 255),	/* quadrature */
+		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
+		MAKE_RGBA(rgb, 9, 9, 9, 255),		/* 3.5% */
+		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* 7.5% */
+		MAKE_RGBA(rgb, 29, 29, 29, 255),	/* 11.5% */
+		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
+	};
+	unsigned int x;
+	unsigned int y;
+
+	for (y = 0; y < height * 6 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			((uint16_t *)mem)[x] = colors_top[x * 7 / width];
+		mem += stride;
+	}
+
+	for (; y < height * 7 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			((uint16_t *)mem)[x] = colors_middle[x * 7 / width];
+		mem += stride;
+	}
+
+	for (; y < height; ++y) {
+		for (x = 0; x < width * 5 / 7; ++x)
+			((uint16_t *)mem)[x] =
+				colors_bottom[x * 4 / (width * 5 / 7)];
+		for (; x < width * 6 / 7; ++x)
+			((uint16_t *)mem)[x] =
+				colors_bottom[(x - width * 5 / 7) * 3
+					      / (width / 7) + 4];
+		for (; x < width; ++x)
+			((uint16_t *)mem)[x] = colors_bottom[7];
+		mem += stride;
+	}
+}
+
+static void fill_smpte_rgb24(const struct util_rgb_info *rgb, void *mem,
+			     unsigned int width, unsigned int height,
+			     unsigned int stride)
+{
+	const struct color_rgb24 colors_top[] = {
+		MAKE_RGB24(rgb, 192, 192, 192),	/* grey */
+		MAKE_RGB24(rgb, 192, 192, 0),	/* yellow */
+		MAKE_RGB24(rgb, 0, 192, 192),	/* cyan */
+		MAKE_RGB24(rgb, 0, 192, 0),	/* green */
+		MAKE_RGB24(rgb, 192, 0, 192),	/* magenta */
+		MAKE_RGB24(rgb, 192, 0, 0),	/* red */
+		MAKE_RGB24(rgb, 0, 0, 192),	/* blue */
+	};
+	const struct color_rgb24 colors_middle[] = {
+		MAKE_RGB24(rgb, 0, 0, 192),	/* blue */
+		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
+		MAKE_RGB24(rgb, 192, 0, 192),	/* magenta */
+		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
+		MAKE_RGB24(rgb, 0, 192, 192),	/* cyan */
+		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
+		MAKE_RGB24(rgb, 192, 192, 192),	/* grey */
+	};
+	const struct color_rgb24 colors_bottom[] = {
+		MAKE_RGB24(rgb, 0, 33, 76),	/* in-phase */
+		MAKE_RGB24(rgb, 255, 255, 255),	/* super white */
+		MAKE_RGB24(rgb, 50, 0, 106),	/* quadrature */
+		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
+		MAKE_RGB24(rgb, 9, 9, 9),	/* 3.5% */
+		MAKE_RGB24(rgb, 19, 19, 19),	/* 7.5% */
+		MAKE_RGB24(rgb, 29, 29, 29),	/* 11.5% */
+		MAKE_RGB24(rgb, 19, 19, 19),	/* black */
+	};
+	unsigned int x;
+	unsigned int y;
+
+	for (y = 0; y < height * 6 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			((struct color_rgb24 *)mem)[x] =
+				colors_top[x * 7 / width];
+		mem += stride;
+	}
+
+	for (; y < height * 7 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			((struct color_rgb24 *)mem)[x] =
+				colors_middle[x * 7 / width];
+		mem += stride;
+	}
+
+	for (; y < height; ++y) {
+		for (x = 0; x < width * 5 / 7; ++x)
+			((struct color_rgb24 *)mem)[x] =
+				colors_bottom[x * 4 / (width * 5 / 7)];
+		for (; x < width * 6 / 7; ++x)
+			((struct color_rgb24 *)mem)[x] =
+				colors_bottom[(x - width * 5 / 7) * 3
+					      / (width / 7) + 4];
+		for (; x < width; ++x)
+			((struct color_rgb24 *)mem)[x] = colors_bottom[7];
+		mem += stride;
+	}
+}
+
+static void fill_smpte_rgb32(const struct util_rgb_info *rgb, void *mem,
+			     unsigned int width, unsigned int height,
+			     unsigned int stride)
+{
+	const uint32_t colors_top[] = {
+		MAKE_RGBA(rgb, 192, 192, 192, 255),	/* grey */
+		MAKE_RGBA(rgb, 192, 192, 0, 255),	/* yellow */
+		MAKE_RGBA(rgb, 0, 192, 192, 255),	/* cyan */
+		MAKE_RGBA(rgb, 0, 192, 0, 255),		/* green */
+		MAKE_RGBA(rgb, 192, 0, 192, 255),	/* magenta */
+		MAKE_RGBA(rgb, 192, 0, 0, 255),		/* red */
+		MAKE_RGBA(rgb, 0, 0, 192, 255),		/* blue */
+	};
+	const uint32_t colors_middle[] = {
+		MAKE_RGBA(rgb, 0, 0, 192, 127),		/* blue */
+		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
+		MAKE_RGBA(rgb, 192, 0, 192, 127),	/* magenta */
+		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
+		MAKE_RGBA(rgb, 0, 192, 192, 127),	/* cyan */
+		MAKE_RGBA(rgb, 19, 19, 19, 127),	/* black */
+		MAKE_RGBA(rgb, 192, 192, 192, 127),	/* grey */
+	};
+	const uint32_t colors_bottom[] = {
+		MAKE_RGBA(rgb, 0, 33, 76, 255),		/* in-phase */
+		MAKE_RGBA(rgb, 255, 255, 255, 255),	/* super white */
+		MAKE_RGBA(rgb, 50, 0, 106, 255),	/* quadrature */
+		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
+		MAKE_RGBA(rgb, 9, 9, 9, 255),		/* 3.5% */
+		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* 7.5% */
+		MAKE_RGBA(rgb, 29, 29, 29, 255),	/* 11.5% */
+		MAKE_RGBA(rgb, 19, 19, 19, 255),	/* black */
+	};
+	unsigned int x;
+	unsigned int y;
+
+	for (y = 0; y < height * 6 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			((uint32_t *)mem)[x] = colors_top[x * 7 / width];
+		mem += stride;
+	}
+
+	for (; y < height * 7 / 9; ++y) {
+		for (x = 0; x < width; ++x)
+			((uint32_t *)mem)[x] = colors_middle[x * 7 / width];
+		mem += stride;
+	}
+
+	for (; y < height; ++y) {
+		for (x = 0; x < width * 5 / 7; ++x)
+			((uint32_t *)mem)[x] =
+				colors_bottom[x * 4 / (width * 5 / 7)];
+		for (; x < width * 6 / 7; ++x)
+			((uint32_t *)mem)[x] =
+				colors_bottom[(x - width * 5 / 7) * 3
+					      / (width / 7) + 4];
+		for (; x < width; ++x)
+			((uint32_t *)mem)[x] = colors_bottom[7];
+		mem += stride;
+	}
+}
+
+static void fill_smpte(const struct util_format_info *info, void *planes[3],
+		       unsigned int width, unsigned int height,
+		       unsigned int stride)
+{
+	unsigned char *u, *v;
+
+	switch (info->format) {
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		return fill_smpte_yuv_packed(&info->yuv, planes[0], width,
+					     height, stride);
+
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV21:
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV61:
+		u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1;
+		v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1;
+		return fill_smpte_yuv_planar(&info->yuv, planes[0], u, v,
+					     width, height, stride);
+
+	case DRM_FORMAT_YUV420:
+		return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[1],
+					     planes[2], width, height, stride);
+
+	case DRM_FORMAT_YVU420:
+		return fill_smpte_yuv_planar(&info->yuv, planes[0], planes[2],
+					     planes[1], width, height, stride);
+
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_XRGB4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_BGRA4444:
+	case DRM_FORMAT_BGRX4444:
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_BGRA5551:
+	case DRM_FORMAT_BGRX5551:
+		return fill_smpte_rgb16(&info->rgb, planes[0],
+					width, height, stride);
+
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_RGB888:
+		return fill_smpte_rgb24(&info->rgb, planes[0],
+					width, height, stride);
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_BGRA8888:
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_ARGB2101010:
+	case DRM_FORMAT_XRGB2101010:
+	case DRM_FORMAT_ABGR2101010:
+	case DRM_FORMAT_XBGR2101010:
+	case DRM_FORMAT_RGBA1010102:
+	case DRM_FORMAT_RGBX1010102:
+	case DRM_FORMAT_BGRA1010102:
+	case DRM_FORMAT_BGRX1010102:
+		return fill_smpte_rgb32(&info->rgb, planes[0],
+					width, height, stride);
+	}
+}
+
+/* swap these for big endian.. */
+#define RED   2
+#define GREEN 1
+#define BLUE  0
+
+static void make_pwetty(void *data, unsigned int width, unsigned int height,
+			unsigned int stride, uint32_t format)
+{
+#ifdef HAVE_CAIRO
+	cairo_surface_t *surface;
+	cairo_t *cr;
+	int x, y;
+	cairo_format_t cairo_format;
+
+	/* we can ignore the order of R,G,B channels */
+	switch (format) {
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_ABGR8888:
+		cairo_format = CAIRO_FORMAT_ARGB32;
+		break;
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+		cairo_format = CAIRO_FORMAT_RGB16_565;
+		break;
+	default:
+		return;
+	}
+
+	surface = cairo_image_surface_create_for_data(data,
+						      cairo_format,
+						      width, height,
+						      stride);
+	cr = cairo_create(surface);
+	cairo_surface_destroy(surface);
+
+	cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
+	for (x = 0; x < width; x += 250)
+		for (y = 0; y < height; y += 250) {
+			char buf[64];
+
+			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);
+
+			snprintf(buf, sizeof buf, "%d, %d", x, y);
+			cairo_move_to(cr, x + 20, y + 20);
+			cairo_text_path(cr, buf);
+			cairo_set_source_rgb(cr, 0, 0, 0);
+			cairo_stroke_preserve(cr);
+			cairo_set_source_rgb(cr, 1, 1, 1);
+			cairo_fill(cr);
+		}
+
+	cairo_destroy(cr);
+#endif
+}
+
+static void fill_tiles_yuv_planar(const struct util_format_info *info,
+				  unsigned char *y_mem, unsigned char *u_mem,
+				  unsigned char *v_mem, unsigned int width,
+				  unsigned int height, unsigned int stride)
+{
+	const struct util_yuv_info *yuv = &info->yuv;
+	unsigned int cs = yuv->chroma_stride;
+	unsigned int xsub = yuv->xsub;
+	unsigned int ysub = yuv->ysub;
+	unsigned int x;
+	unsigned int y;
+
+	for (y = 0; y < height; ++y) {
+		for (x = 0; x < width; ++x) {
+			div_t d = div(x+y, width);
+			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
+				       + 0x000a1120 * (d.rem >> 6);
+			struct color_yuv color =
+				MAKE_YUV_601((rgb32 >> 16) & 0xff,
+					     (rgb32 >> 8) & 0xff, rgb32 & 0xff);
+
+			y_mem[x] = color.y;
+			u_mem[x/xsub*cs] = color.u;
+			v_mem[x/xsub*cs] = color.v;
+		}
+
+		y_mem += stride;
+		if ((y + 1) % ysub == 0) {
+			u_mem += stride * cs / xsub;
+			v_mem += stride * cs / xsub;
+		}
+	}
+}
+
+static void fill_tiles_yuv_packed(const struct util_format_info *info,
+				  void *mem, unsigned int width,
+				  unsigned int height, unsigned int stride)
+{
+	const struct util_yuv_info *yuv = &info->yuv;
+	unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1;
+	unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1;
+	unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0;
+	unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0;
+	unsigned int x;
+	unsigned int y;
+
+	for (y = 0; y < height; ++y) {
+		for (x = 0; x < width; x += 2) {
+			div_t d = div(x+y, width);
+			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
+				       + 0x000a1120 * (d.rem >> 6);
+			struct color_yuv color =
+				MAKE_YUV_601((rgb32 >> 16) & 0xff,
+					     (rgb32 >> 8) & 0xff, rgb32 & 0xff);
+
+			y_mem[2*x] = color.y;
+			c_mem[2*x+u] = color.u;
+			y_mem[2*x+2] = color.y;
+			c_mem[2*x+v] = color.v;
+		}
+
+		y_mem += stride;
+		c_mem += stride;
+	}
+}
+
+static void fill_tiles_rgb16(const struct util_format_info *info, void *mem,
+			     unsigned int width, unsigned int height,
+			     unsigned int stride)
+{
+	const struct util_rgb_info *rgb = &info->rgb;
+	void *mem_base = mem;
+	unsigned int x, y;
+
+	for (y = 0; y < height; ++y) {
+		for (x = 0; x < width; ++x) {
+			div_t d = div(x+y, width);
+			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
+				       + 0x000a1120 * (d.rem >> 6);
+			uint16_t color =
+				MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff,
+					  (rgb32 >> 8) & 0xff, rgb32 & 0xff,
+					  255);
+
+			((uint16_t *)mem)[x] = color;
+		}
+		mem += stride;
+	}
+
+	make_pwetty(mem_base, width, height, stride, info->format);
+}
+
+static void fill_tiles_rgb24(const struct util_format_info *info, void *mem,
+			     unsigned int width, unsigned int height,
+			     unsigned int stride)
+{
+	const struct util_rgb_info *rgb = &info->rgb;
+	unsigned int x, y;
+
+	for (y = 0; y < height; ++y) {
+		for (x = 0; x < width; ++x) {
+			div_t d = div(x+y, width);
+			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
+				       + 0x000a1120 * (d.rem >> 6);
+			struct color_rgb24 color =
+				MAKE_RGB24(rgb, (rgb32 >> 16) & 0xff,
+					   (rgb32 >> 8) & 0xff, rgb32 & 0xff);
+
+			((struct color_rgb24 *)mem)[x] = color;
+		}
+		mem += stride;
+	}
+}
+
+static void fill_tiles_rgb32(const struct util_format_info *info, void *mem,
+			     unsigned int width, unsigned int height,
+			     unsigned int stride)
+{
+	const struct util_rgb_info *rgb = &info->rgb;
+	void *mem_base = mem;
+	unsigned int x, y;
+
+	for (y = 0; y < height; ++y) {
+		for (x = 0; x < width; ++x) {
+			div_t d = div(x+y, width);
+			uint32_t rgb32 = 0x00130502 * (d.quot >> 6)
+				       + 0x000a1120 * (d.rem >> 6);
+			uint32_t alpha = ((y < height/2) && (x < width/2)) ? 127 : 255;
+			uint32_t color =
+				MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff,
+					  (rgb32 >> 8) & 0xff, rgb32 & 0xff,
+					  alpha);
+
+			((uint32_t *)mem)[x] = color;
+		}
+		mem += stride;
+	}
+
+	make_pwetty(mem_base, width, height, stride, info->format);
+}
+
+static void fill_tiles(const struct util_format_info *info, void *planes[3],
+		       unsigned int width, unsigned int height,
+		       unsigned int stride)
+{
+	unsigned char *u, *v;
+
+	switch (info->format) {
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+		return fill_tiles_yuv_packed(info, planes[0],
+					     width, height, stride);
+
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV21:
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV61:
+		u = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1;
+		v = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1;
+		return fill_tiles_yuv_planar(info, planes[0], u, v,
+					     width, height, stride);
+
+	case DRM_FORMAT_YUV420:
+		return fill_tiles_yuv_planar(info, planes[0], planes[1],
+					     planes[2], width, height, stride);
+
+	case DRM_FORMAT_YVU420:
+		return fill_tiles_yuv_planar(info, planes[0], planes[2],
+					     planes[1], width, height, stride);
+
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_XRGB4444:
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR4444:
+	case DRM_FORMAT_RGBA4444:
+	case DRM_FORMAT_RGBX4444:
+	case DRM_FORMAT_BGRA4444:
+	case DRM_FORMAT_BGRX4444:
+	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_BGR565:
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_XBGR1555:
+	case DRM_FORMAT_RGBA5551:
+	case DRM_FORMAT_RGBX5551:
+	case DRM_FORMAT_BGRA5551:
+	case DRM_FORMAT_BGRX5551:
+		return fill_tiles_rgb16(info, planes[0],
+					width, height, stride);
+
+	case DRM_FORMAT_BGR888:
+	case DRM_FORMAT_RGB888:
+		return fill_tiles_rgb24(info, planes[0],
+					width, height, stride);
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XBGR8888:
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_RGBX8888:
+	case DRM_FORMAT_BGRA8888:
+	case DRM_FORMAT_BGRX8888:
+	case DRM_FORMAT_ARGB2101010:
+	case DRM_FORMAT_XRGB2101010:
+	case DRM_FORMAT_ABGR2101010:
+	case DRM_FORMAT_XBGR2101010:
+	case DRM_FORMAT_RGBA1010102:
+	case DRM_FORMAT_RGBX1010102:
+	case DRM_FORMAT_BGRA1010102:
+	case DRM_FORMAT_BGRX1010102:
+		return fill_tiles_rgb32(info, planes[0],
+					width, height, stride);
+	}
+}
+
+static void fill_plain(const struct util_format_info *info, void *planes[3],
+		       unsigned int width, unsigned int height,
+		       unsigned int stride)
+{
+	memset(planes[0], 0x77, stride * height);
+}
+
+/*
+ * util_fill_pattern - Fill a buffer with a test pattern
+ * @format: Pixel format
+ * @pattern: Test pattern
+ * @planes: Array of buffers
+ * @width: Width in pixels
+ * @height: Height in pixels
+ * @stride: Line stride (pitch) in bytes
+ *
+ * Fill the buffers with the test pattern specified by the pattern parameter.
+ * Supported formats vary depending on the selected pattern.
+ */
+void util_fill_pattern(uint32_t format, enum util_fill_pattern pattern,
+		       void *planes[3], unsigned int width,
+		       unsigned int height, unsigned int stride)
+{
+	const struct util_format_info *info;
+
+	info = util_format_info_find(format);
+	if (info == NULL)
+		return;
+
+	switch (pattern) {
+	case UTIL_PATTERN_TILES:
+		return fill_tiles(info, planes, width, height, stride);
+
+	case UTIL_PATTERN_SMPTE:
+		return fill_smpte(info, planes, width, height, stride);
+
+	case UTIL_PATTERN_PLAIN:
+		return fill_plain(info, planes, width, height, stride);
+
+	default:
+		printf("Error: unsupported test pattern %u.\n", pattern);
+		break;
+	}
+}
diff --git a/tests/util/pattern.h b/tests/util/pattern.h
new file mode 100644
index 0000000..d5c4260
--- /dev/null
+++ b/tests/util/pattern.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008 Tungsten Graphics
+ *   Jakob Bornecrantz <[email protected]>
+ * Copyright 2008 Intel Corporation
+ *   Jesse Barnes <[email protected]>
+ *
+ * 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 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.
+ */
+
+#ifndef UTIL_PATTERN_H
+#define UTIL_PATTERN_H
+
+enum util_fill_pattern {
+	UTIL_PATTERN_TILES,
+	UTIL_PATTERN_PLAIN,
+	UTIL_PATTERN_SMPTE,
+};
+
+void util_fill_pattern(uint32_t format, enum util_fill_pattern pattern,
+		       void *planes[3], unsigned int width,
+		       unsigned int height, unsigned int stride);
+
+#endif /* UTIL_PATTERN_H */
diff --git a/tests/vbltest/Makefile.am b/tests/vbltest/Makefile.am
index 4d87887..182e3b6 100644
--- a/tests/vbltest/Makefile.am
+++ b/tests/vbltest/Makefile.am
@@ -1,6 +1,7 @@
 AM_CFLAGS = \
 	$(WARN_CFLAGS)\
 	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/tests \
 	-I$(top_srcdir)
 
 if HAVE_INSTALL_TESTS
diff --git a/tests/vbltest/vbltest.c b/tests/vbltest/vbltest.c
index e27f45c..de93e77 100644
--- a/tests/vbltest/vbltest.c
+++ b/tests/vbltest/vbltest.c
@@ -54,7 +54,7 @@
 #include "xf86drm.h"
 #include "xf86drmMode.h"
 
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#include "util/common.h"
 
 extern char *optarg;
 extern int optind, opterr, optopt;
commit 15d90ef3504128a276923db840af1410ca255647
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:21 2015 +0900

    exynos: bump version number
    
    The Exynos API was extended quite a bit, so reflect this in the
    version number.
    
    Signed-off-by: Tobias Jakobi <[email protected]>

diff --git a/exynos/libdrm_exynos.pc.in b/exynos/libdrm_exynos.pc.in
index 5ce9118..ff1c432 100644
--- a/exynos/libdrm_exynos.pc.in
+++ b/exynos/libdrm_exynos.pc.in
@@ -5,7 +5,7 @@ includedir=@includedir@
 
 Name: libdrm_exynos
 Description: Userspace interface to exynos kernel DRM services
-Version: 0.6
+Version: 0.7
 Libs: -L${libdir} -ldrm_exynos
 Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/exynos
 Requires.private: libdrm
commit 6689e4ee14f2dd0f04db9b5b816a252dfeb01ca5
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:20 2015 +0900

    tests/exynos: add test for g2d_move
    
    To check if g2d_move() works properly we create a small checkerboard
    pattern in the center of the screen and then shift this pattern
    around with g2d_move(). The pattern should be properly preserved
    by the operation (but not the surrounding area).
    
    Tested-by: Hyungwon Hwang <[email protected]>
    Reviewed-by: Hyungwon Hwang <[email protected]>
    Signed-off-by: Tobias Jakobi <[email protected]>
    [Emil Velikov: add g2d_move to the symbol check]
    Signed-off-by: Emil Velikov <[email protected]>

diff --git a/exynos/exynos-symbol-check b/exynos/exynos-symbol-check
index bb12315..9692caa 100755
--- a/exynos/exynos-symbol-check
+++ b/exynos/exynos-symbol-check
@@ -30,6 +30,7 @@ g2d_exec
 g2d_config_event
 g2d_fini
 g2d_init
+g2d_move
 g2d_scale_and_blend
 g2d_solid_fill
 EOF
diff --git a/tests/exynos/exynos_fimg2d_test.c b/tests/exynos/exynos_fimg2d_test.c
index dfb00a0..797fb6e 100644
--- a/tests/exynos/exynos_fimg2d_test.c
+++ b/tests/exynos/exynos_fimg2d_test.c
@@ -313,6 +313,130 @@ fail:
 	return ret;
 }
 
+static int g2d_move_test(struct exynos_device *dev,
+				struct exynos_bo *tmp,
+				struct exynos_bo *buf,
+				enum e_g2d_buf_type type)
+{
+	struct g2d_context *ctx;
+	struct g2d_image img = {0}, tmp_img = {0};
+	unsigned int img_w, img_h, count;
+	int cur_x, cur_y;
+	void *checkerboard;
+	int ret;
+
+	static const struct g2d_step {
+		int x, y;
+	} steps[] = {
+		{ 1,  0}, { 0,  1},
+		{-1,  0}, { 0, -1},
+		{ 1,  1}, {-1, -1},
+		{ 1, -1}, {-1,  1},
+		{ 2,  1}, { 1,  2},
+		{-2, -1}, {-1, -2},
+		{ 2, -1}, { 1, -2},
+		{-2,  1}, {-1,  2}
+	};
+	static const unsigned int num_steps =
+		sizeof(steps) / sizeof(struct g2d_step);
+
+	ctx = g2d_init(dev->fd);
+	if (!ctx)
+		return -EFAULT;
+
+	img.bo[0] = buf->handle;
+
+	/* create pattern of half the screen size */
+	checkerboard = create_checkerboard_pattern(screen_width / 64, screen_height / 64, 32);
+	if (!checkerboard) {
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	img_w = (screen_width / 64) * 32;
+	img_h = (screen_height / 64) * 32;
+
+	switch (type) {
+	case G2D_IMGBUF_GEM:
+		memcpy(tmp->vaddr, checkerboard, img_w * img_h * 4);
+		tmp_img.bo[0] = tmp->handle;
+		break;
+	case G2D_IMGBUF_USERPTR:
+		tmp_img.user_ptr[0].userptr = (unsigned long)checkerboard;
+		tmp_img.user_ptr[0].size = img_w * img_h * 4;
+		break;
+	case G2D_IMGBUF_COLOR:
+	default:
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	/* solid fill framebuffer with white color */
+	img.width = screen_width;
+	img.height = screen_height;
+	img.stride = screen_width * 4;
+	img.buf_type = G2D_IMGBUF_GEM;
+	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
+	img.color = 0xffffffff;
+
+	/* put checkerboard pattern in the center of the framebuffer */
+	cur_x = (screen_width - img_w) / 2;
+	cur_y = (screen_height - img_h) / 2;
+	tmp_img.width = img_w;
+	tmp_img.height = img_h;
+	tmp_img.stride = img_w * 4;
+	tmp_img.buf_type = type;
+	tmp_img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
+
+	ret = g2d_solid_fill(ctx, &img, 0, 0, screen_width, screen_height) ||
+		g2d_copy(ctx, &tmp_img, &img, 0, 0, cur_x, cur_y, img_w, img_h);
+
+	if (!ret)
+		ret = g2d_exec(ctx);
+	if (ret < 0)
+			goto fail;
+
+	printf("move test with %s.\n",
+			type == G2D_IMGBUF_GEM ? "gem" : "userptr");
+
+	srand(time(NULL));
+	for (count = 0; count < 256; ++count) {
+		const struct g2d_step *s;
+
+		/* select step and validate it */
+		while (1) {
+			s = &steps[random() % num_steps];
+
+			if (cur_x + s->x < 0 || cur_y + s->y < 0 ||
+				cur_x + img_w + s->x >= screen_width ||
+				cur_y + img_h + s->y >= screen_height)
+				continue;
+			else
+				break;
+		}
+
+		ret = g2d_move(ctx, &img, cur_x, cur_y, cur_x + s->x, cur_y + s->y,
+			img_w, img_h);
+		if (!ret)
+			ret = g2d_exec(ctx);
+
+		if (ret < 0)
+			goto fail;
+
+		cur_x += s->x;
+		cur_y += s->y;
+
+		usleep(100000);
+	}
+
+fail:
+	g2d_fini(ctx);
+
+	free(checkerboard);
+
+	return ret;
+}
+
 static int g2d_copy_with_scale_test(struct exynos_device *dev,
 					struct exynos_bo *src,
 					struct exynos_bo *dst,
@@ -708,6 +832,14 @@ int main(int argc, char **argv)
 
 	wait_for_user_input(0);
 
+	ret = g2d_move_test(dev, src, bo, G2D_IMGBUF_GEM);
+	if (ret < 0) {
+		fprintf(stderr, "failed to test move operation.\n");
+		goto err_free_src;
+	}
+
+	wait_for_user_input(0);
+
 	ret = g2d_copy_with_scale_test(dev, src, bo, G2D_IMGBUF_GEM);
 	if (ret < 0) {
 		fprintf(stderr, "failed to test copy and scale operation.\n");
commit 676462c43e97896caa891b1e0aeb27d5d4f13637
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:19 2015 +0900

    exynos/fimg2d: add g2d_move
    
    We already have g2d_copy() which implements G2D copy
    operations from one buffer to another. However we can't
    do a overlapping copy operation in one buffer.
    
    Add g2d_move() which acts like the standard memmove()
    and properly handles overlapping copies.
    
    Signed-off-by: Tobias Jakobi <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index 3bef93b..7f1d105 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -567,6 +567,101 @@ g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
 }
 
 /**
+ * g2d_move - copy content inside single buffer.
+ *	Similar to libc's memmove() this copies a rectangular
+ *	region of the provided buffer to another location, while
+ *	properly handling the situation where source and
+ *	destination rectangle overlap.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to g2d_image structure providing
+ *	buffer information.
+ * @src_x: x position of source rectangle.
+ * @src_y: y position of source rectangle.
+ * @dst_x: x position of destination rectangle.
+ * @dst_y: y position of destination rectangle.
+ * @w: width of rectangle to move.
+ * @h: height of rectangle to move.
+ */
+int
+g2d_move(struct g2d_context *ctx, struct g2d_image *img,
+		unsigned int src_x, unsigned int src_y,
+		unsigned int dst_x, unsigned dst_y, unsigned int w,
+		unsigned int h)
+{
+	union g2d_rop4_val rop4;
+	union g2d_point_val pt;
+	union g2d_direction_val dir;
+	unsigned int src_w, src_h, dst_w, dst_h;
+
+	src_w = w;
+	src_h = h;
+	if (src_x + img->width > w)
+		src_w = img->width - src_x;
+	if (src_y + img->height > h)
+		src_h = img->height - src_y;
+
+	dst_w = w;
+	dst_h = w;
+	if (dst_x + img->width > w)
+		dst_w = img->width - dst_x;
+	if (dst_y + img->height > h)
+		dst_h = img->height - dst_y;
+
+	w = MIN(src_w, dst_w);
+	h = MIN(src_h, dst_h);
+
+	if (w == 0 || h == 0) {
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (g2d_check_space(ctx, 13, 2))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode);
+
+	g2d_add_base_addr(ctx, img, g2d_dst);
+	g2d_add_base_addr(ctx, img, g2d_src);
+
+	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
+	g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride);
+
+	dir.val[0] = dir.val[1] = 0;
+
+	if (dst_x >= src_x)
+		dir.data.src_x_direction = dir.data.dst_x_direction = 1;
+	if (dst_y >= src_y)
+		dir.data.src_y_direction = dir.data.dst_y_direction = 1;
+
+	g2d_set_direction(ctx, &dir);
+
+	pt.data.x = src_x;
+	pt.data.y = src_y;
+	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+	pt.data.x = src_x + w;
+	pt.data.y = src_y + h;
+	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+	pt.data.x = dst_x;
+	pt.data.y = dst_y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+	pt.data.x = dst_x + w;
+	pt.data.y = dst_y + h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	rop4.val = 0;
+	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+	return g2d_flush(ctx);
+}
+
+/**
  * g2d_copy_with_scale - copy contents in source buffer to destination buffer
  *	scaling up or down properly.
  *
diff --git a/exynos/exynos_fimg2d.h b/exynos/exynos_fimg2d.h
index a2c22da..a825c68 100644
--- a/exynos/exynos_fimg2d.h
+++ b/exynos/exynos_fimg2d.h
@@ -299,6 +299,9 @@ int g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
 		struct g2d_image *dst, unsigned int src_x,
 		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
 		unsigned int w, unsigned int h);
+int g2d_move(struct g2d_context *ctx, struct g2d_image *img,
+		unsigned int src_x, unsigned int src_y, unsigned int dst_x,
+		unsigned dst_y, unsigned int w, unsigned int h);
 int g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
 				struct g2d_image *dst, unsigned int src_x,
 				unsigned int src_y, unsigned int src_w,
commit 51fd93bc38a33df33f7bf6cbf1ae3fbf74a47dba
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:18 2015 +0900

    exynos: fimg2d: add g2d_set_direction
    
    This allows setting the two direction registers, which specify how
    the engine blits pixels. This can be used for overlapping blits,
    which happen e.g. when 'moving' a rectangular region inside a
    fixed buffer.
    
    Reviewed-by: Hyungwon Hwang <[email protected]>
    Signed-off-by: Tobias Jakobi <[email protected]>
    Signed-off-by: Hyungwon Hwang <[email protected]>

diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index b10620b..3bef93b 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -65,6 +65,44 @@ enum g2d_base_addr_reg {
 	g2d_src
 };
 
+enum e_g2d_dir_mode {
+	G2D_DIR_MODE_POSITIVE = 0,
+	G2D_DIR_MODE_NEGATIVE = 1
+};
+
+union g2d_direction_val {
+	unsigned int val[2];
+	struct {
+		/* SRC_MSK_DIRECT_REG [0:1] (source) */
+		enum e_g2d_dir_mode		src_x_direction:1;
+		enum e_g2d_dir_mode		src_y_direction:1;
+
+		/* SRC_MSK_DIRECT_REG [2:3] */
+		unsigned int			reversed1:2;
+
+		/* SRC_MSK_DIRECT_REG [4:5] (mask) */
+		enum e_g2d_dir_mode		mask_x_direction:1;
+		enum e_g2d_dir_mode		mask_y_direction:1;
+
+		/* SRC_MSK_DIRECT_REG [6:31] */
+		unsigned int			padding1:26;
+
+		/* DST_PAT_DIRECT_REG [0:1] (destination) */
+		enum e_g2d_dir_mode		dst_x_direction:1;
+		enum e_g2d_dir_mode		dst_y_direction:1;
+
+		/* DST_PAT_DIRECT_REG [2:3] */
+		unsigned int			reversed2:2;
+
+		/* DST_PAT_DIRECT_REG [4:5] (pattern) */
+		enum e_g2d_dir_mode		pat_x_direction:1;
+		enum e_g2d_dir_mode		pat_y_direction:1;
+
+		/* DST_PAT_DIRECT_REG [6:31] */
+		unsigned int			padding2:26;
+	} data;
+};
+
 static unsigned int g2d_get_scaling(unsigned int src, unsigned int dst)
 {
 	/*
@@ -242,6 +280,19 @@ static void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img,
 }
 
 /*
+ * g2d_set_direction - setup direction register (useful for overlapping blits).
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @dir: a pointer to the g2d_direction_val structure.
+ */
+static void g2d_set_direction(struct g2d_context *ctx,
+			const union g2d_direction_val *dir)
+{
+	g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]);
+	g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]);
+}
+
+/*
  * g2d_reset - reset fimg2d hardware.
  *
  * @ctx: a pointer to g2d_context structure.
commit 1b32e5da44f4c1e24a60c6bad1fa8766cf36be39
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:17 2015 +0900

    tests/exynos: use XRGB8888 for framebuffer
    
    This matches the G2D color mode that is used in the entire code.
    The previous (incorrect) RGBA8888 would only work since the
    Exynos mixer did its configuration based on the bpp, and not
    based on the actual pixelformat.
    
    Reviewed-by: Hyungwon Hwang <[email protected]>
    Signed-off-by: Tobias Jakobi <[email protected]>

diff --git a/tests/exynos/exynos_fimg2d_test.c b/tests/exynos/exynos_fimg2d_test.c
index 8794dac..dfb00a0 100644
--- a/tests/exynos/exynos_fimg2d_test.c
+++ b/tests/exynos/exynos_fimg2d_test.c
@@ -675,7 +675,7 @@ int main(int argc, char **argv)
 	offsets[0] = 0;
 
 	ret = drmModeAddFB2(dev->fd, screen_width, screen_height,
-				DRM_FORMAT_RGBA8888, handles,
+				DRM_FORMAT_XRGB8888, handles,
 				pitches, offsets, &fb_id, 0);
 	if (ret < 0)
 		goto err_destroy_buffer;
commit 58a39f6ba113073432dc0cd0ae702aba332c81b7
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:16 2015 +0900

    tests/exynos: add fimg2d event test
    
    This tests async processing of G2D jobs. A separate thread is spawned
    to monitor the DRM fd for events and check whether a G2D job was
    completed.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Signed-off-by: Hyungwon Hwang <[email protected]>

diff --git a/.gitignore b/.gitignore
index ddb37f1..c1e87c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -95,6 +95,7 @@ tests/proptest/proptest
 tests/kmstest/kmstest
 tests/vbltest/vbltest
 tests/radeon/radeon_ttm
+tests/exynos/exynos_fimg2d_event
 tests/exynos/exynos_fimg2d_perf
 tests/exynos/exynos_fimg2d_test
 man/*.3
diff --git a/tests/exynos/Makefile.am b/tests/exynos/Makefile.am
index e82d199..357d6b8 100644
--- a/tests/exynos/Makefile.am
+++ b/tests/exynos/Makefile.am
@@ -20,16 +20,23 @@ endif
 
 if HAVE_INSTALL_TESTS
 bin_PROGRAMS += \
-	exynos_fimg2d_perf
+	exynos_fimg2d_perf \
+	exynos_fimg2d_event
 else
 noinst_PROGRAMS += \
-	exynos_fimg2d_perf
+	exynos_fimg2d_perf \
+	exynos_fimg2d_event
 endif
 
 exynos_fimg2d_perf_LDADD = \
 	$(top_builddir)/libdrm.la \
 	$(top_builddir)/exynos/libdrm_exynos.la
 
+exynos_fimg2d_event_LDADD = \
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/exynos/libdrm_exynos.la \
+	-lpthread
+
 exynos_fimg2d_test_LDADD = \
 	$(top_builddir)/libdrm.la \
 	$(top_builddir)/libkms/libkms.la \
diff --git a/tests/exynos/exynos_fimg2d_event.c b/tests/exynos/exynos_fimg2d_event.c
new file mode 100644
index 0000000..9ed5a30
--- /dev/null
+++ b/tests/exynos/exynos_fimg2d_event.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2015 - Tobias Jakobi
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * It is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with it. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <poll.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <getopt.h>
+
+#include <pthread.h>
+
+#include <xf86drm.h>
+
+#include "exynos_drm.h"
+#include "exynos_drmif.h"
+#include "exynos_fimg2d.h"
+
+struct g2d_job {
+	unsigned int id;
+	unsigned int busy;
+};
+
+struct exynos_evhandler {
+	struct pollfd fds;
+	struct exynos_event_context evctx;
+};
+
+struct threaddata {
+	unsigned int stop;
+	struct exynos_device *dev;
+	struct exynos_evhandler evhandler;
+};
+
+static void g2d_event_handler(int fd, unsigned int cmdlist_no, unsigned int tv_sec,
+							unsigned int tv_usec, void *user_data)
+{
+	struct g2d_job *job = user_data;
+
+	fprintf(stderr, "info: g2d job (id = %u, cmdlist number = %u) finished!\n",
+			job->id, cmdlist_no);
+
+	job->busy = 0;
+}
+
+static void setup_g2d_event_handler(struct exynos_evhandler *evhandler, int fd)
+{
+	evhandler->fds.fd = fd;
+	evhandler->fds.events = POLLIN;
+	evhandler->evctx.base.version = 2;
+	evhandler->evctx.version = 1;
+	evhandler->evctx.g2d_event_handler = g2d_event_handler;
+}
+
+static void* threadfunc(void *arg) {
+	const int timeout = 0;
+	struct threaddata *data;
+
+	data = arg;
+
+	while (1) {
+		if (data->stop) break;
+
+		usleep(500);
+
+		data->evhandler.fds.revents = 0;
+
+		if (poll(&data->evhandler.fds, 1, timeout) < 0)
+			continue;
+
+		if (data->evhandler.fds.revents & (POLLHUP | POLLERR))
+			continue;
+
+		if (data->evhandler.fds.revents & POLLIN)
+			exynos_handle_event(data->dev, &data->evhandler.evctx);
+	}
+
+	pthread_exit(0);
+}
+
+/*
+ * We need to wait until all G2D jobs are finished, otherwise we
+ * potentially remove a BO which the engine still operates on.
+ * This results in the following kernel message:
+ * [drm:exynos_drm_gem_put_dma_addr] *ERROR* failed to lookup gem object.
+ * Also any subsequent BO allocations fail then with:
+ * [drm:exynos_drm_alloc_buf] *ERROR* failed to allocate buffer.
+ */
+static void wait_all_jobs(struct g2d_job* jobs, unsigned num_jobs)
+{
+	unsigned i;
+
+	for (i = 0; i < num_jobs; ++i) {
+		while (jobs[i].busy)
+			usleep(500);
+	}
+
+}
+
+static struct g2d_job* free_job(struct g2d_job* jobs, unsigned num_jobs)
+{
+	unsigned i;
+
+	for (i = 0; i < num_jobs; ++i) {
+		if (jobs[i].busy == 0)
+			return &jobs[i];
+	}
+
+	return NULL;
+}
+
+static int g2d_work(struct g2d_context *ctx, struct g2d_image *img,
+					unsigned num_jobs, unsigned iterations)
+{
+	struct g2d_job *jobs = calloc(num_jobs, sizeof(struct g2d_job));
+	int ret;
+	unsigned i;
+
+	/* setup job ids */
+	for (i = 0; i < num_jobs; ++i)
+		jobs[i].id = i;
+
+	for (i = 0; i < iterations; ++i) {
+		unsigned x, y, w, h;
+
+		struct g2d_job *j = NULL;
+
+		while (1) {
+			j = free_job(jobs, num_jobs);
+
+			if (j)
+				break;
+			else
+				usleep(500);
+		}
+
+		x = rand() % img->width;
+		y = rand() % img->height;
+
+		if (x == (img->width - 1))
+			x -= 1;
+		if (y == (img->height - 1))
+			y -= 1;
+
+		w = rand() % (img->width - x);
+		h = rand() % (img->height - y);
+
+		if (w == 0) w = 1;
+		if (h == 0) h = 1;
+
+		img->color = rand();
+
+		j->busy = 1;
+		g2d_config_event(ctx, j);
+
+		ret = g2d_solid_fill(ctx, img, x, y, w, h);
+
+		if (ret == 0)
+			g2d_exec(ctx);
+
+		if (ret != 0) {
+			fprintf(stderr, "error: iteration %u (x = %u, x = %u, x = %u, x = %u) failed\n",
+					i, x, y, w, h);
+			break;
+		}
+	}
+
+	wait_all_jobs(jobs, num_jobs);
+	free(jobs);
+
+	return 0;
+}
+
+static void usage(const char *name)
+{
+	fprintf(stderr, "usage: %s [-ijwh]\n\n", name);
+
+	fprintf(stderr, "\t-i <number of iterations>\n");
+	fprintf(stderr, "\t-j <number of G2D jobs> (default = 4)\n\n");
+
+	fprintf(stderr, "\t-w <buffer width> (default = 4096)\n");
+	fprintf(stderr, "\t-h <buffer height> (default = 4096)\n");
+
+	exit(0);
+}
+
+int main(int argc, char **argv)
+{
+	int fd, ret, c, parsefail;
+
+	pthread_t event_thread;
+	struct threaddata event_data = {0};
+
+	struct exynos_device *dev;
+	struct g2d_context *ctx;
+	struct exynos_bo *bo;
+
+	struct g2d_image img = {0};
+
+	unsigned int iters = 0, njobs = 4;
+	unsigned int bufw = 4096, bufh = 4096;
+
+	ret = 0;
+	parsefail = 0;
+
+	while ((c = getopt(argc, argv, "i:j:w:h:")) != -1) {
+		switch (c) {
+		case 'i':
+			if (sscanf(optarg, "%u", &iters) != 1)
+				parsefail = 1;
+			break;
+		case 'j':
+			if (sscanf(optarg, "%u", &njobs) != 1)
+				parsefail = 1;
+			break;
+		case 'w':
+			if (sscanf(optarg, "%u", &bufw) != 1)
+				parsefail = 1;
+			break;
+		case 'h':
+			if (sscanf(optarg, "%u", &bufh) != 1)
+				parsefail = 1;
+			break;
+		default:
+			parsefail = 1;
+			break;
+		}
+	}
+
+	if (parsefail || (argc == 1) || (iters == 0))
+		usage(argv[0]);
+
+	if (bufw > 4096 || bufh > 4096) {
+		fprintf(stderr, "error: buffer width/height should be less than 4096.\n");
+		ret = -1;
+
+		goto out;
+	}
+
+	if (bufw == 0 || bufh == 0) {
+		fprintf(stderr, "error: buffer width/height should be non-zero.\n");
+		ret = -1;
+
+		goto out;
+	}
+
+	fd = drmOpen("exynos", NULL);
+	if (fd < 0) {
+		fprintf(stderr, "error: failed to open drm\n");
+		ret = -1;
+
+		goto out;
+	}
+
+	dev = exynos_device_create(fd);
+	if (dev == NULL) {
+		fprintf(stderr, "error: failed to create device\n");
+		ret = -2;
+
+		goto fail;
+	}
+
+	ctx = g2d_init(fd);
+	if (ctx == NULL) {
+		fprintf(stderr, "error: failed to init G2D\n");
+		ret = -3;
+
+		goto g2d_fail;
+	}
+
+	bo = exynos_bo_create(dev, bufw * bufh * 4, 0);
+	if (bo == NULL) {
+		fprintf(stderr, "error: failed to create bo\n");
+		ret = -4;
+
+		goto bo_fail;
+	}
+
+	/* setup g2d image object */
+	img.width = bufw;
+	img.height = bufh;
+	img.stride = bufw * 4;
+	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
+	img.buf_type = G2D_IMGBUF_GEM;
+	img.bo[0] = bo->handle;
+
+	event_data.dev = dev;
+	setup_g2d_event_handler(&event_data.evhandler, fd);
+
+	pthread_create(&event_thread, NULL, threadfunc, &event_data);
+
+	ret = g2d_work(ctx, &img, njobs, iters);
+	if (ret != 0)
+		fprintf(stderr, "error: g2d_work failed\n");
+
+	event_data.stop = 1;
+	pthread_join(event_thread, NULL);
+
+	exynos_bo_destroy(bo);
+
+bo_fail:
+	g2d_fini(ctx);
+
+g2d_fail:
+	exynos_device_destroy(dev);
+
+fail:
+	drmClose(fd);
+
+out:
+	return ret;
+}
commit 60df3581961ef7f1003459f696e5954b5df30aeb
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:15 2015 +0900

    exynos/fimg2d: add g2d_config_event
    
    This enables us to pass command buffers to the kernel which
    trigger an event on the DRM fd upon completion.
    The final goal is to enable asynchronous operation of the
    G2D engine, similar to async page flips.
    
    Passing the event userdata pointer through the G2D context
    was chosen to not change the current API (e.g. by adding
    a userdata argument to each public functions).
    
    Signed-off-by: Tobias Jakobi <[email protected]>

diff --git a/exynos/exynos-symbol-check b/exynos/exynos-symbol-check
index c3ddbe4..bb12315 100755
--- a/exynos/exynos-symbol-check
+++ b/exynos/exynos-symbol-check
@@ -27,6 +27,7 @@ g2d_blend
 g2d_copy
 g2d_copy_with_scale
 g2d_exec
+g2d_config_event
 g2d_fini
 g2d_init
 g2d_scale_and_blend
diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
index e734144..b10620b 100644
--- a/exynos/exynos_fimg2d.c
+++ b/exynos/exynos_fimg2d.c
@@ -57,6 +57,7 @@ struct g2d_context {
 	unsigned int			cmd_nr;
 	unsigned int			cmd_buf_nr;
 	unsigned int			cmdlist_nr;
+	void				*event_userdata;
 };
 
 enum g2d_base_addr_reg {
@@ -280,8 +281,15 @@ static int g2d_flush(struct g2d_context *ctx)
 	cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
 	cmdlist.cmd_nr = ctx->cmd_nr;
 	cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
-	cmdlist.event_type = G2D_EVENT_NOT;
-	cmdlist.user_data = 0;
+
+	if (ctx->event_userdata) {
+		cmdlist.event_type = G2D_EVENT_NONSTOP;
+		cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata);
+		ctx->event_userdata = NULL;
+	} else {
+		cmdlist.event_type = G2D_EVENT_NOT;
+		cmdlist.user_data = 0;
+	}
 
 	ctx->cmd_nr = 0;
 	ctx->cmd_buf_nr = 0;
@@ -336,6 +344,22 @@ void g2d_fini(struct g2d_context *ctx)
 }
 
 /**
+ * g2d_config_event - setup userdata configuration for a g2d event.
+ *		The next invocation of a g2d call (e.g. g2d_solid_fill) is
+ *		then going to flag the command buffer as 'nonstop'.
+ *		Completion of the command buffer execution can then be
+ *		determined by using drmHandleEvent on the DRM fd.
+ *		The userdata is 'consumed' in the process.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @userdata: a pointer to the user data
+ */
+void g2d_config_event(struct g2d_context *ctx, void *userdata)
+{
+	ctx->event_userdata = userdata;
+}
+
+/**
  * g2d_exec - start the dma to process all commands summited by g2d_flush().
  *
  * @ctx: a pointer to g2d_context structure.
diff --git a/exynos/exynos_fimg2d.h b/exynos/exynos_fimg2d.h
index 4aa1568..a2c22da 100644
--- a/exynos/exynos_fimg2d.h
+++ b/exynos/exynos_fimg2d.h
@@ -290,6 +290,7 @@ struct g2d_context;
 
 struct g2d_context *g2d_init(int fd);
 void g2d_fini(struct g2d_context *ctx);
+void g2d_config_event(struct g2d_context *ctx, void *userdata);
 int g2d_exec(struct g2d_context *ctx);
 int g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
 			unsigned int x, unsigned int y, unsigned int w,
commit 2e4b9f08c74fa591a0fd1655c0caa5f41eb9a5b6
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:14 2015 +0900

    tests/exynos: add fimg2d performance analysis
    
    Currently only fast solid color clear performance is measured.
    A large buffer is allocated and solid color clear operations
    are executed on it with randomly chosen properties (position
    and size of the region, clear color). Execution time is
    measured and output together with the amount of pixels
    processed.
    
    The 'simple' variant only executes one G2D command buffer at
    a time, while the 'multi' variant executes multiple ones. This
    can be used to measure setup/exec overhead.
    
    The test also serves a stability check. If clocks/voltages are
    too high or low respectively, the test quickly reveals this.
    
    Tested-by: Hyungwon Hwang <[email protected]>
    Reviewed-by: Hyungwon Hwang <[email protected]>
    Signed-off-by: Tobias Jakobi <[email protected]>

diff --git a/.gitignore b/.gitignore
index a45e0af..ddb37f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -95,5 +95,6 @@ tests/proptest/proptest
 tests/kmstest/kmstest
 tests/vbltest/vbltest
 tests/radeon/radeon_ttm
+tests/exynos/exynos_fimg2d_perf
 tests/exynos/exynos_fimg2d_test
 man/*.3
diff --git a/tests/exynos/Makefile.am b/tests/exynos/Makefile.am
index b21d016..e82d199 100644
--- a/tests/exynos/Makefile.am
+++ b/tests/exynos/Makefile.am
@@ -5,16 +5,31 @@ AM_CFLAGS = \
 	-I $(top_srcdir)/exynos \
 	-I $(top_srcdir)
 
+bin_PROGRAMS =
+noinst_PROGRAMS =
+
 if HAVE_LIBKMS
 if HAVE_INSTALL_TESTS
-bin_PROGRAMS = \
+bin_PROGRAMS += \
 	exynos_fimg2d_test
 else
-noinst_PROGRAMS = \
+noinst_PROGRAMS += \
 	exynos_fimg2d_test
 endif
 endif
 
+if HAVE_INSTALL_TESTS
+bin_PROGRAMS += \
+	exynos_fimg2d_perf
+else
+noinst_PROGRAMS += \
+	exynos_fimg2d_perf
+endif
+
+exynos_fimg2d_perf_LDADD = \
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/exynos/libdrm_exynos.la
+
 exynos_fimg2d_test_LDADD = \
 	$(top_builddir)/libdrm.la \
 	$(top_builddir)/libkms/libkms.la \
diff --git a/tests/exynos/exynos_fimg2d_perf.c b/tests/exynos/exynos_fimg2d_perf.c
new file mode 100644
index 0000000..1699bba
--- /dev/null
+++ b/tests/exynos/exynos_fimg2d_perf.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2015 - Tobias Jakobi
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * It is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with it. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+
+#include "exynos_drm.h"
+#include "exynos_drmif.h"
+#include "exynos_fimg2d.h"
+
+static int output_mathematica = 0;
+
+static int fimg2d_perf_simple(struct exynos_bo *bo, struct g2d_context *ctx,
+			unsigned buf_width, unsigned buf_height, unsigned iterations)
+{
+	struct timespec tspec = { 0 };
+	struct g2d_image img = { 0 };
+
+	unsigned long long g2d_time;
+	unsigned i;
+	int ret = 0;
+
+	img.width = buf_width;
+	img.height = buf_height;
+	img.stride = buf_width * 4;
+	img.color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
+	img.buf_type = G2D_IMGBUF_GEM;
+	img.bo[0] = bo->handle;
+
+	srand(time(NULL));
+
+	printf("starting simple G2D performance test\n");
+	printf("buffer width = %u, buffer height = %u, iterations = %u\n",
+		buf_width, buf_height, iterations);
+
+	if (output_mathematica)
+		putchar('{');
+
+	for (i = 0; i < iterations; ++i) {
+		unsigned x, y, w, h;
+
+		x = rand() % buf_width;
+		y = rand() % buf_height;
+
+		if (x == (buf_width - 1))
+			x -= 1;
+		if (y == (buf_height - 1))
+			y -= 1;
+
+		w = rand() % (buf_width - x);
+		h = rand() % (buf_height - y);
+
+		if (w == 0) w = 1;
+		if (h == 0) h = 1;
+
+		img.color = rand();
+
+		ret = g2d_solid_fill(ctx, &img, x, y, w, h);
+
+		clock_gettime(CLOCK_MONOTONIC, &tspec);
+
+		if (ret == 0)
+			ret = g2d_exec(ctx);
+
+		if (ret != 0) {
+			fprintf(stderr, "error: iteration %u failed (x = %u, y = %u, w = %u, h = %u)\n",
+				i, x, y, w, h);
+			break;
+		} else {
+			struct timespec end = { 0 };
+			clock_gettime(CLOCK_MONOTONIC, &end);
+
+			g2d_time = (end.tv_sec - tspec.tv_sec) * 1000000000ULL;
+			g2d_time += (end.tv_nsec - tspec.tv_nsec);
+
+			if (output_mathematica) {
+				if (i != 0) putchar(',');
+				printf("{%u,%llu}", w * h, g2d_time);
+			} else {
+				printf("num_pixels = %u, usecs = %llu\n", w * h, g2d_time);
+			}
+		}
+	}
+
+	if (output_mathematica)
+		printf("}\n");
+
+	return ret;
+}
+
+static int fimg2d_perf_multi(struct exynos_bo *bo, struct g2d_context *ctx,
+			unsigned buf_width, unsigned buf_height, unsigned iterations, unsigned batch)
+{
+	struct timespec tspec = { 0 };
+	struct g2d_image *images;
+
+	unsigned long long g2d_time;
+	unsigned i, j;
+	int ret = 0;
+
+	images = calloc(batch, sizeof(struct g2d_image));
+	if (images == NULL) {
+		fprintf(stderr, "error: failed to allocate G2D images.\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < batch; ++i) {
+		images[i].width = buf_width;
+		images[i].height = buf_height;
+		images[i].stride = buf_width * 4;
+		images[i].color_mode = G2D_COLOR_FMT_ARGB8888 | G2D_ORDER_AXRGB;
+		images[i].buf_type = G2D_IMGBUF_GEM;
+		images[i].bo[0] = bo->handle;
+	}
+
+	srand(time(NULL));
+
+	printf("starting multi G2D performance test (batch size = %u)\n", batch);
+	printf("buffer width = %u, buffer height = %u, iterations = %u\n",
+		buf_width, buf_height, iterations);
+
+	if (output_mathematica)
+		putchar('{');
+
+	for (i = 0; i < iterations; ++i) {
+		unsigned num_pixels = 0;
+
+		for (j = 0; j < batch; ++j) {
+			unsigned x, y, w, h;
+
+			x = rand() % buf_width;
+			y = rand() % buf_height;
+
+			if (x == (buf_width - 1))
+				x -= 1;
+			if (y == (buf_height - 1))
+				y -= 1;
+
+			w = rand() % (buf_width - x);
+			h = rand() % (buf_height - y);
+
+			if (w == 0) w = 1;
+			if (h == 0) h = 1;
+
+			images[j].color = rand();
+
+			num_pixels += w * h;
+
+			ret = g2d_solid_fill(ctx, &images[j], x, y, w, h);
+			if (ret != 0)
+				break;
+		}
+
+		clock_gettime(CLOCK_MONOTONIC, &tspec);
+
+		if (ret == 0)
+			ret = g2d_exec(ctx);
+
+		if (ret != 0) {
+			fprintf(stderr, "error: iteration %u failed (num_pixels = %u)\n", i, num_pixels);
+			break;
+		} else {
+			struct timespec end = { 0 };
+			clock_gettime(CLOCK_MONOTONIC, &end);
+
+			g2d_time = (end.tv_sec - tspec.tv_sec) * 1000000000ULL;
+			g2d_time += (end.tv_nsec - tspec.tv_nsec);
+
+			if (output_mathematica) {
+				if (i != 0) putchar(',');
+				printf("{%u,%llu}", num_pixels, g2d_time);
+			} else {
+				printf("num_pixels = %u, usecs = %llu\n", num_pixels, g2d_time);
+			}
+		}
+	}
+
+	if (output_mathematica)
+		printf("}\n");
+
+	free(images);
+
+	return ret;
+}
+
+static void usage(const char *name)
+{
+	fprintf(stderr, "usage: %s [-ibwh]\n\n", name);
+
+	fprintf(stderr, "\t-i <number of iterations>\n");
+	fprintf(stderr, "\t-b <size of a batch> (default = 3)\n\n");
+
+	fprintf(stderr, "\t-w <buffer width> (default = 4096)\n");
+	fprintf(stderr, "\t-h <buffer height> (default = 4096)\n\n");
+
+	fprintf(stderr, "\t-M <enable Mathematica styled output>\n");
+
+	exit(0);
+}
+
+int main(int argc, char **argv)
+{
+	int fd, ret, c, parsefail;
+
+	struct exynos_device *dev;
+	struct g2d_context *ctx;
+	struct exynos_bo *bo;
+
+	unsigned int iters = 0, batch = 3;
+	unsigned int bufw = 4096, bufh = 4096;
+
+	ret = 0;
+	parsefail = 0;
+
+	while ((c = getopt(argc, argv, "i:b:w:h:M")) != -1) {
+		switch (c) {
+		case 'i':
+			if (sscanf(optarg, "%u", &iters) != 1)
+				parsefail = 1;
+			break;
+		case 'b':
+			if (sscanf(optarg, "%u", &batch) != 1)
+				parsefail = 1;
+			break;
+		case 'w':
+			if (sscanf(optarg, "%u", &bufw) != 1)
+				parsefail = 1;
+			break;
+		case 'h':
+			if (sscanf(optarg, "%u", &bufh) != 1)
+				parsefail = 1;
+			break;
+		case 'M':
+			output_mathematica = 1;
+			break;
+		default:
+			parsefail = 1;
+			break;
+		}
+	}
+
+	if (parsefail || (argc == 1) || (iters == 0))
+		usage(argv[0]);
+
+	if (bufw < 2 || bufw > 4096 || bufh < 2 || bufh > 4096) {
+		fprintf(stderr, "error: buffer width/height should be in the range 2 to 4096.\n");
+		ret = -1;
+
+		goto out;
+	}
+
+	if (bufw == 0 || bufh == 0) {
+		fprintf(stderr, "error: buffer width/height should be non-zero.\n");
+		ret = -1;
+
+		goto out;
+	}
+
+	fd = drmOpen("exynos", NULL);
+	if (fd < 0) {
+		fprintf(stderr, "error: failed to open drm\n");
+		ret = -1;
+
+		goto out;
+	}
+
+	dev = exynos_device_create(fd);
+	if (dev == NULL) {
+		fprintf(stderr, "error: failed to create device\n");
+		ret = -2;
+
+		goto fail;
+	}
+
+	ctx = g2d_init(fd);
+	if (ctx == NULL) {
+		fprintf(stderr, "error: failed to init G2D\n");
+		ret = -3;
+
+		goto g2d_fail;
+	}
+
+	bo = exynos_bo_create(dev, bufw * bufh * 4, 0);
+	if (bo == NULL) {
+		fprintf(stderr, "error: failed to create bo\n");
+		ret = -4;
+
+		goto bo_fail;
+	}
+
+	ret = fimg2d_perf_simple(bo, ctx, bufw, bufh, iters);
+
+	if (ret == 0)
+		ret = fimg2d_perf_multi(bo, ctx, bufw, bufh, iters, batch);
+
+	exynos_bo_destroy(bo);
+
+bo_fail:
+	g2d_fini(ctx);
+
+g2d_fail:
+	exynos_device_destroy(dev);
+
+fail:
+	drmClose(fd);
+
+out:
+	return ret;
+}
commit 2191ed904f9301d25d16c8b462ab029a81810601
Author: Tobias Jakobi <[email protected]>
Date:   Mon Nov 30 12:12:13 2015 +0900

    exynos: Introduce exynos_handle_event()
    
    Used to handle kernel events specific to the Exynos platform.
    Currently only G2D events are handled.
    
    Signed-off-by: Tobias Jakobi <[email protected]>
    Signed-off-by: Hyungwon Hwang <[email protected]>

diff --git a/exynos/exynos-symbol-check b/exynos/exynos-symbol-check
index 1a1be89..c3ddbe4 100755
--- a/exynos/exynos-symbol-check
+++ b/exynos/exynos-symbol-check
@@ -22,6 +22,7 @@ exynos_device_destroy
 exynos_prime_fd_to_handle
 exynos_prime_handle_to_fd
 exynos_vidi_connection
+exynos_handle_event
 g2d_blend
 g2d_copy
 g2d_copy_with_scale
diff --git a/exynos/exynos_drm.c b/exynos/exynos_drm.c
index df9b8ed..e689781 100644
--- a/exynos/exynos_drm.c
+++ b/exynos/exynos_drm.c
@@ -32,6 +32,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <unistd.h>
 
 #include <sys/mman.h>
 #include <linux/stddef.h>
@@ -42,6 +43,8 @@
 #include "exynos_drm.h"
 #include "exynos_drmif.h"
 
+#define U642VOID(x) ((void *)(unsigned long)(x))
+
 /*
  * Create exynos drm device object.
  *
@@ -374,3 +377,76 @@ exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
 
 	return 0;
 }
+
+static void
+exynos_handle_vendor(int fd, struct drm_event *e, void *ctx)
+{
+	struct drm_exynos_g2d_event *g2d;
+	struct exynos_event_context *ectx = ctx;
+
+	switch (e->type) {
+		case DRM_EXYNOS_G2D_EVENT:
+			if (ectx->version < 1 || ectx->g2d_event_handler == NULL)
+				break;
+			g2d = (struct drm_exynos_g2d_event *)e;
+			ectx->g2d_event_handler(fd, g2d->cmdlist_no, g2d->tv_sec,
+						g2d->tv_usec, U642VOID(g2d->user_data));
+			break;
+
+		default:
+			break;
+	}
+}
+
+int
+exynos_handle_event(struct exynos_device *dev, struct exynos_event_context *ctx)
+{
+	char buffer[1024];
+	int len, i;
+	struct drm_event *e;
+	struct drm_event_vblank *vblank;
+	drmEventContextPtr evctx = &ctx->base;
+
+	/* The DRM read semantics guarantees that we always get only
+	 * complete events. */
+	len = read(dev->fd, buffer, sizeof buffer);
+	if (len == 0)
+		return 0;
+	if (len < (int)sizeof *e)
+		return -1;
+
+	i = 0;
+	while (i < len) {
+		e = (struct drm_event *) &buffer[i];
+		switch (e->type) {
+		case DRM_EVENT_VBLANK:
+			if (evctx->version < 1 ||
+			    evctx->vblank_handler == NULL)
+				break;
+			vblank = (struct drm_event_vblank *) e;
+			evctx->vblank_handler(dev->fd,
+					      vblank->sequence,
+					      vblank->tv_sec,
+					      vblank->tv_usec,
+					      U642VOID (vblank->user_data));
+			break;
+		case DRM_EVENT_FLIP_COMPLETE:
+			if (evctx->version < 2 ||
+			    evctx->page_flip_handler == NULL)
+				break;
+			vblank = (struct drm_event_vblank *) e;
+			evctx->page_flip_handler(dev->fd,
+						 vblank->sequence,
+						 vblank->tv_sec,
+						 vblank->tv_usec,
+						 U642VOID (vblank->user_data));
+			break;
+		default:
+			exynos_handle_vendor(dev->fd, e, evctx);
+			break;
+		}
+		i += e->length;
+	}
+
+	return 0;
+}
diff --git a/exynos/exynos_drm.h b/exynos/exynos_drm.h
index 256c02f..c3af0ac 100644
--- a/exynos/exynos_drm.h
+++ b/exynos/exynos_drm.h
@@ -157,4 +157,16 @@ struct drm_exynos_g2d_exec {
 #define DRM_IOCTL_EXYNOS_G2D_EXEC		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
 
+/* EXYNOS specific events */
+#define DRM_EXYNOS_G2D_EVENT		0x80000000
+
+struct drm_exynos_g2d_event {
+	struct drm_event	base;
+	__u64				user_data;
+	__u32				tv_sec;
+	__u32				tv_usec;
+	__u32				cmdlist_no;
+	__u32				reserved;
+};
+
 #endif
diff --git a/exynos/exynos_drmif.h b/exynos/exynos_drmif.h
index c7c1d44..626e399 100644
--- a/exynos/exynos_drmif.h
+++ b/exynos/exynos_drmif.h
@@ -54,6 +54,25 @@ struct exynos_bo {
 	uint32_t		name;
 };
 
+#define EXYNOS_EVENT_CONTEXT_VERSION 1
+
+/*
+ * Exynos Event Context structure.
+ *
+ * @base: base context (for core events).
+ * @version: version info similar to the one in 'drmEventContext'.
+ * @g2d_event_handler: handler for G2D events.
+ */
+struct exynos_event_context {
+	drmEventContext base;
+
+	int version;
+
+	void (*g2d_event_handler)(int fd, unsigned int cmdlist_no,
+							  unsigned int tv_sec, unsigned int tv_usec,
+							  void *user_data);
+};
+
 /*
  * device related functions:
  */
@@ -83,4 +102,11 @@ int exynos_prime_fd_to_handle(struct exynos_device *dev, int fd,
 int exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
 				uint32_t ext, void *edid);
 
+/*
+ * event handling related functions:
+ */
+int exynos_handle_event(struct exynos_device *dev,
+				struct exynos_event_context *ctx);
+
+
 #endif /* EXYNOS_DRMIF_H_ */

------------------------------------------------------------------------------

--
_______________________________________________
Dri-patches mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-patches
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.