diff --git a/drivers/media/platform/rk3288-vpu/Makefile b/drivers/media/platform/rk3288-vpu/Makefile index 3eb058bc5fde..ac7a172eaf76 100644 --- a/drivers/media/platform/rk3288-vpu/Makefile +++ b/drivers/media/platform/rk3288-vpu/Makefile @@ -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 diff --git a/drivers/media/platform/rk3288-vpu/rk3288_vpu.c b/drivers/media/platform/rk3288-vpu/rk3288_vpu.c index a217a8facce4..c7e5d9918671 100644 --- a/drivers/media/platform/rk3288-vpu/rk3288_vpu.c +++ b/drivers/media/platform/rk3288-vpu/rk3288_vpu.c @@ -31,6 +31,7 @@ #include #include +#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); diff --git a/drivers/media/platform/rk3288-vpu/rk3288_vpu_common.h b/drivers/media/platform/rk3288-vpu/rk3288_vpu_common.h index d4d58ff46ad1..32ae67f21ae8 100644 --- a/drivers/media/platform/rk3288-vpu/rk3288_vpu_common.h +++ b/drivers/media/platform/rk3288-vpu/rk3288_vpu_common.h @@ -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 index 000000000000..db8a1681ec22 --- /dev/null +++ b/drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.c @@ -0,0 +1,996 @@ +/* + * Rockchip RK3288 VPU codec driver + * + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * 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 +#include +#include + +#include +#include +#include +#include + +#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 index 000000000000..ac2598751083 --- /dev/null +++ b/drivers/media/platform/rk3288-vpu/rk3288_vpu_dec.h @@ -0,0 +1,33 @@ +/* + * Rockchip RK3288 VPU codec driver + * + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + * Hertz Wong + * + * Copyright (C) 2014 Google, Inc. + * Tomasz Figa + * + * 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_ */