diff options
author | Inki Dae <inki.dae@samsung.com> | 2014-05-08 21:03:20 +0900 |
---|---|---|
committer | Quanxian Wang <quanxian.wang@intel.com> | 2014-10-15 12:07:06 +0800 |
commit | 52de1c57e2e6f3b2a1259478d5ae260ed4c5706e (patch) | |
tree | b7e7d460bb2ebf024d53d883012b340d653fc23b | |
parent | 12528efc426d3f4b88fee165baf99f4a9ddf264b (diff) | |
download | libdrm-52de1c57e2e6f3b2a1259478d5ae260ed4c5706e.tar.gz libdrm-52de1c57e2e6f3b2a1259478d5ae260ed4c5706e.tar.bz2 libdrm-52de1c57e2e6f3b2a1259478d5ae260ed4c5706e.zip |
ipptest: add fimc and rotator test applications
This patch adds two applications to test fimc and rotator
device drivers.
These devices are controlled by IPP(Integrated Post Processor)
framework of Exynos drm, and this patch updates IPP relevant
header and includes relevant test applications.
Change-Id: I3a0384375a082855162f5821c000389e234d5c15
Signed-off-by: Inki Dae <inki.dae@samsung.com>
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | exynos/exynos_drm.h | 215 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/ipptest/Makefile.am | 19 | ||||
-rw-r--r-- | tests/ipptest/fimc.c | 593 | ||||
-rw-r--r-- | tests/ipptest/fimc.h | 11 | ||||
-rw-r--r-- | tests/ipptest/fimctest.c | 477 | ||||
-rw-r--r-- | tests/ipptest/fimctest.h | 27 | ||||
-rw-r--r-- | tests/ipptest/gem.c | 72 | ||||
-rw-r--r-- | tests/ipptest/gem.h | 8 | ||||
-rw-r--r-- | tests/ipptest/util.c | 132 | ||||
-rw-r--r-- | tests/ipptest/util.h | 12 | ||||
-rw-r--r-- | tests/rottest/Makefile.am | 19 | ||||
-rw-r--r-- | tests/rottest/gem.c | 72 | ||||
-rw-r--r-- | tests/rottest/gem.h | 8 | ||||
-rw-r--r-- | tests/rottest/rotator.c | 605 | ||||
-rw-r--r-- | tests/rottest/rotator.h | 9 | ||||
-rw-r--r-- | tests/rottest/rottest.c | 464 | ||||
-rw-r--r-- | tests/rottest/rottest.h | 26 | ||||
-rw-r--r-- | tests/rottest/util.c | 140 | ||||
-rw-r--r-- | tests/rottest/util.h | 13 |
21 files changed, 2927 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index 7e9d2fcf..c0fdd98c 100644 --- a/configure.ac +++ b/configure.ac @@ -507,6 +507,10 @@ AC_CONFIG_FILES([ tests/radeon/Makefile tests/vbltest/Makefile tests/exynos/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 c3c6579e..b31d22f2 100644 --- a/exynos/exynos_drm.h +++ b/exynos/exynos_drm.h @@ -163,10 +163,185 @@ 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 #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 #define DRM_EXYNOS_GEM_MMAP 0x02 -/* Reserved 0x04 ~ 0x05 for exynos specific gem ioctl */ +/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ #define DRM_EXYNOS_GEM_GET 0x04 #define DRM_EXYNOS_VIDI_CONNECTION 0x07 @@ -175,6 +350,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) @@ -197,4 +378,36 @@ 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; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 cmdlist_no; + __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 cd114913..895e8e8b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,8 @@ endif if HAVE_EXYNOS SUBDIRS += exynos +SUBDIRS += ipptest +SUBDIRS += rottest endif if HAVE_LIBUDEV 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 |