CHROMIUM: [media] rk3288-vpu: Implement basic V4L2 APIs for decoder
authorTomasz Figa <tfiga@chromium.org>
Mon, 1 Dec 2014 10:42:38 +0000 (19:42 +0900)
committerHuang, Tao <huangtao@rock-chips.com>
Thu, 30 Jun 2016 11:50:19 +0000 (19:50 +0800)
This patch adds implementation of basic V4L2 APIs for decoder part of
the driver. It is still missing the hardware specific part, including
the hardware specific controls containing certain data precalculated
in userspace.

BUG=chrome-os-partner:33728
TEST=compile

Signed-off-by: Tomasz Figa <tfiga@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/237617
Reviewed-by: Pawel Osciak <posciak@chromium.org>
Change-Id: I3cad74aad3c6c4ac5e6b2123b5d93e3173b74c7a
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
drivers/media/platform/rk3288-vpu/Makefile
drivers/media/platform/rk3288-vpu/rk3288_vpu.c
drivers/media/platform/rk3288-vpu/rk3288_vpu_common.h
drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.c [new file with mode: 0644]
drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.h [new file with mode: 0644]

index 3eb058bc5fdeb2a0f53771e436600b5c41768707..ac7a172eaf7670f25297238fcc73f67370437d45 100644 (file)
@@ -2,6 +2,7 @@
 obj-$(CONFIG_VIDEO_RK3288_VPU) += rk3288-vpu.o
 
 rk3288-vpu-y += rk3288_vpu.o \
+               rk3288_vpu_dec.o \
                rk3288_vpu_enc.o \
                rk3288_vpu_hw.o \
                rk3288_vpu_hw_vp8e.o
index a217a8facce4886f8045426e6c7b6960c48f8a90..c7e5d9918671046ff88c17ad40fceefa48918d57 100644 (file)
@@ -31,6 +31,7 @@
 #include <media/videobuf2-core.h>
 #include <media/videobuf2-dma-contig.h>
 
+#include "rk3288_vpu_dec.h"
 #include "rk3288_vpu_enc.h"
 #include "rk3288_vpu_hw.h"
 
@@ -340,6 +341,13 @@ static int rk3288_vpu_open(struct file *filp)
                        vpu_err("Failed to initialize encoder context\n");
                        goto err_fh_free;
                }
+       } else if (vdev == dev->vfd_dec) {
+               /* only for decoder */
+               ret = rk3288_vpu_dec_init(ctx);
+               if (ret) {
+                       vpu_err("Failed to initialize decoder context\n");
+                       goto err_fh_free;
+               }
        } else {
                ret = -ENOENT;
                goto err_fh_free;
@@ -356,6 +364,8 @@ static int rk3288_vpu_open(struct file *filp)
 
        if (vdev == dev->vfd_enc)
                q->ops = get_enc_queue_ops();
+       else if (vdev == dev->vfd_dec)
+               q->ops = get_dec_queue_ops();
 
        q->mem_ops = &vb2_dma_contig_memops;
        q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -376,6 +386,8 @@ static int rk3288_vpu_open(struct file *filp)
 
        if (vdev == dev->vfd_enc)
                q->ops = get_enc_queue_ops();
+       else if (vdev == dev->vfd_dec)
+               q->ops = get_dec_queue_ops();
 
        q->mem_ops = &vb2_dma_contig_memops;
        q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -395,6 +407,8 @@ err_vq_dst_release:
 err_enc_dec_exit:
        if (vdev == dev->vfd_enc)
                rk3288_vpu_enc_exit(ctx);
+       else if (vdev == dev->vfd_dec)
+               rk3288_vpu_dec_exit(ctx);
 err_fh_free:
        v4l2_fh_del(&ctx->fh);
        v4l2_fh_exit(&ctx->fh);
@@ -431,6 +445,8 @@ static int rk3288_vpu_release(struct file *filp)
 
        if (vdev == dev->vfd_enc)
                rk3288_vpu_enc_exit(ctx);
+       else if (vdev == dev->vfd_dec)
+               rk3288_vpu_dec_exit(ctx);
 
        kfree(ctx);
 
@@ -607,10 +623,44 @@ static int rk3288_vpu_probe(struct platform_device *pdev)
                "Rockchip RK3288 VPU encoder registered as /vpu/video%d\n",
                vfd->num);
 
