summaryrefslogtreecommitdiff
path: root/v4l2-mfc-example/ipp.c
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-mfc-example/ipp.c')
-rw-r--r--v4l2-mfc-example/ipp.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/v4l2-mfc-example/ipp.c b/v4l2-mfc-example/ipp.c
new file mode 100644
index 0000000..3203f39
--- /dev/null
+++ b/v4l2-mfc-example/ipp.c
@@ -0,0 +1,336 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ * Mateusz Krawczuk <m.krawczuk@samsung.com>
+ *
+ * DRM IPP operations
+ *
+ * Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <linux/videodev2.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "fimc.h"
+#include <drm/exynos_drm.h>
+
+#include <errno.h>
+#include <drm/drm_fourcc.h>
+#include "drm.h"
+#include "gem.h"
+
+#define MAX_BUF 3
+
+#ifndef DRM_FORMAT_NV12MT
+#define DRM_FORMAT_NV12MT fourcc_code('T', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane 64x32 macroblocks */
+#endif
+
+static drmEventContext event_ctx;
+
+/*
+ * exynos_drm_ipp_do_buffer is for enqueue or dequeue buffor of both types
+ *
+ */
+int exynos_drm_ipp_do_buffer(int index,int buf_type, int type,struct instance *inst)
+{
+ struct drm_exynos_ipp_queue_buf* qbuf ;
+ int d, ret;
+ for(d=0; d < DRM_IPP_MAX_BUF; d++) {
+ if (type == EXYNOS_DRM_OPS_SRC) {
+ qbuf = inst->ipp.src_buff[d];
+ qbuf->handle[EXYNOS_DRM_PLANAR_Y] = inst->drm.gem_src[index*2].handle;
+ qbuf->handle[EXYNOS_DRM_PLANAR_CB] = inst->drm.gem_src[(index*2)+1].handle;
+ }
+ else
+ qbuf = inst->ipp.dst_buff[d];
+
+ qbuf->ops_id = type;
+ qbuf->buf_type = buf_type;
+ qbuf->prop_id = inst->ipp.prop.prop_id;
+ qbuf->user_data = 0;
+ qbuf->buf_id = 0;
+ ret = ioctl(inst->drm.fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, qbuf);
+ }
+ if (ret) {
+ err("Failed to do queue the buffer.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * exynos_drm_ipp_cmd_ctrl should be launched after all buffors are queued
+ * Command for controling ipp
+ * IPP_CTRL_PLAY
+ * IPP_CTRL_STOP
+ * IPP_CTRL_PAUSE
+ * IPP_CTRL_RESUME
+ */
+int exynos_drm_ipp_cmd_ctrl(enum drm_exynos_ipp_ctrl ctrl, struct instance *inst)
+{
+ struct drm_exynos_ipp_cmd_ctrl cmd_ctrl = {0,};
+
+ cmd_ctrl.prop_id = 1;
+ cmd_ctrl.ctrl = ctrl;
+
+ if (ioctl(inst->drm.fd, DRM_IOCTL_EXYNOS_IPP_CMD_CTRL, &cmd_ctrl) < 0) {
+ err("Failed to set the control\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+/*
+ *
+ */
+int exynos_drm_ipp_display_flip(int index,struct instance *inst)
+{
+ struct timeval timeout = {.tv_sec = 3, .tv_usec = 0};
+ fd_set fds;
+ int ret;
+
+ ret = drmModePageFlip(inst->drm.fd, inst->drm.crtc[0], inst->drm.fb[0],
+ DRM_MODE_PAGE_FLIP_EVENT, (void*)(unsigned long)index);
+ if(ret) {
+
+ err("Failed to page flip: %d\n",ret);
+ return -EINVAL;
+ }
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(inst->drm.fd, &fds);
+
+ ret = select(inst->drm.fd +1, &fds, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ err("Select timed out or error occured\n");
+ return -EINVAL;
+ } else if (FD_ISSET(0, &fds)) {
+ err("Failed on select \n");
+ return -EINVAL;
+ }
+ drmHandleEvent(inst->drm.fd, &event_ctx);
+ return 0;
+}
+
+int exynos_drm_ipp_dequeue_buffors(struct instance *inst, int index)
+{
+ dbg("Dequeueing buffer %d\n", index);
+ if (exynos_drm_ipp_do_buffer(index, IPP_BUF_DEQUEUE,EXYNOS_DRM_OPS_SRC,inst))
+ err("Failed to queue source buffer\n");
+ if (exynos_drm_ipp_do_buffer(index, IPP_BUF_DEQUEUE,EXYNOS_DRM_OPS_DST,inst))
+ err("Failed to queue destination buffer\n");
+ return 0;
+}
+/*
+ *
+ */
+int exynos_drm_ipp_process_frame(struct instance *inst, int index)
+{
+ char buffer[1024];
+ int len, i;
+ struct drm_event *event;
+ struct drm_exynos_ipp_event *ipp_event;
+ int count = 20;
+ int ret;
+
+ dbg("Queueing buffer %d\n", index);
+ if (exynos_drm_ipp_do_buffer(index, IPP_BUF_ENQUEUE,EXYNOS_DRM_OPS_SRC,inst))
+ err("Failed to queue source buffer\n");
+ if (exynos_drm_ipp_do_buffer(index, IPP_BUF_ENQUEUE,EXYNOS_DRM_OPS_DST,inst))
+ err("Failed to queue destination buffer\n");
+
+ dbg("IPP process frame\n");
+ while (count) {
+ struct timeval timeout = { .tv_sec = 3, .tv_usec = 0};
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(inst->drm.fd, &fds);
+ ret = select(inst->drm.fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret <= 0) {
+ err("Failed on select - remainig attempts: %d\n", count);
+ --count;
+ continue;
+ } else if (FD_ISSET(0, &fds))
+ break;
+ /* This is painfull ... */
+ len = read(inst->drm.fd, buffer, sizeof(struct drm_exynos_ipp_event));
+ if (len >= sizeof(*event)) {
+ int src_id, dest_id;
+
+ i = 0;
+ while (i < len) {
+ event = (struct drm_event*) &buffer[i];
+
+ switch (event->type) {
+ case DRM_EXYNOS_IPP_EVENT:
+ dbg("Received event:\n");
+ ipp_event = (struct drm_exynos_ipp_event*)event;
+ src_id = ipp_event->buf_id[EXYNOS_DRM_OPS_SRC];
+ dest_id = ipp_event->buf_id[EXYNOS_DRM_OPS_DST];
+
+ exynos_drm_ipp_display_flip(dest_id, inst);
+ dbg("SRC ID: %d DEST ID: %d INDEX: %d\n", src_id, dest_id, index);
+ break;
+ default:
+ break;
+ }
+ i += event->length;
+ }
+ /* Done */
+ return 0;
+ }
+ /* Handle event */
+ --count;
+ }
+ // leave:
+ return -EINVAL;
+}
+/*
+ * Send command to stop ctrl and then clean up buffors and memory
+ */
+void exynos_drm_ipp_close(struct instance *inst)
+{
+ int i;
+ struct drm_gem_close gem_close;
+
+ memset(&gem_close, 0, sizeof(gem_close));
+ exynos_drm_ipp_cmd_ctrl( IPP_CTRL_STOP, inst);
+ for (i = 0; i < inst->mfc.cap_buf_cnt*2; ++i) {
+ gem_close.handle = inst->drm.gem_src[i].handle;
+ exynos_gem_close(inst->drm.fd, &gem_close);
+ }
+ for (i = 0; i < DRM_IPP_MAX_BUF; ++i)
+ exynos_drm_ipp_do_buffer(i, IPP_BUF_DEQUEUE, EXYNOS_DRM_OPS_DST,inst);
+ for (i = 0; i < MAX_BUFS; ++i) {
+ exynos_drm_ipp_do_buffer(i, IPP_BUF_DEQUEUE, EXYNOS_DRM_OPS_DST,inst);
+ gem_close.handle = inst->drm.gem[i].handle;
+ exynos_gem_close(inst->drm.fd, &gem_close);
+ }
+ drmModeRmFB(inst->drm.fd, (uint32_t)inst->drm.fb);
+
+ close(inst->drm.fd);
+}
+/*
+ * Set ipp properties
+ */
+int exynos_drm_ipp_setup(struct instance *inst)
+{
+ int ret;
+ struct drm_exynos_sz input_size = {inst->mfc.cap_crop_w, inst->mfc.cap_crop_h};
+
+ dbg("Exynos DRM IPP init start\n");
+
+ struct drm_exynos_pos cpos = {inst->mfc.cap_crop_left, inst->mfc.cap_crop_top, input_size.hsize, input_size.vsize};
+ struct drm_exynos_sz output_size = {inst->drm.width, inst->drm.height};
+
+ struct drm_exynos_pos spos = {0, 0, output_size.hsize, output_size.vsize};
+
+ if (inst->mfc.cap_pixfmt == V4L2_PIX_FMT_NV12MT)
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].fmt = DRM_FORMAT_NV12MT;
+ else
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].fmt = DRM_FORMAT_NV12;
+
+ inst->ipp.prop.cmd = IPP_CMD_M2M;
+ inst->ipp.prop.prop_id = 0;
+ /* input */
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].ops_id = EXYNOS_DRM_OPS_SRC;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].flip = EXYNOS_DRM_FLIP_NONE;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].degree = EXYNOS_DRM_DEGREE_0;
+ // inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].fmt = DRM_FORMAT_NV12MT;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].pos = cpos;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_SRC].sz = input_size;
+ /* output */
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_DST].ops_id = EXYNOS_DRM_OPS_DST;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_DST].flip = EXYNOS_DRM_FLIP_NONE;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_DST].degree = EXYNOS_DRM_DEGREE_0;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_DST].fmt = DRM_FORMAT_XRGB8888;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_DST].pos = spos;
+ inst->ipp.prop.config[EXYNOS_DRM_OPS_DST].sz = output_size;
+
+ ret = ioctl(inst->drm.fd, DRM_IOCTL_EXYNOS_IPP_SET_PROPERTY, &inst->ipp.prop);
+ if (ret) {
+ err("Failed to set properties [%d]\n", errno);
+ close(inst->drm.fd);
+ return -EINVAL;
+ }
+
+ dbg("Exynos DRM IPP init done\n prop_id %d\n", inst->ipp.prop.prop_id);
+ return 0;
+}
+/*
+ * Allocate and setup ipp buffors
+ */
+int exynos_drm_ipp_allocate_ipp_buffers(struct instance *inst) {
+ int ret=0, d=0;
+
+ for(d = 0; d < inst->mfc.cap_buf_cnt; d++) {
+ ret = drmPrimeFDToHandle(inst->drm.fd, inst->mfc.dbuf[d][0], &inst->drm.gem_src[d*2].handle);
+ ret = drmPrimeFDToHandle(inst->drm.fd, inst->mfc.dbuf[d][1], &inst->drm.gem_src[(d*2)+1].handle);
+ close(inst->mfc.dbuf[d][0]);
+ close(inst->mfc.dbuf[d][1]);
+ }
+
+ for (d=0; d<DRM_IPP_MAX_BUF; d++) {
+ inst->ipp.src_buff[d]= (struct drm_exynos_ipp_queue_buf *) calloc (1,sizeof(struct drm_exynos_ipp_queue_buf));
+ inst->ipp.src_buff[d]->ops_id = EXYNOS_DRM_OPS_SRC;
+ inst->ipp.src_buff[d]->buf_type = IPP_BUF_ENQUEUE;
+ inst->ipp.src_buff[d]->user_data = 0;
+ inst->ipp.src_buff[d]->prop_id = inst->ipp.prop.prop_id;
+ inst->ipp.src_buff[d]->buf_id = d;
+
+ inst->ipp.src_buff[d]->handle[EXYNOS_DRM_PLANAR_Y] = inst->drm.gem_src[d*2].handle;
+ inst->ipp.src_buff[d]->handle[EXYNOS_DRM_PLANAR_CB] = inst->drm.gem_src[(d*2)+1].handle;
+ inst->ipp.src_buff[d]->handle[EXYNOS_DRM_PLANAR_CR] = 0;
+ ret = drmIoctl(inst->drm.fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, inst->ipp.src_buff[d]);
+ if (ret) {
+ fprintf(stderr,
+ "failed to SRC DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF[id:%d][buf addr:%p] : %s\n",
+ inst->ipp.src_buff[d]->buf_id, inst->ipp.src_buff[d], strerror(errno));
+
+ return ret;
+ }
+
+ inst->ipp.dst_buff[d]= (struct drm_exynos_ipp_queue_buf *) calloc (1,sizeof(struct drm_exynos_ipp_queue_buf));
+ inst->ipp.dst_buff[d]->ops_id = EXYNOS_DRM_OPS_DST;
+ inst->ipp.dst_buff[d]->buf_type = IPP_BUF_ENQUEUE;
+ inst->ipp.dst_buff[d]->user_data = 0;
+ inst->ipp.dst_buff[d]->prop_id = inst->ipp.prop.prop_id;
+ inst->ipp.dst_buff[d]->buf_id = d;
+
+ inst->ipp.dst_buff[d]->handle[EXYNOS_DRM_PLANAR_Y] = inst->drm.gem[0].handle;
+ inst->ipp.dst_buff[d]->handle[EXYNOS_DRM_PLANAR_CB] = 0;
+ inst->ipp.dst_buff[d]->handle[EXYNOS_DRM_PLANAR_CR] = 0;
+ ret = drmIoctl(inst->drm.fd, DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF, inst->ipp.dst_buff[d]);
+ if (ret) {
+ fprintf(stderr,
+ "failed to DST DRM_IOCTL_EXYNOS_IPP_QUEUE_BUF[id:%d][buf addr:%p] : %s\n",
+ inst->ipp.src_buff[d]->buf_id, inst->ipp.src_buff[d], strerror(errno));
+
+ return ret;
+ }
+ }
+ dbg("ipp_setup_output_from_mfc\n");
+ return 0;
+}