diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | exynos/exynos_drm.h | 201 | ||||
-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, 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 |