diff mbox series

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

Message ID 1555433029-25490-1-git-send-email-john.stultz@linaro.org
State New
Headers show
Series [RFC] libdrm: tests: Add planetest test from AOSP sources | expand

Commit Message

John Stultz April 16, 2019, 4:43 p.m. UTC
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@chromium.org>
With fixes folded in by:
  Benjamin Gaignard <benjamin.gaignard@linaro.org>
  Vincent Palatin <vpalatin@chromium.org>

Cc: Sean Paul <seanpaul@chromium.org>
Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Cc: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: John Stultz <john.stultz@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

-- 
2.7.4

Comments

Sean Paul April 16, 2019, 6:29 p.m. UTC | #1
On Tue, Apr 16, 2019 at 09:43:49AM -0700, John Stultz wrote:
> 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.

I'll start :)

IMO, we should just punt this into orbit. I just wrote it to do some very basic
testing when atomic was first being developed. I think modetest does all of
this and more, not to mention igt which does _much_ more. So unless someone
actually uses this, and has a compelling reason to upstream it I'd say revert it
from Android and we'll be in sync.

> Feedback and thoughts would be appreciated!
> 

/snip

> +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;

Terrible names for these structs, geez!

Sean

> +	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
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
John Stultz April 16, 2019, 6:48 p.m. UTC | #2
On Tue, Apr 16, 2019 at 11:29 AM Sean Paul <sean@poorly.run> wrote:
> On Tue, Apr 16, 2019 at 09:43:49AM -0700, John Stultz wrote:
> > 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.
>
> I'll start :)
>
> IMO, we should just punt this into orbit. I just wrote it to do some very basic
> testing when atomic was first being developed. I think modetest does all of
> this and more, not to mention igt which does _much_ more. So unless someone
> actually uses this, and has a compelling reason to upstream it I'd say revert it
> from Android and we'll be in sync.

Ok, sounds good. I'll follow up with folks on the AOSP side and nudge
them to ok dropping it.

Thanks so much for the input!
-john
Benjamin Gaignard April 17, 2019, 11:41 a.m. UTC | #3
Le mar. 16 avr. 2019 à 20:48, John Stultz <john.stultz@linaro.org> a écrit :
>
> On Tue, Apr 16, 2019 at 11:29 AM Sean Paul <sean@poorly.run> wrote:
> > On Tue, Apr 16, 2019 at 09:43:49AM -0700, John Stultz wrote:
> > > 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.
> >
> > I'll start :)
> >
> > IMO, we should just punt this into orbit. I just wrote it to do some very basic
> > testing when atomic was first being developed. I think modetest does all of
> > this and more, not to mention igt which does _much_ more. So unless someone
> > actually uses this, and has a compelling reason to upstream it I'd say revert it
> > from Android and we'll be in sync.
>
> Ok, sounds good. I'll follow up with folks on the AOSP side and nudge
> them to ok dropping it.

Like Sean said 'modetest -a' does the same than planetest except for one
funny feature: moving the planes on the screen.
Maybe you can add it in modetest ?

Benjamin

>
> Thanks so much for the input!
> -john
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Emil Velikov April 17, 2019, 6:03 p.m. UTC | #4
On Wed, 17 Apr 2019 at 12:41, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
>
> Le mar. 16 avr. 2019 à 20:48, John Stultz <john.stultz@linaro.org> a écrit :
> >
> > On Tue, Apr 16, 2019 at 11:29 AM Sean Paul <sean@poorly.run> wrote:
> > > On Tue, Apr 16, 2019 at 09:43:49AM -0700, John Stultz wrote:
> > > > 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.
> > >
> > > I'll start :)
> > >
> > > IMO, we should just punt this into orbit. I just wrote it to do some very basic
> > > testing when atomic was first being developed. I think modetest does all of
> > > this and more, not to mention igt which does _much_ more. So unless someone
> > > actually uses this, and has a compelling reason to upstream it I'd say revert it
> > > from Android and we'll be in sync.
> >
> > Ok, sounds good. I'll follow up with folks on the AOSP side and nudge
> > them to ok dropping it.
>
> Like Sean said 'modetest -a' does the same than planetest except for one
> funny feature: moving the planes on the screen.
> Maybe you can add it in modetest ?
>
Adding the missing feature to modetest sound perfectly reasonable IMHO.

-Emil
diff mbox series

Patch

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;
+}