+       /* decoder */
+       vfd = video_device_alloc();
+       if (!vfd) {
+               v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n");
+               ret = -ENOMEM;
+               goto err_dec_alloc;
+       }
+
+       vfd->fops = &rk3288_vpu_fops;
+       vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
+       vfd->release = video_device_release;
+       vfd->lock = &vpu->vpu_mutex;
+       vfd->v4l2_dev = &vpu->v4l2_dev;
+       vfd->vfl_dir = VFL_DIR_M2M;
+       snprintf(vfd->name, sizeof(vfd->name), "%s", RK3288_VPU_DEC_NAME);
+       vpu->vfd_dec = vfd;
+
+       video_set_drvdata(vfd, vpu);
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+       if (ret) {
+               v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n");
+               video_device_release(vfd);
+               goto err_dec_reg;
+       }
+
+       v4l2_info(&vpu->v4l2_dev,
+               "Rockchip RK3288 VPU decoder registered as /vpu/video%d\n",
+               vfd->num);
+
        vpu_debug_leave();
 
        return 0;
 
+err_dec_reg:
+       video_device_release(vpu->vfd_dec);
+err_dec_alloc:
+       video_unregister_device(vpu->vfd_enc);
 err_enc_reg:
        video_device_release(vpu->vfd_enc);
 err_enc_alloc:
@@ -640,6 +690,7 @@ static int rk3288_vpu_remove(struct platform_device *pdev)
         * contexts have been released.
         */
 
+       video_unregister_device(vpu->vfd_dec);
        video_unregister_device(vpu->vfd_enc);
        v4l2_device_unregister(&vpu->v4l2_dev);
        vb2_dma_contig_cleanup_ctx(vpu->alloc_ctx);
index d4d58ff46ad1fc10ce06efb489f07fe459d6310f..32ae67f21ae880de1ba76c98357b7cec9878e6f4 100644 (file)
@@ -197,6 +197,22 @@ struct rk3288_vpu_vp8e_run {
        const struct rk3288_vp8e_reg_params *reg_params;
 };
 
+/**
+ * struct rk3288_vpu_h264d_run - per-run data specific to H264 decoding.
+ * @sps:               Pointer to a buffer containing H264 SPS.
+ * @pps:               Pointer to a buffer containing H264 PPS.
+ * @scaling_matrix:    Pointer to a buffer containing scaling matrix.
+ * @slice_param:       Pointer to a buffer containing slice parameters array.
+ * @decode_param:      Pointer to a buffer containing decode parameters.
+ */
+struct rk3288_vpu_h264d_run {
+       const struct v4l2_ctrl_h264_sps *sps;
+       const struct v4l2_ctrl_h264_pps *pps;
+       const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix;
+       const struct v4l2_ctrl_h264_slice_param *slice_param;
+       const struct v4l2_ctrl_h264_decode_param *decode_param;
+};
+
 /**
  * struct rk3288_vpu_run - per-run data for hardware code.
  * @src:               Source buffer to be processed.
@@ -215,6 +231,7 @@ struct rk3288_vpu_run {
        /* Specific for particular operating modes. */
        union {
                struct rk3288_vpu_vp8e_run vp8e;
+               struct rk3288_vpu_h264d_run h264d;
                /* Other modes will need different data. */
        };
 };
