[RFC][PATCH] libdrm: tests: Add planetest test from AOSP sources

John Stultz john.stultz at linaro.org
Tue Apr 16 16:43:49 UTC 2019


In trying to further align the AOSP libdrm branch with upstream,
I wanted to submit the added test planetest that they have been
carrying for awhile.

Mostly sending this out for initial reactions and to stir some
discussion on if folks think upstreaming this would be useful.
Feedback and thoughts would be appreciated!

Originally-by: Sean Paul <seanpaul at chromium.org>
With fixes folded in by:
  Benjamin Gaignard <benjamin.gaignard at linaro.org>
  Vincent Palatin <vpalatin at chromium.org>

Cc: Sean Paul <seanpaul at chromium.org>
Cc: Benjamin Gaignard <benjamin.gaignard at linaro.org>
Cc: Alex Deucher <alexander.deucher at amd.com>
Signed-off-by: John Stultz <john.stultz at linaro.org>
---
 tests/Makefile.am                |   2 +-
 tests/planetest/Makefile.am      |  30 ++++
 tests/planetest/Makefile.sources |  13 ++
 tests/planetest/atomictest.c     | 151 ++++++++++++++++
 tests/planetest/bo.c             | 234 +++++++++++++++++++++++++
 tests/planetest/bo.h             |  34 ++++
 tests/planetest/dev.c            | 367 +++++++++++++++++++++++++++++++++++++++
 tests/planetest/dev.h            |  65 +++++++
 tests/planetest/modeset.c        | 232 +++++++++++++++++++++++++
 tests/planetest/modeset.h        |  19 ++
 tests/planetest/planetest.c      | 116 +++++++++++++
 11 files changed, 1262 insertions(+), 1 deletion(-)
 create mode 100644 tests/planetest/Makefile.am
 create mode 100644 tests/planetest/Makefile.sources
 create mode 100644 tests/planetest/atomictest.c
 create mode 100644 tests/planetest/bo.c
 create mode 100644 tests/planetest/bo.h
 create mode 100644 tests/planetest/dev.c
 create mode 100644 tests/planetest/dev.h
 create mode 100644 tests/planetest/modeset.c
 create mode 100644 tests/planetest/modeset.h
 create mode 100644 tests/planetest/planetest.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index d274a3e..42d9854 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,7 +1,7 @@
 SUBDIRS = util kms modeprint proptest modetest vbltest
 
 if HAVE_LIBKMS
-SUBDIRS += kmstest
+SUBDIRS += kmstest planetest
 endif
 
 if HAVE_RADEON
