exynos: Added exynos specific DRM test

Added exynosdrmtest, a exynos specific DRM test which will be
used for testing the direct IOCTLs to exynos gem module as well
as test the exynos drm prime module for importing and exporting
gem objects to fd.

BUG=None
TEST=Tested with DRM application - exynosdrmtest

Signed-off-by: Prathyush K <prathyush.k@samsung.com>
Change-Id: Ia384542adda22d562573e080d8383f2b011922c4
Reviewed-on: https://gerrit.chromium.org/gerrit/22141
Commit-Ready: Anush Elangovan <anush@google.com>
Reviewed-by: Anush Elangovan <anush@google.com>
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h
new file mode 100644
index 0000000..c41264f
--- /dev/null
+++ b/include/drm/exynos_drm.h
@@ -0,0 +1,159 @@
+/* exynos_drm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ *	Inki Dae <inki.dae@samsung.com>
+ *	Joonyoung Shim <jy0922.shim@samsung.com>
+ *	Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_H_
+#define _EXYNOS_DRM_H_
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ *	- this size value would be page-aligned internally.
+ * @flags: user request for setting memory type or cache attributes.
+ * @handle: returned a handle to created gem object.
+ *	- this handle will be set by gem module of kernel side.
+ */
+struct drm_exynos_gem_create {
+	uint64_t size;
+	unsigned int flags;
+	unsigned int handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ *	- this value should be set by user.
+ */
+struct drm_exynos_gem_map_off {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *	- this variable would be filled by exynos gem module
+ *	of kernel side with user virtual address which is allocated
+ *	by do_mmap().
+ */
+struct drm_exynos_gem_mmap {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t size;
+	uint64_t mapped;
+};
+
+struct drm_exynos_plane_set_zpos {
+	__u32 plane_id;
+	__s32 zpos;
+};
+
+#define DRM_EXYNOS_GEM_CREATE		0x00
+#define DRM_EXYNOS_GEM_MAP_OFFSET	0x01
+#define DRM_EXYNOS_GEM_MMAP		0x02
+/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
+#define DRM_EXYNOS_PLANE_SET_ZPOS	0x06
+
+#define DRM_IOCTL_EXYNOS_GEM_CREATE		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
+
+#define DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_GEM_MAP_OFFSET, struct drm_exynos_gem_map_off)
+
+#define DRM_IOCTL_EXYNOS_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_GEM_MMAP, struct drm_exynos_gem_mmap)
+
+#define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos)
+
+#ifdef __KERNEL__
+
+/**
+ * A structure for lcd panel information.
+ *
+ * @timing: default video mode for initializing
+ * @width_mm: physical size of lcd width.
+ * @height_mm: physical size of lcd height.
+ */
+struct exynos_drm_panel_info {
+	struct fb_videomode timing;
+	u32 width_mm;
+	u32 height_mm;
+};
+
+/**
+ * Platform Specific Structure for DRM based FIMD.
+ *
+ * @panel: default panel info for initializing
+ * @default_win: default window layer number to be used for UI.
+ * @bpp: default bit per pixel.
+ */
+struct exynos_drm_fimd_pdata {
+	struct exynos_drm_panel_info panel;
+	u32				vidcon0;
+	u32				vidcon1;
+	unsigned int			default_win;
+	unsigned int			bpp;
+};
+
+/**
+ * Platform Specific Structure for DRM based HDMI.
+ *
+ * @hdmi_dev: device point to specific hdmi driver.
+ * @mixer_dev: device point to specific mixer driver.
+ *
+ * this structure is used for common hdmi driver and each device object
+ * would be used to access specific device driver(hdmi or mixer driver)
+ */
+struct exynos_drm_common_hdmi_pd {
+	struct device *hdmi_dev;
+	struct device *mixer_dev;
+};
+
+/**
+ * Platform Specific Structure for DRM based HDMI core.
+ *
+ * @timing: default video mode for initializing
+ * @default_win: default window layer number to be used for UI.
+ * @bpp: default bit per pixel.
+ */
+struct exynos_drm_hdmi_pdata {
+	struct fb_videomode		timing;
+	unsigned int			default_win;
+	unsigned int			bpp;
+};
+
+#endif	/* __KERNEL__ */
+#endif	/* _EXYNOS_DRM_H_ */
diff --git a/tests/exynos/Makefile.am b/tests/exynos/Makefile.am
index e69de29..df4f84f 100644
--- a/tests/exynos/Makefile.am
+++ b/tests/exynos/Makefile.am
@@ -0,0 +1,15 @@
+AM_CFLAGS = \
+	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/libkms/ \
+	-I$(top_srcdir) \
+	$(CAIRO_CFLAGS)
+
+noinst_PROGRAMS = \
+	exynosdrmtest
+
+exynosdrmtest_SOURCES = \
+	exynosdrmtest.c
+exynosdrmtest_LDADD = \
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/libkms/libkms.la \
+	$(CAIRO_LIBS)
diff --git a/tests/exynos/exynosdrmtest.c b/tests/exynos/exynosdrmtest.c
new file mode 100644
index 0000000..5181117
--- /dev/null
+++ b/tests/exynos/exynosdrmtest.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright © 2011 Red Hat
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *	Prathyush K <prathyush.k@samsung.com>
+ */
+
+/*
+ * This test program is similar to the modetest program. It tests the exynos
+ * specific drm gem ioctls and mmaps. It also tests the exynos drm prime
+ * module for exporting gem handle to fd and importing fd to gem handle.
+ *
+ * MMAP:
+ * There are three ways to map a exynos gem object to user space.
+ * 1> mmap using ioctl DRM_IOCTL_EXYNOS_GEM_MMAP
+ * 2> mmap of offset.
+ * 	This is done by first mapping the offset using ioctl
+ * 	DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET and then calling mmap with this offset
+ * 3> mmap of fd.
+ * 	This is done by first exporting the gem object to dmabuf FD and
+ * 	then calling mmap on this fd.
+ *
+ * DMABUF:
+ * A gem object can be exported to a FD by caling ioctl
+ * DRM_IOCTL_PRIME_HANDLE_TO_FD. This will return a FD.
+ * A dmabuf FD can be imported to create a new gem object. This is done using
+ * the ioctl DRM_IOCTL_PRIME_FD_TO_HANDLE.
+ *
+ * These 3 different mmaps are tested in this program as well as the two
+ * ioctls which test handle to fd and fd to handle. The new gem object
+ * created from FD is also mapped to user space using ioctl and mmap.
+ */
+
+#include "config.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+#include "drm_fourcc.h"
+#include "libkms.h"
+#include <sys/mman.h>
+#include "drm.h"
+#include "exynos_drm.h"
+
+drmModeRes *resources;
+int fd;
+static char optstr[] = "ecpmfs:P:v";
+
+/*
+ * Mode setting with the kernel interfaces is a bit of a chore.
+ * First you have to find the connector in question and make sure the
+ * requested mode is available.
+ * Then you need to find the encoder attached to that connector so you
+ * can bind it with a free crtc.
+ */
+struct connector {
+	uint32_t id;
+	char mode_str[64];
+	drmModeModeInfo *mode;
+	drmModeEncoder *encoder;
+	int crtc;
+	int pipe;
+	unsigned int fb_id[2], current_fb_id;
+	struct timeval start;
+
+	int swap_count;
+};
+
+static void
+connector_find_mode(struct connector *c)
+{
+	drmModeConnector *connector;
+	int i, j;
+
+	/* First, find the connector & mode */
+	c->mode = NULL;
+	for (i = 0; i < resources->count_connectors; i++) {
+		connector = drmModeGetConnector(fd, resources->connectors[i]);
+
+		if (!connector) {
+			fprintf(stderr, "could not get connector %i: %s\n",
+				resources->connectors[i], strerror(errno));
+			drmModeFreeConnector(connector);
+			continue;
+		}
+
+		if (!connector->count_modes) {
+			drmModeFreeConnector(connector);
+			continue;
+		}
+
+		if (connector->connector_id != c->id) {
+			drmModeFreeConnector(connector);
+			continue;
+		}
+
+		for (j = 0; j < connector->count_modes; j++) {
+			c->mode = &connector->modes[j];
+			if (!strcmp(c->mode->name, c->mode_str))
+				break;
+		}
+
+		/* Found it, break out */
+		if (c->mode)
+			break;
+
+		drmModeFreeConnector(connector);
+	}
+
+	if (!c->mode) {
+		fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
+		return;
+	}
+
+	/* Now get the encoder */
+	for (i = 0; i < resources->count_encoders; i++) {
+		c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
+
+		if (!c->encoder) {
+			fprintf(stderr, "could not get encoder %i: %s\n",
+				resources->encoders[i], strerror(errno));
+			drmModeFreeEncoder(c->encoder);
+			continue;
+		}
+
+		if (c->encoder->encoder_id  == connector->encoder_id)
+			break;
+
+		drmModeFreeEncoder(c->encoder);
+	}
+
+	if (c->crtc == -1)
+		c->crtc = c->encoder->crtc_id;
+
+	/* and figure out which crtc index it is: */
+	for (i = 0; i < resources->count_crtcs; i++) {
+		if (c->crtc == resources->crtcs[i]) {
+			c->pipe = i;
+			break;
+		}
+	}
+
+}
+
+static void
+set_mode(struct connector *c, int count)
+{
+	unsigned int fb_id;
+	int ret, size, width, height, i;
+	unsigned handle;
+	void *usr_addr;
+
+	struct drm_exynos_gem_create gem;
+	struct drm_exynos_gem_map_off map_off;
+	struct drm_exynos_gem_mmap mmapbuf;
+	struct drm_prime_handle prime;
+
+	width = 0;
+	height = 0;
+	for (i = 0; i < count; i++) {
+		connector_find_mode(&c[i]);
+		if (c[i].mode == NULL)
+			continue;
+		width += c[i].mode->hdisplay;
+		if (height < c[i].mode->vdisplay)
+			height = c[i].mode->vdisplay;
+	}
+
+	size = width*height*4;
+
+	/* Allocate GEM object */
+	gem.size = size;
+	ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &gem);
+	if (ret < 0) {
+		printf("Failed to create gem object\n");
+		return;
+	}
+
+	/* The allocated buffer is split into 5 parts and each part
+	 * is memset using a different mapping to user space.
+	 * 1> mmap using ioctl
+	 * 2> mmap using mapped offset
+	 * 3> mmap using dmabuf fd
+	 * 4> mmap using ioctl on gem object created from fd
+	 * 5> mmap using mapped offset of gem object created from fd
+	 */
+
+	/* Map gem object to user space: mmap ioctl*/
+	mmapbuf.handle = gem.handle;
+	mmapbuf.size = gem.size;
+	ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MMAP, &mmapbuf);
+	if (ret < 0) {
+		printf("Failed to mmap gem object\n");
+		return;
+	}
+	usr_addr = (void *)(unsigned long)mmapbuf.mapped;
+
+	printf("vAddr of gem(%x) using mmap ioctl is: %x\n",
+			gem.handle, usr_addr);
+
+	/* Memset first 1/5th of the buffer*/
+	if (usr_addr)
+		memset(usr_addr, 0xFF, size/5);
+
+	/* Map gem object to user space: mmap*/
+	map_off.offset = 0;
+	map_off.handle = gem.handle;
+	ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET, &map_off);
+	if (ret < 0) {
+		printf("Failed to map offset\n");
+		return;
+	}
+
+	usr_addr = mmap(0, gem.size, PROT_READ | PROT_WRITE,
+			MAP_SHARED, fd, map_off.offset);
+
+	printf("vAddr of gem(%x) using mmap of offset(%x) is: %x\n",
+			gem.handle, (unsigned long)map_off.offset, usr_addr);
+
+	/* Memset second 1/5th of the buffer*/
+	if (usr_addr)
+		memset(usr_addr+size/5, 0x44, size/5);
+
+	/* GEM object to FD using DRM PRIME interface*/
+	memset(&prime, 0, sizeof(struct drm_prime_handle));
+	prime.handle = gem.handle;
+	ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime);
+	if (ret < 0) {
+		printf("Failed to get FD from prime handle\n");
+		return;
+	}
+	printf("Prime Handle: %x to FD: %d\n", prime.handle, prime.fd);
+
+	/* Map gem object to user space: mmap FD*/
+	usr_addr = mmap(0, size, PROT_READ|PROT_WRITE,
+		MAP_SHARED, prime.fd, 0);
+	printf("vAddr using mmap of FD(%x) is: %x\n", prime.fd, usr_addr);
+
+	/* Memset third 1/5th of the buffer*/
+	if (usr_addr)
+		memset(usr_addr+2*size/5, 0x88, size/5);
+	else {
+		printf("Failed to mmap FD(%x)\n", prime.fd);
+		return;
+	}
+
+	/* FD to GEM object using DRM PRIME interface*/
+	prime.handle = 0;
+	ret = ioctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime);
+	if (ret < 0) {
+		printf("Failed to get prime handle from FD\n");
+		return;
+	}
+	printf("FD: %x to Prime Handle: %x\n", prime.fd, prime.handle);
+
+	/* Map gem object to user space: mmap ioctl*/
+	mmapbuf.handle = prime.handle;
+	mmapbuf.size = size;
+	ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MMAP, &mmapbuf);
+	if (ret < 0) {
+		printf("Failed to mmap gem object\n");
+		return;
+	}
+	usr_addr = (void *)(unsigned long)mmapbuf.mapped;
+
+	printf("vAddr of gem(%x) using mmap ioctl is: %x\n",
+			prime.handle, usr_addr);
+
+	/* Memset fourth 1/5th of the buffer*/
+	if (usr_addr)
+		memset(usr_addr+3*size/5, 0xBB, size/5);
+
+	/* Map gem object to user space: mmap*/
+	map_off.handle = prime.handle;
+	ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET, &map_off);
+	if (ret < 0) {
+		printf("Failed to map offset\n");
+		return;
+	}
+
+	usr_addr = mmap(0, gem.size, PROT_READ | PROT_WRITE,
+				MAP_SHARED, fd, map_off.offset);
+
+	printf("vAddr of gem(%x) using mmap of offset(%x) is: %x\n",
+			prime.handle, (unsigned long)map_off.offset, usr_addr);
+
+	/* Memset last 1/5th of the buffer*/
+	if (usr_addr)
+		memset(usr_addr+4*size/5, 0xFF, size/5);
+
+	/* Create FB from gem object*/
+	/* The framebuffer will contain 5 bands from each memset*/
+	ret = drmModeAddFB(fd, width, height, 32, 32, 0, gem.handle, &fb_id);
+	if (ret) {
+		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
+		return;
+	}
+
+	/* Set FB to Crtc*/
+	printf("setting mode %s on connector %d, crtc %d\n",
+					c[0].mode_str, c[0].id, c[0].crtc);
+	ret = drmModeSetCrtc(fd, c[0].crtc, fb_id,
+					0, 0, &c[0].id, 1, c[0].mode);
+	if (ret)
+		fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
+}
+
+void usage(char *name)
+{
+	fprintf(stderr, "usage: %s [-ecpmf]\n", name);
+	fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
+	exit(0);
+}
+
+int main(int argc, char **argv)
+{
+	int c;
+	int count = 0;
+	struct connector con_args[2];
+
+	opterr = 0;
+	while ((c = getopt(argc, argv, optstr)) != -1) {
+		switch (c) {
+		case 's':
+			con_args[count].crtc = -1;
+			if (sscanf(optarg, "%d:%64s",
+				   &con_args[count].id,
+				   con_args[count].mode_str) != 2 &&
+			    sscanf(optarg, "%d@%d:%64s",
+				   &con_args[count].id,
+				   &con_args[count].crtc,
+				   con_args[count].mode_str) != 3)
+				usage(argv[0]);
+			count++;
+			break;
+		default:
+			usage(argv[0]);
+			break;
+		}
+	}
+
+	fd = drmOpen("exynos", NULL);
+	if (fd < 0) {
+		printf("Failed to open drm device\n");
+		return 0;
+	}
+
+	resources = drmModeGetResources(fd);
+	if (!resources) {
+		fprintf(stderr, "drmModeGetResources failed: %s\n",
+			strerror(errno));
+		drmClose(fd);
+		return 1;
+	}
+
+	if (count > 0) {
+		set_mode(con_args, count);
+		getchar();
+	}
+
+	drmModeFreeResources(resources);
+
+	return 0;
+}