@@ -235,6 +252,7 @@ struct rk3288_vpu_run {
  * @src_crop:          Configured source crop rectangle (encoder-only).
  * @vq_dst:            Videobuf2 destination queue
  * @dst_queue:         Internal destination buffer queue.
+ * @dst_bufs:          Private buffers wrapping VB2 buffers (destination).
  *
  * @ctrls:             Array containing pointer to registered controls.
  * @ctrl_handler:      Control handler used to register controls.
@@ -263,6 +281,7 @@ struct rk3288_vpu_ctx {
        struct v4l2_rect src_crop;
        struct vb2_queue vq_dst;
        struct list_head dst_queue;
+       struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
 
        /* Controls */
        struct v4l2_ctrl *ctrls[RK3288_VPU_MAX_CTRLS];
diff --git a/drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.c b/drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.c
new file mode 100644 (file)
index 0000000..db8a168
--- /dev/null
@@ -0,0 +1,996 @@
+/*
+ * Rockchip RK3288 VPU codec driver
+ *
+ * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
+ *     Hertz Wong <hertz.wong@rock-chips.com>
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *     Tomasz Figa <tfiga@chromium.org>
+ *
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "rk3288_vpu_common.h"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "rk3288_vpu_dec.h"
+#include "rk3288_vpu_hw.h"
+
+#define DEF_SRC_FMT_DEC                                V4L2_PIX_FMT_H264_SLICE
+#define DEF_DST_FMT_DEC                                V4L2_PIX_FMT_NV12
+
+#define RK3288_H264_MAX_SLICES_PER_FRAME       16
+
+static struct rk3288_vpu_fmt formats[] = {
+       {
+               .name = "4:2:0 1 plane Y/CbCr",
+               .fourcc = V4L2_PIX_FMT_NV12,
+               .codec_mode = RK_VPU_CODEC_NONE,
+               .num_planes = 1,
+               .depth = { 12 },
+       },
+       {
+               .name = "Slices of H264 Encoded Stream",
+               .fourcc = V4L2_PIX_FMT_H264_SLICE,
+               .codec_mode = RK_VPU_CODEC_H264D,
+               .num_planes = 1,
+       },
+};
+
+static struct rk3288_vpu_fmt *find_format(struct v4l2_format *f, bool bitstream)
+{
+       unsigned int i;
+
+       vpu_debug_enter();
+
+       for (i = 0; i < ARRAY_SIZE(formats); i++) {
+               if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+                   !!bitstream == (formats[i].codec_mode != RK_VPU_CODEC_NONE))
+                       return &formats[i];
+       }
+
+       return NULL;
+}
+
+/* Indices of controls that need to be accessed directly. */
+enum {
+       RK3288_VPU_DEC_CTRL_H264_SPS,
+       RK3288_VPU_DEC_CTRL_H264_PPS,
+       RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX,
+       RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM,
+       RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM,
+};
+
+static struct rk3288_vpu_control controls[] = {
+       /* H264 slice-based interface. */
+       [RK3288_VPU_DEC_CTRL_H264_SPS] = {
+               .id = V4L2_CID_MPEG_VIDEO_H264_SPS,
+               .type = V4L2_CTRL_TYPE_PRIVATE,
+               .name = "H264 SPS Parameters",
+               .elem_size = sizeof(struct v4l2_ctrl_h264_sps),
+               .max_stores = VIDEO_MAX_FRAME,
+               .can_store = true,
+       },
+       [RK3288_VPU_DEC_CTRL_H264_PPS] = {
+               .id = V4L2_CID_MPEG_VIDEO_H264_PPS,
+               .type = V4L2_CTRL_TYPE_PRIVATE,
+               .name = "H264 PPS Parameters",
+               .elem_size = sizeof(struct v4l2_ctrl_h264_pps),
+               .max_stores = VIDEO_MAX_FRAME,
+               .can_store = true,
+       },
+       [RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX] = {
+               .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX,
+               .type = V4L2_CTRL_TYPE_PRIVATE,
+               .name = "H264 Scaling Matrix",
+               .elem_size = sizeof(struct v4l2_ctrl_h264_scaling_matrix),
+               .max_stores = VIDEO_MAX_FRAME,
+               .can_store = true,
+       },
+       [RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM] = {
+               .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM,
+               .type = V4L2_CTRL_TYPE_PRIVATE,
+               .name = "H264 Slice Parameters",
+               .max_stores = VIDEO_MAX_FRAME,
+               .elem_size = sizeof(struct v4l2_ctrl_h264_slice_param),
+               .dims = { RK3288_H264_MAX_SLICES_PER_FRAME, },
+               .can_store = true,
+       },
+       [RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM] = {
+               .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM,
+               .type = V4L2_CTRL_TYPE_PRIVATE,
+               .name = "H264 Decode Parameters",
+               .max_stores = VIDEO_MAX_FRAME,
+               .elem_size = sizeof(struct v4l2_ctrl_h264_decode_param),
+               .can_store = true,
+       },
+};
+
+static inline const void *get_ctrl_ptr(struct rk3288_vpu_ctx *ctx, unsigned id)
+{
+       struct v4l2_ctrl *ctrl = ctx->ctrls[id];
+
+       return ctrl->p_cur.p;
+}
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+                          struct v4l2_capability *cap)
+{
+       struct rk3288_vpu_dev *dev = video_drvdata(file);
+
+       vpu_debug_enter();
+
+       strlcpy(cap->driver, RK3288_VPU_DEC_NAME, sizeof(cap->driver));
+       strlcpy(cap->card, dev->pdev->name, sizeof(cap->card));
+       strlcpy(cap->bus_info, "platform:" RK3288_VPU_NAME,
+               sizeof(cap->bus_info));
+
+       /*
+        * This is only a mem-to-mem video device. The capture and output
+        * device capability flags are left only for backward compatibility
+        * and are scheduled for removal.
+        */
+       cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+           V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+       vpu_debug_leave();
+
+       return 0;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool out)
+{
+       struct rk3288_vpu_fmt *fmt;
+       int i, j = 0;
+
+       vpu_debug_enter();
+
+       for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+               if (out && formats[i].codec_mode == RK_VPU_CODEC_NONE)
+                       continue;
+               else if (!out && (formats[i].codec_mode != RK_VPU_CODEC_NONE))
+                       continue;
+
+               if (j == f->index) {
+                       fmt = &formats[i];
+                       strlcpy(f->description, fmt->name,
+                               sizeof(f->description));
+                       f->pixelformat = fmt->fourcc;
+
+                       f->flags = 0;
+                       if (formats[i].codec_mode != RK_VPU_CODEC_NONE)
+                               f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+
+                       vpu_debug_leave();
+
+                       return 0;
+               }
+
+               ++j;
+       }
+
+       vpu_debug_leave();
+
+       return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+                                         struct v4l2_fmtdesc *f)
+{
+       return vidioc_enum_fmt(f, false);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
+                                         struct v4l2_fmtdesc *f)
+{
+       return vidioc_enum_fmt(f, true);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+
+       vpu_debug_enter();
+
+       vpu_debug(4, "f->type = %d\n", f->type);
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               f->fmt.pix_mp = ctx->dst_fmt;
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               f->fmt.pix_mp = ctx->src_fmt;
+               break;
+
+       default:
+               vpu_err("invalid buf type\n");
+               return -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct rk3288_vpu_fmt *fmt;
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+       char str[5];
+
+       vpu_debug_enter();
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str));
+
+               fmt = find_format(f, true);
+               if (!fmt) {
+                       vpu_err("failed to try output format\n");
+                       return -EINVAL;
+               }
+
+               if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+                       vpu_err("sizeimage of output format must be given\n");
+                       return -EINVAL;
+               }
+
+               pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               vpu_debug(4, "%s\n", fmt2str(f->fmt.pix_mp.pixelformat, str));
+
+               fmt = find_format(f, false);
+               if (!fmt) {
+                       vpu_err("failed to try capture format\n");
+                       return -EINVAL;
+               }
+
+               if (fmt->num_planes != pix_fmt_mp->num_planes) {
+                       vpu_err("plane number mismatches on capture format\n");
+                       return -EINVAL;
+               }
+
+               /* Limit to hardware min/max. */
+               v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 0,
+                                     &pix_fmt_mp->height, 4, 1088, 0, 0);
+
+               /* Round up to macroblocks. */
+               pix_fmt_mp->width = round_up(pix_fmt_mp->width, MB_DIM);
+               pix_fmt_mp->height = round_up(pix_fmt_mp->height, MB_DIM);
+               break;
+
+       default:
+               vpu_err("invalid buf type\n");
+               return -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       unsigned int mb_width, mb_height;
+       struct rk3288_vpu_fmt *fmt;
+       int ret = 0;
+       int i;
+
+       vpu_debug_enter();
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               /* Change not allowed if any queue is streaming. */
+               if (vb2_is_streaming(&ctx->vq_src)
+                   || vb2_is_streaming(&ctx->vq_dst)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               /*
+                * Pixel format change is not allowed when the other queue has
+                * buffers allocated.
+                */
+               if (vb2_is_busy(&ctx->vq_dst)
+                   && pix_fmt_mp->pixelformat != ctx->src_fmt.pixelformat) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+
+               ret = vidioc_try_fmt(file, priv, f);
+               if (ret)
+                       goto out;
+
+               ctx->vpu_src_fmt = find_format(f, true);
+               ctx->src_fmt = *pix_fmt_mp;
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               /*
+                * Change not allowed if this queue is streaming.
+                *
+                * NOTE: We allow changes with source queue streaming
+                * to support resolution change in decoded stream.
+                */
+               if (vb2_is_streaming(&ctx->vq_dst)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               /*
+                * Pixel format change is not allowed when the other queue has
+                * buffers allocated.
+                */
+               if (vb2_is_busy(&ctx->vq_src)
+                   && pix_fmt_mp->pixelformat != ctx->dst_fmt.pixelformat) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+
+               ret = vidioc_try_fmt(file, priv, f);
+               if (ret)
+                       goto out;
+
+               fmt = find_format(f, false);
+               ctx->vpu_dst_fmt = fmt;
+
+               mb_width = MB_WIDTH(pix_fmt_mp->width);
+               mb_height = MB_HEIGHT(pix_fmt_mp->height);
+
+               vpu_debug(0, "CAPTURE codec mode: %d\n", fmt->codec_mode);
+               vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n",
+                         pix_fmt_mp->width, pix_fmt_mp->width,
+                         mb_width, mb_height);
+
+               for (i = 0; i < fmt->num_planes; ++i) {
+                       pix_fmt_mp->plane_fmt[i].bytesperline =
+                               mb_width * MB_DIM * fmt->depth[i] / 8;
+                       pix_fmt_mp->plane_fmt[i].sizeimage =
+                               pix_fmt_mp->plane_fmt[i].bytesperline
+                               * mb_height * MB_DIM;
+                       /*
+                        * All of multiplanar formats we support have chroma
+                        * planes subsampled by 2.
+                        */
+                       if (i != 0)
+                               pix_fmt_mp->plane_fmt[i].sizeimage /= 2;
+               }
+
+               ctx->dst_fmt = *pix_fmt_mp;
+               break;
+
+       default:
+               vpu_err("invalid buf type\n");
+               return -EINVAL;
+       }
+
+out:
+       vpu_debug_leave();
+
+       return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *reqbufs)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       vpu_debug_enter();
+
+       switch (reqbufs->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+               if (ret != 0) {
+                       vpu_err("error in vb2_reqbufs() for E(S)\n");
+                       goto out;
+               }
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+               if (ret != 0) {
+                       vpu_err("error in vb2_reqbufs() for E(D)\n");
+                       goto out;
+               }
+               break;
+
+       default:
+               vpu_err("invalid buf type\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+out:
+       vpu_debug_leave();
+
+       return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+                          struct v4l2_buffer *buf)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       vpu_debug_enter();
+
+       switch (buf->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_querybuf(&ctx->vq_dst, buf);
+               if (ret != 0) {
+                       vpu_err("error in vb2_querybuf() for E(D)\n");
+                       goto out;
+               }
+
+               buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_querybuf(&ctx->vq_src, buf);
+               if (ret != 0) {
+                       vpu_err("error in vb2_querybuf() for E(S)\n");
+                       goto out;
+               }
+               break;
+
+       default:
+               vpu_err("invalid buf type\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+out:
+       vpu_debug_leave();
+
+       return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+       int i;
+
+       vpu_debug_enter();
+
+       for (i = 0; i < buf->length; i++)
+               vpu_debug(4, "plane[%d]->length %d bytesused %d\n",
+                               i, buf->m.planes[i].length,
+                               buf->m.planes[i].bytesused);
+
+       switch (buf->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_qbuf(&ctx->vq_src, buf);
+               vpu_debug(4, "OUTPUT_MPLANE : vb2_qbuf return %d\n", ret);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_qbuf(&ctx->vq_dst, buf);
+               vpu_debug(4, "CAPTURE_MPLANE: vb2_qbuf return %d\n", ret);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       vpu_debug_enter();
+
+       switch (buf->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+/* Export DMA buffer */
+static int vidioc_expbuf(struct file *file, void *priv,
+                        struct v4l2_exportbuffer *eb)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       vpu_debug_enter();
+
+       switch (eb->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_expbuf(&ctx->vq_src, eb);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_expbuf(&ctx->vq_dst, eb);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+                          enum v4l2_buf_type type)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       vpu_debug_enter();
+
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_streamon(&ctx->vq_src, type);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_streamon(&ctx->vq_dst, type);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+                           enum v4l2_buf_type type)
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(priv);
+       int ret;
+
+       vpu_debug_enter();
+
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               ret = vb2_streamoff(&ctx->vq_src, type);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               ret = vb2_streamoff(&ctx->vq_dst, type);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+static int rk3288_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct rk3288_vpu_ctx *ctx = ctrl_to_ctx(ctrl);
+       struct rk3288_vpu_dev *dev = ctx->dev;
+       int ret = 0;
+
+       vpu_debug_enter();
+
+       vpu_debug(4, "ctrl id %d\n", ctrl->id);
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_VIDEO_H264_SPS:
+       case V4L2_CID_MPEG_VIDEO_H264_PPS:
+       case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX:
+       case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM:
+       case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM:
+               /* These controls are used directly. */
+               break;
+
+       default:
+               v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
+                        ctrl->id, ctrl->val);
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+static const struct v4l2_ctrl_ops rk3288_vpu_dec_ctrl_ops = {
+       .s_ctrl = rk3288_vpu_dec_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops rk3288_vpu_dec_ioctl_ops = {
+       .vidioc_querycap = vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+       .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+       .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+       .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+       .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+       .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+       .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+       .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+       .vidioc_reqbufs = vidioc_reqbufs,
+       .vidioc_querybuf = vidioc_querybuf,
+       .vidioc_qbuf = vidioc_qbuf,
+       .vidioc_dqbuf = vidioc_dqbuf,
+       .vidioc_expbuf = vidioc_expbuf,
+       .vidioc_streamon = vidioc_streamon,
+       .vidioc_streamoff = vidioc_streamoff,
+};
+
+static int rk3288_vpu_queue_setup(struct vb2_queue *vq,
+                                 const struct v4l2_format *fmt,
+                                 unsigned int *buf_count,
+                                 unsigned int *plane_count,
+                                 unsigned int psize[], void *allocators[])
+{
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+       int ret = 0;
+
+       vpu_debug_enter();
+
+       switch (vq->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               *plane_count = ctx->vpu_src_fmt->num_planes;
+
+               if (*buf_count < 1)
+                       *buf_count = 1;
+
+               if (*buf_count > VIDEO_MAX_FRAME)
+                       *buf_count = VIDEO_MAX_FRAME;
+
+               psize[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
+               allocators[0] = ctx->dev->alloc_ctx;
+               vpu_debug(0, "output psize[%d]: %d\n", 0, psize[0]);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               *plane_count = ctx->vpu_dst_fmt->num_planes;
+
+               if (*buf_count < 1)
+                       *buf_count = 1;
+
+               if (*buf_count > VIDEO_MAX_FRAME)
+                       *buf_count = VIDEO_MAX_FRAME;
+
+               psize[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8);
+               allocators[0] = ctx->dev->alloc_ctx;
+
+               if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE)
+                       /* Add space for appended motion vectors. */
+                       psize[0] += 64 * MB_WIDTH(ctx->dst_fmt.width)
+                                       * MB_HEIGHT(ctx->dst_fmt.height);
+
+               vpu_debug(0, "capture psize[%d]: %d\n", 0, psize[0]);
+               break;
+
+       default:
+               vpu_err("invalid queue type: %d\n", vq->type);
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+static int rk3288_vpu_buf_init(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+       vpu_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+               ctx->dst_bufs[vb->v4l2_buf.index] = vb;
+
+       vpu_debug_leave();
+
+       return 0;
+}
+
+static void rk3288_vpu_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+       vpu_debug_enter();
+
+       if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+               ctx->dst_bufs[vb->v4l2_buf.index] = NULL;
+
+       vpu_debug_leave();
+}
+
+static int rk3288_vpu_buf_prepare(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+       int ret = 0;
+       int i;
+
+       vpu_debug_enter();
+
+       switch (vq->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               vpu_debug(4, "plane size: %ld, dst size: %d\n",
+                               vb2_plane_size(vb, 0),
+                               ctx->src_fmt.plane_fmt[0].sizeimage);
+
+               if (vb2_plane_size(vb, 0)
+                   < ctx->src_fmt.plane_fmt[0].sizeimage) {
+                       vpu_err("plane size is too small for output\n");
+                       ret = -EINVAL;
+               }
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) {
+                       vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", i,
+                                       vb2_plane_size(vb, i),
+                                       ctx->dst_fmt.plane_fmt[i].sizeimage);
+
+                       if (vb2_plane_size(vb, i)
+                           < ctx->dst_fmt.plane_fmt[i].sizeimage) {
+                               vpu_err("size of plane %d is too small for capture\n",
+                                       i);
+                               break;
+                       }
+               }
+
+               if (i != ctx->vpu_dst_fmt->num_planes)
+                       ret = -EINVAL;
+               break;
+
+       default:
+               vpu_err("invalid queue type: %d\n", vq->type);
+               ret = -EINVAL;
+       }
+
+       vpu_debug_leave();
+
+       return ret;
+}
+
+static int rk3288_vpu_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       int ret = 0;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv);
+       struct rk3288_vpu_dev *dev = ctx->dev;
+       bool ready = false;
+
+       vpu_debug_enter();
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               ret = rk3288_vpu_init(ctx);
+               if (ret < 0) {
+                       vpu_err("rk3288_vpu_init failed\n");
+                       return ret;
+               }
+
+               ready = vb2_is_streaming(&ctx->vq_src);
+       } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               ready = vb2_is_streaming(&ctx->vq_dst);
+       }
+
+       if (ready)
+               rk3288_vpu_try_context(dev, ctx);
+
+       vpu_debug_leave();
+
+       return 0;
+}
+
+static void rk3288_vpu_stop_streaming(struct vb2_queue *q)
+{
+       unsigned long flags;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(q->drv_priv);
+       struct rk3288_vpu_dev *dev = ctx->dev;
+       struct rk3288_vpu_buf *b;
+       LIST_HEAD(queue);
+       int i;
+
+       vpu_debug_enter();
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+
+       list_del_init(&ctx->list);
+
+       switch (q->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               list_splice_init(&ctx->dst_queue, &queue);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               list_splice_init(&ctx->src_queue, &queue);
+               break;
+
+       default:
+               break;
+       }
+
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+
+       wait_event(dev->run_wq, dev->current_ctx != ctx);
+
+       while (!list_empty(&queue)) {
+               b = list_first_entry(&queue, struct rk3288_vpu_buf, list);
+               for (i = 0; i < b->b.num_planes; i++)
+                       vb2_set_plane_payload(&b->b, i, 0);
+               vb2_buffer_done(&b->b, VB2_BUF_STATE_ERROR);
+               list_del(&b->list);
+       }
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+               rk3288_vpu_deinit(ctx);
+
+       vpu_debug_leave();
+}
+
+static void rk3288_vpu_buf_queue(struct vb2_buffer *vb)
+{
+       struct vb2_queue *vq = vb->vb2_queue;
+       struct rk3288_vpu_ctx *ctx = fh_to_ctx(vq->drv_priv);
+       struct rk3288_vpu_dev *dev = ctx->dev;
+       struct rk3288_vpu_buf *vpu_buf;
+       unsigned long flags;
+
+       vpu_debug_enter();
+
+       switch (vq->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+               vpu_buf = vb_to_buf(vb);
+
+               /* Mark destination as available for use by VPU */
+               spin_lock_irqsave(&dev->irqlock, flags);
+
+               list_add_tail(&vpu_buf->list, &ctx->dst_queue);
+
+               spin_unlock_irqrestore(&dev->irqlock, flags);
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+               vpu_buf = vb_to_buf(vb);
+
+               spin_lock_irqsave(&dev->irqlock, flags);
+
+               list_add_tail(&vpu_buf->list, &ctx->src_queue);
+
+               spin_unlock_irqrestore(&dev->irqlock, flags);
+               break;
+
+       default:
+               vpu_err("unsupported buffer type (%d)\n", vq->type);
+       }
+
+       if (vb2_is_streaming(&ctx->vq_src) && vb2_is_streaming(&ctx->vq_dst))
+               rk3288_vpu_try_context(dev, ctx);
+
+       vpu_debug_leave();
+}
+
+static struct vb2_ops rk3288_vpu_dec_qops = {
+       .queue_setup = rk3288_vpu_queue_setup,
+       .wait_prepare = vb2_ops_wait_prepare,
+       .wait_finish = vb2_ops_wait_finish,
+       .buf_init = rk3288_vpu_buf_init,
+       .buf_prepare = rk3288_vpu_buf_prepare,
+       .buf_cleanup = rk3288_vpu_buf_cleanup,
+       .start_streaming = rk3288_vpu_start_streaming,
+       .stop_streaming = rk3288_vpu_stop_streaming,
+       .buf_queue = rk3288_vpu_buf_queue,
+};
+
+struct vb2_ops *get_dec_queue_ops(void)
+{
+       return &rk3288_vpu_dec_qops;
+}
+
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
+{
+       return &rk3288_vpu_dec_ioctl_ops;
+}
+
+static void rk3288_vpu_dec_prepare_run(struct rk3288_vpu_ctx *ctx)
+{
+       struct v4l2_buffer *src = &ctx->run.src->b.v4l2_buf;
+
+       v4l2_ctrl_apply_store(&ctx->ctrl_handler, src->config_store);
+
+       ctx->run.h264d.sps = get_ctrl_ptr(ctx, RK3288_VPU_DEC_CTRL_H264_SPS);
+       ctx->run.h264d.pps = get_ctrl_ptr(ctx, RK3288_VPU_DEC_CTRL_H264_PPS);
+       ctx->run.h264d.scaling_matrix = get_ctrl_ptr(ctx,
+                               RK3288_VPU_DEC_CTRL_H264_SCALING_MATRIX);
+       ctx->run.h264d.slice_param = get_ctrl_ptr(ctx,
+                               RK3288_VPU_DEC_CTRL_H264_SLICE_PARAM);
+       ctx->run.h264d.decode_param = get_ctrl_ptr(ctx,
+                               RK3288_VPU_DEC_CTRL_H264_DECODE_PARAM);
+}
+
+static void rk3288_vpu_dec_run_done(struct rk3288_vpu_ctx *ctx,
+                                   enum vb2_buffer_state result)
+{
+       struct v4l2_plane_pix_format *plane_fmts = ctx->dst_fmt.plane_fmt;
+       struct vb2_buffer *dst = &ctx->run.dst->b;
+       int i;
+
+       if (result != VB2_BUF_STATE_DONE) {
+               /* Assume no payload after failed run. */
+               for (i = 0; i < dst->num_planes; ++i)
+                       vb2_set_plane_payload(dst, i, 0);
+               return;
+       }
+
+       for (i = 0; i < dst->num_planes; ++i)
+               vb2_set_plane_payload(dst, i, plane_fmts[i].sizeimage);
+}
+
+static const struct rk3288_vpu_run_ops rk3288_vpu_dec_run_ops = {
+       .prepare_run = rk3288_vpu_dec_prepare_run,
+       .run_done = rk3288_vpu_dec_run_done,
+};
+
+int rk3288_vpu_dec_init(struct rk3288_vpu_ctx *ctx)
+{
+       struct v4l2_format f;
+
+       f.fmt.pix_mp.pixelformat = DEF_SRC_FMT_DEC;
+       ctx->vpu_src_fmt = find_format(&f, false);
+       f.fmt.pix_mp.pixelformat = DEF_DST_FMT_DEC;
+       ctx->vpu_dst_fmt = find_format(&f, true);
+
+       ctx->run_ops = &rk3288_vpu_dec_run_ops;
+
+       return rk3288_vpu_ctrls_setup(ctx, &rk3288_vpu_dec_ctrl_ops,
+                                       controls, ARRAY_SIZE(controls), NULL);
+}
+
+void rk3288_vpu_dec_exit(struct rk3288_vpu_ctx *ctx)
+{
+       rk3288_vpu_ctrls_delete(ctx);
+}
diff --git a/drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.h b/drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.h
new file mode 100644 (file)
index 0000000..ac25987
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Rockchip RK3288 VPU codec driver
+ *
+ * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
+ *     Hertz Wong <hertz.wong@rock-chips.com>
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *     Tomasz Figa <tfiga@chromium.org>
+ *
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef RK3288_VPU_DEC_H_
+#define RK3288_VPU_DEC_H_
+
+struct vb2_ops *get_dec_queue_ops(void);
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
+struct rk3288_vpu_fmt *get_dec_def_fmt(bool src);
+int rk3288_vpu_dec_init(struct rk3288_vpu_ctx *ctx);
+void rk3288_vpu_dec_exit(struct rk3288_vpu_ctx *ctx);
+
+#endif /* RK3288_VPU_DEC_H_ */