diff --git a/tests/planetest/Makefile.am b/tests/planetest/Makefile.am
new file mode 100644
index 0000000..b82d05b
--- /dev/null
+++ b/tests/planetest/Makefile.am
@@ -0,0 +1,30 @@
+include Makefile.sources
+
+AM_CFLAGS = $(filter-out -Wpointer-arith, $(WARN_CFLAGS))
+
+AM_CFLAGS += \
+	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/libkms/ \
+	-I$(top_srcdir)
+
+PLANETEST_COMMON_LDADD = \
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/libkms/libkms.la \
+	-lpthread
+
+if HAVE_INSTALL_TESTS
+bin_PROGRAMS = \
+	atomictest \
+	planetest
+else
+noinst_PROGRAMS = \
+	atomictest \
+	planetest
+endif
+
+atomictest_CFLAGS=-DUSE_ATOMIC_API ${AM_CFLAGS}
+atomictest_SOURCES=${PLANETEST_COMMON_FILES} ${ATOMICTEST_FILES}
+planetest_SOURCES=${PLANETEST_COMMON_FILES} ${PLANETEST_FILES}
+
+atomictest_LDADD=${PLANETEST_COMMON_LDADD}
+planetest_LDADD=${PLANETEST_COMMON_LDADD}
diff --git a/tests/planetest/Makefile.sources b/tests/planetest/Makefile.sources
new file mode 100644
index 0000000..3cbeb2b
--- /dev/null
+++ b/tests/planetest/Makefile.sources
@@ -0,0 +1,13 @@
+PLANETEST_COMMON_FILES := \
+	bo.c \
+	bo.h \
+	dev.c \
+	dev.h \
+	modeset.c \
+	modeset.h
+
+ATOMICTEST_FILES := \
+	atomictest.c
+
+PLANETEST_FILES := \
+	planetest.c
diff --git a/tests/planetest/atomictest.c b/tests/planetest/atomictest.c
new file mode 100644
index 0000000..891d242
--- /dev/null
+++ b/tests/planetest/atomictest.c
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+
+#include "dev.h"
+#include "bo.h"
+#include "modeset.h"
+
+static int terminate = 0;
+
+static void sigint_handler(int arg)
+{
+	terminate = 1;
+}
+
+static void
+page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
+		unsigned int tv_usec, void *user_data)
+{
+}
+
+static void incrementor(int *inc, int *val, int increment, int lower, int upper)
+{
+	if(*inc > 0)
+		*inc = *val + increment >= upper ? -1 : 1;
+	else
+		*inc = *val - increment <= lower ? 1 : -1;
+	*val += *inc * increment;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret, i, j, num_test_planes;
+	int x_inc = 1, x = 0, y_inc = 1, y = 0;
+	uint32_t plane_w = 128, plane_h = 128;
+	struct sp_dev *dev;
+	struct sp_plane **plane = NULL;
+	struct sp_crtc *test_crtc;
+	fd_set fds;
+	drmModeAtomicReqPtr pset;
+	drmEventContext event_context = {
+		.version = DRM_EVENT_CONTEXT_VERSION,
+		.page_flip_handler = page_flip_handler,
+	};
+	int card = 0, crtc = 0;
+
+	signal(SIGINT, sigint_handler);
+
+	parse_arguments(argc, argv, &card, &crtc);
+
+	dev = create_sp_dev(card);
+	if (!dev) {
+		printf("Failed to create sp_dev\n");
+		return -1;
+	}
+
+	if (crtc >= dev->num_crtcs) {
+		printf("Invalid crtc %d (num=%d)\n", crtc, dev->num_crtcs);
+		return -1;
+	}
+
+	ret = initialize_screens(dev);
+	if (ret) {
+		printf("Failed to initialize screens\n");
+		goto out;
+	}
+	test_crtc = &dev->crtcs[crtc];
+
+	plane = calloc(dev->num_planes, sizeof(*plane));
+	if (!plane) {
+		printf("Failed to allocate plane array\n");
+		goto out;
+	}
+
+	/* Create our planes */
+	num_test_planes = test_crtc->num_planes;
+	for (i = 0; i < num_test_planes; i++) {
+		plane[i] = get_sp_plane(dev, test_crtc);
+		if (!plane[i]) {
+			printf("no unused planes available\n");
+			goto out;
+		}
+
+		plane[i]->bo = create_sp_bo(dev, plane_w, plane_h, 16, plane[i]->format, 0);
+		if (!plane[i]->bo) {
+			printf("failed to create plane bo\n");
+			goto out;
+		}
+
+		fill_bo(plane[i]->bo, 0xFF, 0xFF, 0xFF, 0xFF);
+	}
+
+	pset = drmModeAtomicAlloc();
+	if (!pset) {
+		printf("Failed to allocate the property set\n");
+		goto out;
+	}
+
+	while (!terminate) {
+		FD_ZERO(&fds);
+		FD_SET(dev->fd, &fds);
+
+		incrementor(&x_inc, &x, 5, 0,
+			test_crtc->crtc->mode.hdisplay - plane_w);
+		incrementor(&y_inc, &y, 5, 0, test_crtc->crtc->mode.vdisplay -
+						plane_h * num_test_planes);
+
+		for (j = 0; j < num_test_planes; j++) {
+			ret = set_sp_plane_pset(dev, plane[j], pset, test_crtc,
+					x, y + j * plane_h);
+			if (ret) {
+				printf("failed to move plane %d\n", ret);
+				goto out;
+			}
+		}
+
+		ret = drmModeAtomicCommit(dev->fd, pset,
+					DRM_MODE_PAGE_FLIP_EVENT, NULL);
+		if (ret) {
+			printf("failed to commit properties ret=%d\n", ret);
+			goto out;
+		}
+
+		do {
+			ret = select(dev->fd + 1, &fds, NULL, NULL, NULL);
+		} while (ret == -1 && errno == EINTR);
+
+		if (FD_ISSET(dev->fd, &fds))
+			drmHandleEvent(dev->fd, &event_context);
+	}
+
+	drmModeAtomicFree(pset);
+
+	for (i = 0; i < num_test_planes; i++)
+		put_sp_plane(plane[i]);
+
+out:
+	destroy_sp_dev(dev);
+	free(plane);
+	return ret;
+}
diff --git a/tests/planetest/bo.c b/tests/planetest/bo.c
new file mode 100644
index 0000000..d4b82c6
--- /dev/null
+++ b/tests/planetest/bo.c
@@ -0,0 +1,234 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "bo.h"
+#include "dev.h"
+
+#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)
+
+static void draw_rect_yuv(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
+{
+	uint32_t i, j, xmax = x + width, ymax = y + height;
+
+	if (xmax > bo->width)
+		xmax = bo->width;
+	if (ymax > bo->height)
+		ymax = bo->height;
+
+	for (i = y; i < ymax; i++) {
+		uint8_t *luma = bo->map_addr + i * bo->pitch;
+
+		for (j = x; j < xmax; j++)
+			luma[j] = MAKE_YUV_601_Y(r, g, b);
+	}
+
+	for (i = y; i < ymax / 2; i++) {
+		uint8_t *chroma = bo->map_addr + (i + height) * bo->pitch;
+
+		for (j = x; j < xmax / 2; j++) {
+			chroma[j*2] = MAKE_YUV_601_U(r, g, b);
+			chroma[j*2 + 1] = MAKE_YUV_601_V(r, g, b);
+		}
+	}
+}
+
+void fill_bo(struct sp_bo *bo, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
+{
+	if (bo->format == DRM_FORMAT_NV12)
+		draw_rect_yuv(bo, 0, 0, bo->width, bo->height, a, r, g, b);
+	else
+		draw_rect(bo, 0, 0, bo->width, bo->height, a, r, g, b);
+}
+
+void draw_rect(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
+{
+	uint32_t i, j, xmax = x + width, ymax = y + height;
+
+	if (xmax > bo->width)
+		xmax = bo->width;
+	if (ymax > bo->height)
+		ymax = bo->height;
+
+	for (i = y; i < ymax; i++) {
+		uint8_t *row = bo->map_addr + i * bo->pitch;
+
+		for (j = x; j < xmax; j++) {
+			uint8_t *pixel = row + j * 4;
+
+			if (bo->format == DRM_FORMAT_ARGB8888 ||
+			    bo->format == DRM_FORMAT_XRGB8888)
+			{
+				pixel[0] = b;
+				pixel[1] = g;
+				pixel[2] = r;
+				pixel[3] = a;
+			} else if (bo->format == DRM_FORMAT_RGBA8888) {
+				pixel[0] = r;
+				pixel[1] = g;
+				pixel[2] = b;
+				pixel[3] = a;
+			}
+		}
+	}
+}
+
+static int add_fb_sp_bo(struct sp_bo *bo, uint32_t format)
+{
+	int ret;
+	uint32_t handles[4], pitches[4], offsets[4];
+
+	handles[0] = bo->handle;
+	pitches[0] = bo->pitch;
+	offsets[0] = 0;
+	if (bo->format == DRM_FORMAT_NV12) {
+		handles[1] = bo->handle;
+		pitches[1] = pitches[0];
+		offsets[1] = pitches[0] * bo->height;
+	}
+
+	ret = drmModeAddFB2(bo->dev->fd, bo->width, bo->height,
+			format, handles, pitches, offsets,
+			&bo->fb_id, bo->flags);
+	if (ret) {
+		printf("failed to create fb ret=%d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int map_sp_bo(struct sp_bo *bo)
+{
+	int ret;
+	struct drm_mode_map_dumb md;
+
+	if (bo->map_addr)
+		return 0;
+
+	md.handle = bo->handle;
+	ret = drmIoctl(bo->dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &md);
+	if (ret) {
+		printf("failed to map sp_bo ret=%d\n", ret);
+		return ret;
+	}
+
+	bo->map_addr = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+				bo->dev->fd, md.offset);
+	if (bo->map_addr == MAP_FAILED) {
+		printf("failed to map bo ret=%d\n", -errno);
+		return -errno;
+	}
+	return 0;
+}
+
+static int format_to_bpp(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+		return 8;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_RGBA8888:
+	default:
+		return 32;
+	}
+}
+
+struct sp_bo *create_sp_bo(struct sp_dev *dev, uint32_t width, uint32_t height,
+		uint32_t depth, uint32_t format, uint32_t flags)
+{
+	int ret;
+	struct drm_mode_create_dumb cd;
+	struct sp_bo *bo;
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo)
+		return NULL;
+
+	if (format == DRM_FORMAT_NV12)
+		cd.height = height * 3 / 2;
+	else
+		cd.height = height;
+
+	cd.width = width;
+	cd.bpp = format_to_bpp(format);
+	cd.flags = flags;
+
+	ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_CREATE_DUMB, &cd);
+	if (ret) {
+		printf("failed to create sp_bo %d\n", ret);
+		goto err;
+	}
+
+	bo->dev = dev;
+	bo->width = width;
+	bo->height = height;
+	bo->depth = depth;
+	bo->bpp = format_to_bpp(format);
+	bo->format = format;
+	bo->flags = flags;
+
+	bo->handle = cd.handle;
+	bo->pitch = cd.pitch;
+	bo->size = cd.size;
+
+	ret = add_fb_sp_bo(bo, format);
+	if (ret) {
+		printf("failed to add fb ret=%d\n", ret);
+		goto err;
+	}
+
+	ret = map_sp_bo(bo);
+	if (ret) {
+		printf("failed to map bo ret=%d\n", ret);
+		goto err;
+	}
+
+	return bo;
+
+err:
+	free_sp_bo(bo);
+	return NULL;
+}
+
+void free_sp_bo(struct sp_bo *bo)
+{
+	int ret;
+	struct drm_mode_destroy_dumb dd;
+
+	if (!bo)
+		return;
+
+	if (bo->map_addr)
+		munmap(bo->map_addr, bo->size);
+
+	if (bo->fb_id) {
+		ret = drmModeRmFB(bo->dev->fd, bo->fb_id);
+		if (ret)
+			printf("Failed to rmfb ret=%d!\n", ret);
+	}
+
+	if (bo->handle) {
+		dd.handle = bo->handle;
+		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dd);
+		if (ret)
+			printf("Failed to destroy buffer ret=%d\n", ret);
+	}
+
+	free(bo);
+}
diff --git a/tests/planetest/bo.h b/tests/planetest/bo.h
new file mode 100644
index 0000000..7471e12
--- /dev/null
+++ b/tests/planetest/bo.h
@@ -0,0 +1,34 @@
+#ifndef __BO_H_INCLUDED__
+#define __BO_H_INCLUDED__
+
+#include <stdint.h>
+
+struct sp_dev;
+
+struct sp_bo {
+	struct sp_dev *dev;
+
+	uint32_t width;
+	uint32_t height;
+	uint32_t depth;
+	uint32_t bpp;
+	uint32_t format;
+	uint32_t flags;
+
+	uint32_t fb_id;
+	uint32_t handle;
+	void *map_addr;
+	uint32_t pitch;
+	uint32_t size;
+};
+
+struct sp_bo *create_sp_bo(struct sp_dev *dev, uint32_t width, uint32_t height,
+		uint32_t depth, uint32_t format, uint32_t flags);
+
+void fill_bo(struct sp_bo *bo, uint8_t a, uint8_t r, uint8_t g, uint8_t b);
+void draw_rect(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b);
+
+void free_sp_bo(struct sp_bo *bo);
+
+#endif /* __BO_H_INCLUDED__ */
diff --git a/tests/planetest/dev.c b/tests/planetest/dev.c
new file mode 100644
index 0000000..bd0968c
--- /dev/null
+++ b/tests/planetest/dev.c
@@ -0,0 +1,367 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <drm.h>
+#include <drm_fourcc.h>
+#include <errno.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "bo.h"
+#include "dev.h"
+#include "modeset.h"
+
+static void show_usage(char *name)
+{
+	printf("Usage: %s [OPTION]\n", name);
+	printf("   -c, --card      Index of dri card (ie: /dev/dri/cardN)\n");
+	printf("   -r, --crtc      Index of crtc to use for test\n");
+	printf("\n\n");
+}
+
+void parse_arguments(int argc, char *argv[], int *card, int *crtc)
+{
+	static struct option options[] = {
+		{ "card", required_argument, NULL, 'c' },
+		{ "crtc", required_argument, NULL, 'r' },
+		{ "help", no_argument, NULL, 'h' },
+	};
+	int option_index = 0;
+	int c;
+
+	*card = -1;
+	*crtc = -1;
+	do {
+		c = getopt_long(argc, argv, "c:r:h", options, &option_index);
+		switch (c) {
+		case 0:
+		case 'h':
+			show_usage(argv[0]);
+			exit(0);
+		case -1:
+			break;
+		case 'c':
+			if (optarg[0] < '0' || optarg[0] > '9') {
+				printf("Invalid card value '%s'!\n", optarg);
+				show_usage(argv[0]);
+				exit(-1);
+			}
+			*card = optarg[0] - '0';
+			break;
+		case 'r':
+			if (optarg[0] < '0' || optarg[0] > '9') {
+				printf("Invalid crtc value '%s'!\n", optarg);
+				show_usage(argv[0]);
+				exit(-1);
+			}
+			*crtc = optarg[0] - '0';
+			break;
+		}
+	} while (c != -1);
+
+	if (*card < 0 || *crtc < 0) {
+		show_usage(argv[0]);
+		exit(-1);
+	}
+}
+
+static uint32_t get_prop_id(struct sp_dev *dev,
+			drmModeObjectPropertiesPtr props, const char *name)
+{
+	drmModePropertyPtr p;
+	uint32_t i, prop_id = 0; /* Property ID should always be > 0 */
+
+	for (i = 0; !prop_id && i < props->count_props; i++) {
+		p = drmModeGetProperty(dev->fd, props->props[i]);
+		if (!strcmp(p->name, name))
+			prop_id = p->prop_id;
+		drmModeFreeProperty(p);
+	}
+	if (!prop_id)
+		printf("Could not find %s property\n", name);
+	return prop_id;
+}
+
+static int get_supported_format(struct sp_plane *plane, uint32_t *format)
+{
+	uint32_t i;
+
+	for (i = 0; i < plane->plane->count_formats; i++) {
+		if (plane->plane->formats[i] == DRM_FORMAT_XRGB8888 ||
+		    plane->plane->formats[i] == DRM_FORMAT_ARGB8888 ||
+		    plane->plane->formats[i] == DRM_FORMAT_RGBA8888 ||
+		    plane->plane->formats[i] == DRM_FORMAT_NV12) {
+			*format = plane->plane->formats[i];
+			return 0;
+		}
+	}
+	printf("No suitable formats found!\n");
+	return -ENOENT;
+}
+
+struct sp_dev *create_sp_dev(int card)
+{
+	struct sp_dev *dev;
+	int ret, fd, i, j;
+	drmModeRes *r = NULL;
+	drmModePlaneRes *pr = NULL;
+	char card_path[256];
+
+	snprintf(card_path, sizeof(card_path), "/dev/dri/card%d", card);
+
+	fd = open(card_path, O_RDWR);
+	if (fd < 0) {
+		printf("failed to open card0\n");
+		return NULL;
+	}
+
+	dev = calloc(1, sizeof(*dev));
+	if (!dev) {
+		printf("failed to allocate dev\n");
+		return NULL;
+	}
+
+	dev->fd = fd;
+
+	ret = drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+	if (ret) {
+		printf("failed to set client cap\n");
+		goto err;
+	}
+
+	ret = drmSetClientCap(dev->fd, DRM_CLIENT_CAP_ATOMIC, 1);
+	if (ret) {
+		printf("Failed to set atomic cap %d", ret);
+		goto err;
+	}
+
+	r = drmModeGetResources(dev->fd);
+	if (!r) {
+		printf("failed to get r\n");
+		goto err;
+	}
+
+	dev->num_connectors = r->count_connectors;
+	dev->connectors = calloc(dev->num_connectors,
+				sizeof(struct sp_connector));
+	if (!dev->connectors) {
+		printf("failed to allocate connectors\n");
+		goto err;
+	}
+	for (i = 0; i < dev->num_connectors; i++) {
+		drmModeObjectPropertiesPtr props;
+		dev->connectors[i].conn = drmModeGetConnector(dev->fd,
+					r->connectors[i]);
+		if (!dev->connectors[i].conn) {
+			printf("failed to get connector %d\n", i);
+			goto err;
+		}
+
+		props = drmModeObjectGetProperties(dev->fd, r->connectors[i],
+				DRM_MODE_OBJECT_CONNECTOR);
+		if (!props) {
+			printf("failed to get connector properties\n");
+			goto err;
+		}
+
+		dev->connectors[i].crtc_id_pid = get_prop_id(dev, props,
+								"CRTC_ID");
+		drmModeFreeObjectProperties(props);
+		if (!dev->connectors[i].crtc_id_pid)
+			goto err;
+	}
+
+	dev->num_encoders = r->count_encoders;
+	dev->encoders = calloc(dev->num_encoders, sizeof(*dev->encoders));
+	if (!dev->encoders) {
+		printf("failed to allocate encoders\n");
+		goto err;
+	}
+	for (i = 0; i < dev->num_encoders; i++) {
+		dev->encoders[i] = drmModeGetEncoder(dev->fd, r->encoders[i]);
+		if (!dev->encoders[i]) {
+			printf("failed to get encoder %d\n", i);
+			goto err;
+		}
+	}
+
+	dev->num_crtcs = r->count_crtcs;
+	dev->crtcs = calloc(dev->num_crtcs, sizeof(struct sp_crtc));
+	if (!dev->crtcs) {
+		printf("failed to allocate crtcs\n");
+		goto err;
+	}
+	for (i = 0; i < dev->num_crtcs; i++) {
+		drmModeObjectPropertiesPtr props;
+
+		dev->crtcs[i].crtc = drmModeGetCrtc(dev->fd, r->crtcs[i]);
+		if (!dev->crtcs[i].crtc) {
+			printf("failed to get crtc %d\n", i);
+			goto err;
+		}
+		dev->crtcs[i].pipe = i;
+		dev->crtcs[i].num_planes = 0;
+
+		props = drmModeObjectGetProperties(dev->fd, r->crtcs[i],
+				DRM_MODE_OBJECT_CRTC);
+		if (!props) {
+			printf("failed to get crtc properties\n");
+			goto err;
+		}
+
+		dev->crtcs[i].mode_pid = get_prop_id(dev, props, "MODE_ID");
+		dev->crtcs[i].active_pid = get_prop_id(dev, props, "ACTIVE");
+		drmModeFreeObjectProperties(props);
+		if (!dev->crtcs[i].mode_pid || !dev->crtcs[i].active_pid)
+			goto err;
+	}
+
+	pr = drmModeGetPlaneResources(dev->fd);
+	if (!pr) {
+		printf("failed to get plane resources\n");
+		goto err;
+	}
+	dev->num_planes = pr->count_planes;
+	dev->planes = calloc(dev->num_planes, sizeof(struct sp_plane));
+	for(i = 0; i < dev->num_planes; i++) {
+		drmModeObjectPropertiesPtr props;
+		struct sp_plane *plane = &dev->planes[i];
+
+		plane->dev = dev;
+		plane->plane = drmModeGetPlane(dev->fd, pr->planes[i]);
+		if (!plane->plane) {
+			printf("failed to get plane %d\n", i);
+			goto err;
+		}
+		plane->bo = NULL;
+		plane->in_use = 0;
+
+		ret = get_supported_format(plane, &plane->format);
+		if (ret) {
+			printf("failed to get supported format: %d\n", ret);
+			goto err;
+		}
+
+		for (j = 0; j < dev->num_crtcs; j++) {
+			if (plane->plane->possible_crtcs & (1 << j))
+				dev->crtcs[j].num_planes++;
+		}
+
+		props = drmModeObjectGetProperties(dev->fd, pr->planes[i],
+				DRM_MODE_OBJECT_PLANE);
+		if (!props) {
+			printf("failed to get plane properties\n");
+			goto err;
+		}
+		plane->crtc_pid = get_prop_id(dev, props, "CRTC_ID");
+		if (!plane->crtc_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->fb_pid = get_prop_id(dev, props, "FB_ID");
+		if (!plane->fb_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_x_pid = get_prop_id(dev, props, "CRTC_X");
+		if (!plane->crtc_x_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_y_pid = get_prop_id(dev, props, "CRTC_Y");
+		if (!plane->crtc_y_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_w_pid = get_prop_id(dev, props, "CRTC_W");
+		if (!plane->crtc_w_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_h_pid = get_prop_id(dev, props, "CRTC_H");
+		if (!plane->crtc_h_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_x_pid = get_prop_id(dev, props, "SRC_X");
+		if (!plane->src_x_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_y_pid = get_prop_id(dev, props, "SRC_Y");
+		if (!plane->src_y_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_w_pid = get_prop_id(dev, props, "SRC_W");
+		if (!plane->src_w_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_h_pid = get_prop_id(dev, props, "SRC_H");
+		if (!plane->src_h_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		drmModeFreeObjectProperties(props);
+	}
+
+	if (pr)
+		drmModeFreePlaneResources(pr);
+	if (r)
+		drmModeFreeResources(r);
+
+	return dev;
+err:
+	if (pr)
+		drmModeFreePlaneResources(pr);
+	if (r)
+		drmModeFreeResources(r);
+	destroy_sp_dev(dev);
+	return NULL;
+}
+
+void destroy_sp_dev(struct sp_dev *dev)
+{
+	int i;
+
+	if (dev->planes) {
+		for (i = 0; i< dev->num_planes; i++) {
+			if (dev->planes[i].in_use)
+				put_sp_plane(&dev->planes[i]);
+			if (dev->planes[i].plane)
+				drmModeFreePlane(dev->planes[i].plane);
+			if (dev->planes[i].bo)
+				free_sp_bo(dev->planes[i].bo);
+		}
+		free(dev->planes);
+	}
+	if (dev->crtcs) {
+		for (i = 0; i< dev->num_crtcs; i++) {
+			if (dev->crtcs[i].crtc)
+				drmModeFreeCrtc(dev->crtcs[i].crtc);
+		}
+		free(dev->crtcs);
+	}
+	if (dev->encoders) {
+		for (i = 0; i< dev->num_encoders; i++) {
+			if (dev->encoders[i])
+				drmModeFreeEncoder(dev->encoders[i]);
+		}
+		free(dev->encoders);
+	}
+	if (dev->connectors) {
+		for (i = 0; i< dev->num_connectors; i++) {
+			if (dev->connectors[i].conn)
+				drmModeFreeConnector(dev->connectors[i].conn);
+		}
+		free(dev->connectors);
+	}
+
+	close(dev->fd);
+	free(dev);
+}
diff --git a/tests/planetest/dev.h b/tests/planetest/dev.h
new file mode 100644
index 0000000..04dec79
--- /dev/null
+++ b/tests/planetest/dev.h
@@ -0,0 +1,65 @@
+#ifndef __DEV_H_INCLUDED__
+#define __DEV_H_INCLUDED__
+
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+struct sp_bo;
+struct sp_dev;
+
+struct sp_plane {
+	struct sp_dev *dev;
+	drmModePlanePtr plane;
+	struct sp_bo *bo;
+	int in_use;
+	uint32_t format;
+
+	/* Property ID's */
+	uint32_t crtc_pid;
+	uint32_t fb_pid;
+	uint32_t zpos_pid;
+	uint32_t crtc_x_pid;
+	uint32_t crtc_y_pid;
+	uint32_t crtc_w_pid;
+	uint32_t crtc_h_pid;
+	uint32_t src_x_pid;
+	uint32_t src_y_pid;
+	uint32_t src_w_pid;
+	uint32_t src_h_pid;
+};
+
+struct sp_connector {
+	drmModeConnectorPtr conn;
+	uint32_t crtc_id_pid;
+};
+
+struct sp_crtc {
+	drmModeCrtcPtr crtc;
+	int pipe;
+	int num_planes;
+	uint32_t mode_pid;
+	uint32_t active_pid;
+};
+
+struct sp_dev {
+	int fd;
+
+	int num_connectors;
+	struct sp_connector *connectors;
+
+	int num_encoders;
+	drmModeEncoderPtr *encoders;
+
+	int num_crtcs;
+	struct sp_crtc *crtcs;
+
+	int num_planes;
+	struct sp_plane *planes;
+};
+
+void parse_arguments(int argc, char *argv[], int *card, int *crtc);
+
+struct sp_dev *create_sp_dev(int card);
+void destroy_sp_dev(struct sp_dev *dev);
+
+#endif /* __DEV_H_INCLUDED__ */
diff --git a/tests/planetest/modeset.c b/tests/planetest/modeset.c
new file mode 100644
index 0000000..b8f6690
--- /dev/null
+++ b/tests/planetest/modeset.c
@@ -0,0 +1,232 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "modeset.h"
+#include "bo.h"
+#include "dev.h"
+
+static int set_crtc_mode(struct sp_dev *dev, struct sp_crtc *crtc,
+			struct sp_connector *conn, drmModeModeInfoPtr mode)
+{
+	int ret;
+	struct drm_mode_create_blob create_blob;
+	drmModeAtomicReqPtr pset;
+
+	memset(&create_blob, 0, sizeof(create_blob));
+	create_blob.length = sizeof(struct drm_mode_modeinfo);
+	create_blob.data = (__u64)(uintptr_t)mode;
+
+	ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
+	if (ret) {
+		printf("Failed to create mode property blob %d", ret);
+		return ret;
+	}
+
+	pset = drmModeAtomicAlloc();
+	if (!pset) {
+		printf("Failed to allocate property set");
+		return -1;
+	}
+
+	ret = drmModeAtomicAddProperty(pset, crtc->crtc->crtc_id,
+				    crtc->mode_pid, create_blob.blob_id) ||
+	      drmModeAtomicAddProperty(pset, crtc->crtc->crtc_id,
+				    crtc->active_pid, 1) ||
+	      drmModeAtomicAddProperty(pset, conn->conn->connector_id,
+				conn->crtc_id_pid, crtc->crtc->crtc_id);
+	if (ret) {
+		printf("Failed to add blob %d to pset", create_blob.blob_id);
+		drmModeAtomicFree(pset);
+		return ret;
+	}
+
+	ret = drmModeAtomicCommit(dev->fd, pset, DRM_MODE_ATOMIC_ALLOW_MODESET,
+					NULL);
+
+	drmModeAtomicFree(pset);
+
+	if (ret) {
+		printf("Failed to commit pset ret=%d\n", ret);
+		return ret;
+	}
+
+	memcpy(&crtc->crtc->mode, mode, sizeof(struct drm_mode_modeinfo));
+	crtc->crtc->mode_valid = 1;
+	return 0;
+}
+
+int initialize_screens(struct sp_dev *dev)
+{
+	int ret, i, j;
+	unsigned crtc_mask = 0;
+
+	for (i = 0; i < dev->num_connectors; i++) {
+		struct sp_connector *c = &dev->connectors[i];
+		drmModeModeInfoPtr m = NULL;
+		drmModeEncoderPtr e = NULL;
+		struct sp_crtc *cr = NULL;
+
+		if (c->conn->connection != DRM_MODE_CONNECTED)
+			continue;
+
+		if (!c->conn->count_modes) {
+			printf("connector has no modes, skipping\n");
+			continue;
+		}
+
+		/* Take the first unless there's a preferred mode */
+		m = &c->conn->modes[0];
+		for (j = 0; j < c->conn->count_modes; j++) {
+			drmModeModeInfoPtr tmp_m = &c->conn->modes[j];
+
+			if (!(tmp_m->type & DRM_MODE_TYPE_PREFERRED))
+				continue;
+
+			m = tmp_m;
+			break;
+		}
+
+		if (!c->conn->count_encoders) {
+			printf("no possible encoders for connector\n");
+			continue;
+		}
+
+		for (j = 0; j < dev->num_encoders; j++) {
+			e = dev->encoders[j];
+			if (e->encoder_id == c->conn->encoders[0])
+				break;
+		}
+		if (j == dev->num_encoders) {
+			printf("could not find encoder for the connector\n");
+			continue;
+		}
+
+		for (j = 0; j < dev->num_crtcs; j++) {
+			if ((1 << j) & crtc_mask)
+				continue;
+
+			cr = &dev->crtcs[j];
+
+			if ((1 << j) & e->possible_crtcs)
+				break;
+		}
+		if (j == dev->num_crtcs) {
+			printf("could not find crtc for the encoder\n");
+			continue;
+		}
+
+		ret = set_crtc_mode(dev, cr, c, m);
+		if (ret) {
+			printf("failed to set mode!\n");
+			continue;
+		}
+		crtc_mask |= 1 << j;
+	}
+	return 0;
+}
+
+struct sp_plane *get_sp_plane(struct sp_dev *dev, struct sp_crtc *crtc)
+{
+	int i;
+
+	for(i = 0; i < dev->num_planes; i++) {
+		struct sp_plane *p = &dev->planes[i];
+
+		if (p->in_use)
+			continue;
+
+		if (!(p->plane->possible_crtcs & (1 << crtc->pipe)))
+			continue;
+
+		p->in_use = 1;
+		return p;
+	}
+	return NULL;
+}
+
+void put_sp_plane(struct sp_plane *plane)
+{
+	drmModePlanePtr p;
+
+	/* Get the latest plane information (most notably the crtc_id) */
+	p = drmModeGetPlane(plane->dev->fd, plane->plane->plane_id);
+	if (p)
+		plane->plane = p;
+
+	if (plane->bo) {
+		free_sp_bo(plane->bo);
+		plane->bo = NULL;
+	}
+	plane->in_use = 0;
+}
+
+int set_sp_plane(struct sp_dev *dev, struct sp_plane *plane,
+		struct sp_crtc *crtc, int x, int y)
+{
+	int ret;
+	uint32_t w, h;
+
+	w = plane->bo->width;
+	h = plane->bo->height;
+
+	if ((w + x) > crtc->crtc->mode.hdisplay)
+		w = crtc->crtc->mode.hdisplay - x;
+	if ((h + y) > crtc->crtc->mode.vdisplay)
+		h = crtc->crtc->mode.vdisplay - y;
+
+	ret = drmModeSetPlane(dev->fd, plane->plane->plane_id,
+			crtc->crtc->crtc_id, plane->bo->fb_id, 0, x, y, w, h,
+			0, 0, w << 16, h << 16);
+	if (ret) {
+		printf("failed to set plane to crtc ret=%d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+int set_sp_plane_pset(struct sp_dev *dev, struct sp_plane *plane,
+		drmModeAtomicReqPtr pset, struct sp_crtc *crtc, int x, int y)
+{
+	int ret;
+	uint32_t w, h;
+
+	w = plane->bo->width;
+	h = plane->bo->height;
+
+	if ((w + x) > crtc->crtc->mode.hdisplay)
+		w = crtc->crtc->mode.hdisplay - x;
+	if ((h + y) > crtc->crtc->mode.vdisplay)
+		h = crtc->crtc->mode.vdisplay - y;
+
+	ret = drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->crtc_pid, crtc->crtc->crtc_id)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->fb_pid, plane->bo->fb_id)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->crtc_x_pid, x)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->crtc_y_pid, y)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->crtc_w_pid, w)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->crtc_h_pid, h)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->src_x_pid, 0)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->src_y_pid, 0)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->src_w_pid, w << 16)
+		|| drmModeAtomicAddProperty(pset, plane->plane->plane_id,
+			plane->src_h_pid, h << 16);
+	if (ret) {
+		printf("failed to add properties to the set\n");
+		return -1;
+	}
+
+	return ret;
+}
diff --git a/tests/planetest/modeset.h b/tests/planetest/modeset.h
new file mode 100644
index 0000000..5499959
--- /dev/null
+++ b/tests/planetest/modeset.h
@@ -0,0 +1,19 @@
+#ifndef __MODESET_H_INCLUDED__
+#define __MODESET_H_INCLUDED__
+
+struct sp_dev;
+struct sp_crtc;
+
+int initialize_screens(struct sp_dev *dev);
+
+
+struct sp_plane *get_sp_plane(struct sp_dev *dev, struct sp_crtc *crtc);
+void put_sp_plane(struct sp_plane *plane);
+
+int set_sp_plane(struct sp_dev *dev, struct sp_plane *plane,
+		struct sp_crtc *crtc, int x, int y);
+
+int set_sp_plane_pset(struct sp_dev *dev, struct sp_plane *plane,
+		drmModeAtomicReqPtr pset, struct sp_crtc *crtc, int x, int y);
+
+#endif /* __MODESET_H_INCLUDED__ */
diff --git a/tests/planetest/planetest.c b/tests/planetest/planetest.c
new file mode 100644
index 0000000..5e187c9
--- /dev/null
+++ b/tests/planetest/planetest.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+
+#include "dev.h"
+#include "bo.h"
+#include "modeset.h"
+
+static int terminate = 0;
+
+static void sigint_handler(int arg)
+{
+	terminate = 1;
+}
+
+static void incrementor(int *inc, int *val, int increment, int lower, int upper)
+{
+	if(*inc > 0)
+		*inc = *val + increment >= upper ? -1 : 1;
+	else
+		*inc = *val - increment <= lower ? 1 : -1;
+	*val += *inc * increment;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret, i, j, num_test_planes;
+	int x_inc = 1, x = 0, y_inc = 1, y = 0;
+	uint32_t plane_w = 128, plane_h = 128;
+	struct sp_dev *dev;
+	struct sp_plane **plane = NULL;
+	struct sp_crtc *test_crtc;
+	int card = 0, crtc = 0;
+
+	signal(SIGINT, sigint_handler);
+
+	parse_arguments(argc, argv, &card, &crtc);
+
+	dev = create_sp_dev(card);
+	if (!dev) {
+		printf("Failed to create sp_dev\n");
+		return -1;
+	}
+
+	if (crtc >= dev->num_crtcs) {
+		printf("Invalid crtc %d (num=%d)\n", crtc, dev->num_crtcs);
+		return -1;
+	}
+
+	ret = initialize_screens(dev);
+	if (ret) {
+		printf("Failed to initialize screens\n");
+		goto out;
+	}
+	test_crtc = &dev->crtcs[crtc];
+
+	plane = calloc(dev->num_planes, sizeof(*plane));
+	if (!plane) {
+		printf("Failed to allocate plane array\n");
+		goto out;
+	}
+
+	/* Create our planes */
+	num_test_planes = test_crtc->num_planes;
+	for (i = 0; i < num_test_planes; i++) {
+		plane[i] = get_sp_plane(dev, test_crtc);
+		if (!plane[i]) {
+			printf("no unused planes available\n");
+			goto out;
+		}
+
+		plane[i]->bo = create_sp_bo(dev, plane_w, plane_h, 16,
+				plane[i]->format, 0);
+		if (!plane[i]->bo) {
+			printf("failed to create plane bo\n");
+			goto out;
+		}
+
+		fill_bo(plane[i]->bo, 0xFF, 0xFF, 0xFF, 0xFF);
+	}
+
+	while (!terminate) {
+		incrementor(&x_inc, &x, 5, 0,
+			test_crtc->crtc->mode.hdisplay - plane_w);
+		incrementor(&y_inc, &y, 5, 0, test_crtc->crtc->mode.vdisplay -
+						plane_h * num_test_planes);
+
+		for (j = 0; j < num_test_planes; j++) {
+			ret = set_sp_plane(dev, plane[j], test_crtc,
+					x, y + j * plane_h);
+			if (ret) {
+				printf("failed to set plane %d %d\n", j, ret);
+				goto out;
+			}
+		}
+		usleep(15 * 1000);
+	}
+
+	for (i = 0; i < num_test_planes; i++)
+		put_sp_plane(plane[i]);
+
+out:
+	destroy_sp_dev(dev);
+	free(plane);
+	return ret;
+}
-- 
2.7.4



More information about the dri-devel mailing list