summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--exynos/exynos_drm.h201
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/ipptest/Makefile.am19
-rw-r--r--tests/ipptest/fimc.c593
-rw-r--r--tests/ipptest/fimc.h11
-rw-r--r--tests/ipptest/fimctest.c477
-rw-r--r--tests/ipptest/fimctest.h27
-rw-r--r--tests/ipptest/gem.c72
-rw-r--r--tests/ipptest/gem.h8
-rw-r--r--tests/ipptest/util.c132
-rw-r--r--tests/ipptest/util.h12
-rw-r--r--tests/rottest/Makefile.am19
-rw-r--r--tests/rottest/gem.c72
-rw-r--r--tests/rottest/gem.h8
-rw-r--r--tests/rottest/rotator.c605
-rw-r--r--tests/rottest/rotator.h9
-rw-r--r--tests/rottest/rottest.c464
-rw-r--r--tests/rottest/rottest.h26
-rw-r--r--tests/rottest/util.c140
-rw-r--r--tests/rottest/util.h13
21 files changed, 2914 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 773f7d2f..1563a9ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -558,6 +558,10 @@ AC_CONFIG_FILES([
tests/nouveau/Makefile
tests/etnaviv/Makefile
tests/util/Makefile
+ tests/ipptest/Makefile
+ tests/rottest/Makefile
+ include/Makefile
+ include/drm/Makefile
man/Makefile
libdrm.pc])
AC_OUTPUT
diff --git a/exynos/exynos_drm.h b/exynos/exynos_drm.h
index c3af0ac5..0a2d0d2e 100644
--- a/exynos/exynos_drm.h
+++ b/exynos/exynos_drm.h
@@ -131,6 +131,181 @@ struct drm_exynos_g2d_exec {
__u64 async;
};
+/* definition of operations types */
+enum drm_exynos_ops_id {
+ EXYNOS_DRM_OPS_SRC,
+ EXYNOS_DRM_OPS_DST,
+ EXYNOS_DRM_OPS_MAX,
+};
+
+/* definition of size */
+struct drm_exynos_sz {
+ __u32 hsize;
+ __u32 vsize;
+};
+
+/* definition of position */
+struct drm_exynos_pos {
+ __u32 x;
+ __u32 y;
+ __u32 w;
+ __u32 h;
+};
+
+/* definition of flip */
+enum drm_exynos_flip {
+ EXYNOS_DRM_FLIP_NONE = (0 << 0),
+ EXYNOS_DRM_FLIP_VERTICAL = (1 << 0),
+ EXYNOS_DRM_FLIP_HORIZONTAL = (1 << 1),
+ EXYNOS_DRM_FLIP_BOTH = EXYNOS_DRM_FLIP_VERTICAL |
+ EXYNOS_DRM_FLIP_HORIZONTAL,
+};
+
+/* definition of rotation degree */
+enum drm_exynos_degree {
+ EXYNOS_DRM_DEGREE_0,
+ EXYNOS_DRM_DEGREE_90,
+ EXYNOS_DRM_DEGREE_180,
+ EXYNOS_DRM_DEGREE_270,
+};
+
+/* definition of planar */
+enum drm_exynos_planer {
+ EXYNOS_DRM_PLANAR_Y,
+ EXYNOS_DRM_PLANAR_CB,
+ EXYNOS_DRM_PLANAR_CR,
+ EXYNOS_DRM_PLANAR_MAX,
+};
+
+/**
+ * A structure for ipp supported property list.
+ *
+ * @version: version of this structure.
+ * @ipp_id: id of ipp driver.
+ * @count: count of ipp driver.
+ * @writeback: flag of writeback supporting.
+ * @flip: flag of flip supporting.
+ * @degree: flag of degree information.
+ * @csc: flag of csc supporting.
+ * @crop: flag of crop supporting.
+ * @scale: flag of scale supporting.
+ * @refresh_min: min hz of refresh.
+ * @refresh_max: max hz of refresh.
+ * @crop_min: crop min resolution.
+ * @crop_max: crop max resolution.
+ * @scale_min: scale min resolution.
+ * @scale_max: scale max resolution.
+ */
+struct drm_exynos_ipp_prop_list {
+ __u32 version;
+ __u32 ipp_id;
+ __u32 count;
+ __u32 writeback;
+ __u32 flip;
+ __u32 degree;
+ __u32 csc;
+ __u32 crop;
+ __u32 scale;
+ __u32 refresh_min;
+ __u32 refresh_max;
+ __u32 reserved;
+ struct drm_exynos_sz crop_min;
+ struct drm_exynos_sz crop_max;
+ struct drm_exynos_sz scale_min;
+ struct drm_exynos_sz scale_max;
+};
+
+/**
+ * A structure for ipp config.
+ *
+ * @ops_id: property of operation directions.
+ * @flip: property of mirror, flip.
+ * @degree: property of rotation degree.
+ * @fmt: property of image format.
+ * @sz: property of image size.
+ * @pos: property of image position(src-cropped,dst-scaler).
+ */
+struct drm_exynos_ipp_config {
+ enum drm_exynos_ops_id ops_id;
+ enum drm_exynos_flip flip;
+ enum drm_exynos_degree degree;
+ __u32 fmt;
+ struct drm_exynos_sz sz;
+ struct drm_exynos_pos pos;
+};
+
+/* definition of command */
+enum drm_exynos_ipp_cmd {
+ IPP_CMD_NONE,
+ IPP_CMD_M2M,
+ IPP_CMD_WB,
+ IPP_CMD_OUTPUT,
+ IPP_CMD_MAX,
+};
+
+/**
+ * A structure for ipp property.
+ *
+ * @config: source, destination config.
+ * @cmd: definition of command.
+ * @ipp_id: id of ipp driver.
+ * @prop_id: id of property.
+ * @refresh_rate: refresh rate.
+ */
+struct drm_exynos_ipp_property {
+ struct drm_exynos_ipp_config config[EXYNOS_DRM_OPS_MAX];
+ enum drm_exynos_ipp_cmd cmd;
+ __u32 ipp_id;
+ __u32 prop_id;
+ __u32 refresh_rate;
+};
+
+/* definition of buffer */
+enum drm_exynos_ipp_buf_type {
+ IPP_BUF_ENQUEUE,
+ IPP_BUF_DEQUEUE,
+};
+
+/**
+ * A structure for ipp buffer operations.
+ *
+ * @ops_id: operation directions.
+ * @buf_type: definition of buffer.
+ * @prop_id: id of property.
+ * @buf_id: id of buffer.
+ * @handle: Y, Cb, Cr each planar handle.
+ * @user_data: user data.
+ */
+struct drm_exynos_ipp_queue_buf {
+ enum drm_exynos_ops_id ops_id;
+ enum drm_exynos_ipp_buf_type buf_type;
+ __u32 prop_id;
+ __u32 buf_id;
+ __u32 handle[EXYNOS_DRM_PLANAR_MAX];
+ __u32 reserved;
+ __u64 user_data;
+};
+
+/* definition of control */
+enum drm_exynos_ipp_ctrl {
+ IPP_CTRL_PLAY,
+ IPP_CTRL_STOP,
+ IPP_CTRL_PAUSE,
+ IPP_CTRL_RESUME,
+ IPP_CTRL_MAX,
+};
+
+/**
+ * A structure for ipp start/stop operations.
+ *
+ * @prop_id: id of property.
+ * @ctrl: definition of control.
+ */
+struct drm_exynos_ipp_cmd_ctrl {
+ __u32 prop_id;
+ enum drm_exynos_ipp_ctrl ctrl;
+};
+
#define DRM_EXYNOS_GEM_CREATE 0x00
/* Reserved 0x04 ~ 0x05 for exynos specific gem ioctl */
#define DRM_EXYNOS_GEM_GET 0x04
@@ -141,6 +316,12 @@ struct drm_exynos_g2d_exec {
#define DRM_EXYNOS_G2D_SET_CMDLIST 0x21
#define DRM_EXYNOS_G2D_EXEC 0x22
+/* IPP - Image Post Processing */
+#define DRM_EXYNOS_IPP_GET_PROPERTY 0x30
+#define DRM_EXYNOS_IPP_SET_PROPERTY 0x31
+#define DRM_EXYNOS_IPP_QUEUE_BUF 0x32
+#define DRM_EXYNOS_IPP_CMD_CTRL 0x33
+
#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -157,8 +338,18 @@ struct drm_exynos_g2d_exec {
#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
+#define DRM_IOCTL_EXYNOS_IPP_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_IPP_GET_PROPERTY, struct drm_exynos_ipp_prop_list)
+#define DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_IPP_SET_PROPERTY, struct drm_exynos_ipp_property)
+#define DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_IPP_QUEUE_BUF, struct drm_exynos_ipp_queue_buf)
+#define DRM_IOCTL_EXYNOS_IPP_CMD_CTRL DRM_IOWR(DRM_COMMAND_BASE + \
+ DRM_EXYNOS_IPP_CMD_CTRL, struct drm_exynos_ipp_cmd_ctrl)
+
/* EXYNOS specific events */
#define DRM_EXYNOS_G2D_EVENT 0x80000000
+#define DRM_EXYNOS_IPP_EVENT 0x80000001
struct drm_exynos_g2d_event {
struct drm_event base;
@@ -169,4 +360,14 @@ struct drm_exynos_g2d_event {
__u32 reserved;
};
+struct drm_exynos_ipp_event {
+ struct drm_event base;
+ __u64 user_data;
+ __u32 tv_sec;
+ __u32 tv_usec;
+ __u32 prop_id;
+ __u32 reserved;
+ __u32 buf_id[EXYNOS_DRM_OPS_MAX];
+};
+
#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0355a925..badf8b4e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -16,6 +16,8 @@ endif
if HAVE_EXYNOS
SUBDIRS += exynos
+SUBDIRS += ipptest
+SUBDIRS += rottest
endif
if HAVE_TEGRA
diff --git a/tests/ipptest/Makefile.am b/tests/ipptest/Makefile.am
new file mode 100644
index 00000000..406a2931
--- /dev/null
+++ b/tests/ipptest/Makefile.am
@@ -0,0 +1,19 @@
+AM_CFLAGS = \
+ -I$(top_srcdir)/include/drm \
+ -I$(top_srcdir)/libkms/ \
+ -I$(top_srcdir)/exynos/ \
+ -I$(top_srcdir)
+
+noinst_PROGRAMS = \
+ ipptest
+
+ipptest_SOURCES = \
+ fimctest.c \
+ gem.c \
+ util.c \
+ fimc.c
+
+ipptest_LDADD = \
+ $(top_builddir)/libdrm.la \
+ $(top_builddir)/libkms/libkms.la \
+ $(top_builddir)/exynos/libdrm_exynos.la
diff --git a/tests/ipptest/fimc.c b/tests/ipptest/fimc.c
new file mode 100644
index 00000000..f48c7936
--- /dev/null
+++ b/tests/ipptest/fimc.c
@@ -0,0 +1,593 @@
+/*
+ * DRM based fimc test program
+ * Copyright 2012 Samsung Electronics
+ * Eunchul Kim <chulspro.kim@sasmsung.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 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include "exynos_drm.h"
+#include "fimctest.h"
+#include "gem.h"
+#include "util.h"
+
+#include "drm_fourcc.h"
+
+static int exynos_drm_ipp_set_property(int fd,
+ struct drm_exynos_ipp_property *property,
+ struct drm_exynos_sz *def_sz,
+ enum drm_exynos_ipp_cmd cmd,
+ enum drm_exynos_degree degree)
+{
+ struct drm_exynos_pos crop_pos = {0, 0, def_sz->hsize, def_sz->vsize};
+ struct drm_exynos_pos scale_pos = {0, 0, def_sz->hsize, def_sz->vsize};
+ struct drm_exynos_sz src_sz = {def_sz->hsize, def_sz->vsize};
+ struct drm_exynos_sz dst_sz = {def_sz->hsize, def_sz->vsize};
+ int ret = 0;
+
+ memset(property, 0x00, sizeof(struct drm_exynos_ipp_property));
+ property->cmd = cmd;
+
+ switch(cmd) {
+ case IPP_CMD_M2M:
+ property->config[EXYNOS_DRM_OPS_SRC].ops_id = EXYNOS_DRM_OPS_SRC;
+ property->config[EXYNOS_DRM_OPS_SRC].flip = EXYNOS_DRM_FLIP_NONE;
+ property->config[EXYNOS_DRM_OPS_SRC].degree = EXYNOS_DRM_DEGREE_0;
+ property->config[EXYNOS_DRM_OPS_SRC].fmt = DRM_FORMAT_XRGB8888;
+ property->config[EXYNOS_DRM_OPS_SRC].pos = crop_pos;
+ property->config[EXYNOS_DRM_OPS_SRC].sz = src_sz;
+
+ property->config[EXYNOS_DRM_OPS_DST].ops_id = EXYNOS_DRM_OPS_DST;
+ property->config[EXYNOS_DRM_OPS_DST].flip = EXYNOS_DRM_FLIP_NONE;
+ property->config[EXYNOS_DRM_OPS_DST].degree = degree;
+ property->config[EXYNOS_DRM_OPS_DST].fmt = DRM_FORMAT_XRGB8888;
+ if (property->config[EXYNOS_DRM_OPS_DST].degree == EXYNOS_DRM_DEGREE_90) {
+ dst_sz.hsize = def_sz->vsize;
+ dst_sz.vsize = def_sz->hsize;
+
+ scale_pos.w = def_sz->vsize;
+ scale_pos.h = def_sz->hsize;
+ }
+ property->config[EXYNOS_DRM_OPS_DST].pos = scale_pos;
+ property->config[EXYNOS_DRM_OPS_DST].sz = dst_sz;
+ break;
+ case IPP_CMD_WB:
+ property->config[EXYNOS_DRM_OPS_SRC].ops_id = EXYNOS_DRM_OPS_SRC;
+ property->config[EXYNOS_DRM_OPS_SRC].flip = EXYNOS_DRM_FLIP_NONE;
+ property->config[EXYNOS_DRM_OPS_SRC].degree = EXYNOS_DRM_DEGREE_0;
+ property->config[EXYNOS_DRM_OPS_SRC].fmt = DRM_FORMAT_YUV444;
+ if (property->config[EXYNOS_DRM_OPS_SRC].degree == EXYNOS_DRM_DEGREE_90) {
+ src_sz.hsize = def_sz->vsize;
+ src_sz.vsize = def_sz->hsize;
+
+ crop_pos.w = def_sz->vsize;
+ crop_pos.h = def_sz->hsize;
+ }
+ property->config[EXYNOS_DRM_OPS_SRC].pos = crop_pos;
+ property->config[EXYNOS_DRM_OPS_SRC].sz = src_sz;
+
+ property->config[EXYNOS_DRM_OPS_DST].ops_id = EXYNOS_DRM_OPS_DST;
+ property->config[EXYNOS_DRM_OPS_DST].flip = EXYNOS_DRM_FLIP_NONE;
+ property->config[EXYNOS_DRM_OPS_DST].degree = degree;
+ property->config[EXYNOS_DRM_OPS_DST].fmt = DRM_FORMAT_XRGB8888;
+ if (property->config[EXYNOS_DRM_OPS_DST].degree == EXYNOS_DRM_DEGREE_90) {
+ dst_sz.hsize = def_sz->vsize;
+ dst_sz.vsize = def_sz->hsize;
+
+ scale_pos.w = def_sz->vsize;
+ scale_pos.h = def_sz->hsize;
+ }
+ property->config[EXYNOS_DRM_OPS_DST].pos = scale_pos;
+ property->config[EXYNOS_DRM_OPS_DST].sz = dst_sz;
+ break;
+ case IPP_CMD_OUTPUT:
+ default:
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY, property);
+ if (ret)
+ fprintf(stderr,
+ "failed to DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY : %s\n",
+ strerror(errno));
+
+ printf("DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY : prop_id[%d]\n",
+ property->prop_id);
+
+ return ret;
+}
+
+static int exynos_drm_ipp_queue_buf(int fd, struct drm_exynos_ipp_queue_buf *qbuf,
+ enum drm_exynos_ops_id ops_id,
+ enum drm_exynos_ipp_buf_type buf_type,
+ int prop_id,
+ int buf_id,
+ unsigned int gem_handle)
+{
+ int ret = 0;
+
+ memset(qbuf, 0x00, sizeof(struct drm_exynos_ipp_queue_buf));
+
+ qbuf->ops_id = ops_id;
+ qbuf->buf_type = buf_type;
+ qbuf->user_data = 0;
+ qbuf->prop_id = prop_id;
+ qbuf->buf_id = buf_id;
+ qbuf->handle[EXYNOS_DRM_PLANAR_Y] = gem_handle;
+ qbuf->handle[EXYNOS_DRM_PLANAR_CB] = 0;
+ qbuf->handle[EXYNOS_DRM_PLANAR_CR] = 0;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, qbuf);
+ if (ret)
+ fprintf(stderr,
+ "failed to DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF[id:%d][buf_type:%d] : %s\n",
+ ops_id, buf_type, strerror(errno));
+
+ return ret;
+}
+
+static int exynos_drm_ipp_cmd_ctrl(int fd, struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl,
+ int prop_id,
+ enum drm_exynos_ipp_ctrl ctrl)
+{
+ int ret = 0;
+
+ memset(cmd_ctrl, 0x00, sizeof(struct drm_exynos_ipp_cmd_ctrl));
+
+ cmd_ctrl->prop_id = prop_id;
+ cmd_ctrl->ctrl = ctrl;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_IPP_CMD_CTRL, cmd_ctrl);
+ if (ret)
+ fprintf(stderr,
+ "failed to DRM_IOCTL_EXYNOS_IPP_CMD_CTRL[prop_id:%d][ctrl:%d] : %s\n",
+ prop_id, ctrl, strerror(errno));
+
+ return ret;
+}
+
+int fimc_event_handler(struct drm_exynos_ipp_queue_buf *src_qbuf, struct drm_exynos_gem_create *src_gem,
+ struct drm_exynos_ipp_queue_buf *dst_qbuf, struct drm_exynos_gem_create *dst_gem,
+ struct drm_exynos_ipp_property *property, void **usr_addr,
+ unsigned int width, unsigned int height, enum drm_exynos_ipp_cmd cmd)
+{
+ char buffer[1024];
+ int len, i;
+ struct drm_event *e;
+ struct drm_exynos_ipp_event *ipp_event;
+ char filename[100];
+ int ret = 0;
+ int src_buf_id, dst_buf_id;
+ static bmp_idx = 0;
+
+ len = read(fd, buffer, sizeof buffer);
+ if (len == 0)
+ return 0;
+ if (len < sizeof *e)
+ return -1;
+
+ i = 0;
+ while (i < len) {
+ e = (struct drm_event *) &buffer[i];
+ switch (e->type) {
+ case DRM_EXYNOS_IPP_EVENT:
+ ipp_event = (struct drm_exynos_ipp_event *) e;
+ src_buf_id = ipp_event->buf_id[EXYNOS_DRM_OPS_SRC];
+ dst_buf_id = ipp_event->buf_id[EXYNOS_DRM_OPS_DST];
+
+ printf("%s:src_buf_id[%d]dst_buf_id[%d]bmp_idx[%d]\n", __func__, src_buf_id, dst_buf_id, bmp_idx++);
+ if (cmd == IPP_CMD_M2M) {
+ sprintf(filename, "/opt/media/fimc_m2m_dst%d.bmp", bmp_idx);
+
+ util_write_bmp(filename, usr_addr[dst_buf_id], width, height);
+
+ /* For source buffer queue to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &src_qbuf[src_buf_id], EXYNOS_DRM_OPS_SRC,
+ IPP_BUF_ENQUEUE, property->prop_id,
+ src_buf_id, src_gem[src_buf_id].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf src queue\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ /* For destination buffer queue to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &dst_qbuf[dst_buf_id], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_ENQUEUE, property->prop_id,
+ dst_buf_id, dst_gem[dst_buf_id].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf dst queue\n");
+ goto err_ipp_ctrl_close;
+ }
+ } else if (cmd == IPP_CMD_WB) {
+ sprintf(filename, "/opt/media/fimc_wb_%d.bmp", bmp_idx);
+
+ util_write_bmp(filename, usr_addr[dst_buf_id], width, height);
+
+ /* For destination buffer queue to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &dst_qbuf[dst_buf_id], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_ENQUEUE, property->prop_id,
+ dst_buf_id, dst_gem[dst_buf_id].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf dst queue\n");
+ goto err_ipp_ctrl_close;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ i += e->length;
+ }
+
+err_ipp_ctrl_close:
+ return ret;
+}
+
+void fimc_m2m_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec)
+{
+ struct drm_exynos_ipp_property property;
+ struct drm_exynos_ipp_cmd_ctrl cmd_ctrl;
+ struct drm_exynos_sz def_sz = {720, 1280};
+ struct drm_exynos_ipp_queue_buf qbuf1[MAX_BUF], qbuf2[MAX_BUF];
+ unsigned int width=720, height=1280, stride;
+ int ret, i, j, x;
+ struct drm_exynos_gem_create gem1[MAX_BUF], gem2[MAX_BUF];
+ struct drm_exynos_gem_mmap mmap1[MAX_BUF], mmap2[MAX_BUF];
+ void *usr_addr1[MAX_BUF], *usr_addr2[MAX_BUF];
+ struct timeval begin, end;
+ struct drm_gem_close args;
+ char filename[100];
+
+ /* For mode */
+ width = 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;
+ }
+ stride = width * 4;
+ i =0;
+
+ def_sz.hsize = width;
+ def_sz.vsize = height;
+
+ /* For property */
+ ret = exynos_drm_ipp_set_property(fd, &property, &def_sz, IPP_CMD_M2M, EXYNOS_DRM_DEGREE_90);
+ if (ret) {
+ fprintf(stderr, "failed to ipp property\n");
+ return;
+ }
+
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For source buffer */
+ ret = util_gem_create_mmap(fd, &gem1[i], &mmap1[i], stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %s\n",
+ strerror(errno));
+
+ if (ret == -1)
+ return;
+ else if (ret == -2)
+ goto err_ipp_ctrl_close;
+ }
+ usr_addr1[i] = (void *)(unsigned long)mmap1[i].mapped;
+
+ /* For source buffer map to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf1[i], EXYNOS_DRM_OPS_SRC,
+ IPP_BUF_ENQUEUE, property.prop_id, i, gem1[i].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf src map\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ util_draw_buffer(usr_addr1[i], 1, width, height, stride, 0);
+ sprintf(filename, "/opt/media/fimc_m2m_org_src%d.bmp", j);
+ util_write_bmp(filename, usr_addr1[i], width, height);
+ }
+
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For destination buffer */
+ ret = util_gem_create_mmap(fd, &gem2[i], &mmap2[i], stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %s\n",
+ strerror(errno));
+ if (ret == -1)
+ goto err_ipp_ctrl_close;
+ else if (ret == -2)
+ goto err_ipp_ctrl_close;
+ }
+ usr_addr2[i] = (void*)(unsigned long)mmap2[i].mapped;
+
+ /* For destination buffer map to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf2[i], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_ENQUEUE, property.prop_id, i, gem2[i].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf dst map\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ util_draw_buffer(usr_addr2[i], 0, 0, 0, 0, mmap2[i].size);
+ sprintf(filename, "/opt/media/fimc_m2m_org_dst%d.bmp", j);
+ util_write_bmp(filename, usr_addr2[i], height, width);
+ }
+
+ /* Start */
+ gettimeofday(&begin, NULL);
+ ret = exynos_drm_ipp_cmd_ctrl(fd, &cmd_ctrl, property.prop_id, IPP_CTRL_PLAY);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp ctrl IPP_CMD_M2M start\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ j=0;
+ while (1) {
+ struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(fd, &fds);
+ ret = select(fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ fprintf(stderr, "select timed out or error.\n");
+ continue;
+ } else if (FD_ISSET(0, &fds)) {
+ break;
+ }
+
+ gettimeofday(&end, NULL);
+ usec[j] = (end.tv_sec - begin.tv_sec) * 1000000 +
+ (end.tv_usec - begin.tv_usec);
+
+ if (property.config[EXYNOS_DRM_OPS_DST].degree == EXYNOS_DRM_DEGREE_90 ||
+ property.config[EXYNOS_DRM_OPS_DST].degree == EXYNOS_DRM_DEGREE_270) {
+ if(fimc_event_handler(qbuf1, gem1, qbuf2, gem2, &property, usr_addr2, height, width, IPP_CMD_M2M) < 0)
+ break;
+ } else {
+ if(fimc_event_handler(qbuf1, gem1, qbuf2, gem2, &property, usr_addr2, width, height, IPP_CMD_M2M) < 0)
+ break;
+ }
+
+ if (++j > MAX_LOOP)
+ break;
+
+ gettimeofday(&begin, NULL);
+ }
+
+err_ipp_ctrl_close:
+ /* For source buffer dequeue to IPP */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf1[i], EXYNOS_DRM_OPS_SRC,
+ IPP_BUF_DEQUEUE, property.prop_id, i, gem1[i].handle);
+ if (ret < 0)
+ fprintf(stderr, "failed to ipp buf dst dequeue\n");
+ }
+
+ /* For destination buffer dequeue to IPP */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf2[i], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_DEQUEUE, property.prop_id, i, gem2[i].handle);
+ if (ret < 0)
+ fprintf(stderr, "failed to ipp buf dst dequeue\n");
+ }
+
+ /* Stop */
+ ret = exynos_drm_ipp_cmd_ctrl(fd, &cmd_ctrl, property.prop_id, IPP_CTRL_STOP);
+ if (ret)
+ fprintf(stderr, "failed to ipp ctrl IPP_CMD_WB stop\n");
+
+ /* Close source buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ munmap(usr_addr1[i], mmap1[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem1[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+
+ /* Close destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ munmap(usr_addr2[i], mmap2[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem2[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+
+ return;
+}
+
+void fimc_wb_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec)
+{
+ struct drm_exynos_pos def_pos = {0, 0, 720, 1280};
+ struct drm_exynos_sz def_sz = {720, 1280};
+ struct drm_exynos_ipp_property property;
+ struct drm_exynos_gem_create gem[MAX_BUF];
+ struct drm_exynos_gem_mmap mmap[MAX_BUF];
+ struct drm_exynos_ipp_queue_buf qbuf[MAX_BUF];
+ void *usr_addr[MAX_BUF];
+ struct drm_exynos_ipp_cmd_ctrl cmd_ctrl;
+ unsigned int width, height, stride;
+ int ret, i, j;
+ struct timeval begin, end;
+ struct drm_gem_close args;
+
+ /* For mode */
+ width = 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;
+ }
+ stride = width * 4;
+
+ def_sz.hsize = width;
+ def_sz.vsize = height;
+
+ /* For property */
+ ret = exynos_drm_ipp_set_property(fd, &property, &def_sz, IPP_CMD_WB, EXYNOS_DRM_DEGREE_0);
+ if (ret) {
+ fprintf(stderr, "failed to ipp property\n");
+ return;
+ }
+
+ /* For destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = util_gem_create_mmap(fd, &gem[i], &mmap[i], stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %s\n",
+ strerror(errno));
+ if (ret == -1) return;
+ else if (ret == -2) goto err_ipp_ctrl_close;
+ }
+ usr_addr[i] = (void *)(unsigned long)mmap[i].mapped;
+ /* For destination buffer map to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf[i], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_ENQUEUE, property.prop_id, i, gem[i].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf dst map\n");
+ goto err_ipp_ctrl_close;
+ }
+ }
+
+ /* Start */
+ gettimeofday(&begin, NULL);
+ ret = exynos_drm_ipp_cmd_ctrl(fd, &cmd_ctrl, property.prop_id, IPP_CTRL_PLAY);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp ctrl IPP_CMD_WB start\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ j = 0;
+ while (1) {
+ struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(fd, &fds);
+ ret = select(fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ fprintf(stderr, "select timed out or error.\n");
+ continue;
+ } else if (FD_ISSET(0, &fds)) {
+ fprintf(stderr, "select error.\n");
+ break;
+ }
+
+ gettimeofday(&end, NULL);
+ usec[j] = (end.tv_sec - begin.tv_sec) * 1000000 +
+ (end.tv_usec - begin.tv_usec);
+
+ if (property.config[EXYNOS_DRM_OPS_DST].degree == EXYNOS_DRM_DEGREE_90 ||
+ property.config[EXYNOS_DRM_OPS_DST].degree == EXYNOS_DRM_DEGREE_270 ||
+ property.config[EXYNOS_DRM_OPS_SRC].degree == EXYNOS_DRM_DEGREE_90 ||
+ property.config[EXYNOS_DRM_OPS_SRC].degree == EXYNOS_DRM_DEGREE_270) {
+ if(fimc_event_handler(NULL, NULL, qbuf, gem, &property, usr_addr, height, width, IPP_CMD_WB) < 0)
+ break;
+ } else {
+ if(fimc_event_handler(NULL, NULL, qbuf, gem, &property, usr_addr, width, height, IPP_CMD_WB) < 0)
+ break;
+ }
+
+ if (++j > MAX_LOOP)
+ break;
+
+ if (j == HALF_LOOP) {
+ /* Stop */
+ ret = exynos_drm_ipp_cmd_ctrl(fd, &cmd_ctrl, property.prop_id, IPP_CTRL_STOP);
+ if (ret) {
+ fprintf(stderr, "failed to ipp ctrl IPP_CMD_WB stop\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ /* For property */
+ ret = exynos_drm_ipp_set_property(fd, &property, &def_sz, IPP_CMD_WB, EXYNOS_DRM_DEGREE_90);
+ if (ret) {
+ fprintf(stderr, "failed to ipp property\n");
+ goto err_ipp_ctrl_close;
+ }
+
+ /* For destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For destination buffer map to IPP */
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf[i], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_ENQUEUE, property.prop_id, i, gem[i].handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp buf dst map\n");
+ goto err_ipp_ctrl_close;
+ }
+ }
+
+ /* Start */
+ ret = exynos_drm_ipp_cmd_ctrl(fd, &cmd_ctrl, property.prop_id, IPP_CTRL_PLAY);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp ctrl IPP_CMD_WB start\n");
+ goto err_ipp_ctrl_close;
+ }
+ }
+
+ gettimeofday(&begin, NULL);
+ }
+
+err_ipp_ctrl_close:
+ /* For destination buffer dequeue to IPP */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = exynos_drm_ipp_queue_buf(fd, &qbuf[i], EXYNOS_DRM_OPS_DST,
+ IPP_BUF_DEQUEUE, property.prop_id, i, gem[i].handle);
+ if (ret < 0)
+ fprintf(stderr, "failed to ipp buf dst dequeue\n");
+ }
+
+ /* Stop */
+ ret = exynos_drm_ipp_cmd_ctrl(fd, &cmd_ctrl, property.prop_id, IPP_CTRL_STOP);
+ if (ret)
+ fprintf(stderr, "failed to ipp ctrl IPP_CMD_WB stop\n");
+
+ for (i = 0; i < MAX_BUF; i++) {
+ munmap(usr_addr[i], mmap[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+
+ return;
+}
+
+void fimc_output_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec)
+{
+ fprintf(stderr, "not supported. please wait v2\n");
+}
+
diff --git a/tests/ipptest/fimc.h b/tests/ipptest/fimc.h
new file mode 100644
index 00000000..81e0e008
--- /dev/null
+++ b/tests/ipptest/fimc.h
@@ -0,0 +1,11 @@
+#ifndef __FIMC_H__
+#define __FIMC_H__
+
+void fimc_m2m_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec);
+void fimc_wb_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec);
+void fimc_output_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec);
+
+#endif
diff --git a/tests/ipptest/fimctest.c b/tests/ipptest/fimctest.c
new file mode 100644
index 00000000..f56e9999
--- /dev/null
+++ b/tests/ipptest/fimctest.c
@@ -0,0 +1,477 @@
+/*
+ * DRM based fimc test program
+ * Copyright 2012 Samsung Electronics
+ * Eunchul Kim <chulspro.kim@sasmsung.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 shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This fairly simple test program dumps output in a similar format to the
+ * "xrandr" tool everyone knows & loves. It's necessarily slightly different
+ * since the kernel separates outputs into encoder and connector structures,
+ * each with their own unique ID. The program also allows test testing of the
+ * memory management and mode setting APIs by allowing the user to specify a
+ * connector and mode to use for mode setting. If all works as expected, a
+ * blue background should be painted on the monitor attached to the specified
+ * connector after the selected mode is set.
+ *
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include "libkms.h"
+
+#include "exynos_drm.h"
+
+#include "fimctest.h"
+#include "fimc.h"
+
+drmModeRes *resources;
+int fd, modes;
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+struct type_name {
+ int type;
+ char *name;
+};
+
+#define type_name_fn(res) \
+char * res##_str(int type) { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
+ if (res##_names[i].type == type) \
+ return res##_names[i].name; \
+ } \
+ return "(invalid)"; \
+}
+
+struct type_name encoder_type_names[] = {
+ { DRM_MODE_ENCODER_NONE, "none" },
+ { DRM_MODE_ENCODER_DAC, "DAC" },
+ { DRM_MODE_ENCODER_TMDS, "TMDS" },
+ { DRM_MODE_ENCODER_LVDS, "LVDS" },
+ { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
+};
+
+type_name_fn(encoder_type)
+
+struct type_name connector_status_names[] = {
+ { DRM_MODE_CONNECTED, "connected" },
+ { DRM_MODE_DISCONNECTED, "disconnected" },
+ { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
+};
+
+type_name_fn(connector_status)
+
+struct type_name connector_type_names[] = {
+ { DRM_MODE_CONNECTOR_Unknown, "unknown" },
+ { DRM_MODE_CONNECTOR_VGA, "VGA" },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+ { DRM_MODE_CONNECTOR_Composite, "composite" },
+ { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+ { DRM_MODE_CONNECTOR_Component, "component" },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+ { DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+ { DRM_MODE_CONNECTOR_TV, "TV" },
+ { DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
+};
+
+type_name_fn(connector_type)
+
+static void dump_encoders(void)
+{
+ drmModeEncoder *encoder;
+ int i;
+
+ printf("Encoders:\n");
+ printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
+ for (i = 0; i < resources->count_encoders; i++) {
+ encoder = drmModeGetEncoder(fd, resources->encoders[i]);
+
+ if (!encoder) {
+ fprintf(stderr, "could not get encoder %i: %s\n",
+ resources->encoders[i], strerror(errno));
+ continue;
+ }
+ printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
+ encoder->encoder_id,
+ encoder->crtc_id,
+ encoder_type_str(encoder->encoder_type),
+ encoder->possible_crtcs,
+ encoder->possible_clones);
+ drmModeFreeEncoder(encoder);
+ }
+ printf("\n");
+}
+
+static void dump_mode(drmModeModeInfo *mode)
+{
+ printf(" %s %d %d %d %d %d %d %d %d %d\n",
+ mode->name,
+ mode->vrefresh,
+ mode->hdisplay,
+ mode->hsync_start,
+ mode->hsync_end,
+ mode->htotal,
+ mode->vdisplay,
+ mode->vsync_start,
+ mode->vsync_end,
+ mode->vtotal);
+}
+
+static void dump_props(drmModeConnector *connector)
+{
+ drmModePropertyPtr props;
+ int i;
+
+ for (i = 0; i < connector->count_props; i++) {
+ props = drmModeGetProperty(fd, connector->props[i]);
+ printf("\t%s, flags %d\n", props->name, props->flags);
+ drmModeFreeProperty(props);
+ }
+}
+
+static void dump_connectors(void)
+{
+ drmModeConnector *connector;
+ int i, j;
+
+ printf("Connectors:\n");
+ printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
+ 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));
+ continue;
+ }
+
+ printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
+ connector->connector_id,
+ connector->encoder_id,
+ connector_status_str(connector->connection),
+ connector_type_str(connector->connector_type),
+ connector->mmWidth, connector->mmHeight,
+ connector->count_modes);
+
+ for (j = 0; j < connector->count_encoders; j++)
+ printf("%s%d", j > 0 ? ", " : "",
+ connector->encoders[j]);
+ printf("\n");
+
+ if (!connector->count_modes)
+ continue;
+
+ printf(" modes:\n");
+ printf(" name refresh (Hz) hdisp hss hse htot vdisp "
+ "vss vse vtot)\n");
+ for (j = 0; j < connector->count_modes; j++)
+ dump_mode(&connector->modes[j]);
+
+ printf(" props:\n");
+ dump_props(connector);
+
+ drmModeFreeConnector(connector);
+ }
+ printf("\n");
+}
+
+static void dump_crtcs(void)
+{
+ drmModeCrtc *crtc;
+ int i;
+
+ printf("CRTCs:\n");
+ printf("id\tfb\tpos\tsize\n");
+ for (i = 0; i < resources->count_crtcs; i++) {
+ crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
+
+ if (!crtc) {
+ fprintf(stderr, "could not get crtc %i: %s\n",
+ resources->crtcs[i], strerror(errno));
+ continue;
+ }
+ printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
+ crtc->crtc_id,
+ crtc->buffer_id,
+ crtc->x, crtc->y,
+ crtc->width, crtc->height);
+ dump_mode(&crtc->mode);
+
+ drmModeFreeCrtc(crtc);
+ }
+ printf("\n");
+}
+
+static void dump_framebuffers(void)
+{
+ drmModeFB *fb;
+ int i;
+
+ printf("Frame buffers:\n");
+ printf("id\tsize\tpitch\n");
+ for (i = 0; i < resources->count_fbs; i++) {
+ fb = drmModeGetFB(fd, resources->fbs[i]);
+
+ if (!fb) {
+ fprintf(stderr, "could not get fb %i: %s\n",
+ resources->fbs[i], strerror(errno));
+ continue;
+ }
+ printf("%u\t(%ux%u)\t%u\n",
+ fb->fb_id,
+ fb->width, fb->height,
+ fb->pitch);
+
+ drmModeFreeFB(fb);
+ }
+ printf("\n");
+}
+
+/*
+ * 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.
+ */
+
+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;
+}
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+static char optstr[] = "ecpmfo:s:v";
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage: %s [-ecpmf]\n", name);
+ fprintf(stderr, "\t-e\tlist encoders\n");
+ fprintf(stderr, "\t-c\tlist connectors\n");
+ fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n");
+ fprintf(stderr, "\t-m\tlist modes\n");
+ fprintf(stderr, "\t-f\tlist framebuffers\n");
+ fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
+ fprintf(stderr, "\t-o\tlist of operation id : 0: M2M, 1: Writeback, 2: Output\n");
+ fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
+ fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
+ fprintf(stderr, "\n\tDefault is to dump all info.\n");
+ exit(0);
+}
+
+#define dump_resource(res) if (res) dump_##res()
+
+int main(int argc, char **argv)
+{
+ int c;
+ int operations = 0, encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0;
+ int test_vsync = 0;
+ char *modules[] = {"exynos", "i915", "radeon", "nouveau", "vmwgfx"};
+ char *modeset = NULL;
+ int i, count = 0;
+ struct connector con_args[2];
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ case 'e':
+ encoders = 1;
+ break;
+ case 'c':
+ connectors = 1;
+ break;
+ case 'p':
+ crtcs = 1;
+ break;
+ case 'm':
+ modes = 1;
+ break;
+ case 'f':
+ framebuffers = 1;
+ break;
+ case 'v':
+ test_vsync = 1;
+ break;
+ case 'o':
+ if (optarg)
+ sscanf(optarg, "%d", &operations);
+ break;
+ case 's':
+ modeset = strdup(optarg);
+ 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;
+ }
+ }
+
+ if (argc == 1)
+ encoders = connectors = crtcs = modes = framebuffers = 1;
+
+ for (i = 0; i < ARRAY_SIZE(modules); i++) {
+ printf("trying to load module %s...", modules[i]);
+ fd = drmOpen(modules[i], NULL);
+ if (fd < 0) {
+ printf("failed.\n");
+ } else {
+ printf("success.\n");
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(modules)) {
+ fprintf(stderr, "failed to load any modules, aborting.\n");
+ return -1;
+ }
+
+ resources = drmModeGetResources(fd);
+ if (!resources) {
+ fprintf(stderr, "drmModeGetResources failed: %s\n",
+ strerror(errno));
+ drmClose(fd);
+ return 1;
+ }
+
+ dump_resource(encoders);
+ dump_resource(connectors);
+ dump_resource(crtcs);
+ dump_resource(framebuffers);
+
+ if (count > 0) {
+ long int sum = 0, usec[MAX_LOOP];
+
+ switch(operations) {
+ case 0:
+ fimc_m2m_set_mode(con_args, count, test_vsync, usec);
+ break;
+ case 1:
+ fimc_wb_set_mode(con_args, count, test_vsync, usec);
+ break;
+ case 2:
+ fimc_output_set_mode(con_args, count, test_vsync, usec);
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < MAX_LOOP; i++) {
+ printf("[%d] : %d\n", i + 1, usec[i]);
+ sum += usec[i];
+ }
+ printf("fimc : cma\n");
+ printf("avg : [%d]\n", sum / MAX_LOOP);
+ getchar();
+ }
+
+ drmModeFreeResources(resources);
+
+ return 0;
+}
diff --git a/tests/ipptest/fimctest.h b/tests/ipptest/fimctest.h
new file mode 100644
index 00000000..2dd31fa5
--- /dev/null
+++ b/tests/ipptest/fimctest.h
@@ -0,0 +1,27 @@
+#ifndef __FIMCTEST_H__
+#define __FIMCTEST_H__
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+
+#define MAX_LOOP 20
+#define HALF_LOOP 10
+#define MAX_BUF 3
+
+struct connector {
+ uint32_t id;
+ char mode_str[64];
+ drmModeModeInfo *mode;
+ drmModeEncoder *encoder;
+ int crtc;
+ unsigned int fb_id[2], current_fb_id;
+ struct timeval start;
+
+ int swap_count;
+};
+
+extern int fd;
+
+extern void connector_find_mode(struct connector *c);
+
+#endif
diff --git a/tests/ipptest/gem.c b/tests/ipptest/gem.c
new file mode 100644
index 00000000..fa228df8
--- /dev/null
+++ b/tests/ipptest/gem.c
@@ -0,0 +1,72 @@
+/*
+ * DRM based fimc test program
+ * Copyright 2012 Samsung Electronics
+ * Eunchul Kim <chulspro.kim@sasmsung.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 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.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+#include "libkms.h"
+
+#include "exynos_drm.h"
+
+int exynos_gem_create(int fd, struct drm_exynos_gem_create *gem)
+{
+ int ret = 0;
+
+ if (!gem) {
+ fprintf(stderr, "gem object is null.\n");
+ return -EFAULT;
+ }
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_CREATE, gem);
+ if (ret < 0)
+ fprintf(stderr, "failed to create gem buffer: %s\n",
+ strerror(-ret));
+ return ret;
+}
+
+int exynos_gem_mmap(int fd, struct drm_exynos_gem_mmap *in_mmap)
+{
+ int ret = 0;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MMAP, in_mmap);
+ if (ret < 0)
+ fprintf(stderr, "failed to mmap: %s\n", strerror(-ret));
+ return ret;
+}
+
+int exynos_gem_close(int fd, struct drm_gem_close *gem_close)
+{
+ int ret = 0;
+
+ ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, gem_close);
+ if (ret < 0)
+ fprintf(stderr, "failed to close: %s\n", strerror(-ret));
+ return ret;
+}
diff --git a/tests/ipptest/gem.h b/tests/ipptest/gem.h
new file mode 100644
index 00000000..8d4d9f14
--- /dev/null
+++ b/tests/ipptest/gem.h
@@ -0,0 +1,8 @@
+#ifndef __GEM_H__
+#define __GEM_H__
+
+extern int exynos_gem_create(int fd, struct drm_exynos_gem_create *gem);
+extern int exynos_gem_mmap(int fd, struct drm_exynos_gem_mmap *in_mmap);
+extern int exynos_gem_close(int fd, struct drm_gem_close *gem_close);
+
+#endif
diff --git a/tests/ipptest/util.c b/tests/ipptest/util.c
new file mode 100644
index 00000000..745c2120
--- /dev/null
+++ b/tests/ipptest/util.c
@@ -0,0 +1,132 @@
+/*
+ * DRM based fimc test program
+ * Copyright 2012 Samsung Electronics
+ * Eunchul Kim <chulspro.kim@sasmsung.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 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "exynos_drm.h"
+#include "gem.h"
+
+int util_gem_create_mmap(int fd, struct drm_exynos_gem_create *gem,
+ struct drm_exynos_gem_mmap *mmap,
+ unsigned int size)
+{
+ /* initialize structure for gem create */
+ memset(gem, 0x00, sizeof(struct drm_exynos_gem_create));
+ gem->size = size;
+
+ if (exynos_gem_create(fd, gem) < 0) {
+ fprintf(stderr, "failed to gem create: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* initialize structure for gem mmap */
+ memset(mmap, 0x00, sizeof(struct drm_exynos_gem_mmap));
+ mmap->handle = gem->handle;
+ mmap->size = gem->size;
+
+ if (exynos_gem_mmap(fd, mmap) < 0) {
+ fprintf(stderr, "failed to gem mmap: %s\n", strerror(errno));
+ return -2;
+ }
+
+ return 0;
+}
+
+void util_draw_buffer(void *addr, unsigned int stripe,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int size)
+{
+ if (stripe == 1) {
+ int i, j;
+ unsigned int *fb_ptr;
+ div_t d;
+
+ for (j = 0; j < height; j++) {
+ fb_ptr = (unsigned int *)((char *)addr + j * stride);
+ for (i = 0; i < width; i++) {
+ d = div(i, width);
+ fb_ptr[i] = 0x00130502 * (d.quot >> 6)
+ + 0x000a1120 * (d.rem >> 6);
+ }
+ }
+ } else
+ memset(addr, 0x77, size);
+}
+
+int util_write_bmp(const char *file, const void *data, unsigned int width,
+ unsigned int height)
+{
+ int i;
+ unsigned int * blocks;
+ FILE *fp;
+ struct {
+ unsigned char magic[2];
+ } bmpfile_magic = { {'B', 'M'} };
+ struct {
+ unsigned int filesz;
+ unsigned short creator1;
+ unsigned short creator2;
+ unsigned int bmp_offset;
+ } bmpfile_header = { 0, 0, 0, 0x36 };
+ struct {
+ unsigned int header_sz;
+ unsigned int width;
+ unsigned int height;
+ unsigned short nplanes;
+ unsigned short bitspp;
+ unsigned int compress_type;
+ unsigned int bmp_bytesz;
+ unsigned int hres;
+ unsigned int vres;
+ unsigned int ncolors;
+ unsigned int nimpcolors;
+ } bmp_dib_v3_header_t = { 0x28, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0 };
+
+ fp = fopen(file, "wb");
+ if (fp == NULL) return -1;
+
+ bmpfile_header.filesz = sizeof(bmpfile_magic) + sizeof(bmpfile_header)
+ + sizeof(bmp_dib_v3_header_t) + width * height * 4;
+ bmp_dib_v3_header_t.header_sz = sizeof(bmp_dib_v3_header_t);
+ bmp_dib_v3_header_t.width = width;
+ bmp_dib_v3_header_t.height = -height;
+ bmp_dib_v3_header_t.nplanes = 1;
+ bmp_dib_v3_header_t.bmp_bytesz = width * height * 4;
+
+ fwrite(&bmpfile_magic, sizeof(bmpfile_magic), 1, fp);
+ fwrite(&bmpfile_header, sizeof(bmpfile_header), 1, fp);
+ fwrite(&bmp_dib_v3_header_t, sizeof(bmp_dib_v3_header_t), 1, fp);
+
+ blocks = (unsigned int*)data;
+ for (i = 0; i < height * width; i++)
+ fwrite(&blocks[i], 4, 1, fp);
+
+ fclose(fp);
+ return 0;
+}
diff --git a/tests/ipptest/util.h b/tests/ipptest/util.h
new file mode 100644
index 00000000..e1ffcca4
--- /dev/null
+++ b/tests/ipptest/util.h
@@ -0,0 +1,12 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+extern int util_gem_create_mmap(int fd, struct drm_exynos_gem_create *gem,
+ struct drm_exynos_gem_mmap *mmap,
+ unsigned int size);
+extern void util_draw_buffer(void *addr, unsigned int stripe,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int size);
+extern int util_write_bmp(const char *file, const void *data,
+ unsigned int width, unsigned int height);
+#endif
diff --git a/tests/rottest/Makefile.am b/tests/rottest/Makefile.am
new file mode 100644
index 00000000..38975356
--- /dev/null
+++ b/tests/rottest/Makefile.am
@@ -0,0 +1,19 @@
+AM_CFLAGS = \
+ -I$(top_srcdir)/include/drm \
+ -I$(top_srcdir)/libkms/ \
+ -I$(top_srcdir)/exynos/ \
+ -I$(top_srcdir)
+
+noinst_PROGRAMS = \
+ rottest
+
+rottest_SOURCES = \
+ rottest.c \
+ gem.c \
+ util.c \
+ rotator.c
+
+rottest_LDADD = \
+ $(top_builddir)/libdrm.la \
+ $(top_builddir)/libkms/libkms.la \
+ $(top_builddir)/exynos/libdrm_exynos.la
diff --git a/tests/rottest/gem.c b/tests/rottest/gem.c
new file mode 100644
index 00000000..001aea08
--- /dev/null
+++ b/tests/rottest/gem.c
@@ -0,0 +1,72 @@
+/*
+ * DRM based rotator test program
+ * Copyright 2012 Samsung Electronics
+ * YoungJun Cho <yj44.cho@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 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.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+#include "libkms.h"
+
+#include "exynos_drm.h"
+
+int exynos_gem_create(int fd, struct drm_exynos_gem_create *gem)
+{
+ int ret = 0;
+
+ if (!gem) {
+ fprintf(stderr, "gem object is null.\n");
+ return -EFAULT;
+ }
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_CREATE, gem);
+ if (ret < 0)
+ fprintf(stderr, "failed to create gem buffer: %s\n",
+ strerror(-ret));
+ return ret;
+}
+
+int exynos_gem_mmap(int fd, struct drm_exynos_gem_mmap *in_mmap)
+{
+ int ret = 0;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MMAP, in_mmap);
+ if (ret < 0)
+ fprintf(stderr, "failed to mmap: %s\n", strerror(-ret));
+ return ret;
+}
+
+int exynos_gem_close(int fd, struct drm_gem_close *gem_close)
+{
+ int ret = 0;
+
+ ret = ioctl(fd, DRM_IOCTL_GEM_CLOSE, gem_close);
+ if (ret < 0)
+ fprintf(stderr, "failed to close: %s\n", strerror(-ret));
+ return ret;
+}
diff --git a/tests/rottest/gem.h b/tests/rottest/gem.h
new file mode 100644
index 00000000..8d4d9f14
--- /dev/null
+++ b/tests/rottest/gem.h
@@ -0,0 +1,8 @@
+#ifndef __GEM_H__
+#define __GEM_H__
+
+extern int exynos_gem_create(int fd, struct drm_exynos_gem_create *gem);
+extern int exynos_gem_mmap(int fd, struct drm_exynos_gem_mmap *in_mmap);
+extern int exynos_gem_close(int fd, struct drm_gem_close *gem_close);
+
+#endif
diff --git a/tests/rottest/rotator.c b/tests/rottest/rotator.c
new file mode 100644
index 00000000..a7cf50bc
--- /dev/null
+++ b/tests/rottest/rotator.c
@@ -0,0 +1,605 @@
+/*
+ * DRM based rotator test program
+ * Copyright 2012 Samsung Electronics
+ * YoungJun Cho <yj44.cho@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 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include "exynos_drm.h"
+#include "rottest.h"
+#include "gem.h"
+#include "util.h"
+
+#include "drm_fourcc.h"
+
+static int exynos_drm_ipp_set_property(int fd,
+ struct drm_exynos_ipp_property *property,
+ struct drm_exynos_pos *src_pos,
+ struct drm_exynos_sz *src_sz,
+ struct drm_exynos_pos *dst_pos,
+ struct drm_exynos_sz *dst_sz)
+{
+ int ret;
+
+ memset(property, 0x00, sizeof(struct drm_exynos_ipp_property));
+
+ property->cmd = IPP_CMD_M2M;
+
+ property->config[EXYNOS_DRM_OPS_SRC].ops_id = EXYNOS_DRM_OPS_SRC;
+ property->config[EXYNOS_DRM_OPS_SRC].flip = EXYNOS_DRM_FLIP_NONE;
+ property->config[EXYNOS_DRM_OPS_SRC].degree = EXYNOS_DRM_DEGREE_0;
+ property->config[EXYNOS_DRM_OPS_SRC].fmt = DRM_FORMAT_XRGB8888;
+ property->config[EXYNOS_DRM_OPS_SRC].pos = *src_pos;
+ property->config[EXYNOS_DRM_OPS_SRC].sz = *src_sz;
+
+ property->config[EXYNOS_DRM_OPS_DST].ops_id = EXYNOS_DRM_OPS_DST;
+ property->config[EXYNOS_DRM_OPS_DST].flip = EXYNOS_DRM_FLIP_NONE;
+ property->config[EXYNOS_DRM_OPS_DST].degree = EXYNOS_DRM_DEGREE_90;
+ property->config[EXYNOS_DRM_OPS_DST].fmt = DRM_FORMAT_XRGB8888;
+ property->config[EXYNOS_DRM_OPS_DST].pos = *dst_pos;
+ property->config[EXYNOS_DRM_OPS_DST].sz = *dst_sz;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY, property);
+ if (ret)
+ fprintf(stderr,
+ "failed to DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY : %s\n",
+ strerror(errno));
+
+ return ret;
+}
+
+static int exynos_drm_ipp_queue_buf(int fd, int prop_id,
+ struct drm_exynos_ipp_queue_buf *qbuf,
+ enum drm_exynos_ops_id ops_id,
+ enum drm_exynos_ipp_buf_type buf_type,
+ unsigned int buf_id,
+ unsigned int gem_handle)
+{
+ int ret;
+
+ memset(qbuf, 0x00, sizeof(struct drm_exynos_ipp_queue_buf));
+
+ qbuf->ops_id = ops_id;
+ qbuf->buf_type = buf_type;
+ qbuf->user_data = 0;
+ qbuf->prop_id = prop_id;
+ qbuf->buf_id = buf_id;
+ qbuf->handle[EXYNOS_DRM_PLANAR_Y] = gem_handle;
+ qbuf->handle[EXYNOS_DRM_PLANAR_CB] = 0;
+ qbuf->handle[EXYNOS_DRM_PLANAR_CR] = 0;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, qbuf);
+ if (ret)
+ fprintf(stderr,
+ "failed to DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF[prop_id:%d]"\
+ "[ops_id:%d][buf_id:%d][buf_type:%d] : %s\n",
+ prop_id, ops_id, buf_id, buf_type, strerror(errno));
+
+ return ret;
+}
+
+static int exynos_drm_ipp_cmd_ctrl(int fd, int prop_id,
+ struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl,
+ enum drm_exynos_ipp_ctrl ctrl)
+{
+ int ret;
+
+ memset(cmd_ctrl, 0x00, sizeof(struct drm_exynos_ipp_cmd_ctrl));
+
+ cmd_ctrl->prop_id = prop_id;
+ cmd_ctrl->ctrl = ctrl;
+
+ ret = ioctl(fd, DRM_IOCTL_EXYNOS_IPP_CMD_CTRL, cmd_ctrl);
+ if (ret)
+ fprintf(stderr,
+ "failed to DRM_IOCTL_EXYNOS_IPP_CMD_CTRL[prop_id:%d]"\
+ "[ctrl:%d] : %s\n", prop_id, ctrl, strerror(errno));
+
+ return ret;
+}
+
+static int rotator_set_mode_property(struct connector *c, int count,
+ unsigned int *width, unsigned int *height,
+ unsigned int *stride,
+ struct drm_exynos_ipp_property *property)
+{
+ int ret, i;
+ struct drm_exynos_pos src_pos = {0, 0, 720, 1280};
+ struct drm_exynos_sz src_sz = {720, 1280};
+ struct drm_exynos_pos dst_pos = {0, 0, 1280, 720};
+ struct drm_exynos_sz dst_sz = {1280, 720};
+
+ *width = 0;
+ *height = 0;
+
+ /* For mode */
+ 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;
+ }
+
+ *stride = *width * 4;
+
+ /* For IPP setting property */
+ ret = exynos_drm_ipp_set_property(fd, property, &src_pos, &src_sz,
+ &dst_pos, &dst_sz);
+ if (ret)
+ fprintf(stderr, "failed to ipp set property: %s\n",
+ strerror(errno));
+
+ return ret;
+}
+
+static int rotator_event_handler(int fd, int idx, int prop_id,
+ struct drm_exynos_ipp_queue_buf *src_qbuf,
+ struct drm_exynos_gem_create *src_gem,
+ struct drm_exynos_ipp_queue_buf *dst_qbuf,
+ struct drm_exynos_gem_create *dst_gem,
+ void **usr_addr, unsigned int width,
+ unsigned int height, struct timeval *begin)
+{
+ char buffer[1024];
+ int ret = 0, len, i;
+ struct drm_event *e;
+ struct drm_exynos_ipp_event *ipp_event;
+ int src_buf_id, dst_buf_id;
+ char filename[100];
+
+ len = read(fd, buffer, sizeof(buffer));
+ if (!len)
+ return 0;
+ if (len < sizeof (*e))
+ return -1;
+
+ i = 0;
+ while (i < len) {
+ e = (struct drm_event *)&buffer[i];
+ switch (e->type) {
+ case DRM_EXYNOS_IPP_EVENT:
+ ipp_event = (struct drm_exynos_ipp_event *)e;
+ src_buf_id = ipp_event->buf_id[EXYNOS_DRM_OPS_SRC];
+ dst_buf_id = ipp_event->buf_id[EXYNOS_DRM_OPS_DST];
+
+ sprintf(filename, "/opt/media/rot_%d_%d.bmp", idx,
+ dst_buf_id);
+ util_write_bmp(filename, usr_addr[dst_buf_id], width,
+ height);
+
+ gettimeofday(begin, NULL);
+ ret = exynos_drm_ipp_queue_buf(fd, prop_id, src_qbuf,
+ EXYNOS_DRM_OPS_SRC,
+ IPP_BUF_ENQUEUE,
+ src_buf_id,
+ src_gem->handle);
+ if (ret)
+ fprintf(stderr, "failed to queue src_buf\n");
+
+ ret = exynos_drm_ipp_queue_buf(fd, prop_id,
+ &dst_qbuf[dst_buf_id],
+ EXYNOS_DRM_OPS_DST,
+ IPP_BUF_ENQUEUE, dst_buf_id,
+ dst_gem[dst_buf_id].handle);
+ if (ret)
+ fprintf(stderr, "failed to queue dst_buf\n");
+ break;
+ default:
+ break;
+ }
+ i += e->length;
+ }
+
+ return ret;
+}
+
+void rotator_1_N_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec)
+{
+ struct drm_exynos_ipp_property property;
+ struct drm_exynos_ipp_queue_buf qbuf1, qbuf2[MAX_BUF];
+ struct drm_exynos_ipp_cmd_ctrl cmd_ctrl;
+ unsigned int width, height, stride;
+ int ret, i, counter;
+ struct drm_exynos_gem_create gem1, gem2[MAX_BUF];
+ struct drm_exynos_gem_mmap mmap1, mmap2[MAX_BUF];
+ void *usr_addr1, *usr_addr2[MAX_BUF];
+ struct timeval begin, end;
+ struct drm_gem_close args;
+ char filename[100];
+
+ /* For setting mode / IPP setting property */
+ ret = rotator_set_mode_property(c, count, &width, &height, &stride,
+ &property);
+ if (ret) {
+ fprintf(stderr, "failed to set mode property : %s\n",
+ strerror(errno));
+ return;
+ }
+
+ /* For GEM create / mmap / draw buffer */
+ /* For source buffer */
+ ret = util_gem_create_mmap(fd, &gem1, &mmap1, &usr_addr1,
+ stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %s\n",
+ strerror(errno));
+ return;
+ }
+ util_draw_buffer(usr_addr1, 1, width, height, stride, 0);
+ sprintf(filename, "/opt/media/rot_src.bmp");
+ util_write_bmp(filename, usr_addr1, width, height);
+
+ /* For destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = util_gem_create_mmap(fd, &gem2[i], &mmap2[i],
+ &usr_addr2[i], stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %d : %s\n",
+ i, strerror(errno));
+ goto err_gem_create_mmap;
+ }
+ util_draw_buffer(usr_addr2[i], 0, 0, 0, 0, stride * height);
+ sprintf(filename, "/opt/media/rot_dst%d.bmp", i);
+ util_write_bmp(filename, usr_addr2[i], height, width);
+ }
+
+ /* For IPP queueing */
+ /* For source buffer */
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf1,
+ EXYNOS_DRM_OPS_SRC, IPP_BUF_ENQUEUE,
+ 0, gem1.handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp queue buf src queue\n");
+ goto err_ipp_queue_buf;
+ }
+
+ /* For destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf2[i],
+ EXYNOS_DRM_OPS_DST, IPP_BUF_ENQUEUE,
+ i, gem2[i].handle);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp queue buf dst queue : %d\n", i);
+ goto err_ipp_queue_buf;
+ }
+ }
+
+ /* For IPP starting */
+ gettimeofday(&begin, NULL);
+ ret = exynos_drm_ipp_cmd_ctrl(fd, property.prop_id, &cmd_ctrl,
+ IPP_CTRL_PLAY);
+ if (ret) {
+ fprintf(stderr, "failed to ipp cmd ctrl IPP_CMD_M2M start\n");
+ goto err_ipp_queue_buf;
+ }
+
+ /* For IPP handling event */
+ for (i = 0; i < MAX_LOOP; i++) {
+ counter = 0;
+
+ while (1) {
+ struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(fd, &fds);
+ ret = select(fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ fprintf(stderr, "select timed out or error.\n");
+ continue;
+ } else if (FD_ISSET(0, &fds))
+ break;
+
+ gettimeofday(&end, NULL);
+ usec[MAX_BUF * i + counter] =
+ (end.tv_sec - begin.tv_sec) * 1000000 +
+ (end.tv_usec - begin.tv_usec);
+
+ ret = rotator_event_handler(fd, i, property.prop_id,
+ &qbuf1, &gem1, qbuf2,
+ gem2, usr_addr2, height,
+ width, &begin);
+ if (ret) {
+ fprintf(stderr,
+ "failed to rotator_event_handler()\n");
+ goto err_rotator_event_handler;
+ }
+ if (++counter > MAX_BUF)
+ break;
+ }
+ }
+
+ /* For IPP dequeing */
+ /* For source buffer */
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf1,
+ EXYNOS_DRM_OPS_SRC, IPP_BUF_DEQUEUE, 0,
+ gem1.handle);
+ if (ret) {
+ fprintf(stderr, "failed to ipp queue buf src dequeue\n");
+ goto err_rotator_event_handler;
+ }
+
+ /* For destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf2[i],
+ EXYNOS_DRM_OPS_DST, IPP_BUF_DEQUEUE, i,
+ gem2[i].handle);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp queue buf dst queue : %d\n", i);
+ goto err_rotator_event_handler;
+ }
+ }
+
+ /* For IPP stopping */
+ ret = exynos_drm_ipp_cmd_ctrl(fd, property.prop_id, &cmd_ctrl,
+ IPP_CTRL_STOP);
+ if (ret) {
+ fprintf(stderr, "failed to ipp cmd ctrl IPP_CMD_M2M stop\n");
+ }
+
+ /* For munmap / GEM close */
+ /* For destination buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ munmap(usr_addr2[i], mmap2[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem2[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+
+ /* For source buffer */
+ munmap(usr_addr1, mmap1.size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem1.handle;
+ exynos_gem_close(fd, &args);
+
+ return;
+
+err_rotator_event_handler:
+ exynos_drm_ipp_cmd_ctrl(fd, property.prop_id, &cmd_ctrl, IPP_CTRL_STOP);
+err_ipp_queue_buf:
+ for (i = 0; i < MAX_BUF; i++) {
+ exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf2[i],
+ EXYNOS_DRM_OPS_DST, IPP_BUF_DEQUEUE, i,
+ gem2[i].handle);
+ }
+ exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf1,
+ EXYNOS_DRM_OPS_SRC, IPP_BUF_DEQUEUE, 0, gem1.handle);
+err_gem_create_mmap:
+ for (i = 0; i < MAX_BUF; i++) {
+ munmap(usr_addr2[i], mmap2[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem2[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+
+ munmap(usr_addr1, mmap1.size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem1.handle;
+ exynos_gem_close(fd, &args);
+}
+
+void rotator_N_N_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec)
+{
+ struct drm_exynos_ipp_property property;
+ struct drm_exynos_ipp_queue_buf qbuf1[MAX_BUF], qbuf2[MAX_BUF];
+ struct drm_exynos_ipp_cmd_ctrl cmd_ctrl;
+ unsigned int width, height, stride;
+ int ret, i, counter;
+ struct drm_exynos_gem_create gem1[MAX_BUF], gem2[MAX_BUF];
+ struct drm_exynos_gem_mmap mmap1[MAX_BUF], mmap2[MAX_BUF];
+ void *usr_addr1[MAX_BUF], *usr_addr2[MAX_BUF];
+ struct timeval begin, end;
+ struct drm_gem_close args;
+ char filename[100];
+
+ /* For setting mode / IPP setting property */
+ ret = rotator_set_mode_property(c, count, &width, &height, &stride,
+ &property);
+ if (ret) {
+ fprintf(stderr, "failed to set mode property : %s\n",
+ strerror(errno));
+ return;
+ }
+
+ /* For GEM create / mmap / draw buffer */
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For source buffer */
+ ret = util_gem_create_mmap(fd, &gem1[i], &mmap1[i],
+ &usr_addr1[i], stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %d : %s\n",
+ i, strerror(errno));
+ goto err_gem_create_mmap;
+ }
+ util_draw_buffer(usr_addr1[i], 1, width, height, stride, 0);
+ sprintf(filename, "/opt/media/rot_src%d.bmp", i);
+ util_write_bmp(filename, usr_addr1[i], width, height);
+
+ /* For destination buffer */
+ ret = util_gem_create_mmap(fd, &gem2[i], &mmap2[i],
+ &usr_addr2[i], stride * height);
+ if (ret) {
+ fprintf(stderr, "failed to gem create mmap: %d : %s\n",
+ i, strerror(errno));
+ goto err_gem_create_mmap;
+ }
+ util_draw_buffer(usr_addr2[i], 0, 0, 0, 0, stride * height);
+ sprintf(filename, "/opt/media/rot_dst%d.bmp", i);
+ util_write_bmp(filename, usr_addr2[i], height, width);
+ }
+
+ /* For IPP queueing */
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For source buffer */
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf1[i],
+ EXYNOS_DRM_OPS_SRC, IPP_BUF_ENQUEUE,
+ i, gem1[i].handle);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp queue buf src queue : %d\n", i);
+ goto err_ipp_queue_buf;
+ }
+
+ /* For destination buffer */
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf2[i],
+ EXYNOS_DRM_OPS_DST, IPP_BUF_ENQUEUE,
+ i, gem2[i].handle);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp queue buf dst queue : %d\n", i);
+ goto err_ipp_queue_buf;
+ }
+ }
+
+ /* For IPP starting */
+ gettimeofday(&begin, NULL);
+ ret = exynos_drm_ipp_cmd_ctrl(fd, property.prop_id, &cmd_ctrl,
+ IPP_CTRL_PLAY);
+ if (ret) {
+ fprintf(stderr, "failed to ipp cmd ctrl IPP_CMD_M2M start\n");
+ goto err_ipp_queue_buf;
+ }
+
+ /* For IPP handling event */
+ for (i = 0; i < MAX_LOOP; i++) {
+ counter = 0;
+
+ while (1) {
+ struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(fd, &fds);
+ ret = select(fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ fprintf(stderr, "select timed out or error.\n");
+ continue;
+ } else if (FD_ISSET(0, &fds))
+ break;
+
+ gettimeofday(&end, NULL);
+ usec[MAX_BUF * i + counter] =
+ (end.tv_sec - begin.tv_sec) * 1000000 +
+ (end.tv_usec - begin.tv_usec);
+
+ ret = rotator_event_handler(fd, i, property.prop_id,
+ qbuf1, gem1, qbuf2,
+ gem2, usr_addr2, height,
+ width, &begin);
+ if (ret) {
+ fprintf(stderr,
+ "failed to rotator_event_handler()\n");
+ goto err_rotator_event_handler;
+ }
+ if (++counter > MAX_BUF)
+ break;
+ }
+ }
+
+ /* For IPP dequeing */
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For destination buffer */
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf2[i],
+ EXYNOS_DRM_OPS_DST, IPP_BUF_DEQUEUE, i,
+ gem2[i].handle);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp queue buf dst dequeue: %d\n", i);
+ goto err_rotator_event_handler;
+ }
+
+ /* For source buffer */
+ ret = exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf1[i],
+ EXYNOS_DRM_OPS_SRC, IPP_BUF_DEQUEUE, i,
+ gem1[i].handle);
+ if (ret) {
+ fprintf(stderr,
+ "failed to ipp queue buf src dequeue: %d\n", i);
+ goto err_rotator_event_handler;
+ }
+ }
+
+ /* For IPP stopping */
+ ret = exynos_drm_ipp_cmd_ctrl(fd, property.prop_id, &cmd_ctrl,
+ IPP_CTRL_STOP);
+ if (ret) {
+ fprintf(stderr, "failed to ipp cmd ctrl IPP_CMD_M2M stop\n");
+ }
+
+ /* For munmap / GEM close */
+ for (i = 0; i < MAX_BUF; i++) {
+ /* For destination buffer */
+ munmap(usr_addr2[i], mmap2[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem2[i].handle;
+ exynos_gem_close(fd, &args);
+
+ /* For source buffer */
+ munmap(usr_addr1[i], mmap1[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem1[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+
+ return;
+
+err_rotator_event_handler:
+ exynos_drm_ipp_cmd_ctrl(fd, property.prop_id, &cmd_ctrl, IPP_CTRL_STOP);
+err_ipp_queue_buf:
+ for (i = 0; i < MAX_BUF; i++) {
+ exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf2[i],
+ EXYNOS_DRM_OPS_DST, IPP_BUF_DEQUEUE, i,
+ gem2[i].handle);
+
+ exynos_drm_ipp_queue_buf(fd, property.prop_id, &qbuf1[i],
+ EXYNOS_DRM_OPS_SRC, IPP_BUF_DEQUEUE, i,
+ gem1[i].handle);
+ }
+err_gem_create_mmap:
+ for (i = 0; i < MAX_BUF; i++) {
+ munmap(usr_addr2[i], mmap2[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem2[i].handle;
+ exynos_gem_close(fd, &args);
+
+ munmap(usr_addr1, mmap1[i].size);
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem1[i].handle;
+ exynos_gem_close(fd, &args);
+ }
+}
diff --git a/tests/rottest/rotator.h b/tests/rottest/rotator.h
new file mode 100644
index 00000000..5962a479
--- /dev/null
+++ b/tests/rottest/rotator.h
@@ -0,0 +1,9 @@
+#ifndef __ROTATOR_H__
+#define __ROTATOR_H__
+
+extern void rotator_1_N_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec);
+extern void rotator_N_N_set_mode(struct connector *c, int count, int page_flip,
+ long int *usec);
+
+#endif
diff --git a/tests/rottest/rottest.c b/tests/rottest/rottest.c
new file mode 100644
index 00000000..1f69091c
--- /dev/null
+++ b/tests/rottest/rottest.c
@@ -0,0 +1,464 @@
+/*
+ * DRM based rotator test program
+ * Copyright 2012 Samsung Electronics
+ * YoungJun Cho <yj44.cho@sasmsung.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 shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This fairly simple test program dumps output in a similar format to the
+ * "xrandr" tool everyone knows & loves. It's necessarily slightly different
+ * since the kernel separates outputs into encoder and connector structures,
+ * each with their own unique ID. The program also allows test testing of the
+ * memory management and mode setting APIs by allowing the user to specify a
+ * connector and mode to use for mode setting. If all works as expected, a
+ * blue background should be painted on the monitor attached to the specified
+ * connector after the selected mode is set.
+ *
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include "libkms.h"
+
+#include "exynos_drm.h"
+
+#include "rottest.h"
+#include "rotator.h"
+
+drmModeRes *resources;
+int fd, modes;
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+struct type_name {
+ int type;
+ char *name;
+};
+
+#define type_name_fn(res) \
+char * res##_str(int type) { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
+ if (res##_names[i].type == type) \
+ return res##_names[i].name; \
+ } \
+ return "(invalid)"; \
+}
+
+struct type_name encoder_type_names[] = {
+ { DRM_MODE_ENCODER_NONE, "none" },
+ { DRM_MODE_ENCODER_DAC, "DAC" },
+ { DRM_MODE_ENCODER_TMDS, "TMDS" },
+ { DRM_MODE_ENCODER_LVDS, "LVDS" },
+ { DRM_MODE_ENCODER_TVDAC, "TVDAC" },
+};
+
+type_name_fn(encoder_type)
+
+struct type_name connector_status_names[] = {
+ { DRM_MODE_CONNECTED, "connected" },
+ { DRM_MODE_DISCONNECTED, "disconnected" },
+ { DRM_MODE_UNKNOWNCONNECTION, "unknown" },
+};
+
+type_name_fn(connector_status)
+
+struct type_name connector_type_names[] = {
+ { DRM_MODE_CONNECTOR_Unknown, "unknown" },
+ { DRM_MODE_CONNECTOR_VGA, "VGA" },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+ { DRM_MODE_CONNECTOR_Composite, "composite" },
+ { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+ { DRM_MODE_CONNECTOR_Component, "component" },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+ { DRM_MODE_CONNECTOR_DisplayPort, "displayport" },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+ { DRM_MODE_CONNECTOR_TV, "TV" },
+ { DRM_MODE_CONNECTOR_eDP, "embedded displayport" },
+};
+
+type_name_fn(connector_type)
+
+static void dump_encoders(void)
+{
+ drmModeEncoder *encoder;
+ int i;
+
+ printf("Encoders:\n");
+ printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
+ for (i = 0; i < resources->count_encoders; i++) {
+ encoder = drmModeGetEncoder(fd, resources->encoders[i]);
+
+ if (!encoder) {
+ fprintf(stderr, "could not get encoder %i: %s\n",
+ resources->encoders[i], strerror(errno));
+ continue;
+ }
+ printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
+ encoder->encoder_id,
+ encoder->crtc_id,
+ encoder_type_str(encoder->encoder_type),
+ encoder->possible_crtcs,
+ encoder->possible_clones);
+ drmModeFreeEncoder(encoder);
+ }
+ printf("\n");
+}
+
+static void dump_mode(drmModeModeInfo *mode)
+{
+ printf(" %s %d %d %d %d %d %d %d %d %d\n",
+ mode->name,
+ mode->vrefresh,
+ mode->hdisplay,
+ mode->hsync_start,
+ mode->hsync_end,
+ mode->htotal,
+ mode->vdisplay,
+ mode->vsync_start,
+ mode->vsync_end,
+ mode->vtotal);
+}
+
+static void dump_props(drmModeConnector *connector)
+{
+ drmModePropertyPtr props;
+ int i;
+
+ for (i = 0; i < connector->count_props; i++) {
+ props = drmModeGetProperty(fd, connector->props[i]);
+ printf("\t%s, flags %d\n", props->name, props->flags);
+ drmModeFreeProperty(props);
+ }
+}
+
+static void dump_connectors(void)
+{
+ drmModeConnector *connector;
+ int i, j;
+
+ printf("Connectors:\n");
+ printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\tencoders\n");
+ 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));
+ continue;
+ }
+
+ printf("%d\t%d\t%s\t%s\t%dx%d\t\t%d\t",
+ connector->connector_id,
+ connector->encoder_id,
+ connector_status_str(connector->connection),
+ connector_type_str(connector->connector_type),
+ connector->mmWidth, connector->mmHeight,
+ connector->count_modes);
+
+ for (j = 0; j < connector->count_encoders; j++)
+ printf("%s%d", j > 0 ? ", " : "",
+ connector->encoders[j]);
+ printf("\n");
+
+ if (!connector->count_modes)
+ continue;
+
+ printf(" modes:\n");
+ printf(" name refresh (Hz) hdisp hss hse htot vdisp "
+ "vss vse vtot)\n");
+ for (j = 0; j < connector->count_modes; j++)
+ dump_mode(&connector->modes[j]);
+
+ printf(" props:\n");
+ dump_props(connector);
+
+ drmModeFreeConnector(connector);
+ }
+ printf("\n");
+}
+
+static void dump_crtcs(void)
+{
+ drmModeCrtc *crtc;
+ int i;
+
+ printf("CRTCs:\n");
+ printf("id\tfb\tpos\tsize\n");
+ for (i = 0; i < resources->count_crtcs; i++) {
+ crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
+
+ if (!crtc) {
+ fprintf(stderr, "could not get crtc %i: %s\n",
+ resources->crtcs[i], strerror(errno));
+ continue;
+ }
+ printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
+ crtc->crtc_id,
+ crtc->buffer_id,
+ crtc->x, crtc->y,
+ crtc->width, crtc->height);
+ dump_mode(&crtc->mode);
+
+ drmModeFreeCrtc(crtc);
+ }
+ printf("\n");
+}
+
+static void dump_framebuffers(void)
+{
+ drmModeFB *fb;
+ int i;
+
+ printf("Frame buffers:\n");
+ printf("id\tsize\tpitch\n");
+ for (i = 0; i < resources->count_fbs; i++) {
+ fb = drmModeGetFB(fd, resources->fbs[i]);
+
+ if (!fb) {
+ fprintf(stderr, "could not get fb %i: %s\n",
+ resources->fbs[i], strerror(errno));
+ continue;
+ }
+ printf("%u\t(%ux%u)\t%u\n",
+ fb->fb_id,
+ fb->width, fb->height,
+ fb->pitch);
+
+ drmModeFreeFB(fb);
+ }
+ printf("\n");
+}
+
+/*
+ * 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.
+ */
+
+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;
+}
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+static char optstr[] = "ecpmfn:s:v";
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage: %s [-ecpmf]\n", name);
+ fprintf(stderr, "\t-e\tlist encoders\n");
+ fprintf(stderr, "\t-c\tlist connectors\n");
+ fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n");
+ fprintf(stderr, "\t-m\tlist modes\n");
+ fprintf(stderr, "\t-f\tlist framebuffers\n");
+ fprintf(stderr, "\t-n\ttest multiple buffers : 0->1:N, 1->N:N\n");
+ fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
+ fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
+ fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
+ fprintf(stderr, "\n\tDefault is to dump all info.\n");
+ exit(0);
+}
+
+#define dump_resource(res) if (res) dump_##res()
+
+int main(int argc, char **argv)
+{
+ int c, i, count = 0, encoders = 0, connectors = 0, crtcs = 0,
+ framebuffers = 0, test_multiple_buf = 0, test_vsync = 0;
+ char *modules[] = {"exynos", "i915", "radeon", "nouveau", "vmwgfx"};
+ struct connector con_args[2];
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ case 'e':
+ encoders = 1;
+ break;
+ case 'c':
+ connectors = 1;
+ break;
+ case 'p':
+ crtcs = 1;
+ break;
+ case 'm':
+ modes = 1;
+ break;
+ case 'f':
+ framebuffers = 1;
+ break;
+ case 'n':
+ if (sscanf(optarg, "%d", &test_multiple_buf) != 1)
+ usage(argv[0]);
+ break;
+ case 'v':
+ test_vsync = 1;
+ break;
+ 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;
+ }
+ }
+
+ if (argc == 1)
+ encoders = connectors = crtcs = modes = framebuffers = 1;
+
+ for (i = 0; i < ARRAY_SIZE(modules); i++) {
+ printf("trying to load module %s...", modules[i]);
+ fd = drmOpen(modules[i], NULL);
+ if (fd < 0)
+ printf("failed.\n");
+ else {
+ printf("success.\n");
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(modules)) {
+ fprintf(stderr, "failed to load any modules, aborting.\n");
+ return -1;
+ }
+
+ resources = drmModeGetResources(fd);
+ if (!resources) {
+ fprintf(stderr, "drmModeGetResources failed: %s\n",
+ strerror(errno));
+ drmClose(fd);
+ return 1;
+ }
+
+ dump_resource(encoders);
+ dump_resource(connectors);
+ dump_resource(crtcs);
+ dump_resource(framebuffers);
+
+ if (count > 0) {
+ long int sum = 0, usec[MAX_LOOP * MAX_BUF];
+
+ if (test_multiple_buf)
+ rotator_N_N_set_mode(con_args, count, test_vsync, usec);
+ else
+ rotator_1_N_set_mode(con_args, count, test_vsync, usec);
+
+ for (i = 0; i < MAX_LOOP * MAX_BUF; i++) {
+ printf("[%d] : %d\n", i + 1, usec[i]);
+ sum += usec[i];
+ }
+ printf("rotator : cma\n");
+ printf("avg : [%d]\n", sum / MAX_LOOP);
+ getchar();
+ }
+
+ drmModeFreeResources(resources);
+
+ return 0;
+}
diff --git a/tests/rottest/rottest.h b/tests/rottest/rottest.h
new file mode 100644
index 00000000..f86d46b2
--- /dev/null
+++ b/tests/rottest/rottest.h
@@ -0,0 +1,26 @@
+#ifndef __ROTTEST_H__
+#define __ROTTEST_H__
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+
+#define MAX_LOOP 5
+#define MAX_BUF 3
+
+struct connector {
+ uint32_t id;
+ char mode_str[64];
+ drmModeModeInfo *mode;
+ drmModeEncoder *encoder;
+ int crtc;
+ unsigned int fb_id[2], current_fb_id;
+ struct timeval start;
+
+ int swap_count;
+};
+
+extern int fd;
+
+extern void connector_find_mode(struct connector *c);
+
+#endif
diff --git a/tests/rottest/util.c b/tests/rottest/util.c
new file mode 100644
index 00000000..5520de38
--- /dev/null
+++ b/tests/rottest/util.c
@@ -0,0 +1,140 @@
+/*
+ * DRM based rotator test program
+ * Copyright 2012 Samsung Electronics
+ * YoungJun Cho <yj44.cho@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 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "exynos_drm.h"
+#include "gem.h"
+
+int util_gem_create_mmap(int fd, struct drm_exynos_gem_create *gem,
+ struct drm_exynos_gem_mmap *mmap,
+ void **addr,
+ unsigned int size)
+{
+ struct drm_gem_close args;
+
+ /* initialize structure for gem create */
+ memset(gem, 0x00, sizeof(struct drm_exynos_gem_create));
+ gem->size = size;
+
+ if (exynos_gem_create(fd, gem) < 0) {
+ fprintf(stderr, "failed to gem create: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* initialize structure for gem mmap */
+ memset(mmap, 0x00, sizeof(struct drm_exynos_gem_mmap));
+ mmap->handle = gem->handle;
+ mmap->size = gem->size;
+
+ if (exynos_gem_mmap(fd, mmap) < 0) {
+ fprintf(stderr, "failed to gem mmap: %s\n", strerror(errno));
+ memset(&args, 0x00, sizeof(struct drm_gem_close));
+ args.handle = gem->handle;
+ exynos_gem_close(fd, &args);
+ return -2;
+ }
+
+ *addr = (void *)(unsigned long)mmap->mapped;
+
+ return 0;
+}
+
+void util_draw_buffer(void *addr, unsigned int stripe,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int size)
+{
+ if (stripe == 1) {
+ int i, j;
+ unsigned int *fb_ptr;
+ div_t d;
+
+ for (j = 0; j < height; j++) {
+ fb_ptr = (unsigned int *)((char *)addr + j * stride);
+ for (i = 0; i < width; i++) {
+ d = div(i, width);
+ fb_ptr[i] = 0x00130502 * (d.quot >> 6)
+ + 0x000a1120 * (d.rem >> 6);
+ }
+ }
+ } else
+ memset(addr, 0x77, size);
+}
+
+int util_write_bmp(const char *file, const void *data, unsigned int width,
+ unsigned int height)
+{
+ int i;
+ unsigned int * blocks;
+ FILE *fp;
+ struct {
+ unsigned char magic[2];
+ } bmpfile_magic = { {'B', 'M'} };
+ struct {
+ unsigned int filesz;
+ unsigned short creator1;
+ unsigned short creator2;
+ unsigned int bmp_offset;
+ } bmpfile_header = { 0, 0, 0, 0x36 };
+ struct {
+ unsigned int header_sz;
+ unsigned int width;
+ unsigned int height;
+ unsigned short nplanes;
+ unsigned short bitspp;
+ unsigned int compress_type;
+ unsigned int bmp_bytesz;
+ unsigned int hres;
+ unsigned int vres;
+ unsigned int ncolors;
+ unsigned int nimpcolors;
+ } bmp_dib_v3_header_t = { 0x28, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0 };
+
+ fp = fopen(file, "wb");
+ if (fp == NULL) return -1;
+
+ bmpfile_header.filesz = sizeof(bmpfile_magic) + sizeof(bmpfile_header)
+ + sizeof(bmp_dib_v3_header_t) + width * height * 4;
+ bmp_dib_v3_header_t.header_sz = sizeof(bmp_dib_v3_header_t);
+ bmp_dib_v3_header_t.width = width;
+ bmp_dib_v3_header_t.height = -height;
+ bmp_dib_v3_header_t.nplanes = 1;
+ bmp_dib_v3_header_t.bmp_bytesz = width * height * 4;
+
+ fwrite(&bmpfile_magic, sizeof(bmpfile_magic), 1, fp);
+ fwrite(&bmpfile_header, sizeof(bmpfile_header), 1, fp);
+ fwrite(&bmp_dib_v3_header_t, sizeof(bmp_dib_v3_header_t), 1, fp);
+
+ blocks = (unsigned int*)data;
+ for (i = 0; i < height * width; i++)
+ fwrite(&blocks[i], 4, 1, fp);
+
+ fclose(fp);
+ return 0;
+}
diff --git a/tests/rottest/util.h b/tests/rottest/util.h
new file mode 100644
index 00000000..5b2ba98c
--- /dev/null
+++ b/tests/rottest/util.h
@@ -0,0 +1,13 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+extern int util_gem_create_mmap(int fd, struct drm_exynos_gem_create *gem,
+ struct drm_exynos_gem_mmap *mmap,
+ void **addr,
+ unsigned int size);
+extern void util_draw_buffer(void *addr, unsigned int stripe,
+ unsigned int width, unsigned int height,
+ unsigned int stride, unsigned int size);
+extern int util_write_bmp(const char *file, const void *data,
+ unsigned int width, unsigned int height);
+#endif