lkml.org 
[lkml]   [2016]   [Aug]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec
    Date
    This patch introduces the support of MPEG4 video decoding to the
    sunxi-cedrus video decoder driver.

    Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
    ---
    drivers/media/platform/sunxi-cedrus/Makefile | 3 +-
    drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c | 15 +++
    .../platform/sunxi-cedrus/sunxi_cedrus_common.h | 13 ++
    .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c | 33 +++++
    .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h | 3 +
    .../platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c | 140 +++++++++++++++++++++
    6 files changed, 206 insertions(+), 1 deletion(-)
    create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c

    diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
    index 2d495a2..823d611 100644
    --- a/drivers/media/platform/sunxi-cedrus/Makefile
    +++ b/drivers/media/platform/sunxi-cedrus/Makefile
    @@ -1,2 +1,3 @@
    obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
    - sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o
    + sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o \
    + sunxi_cedrus_mpeg4.o
    diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
    index d1c957a..3001440 100644
    --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
    +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
    @@ -47,6 +47,7 @@ static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
    container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);

    switch (ctrl->id) {
    + case V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR:
    case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
    /* This is kept in memory and used directly. */
    break;
    @@ -71,6 +72,15 @@ static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg2_frame_hdr = {
    .elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr),
    };

    +static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg4_frame_hdr = {
    + .ops = &sunxi_cedrus_ctrl_ops,
    + .id = V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR,
    + .type = V4L2_CTRL_TYPE_PRIVATE,
    + .name = "MPEG4 Frame Header Parameters",
    + .max_reqs = VIDEO_MAX_FRAME,
    + .elem_size = sizeof(struct v4l2_ctrl_mpeg4_frame_hdr),
    +};
    +
    /*
    * File operations
    */
    @@ -99,6 +109,10 @@ static int sunxi_cedrus_open(struct file *file)
    &sunxi_cedrus_ctrl_mpeg2_frame_hdr, NULL);
    ctx->mpeg2_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;

    + ctx->mpeg4_frame_hdr_ctrl = v4l2_ctrl_new_custom(hdl,
    + &sunxi_cedrus_ctrl_mpeg4_frame_hdr, NULL);
    + ctx->mpeg4_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
    +
    if (hdl->error) {
    rc = hdl->error;
    v4l2_ctrl_handler_free(hdl);
    @@ -139,6 +153,7 @@ static int sunxi_cedrus_release(struct file *file)
    v4l2_fh_exit(&ctx->fh);
    v4l2_ctrl_handler_free(&ctx->hdl);
    ctx->mpeg2_frame_hdr_ctrl = NULL;
    + ctx->mpeg4_frame_hdr_ctrl = NULL;
    mutex_lock(&dev->dev_mutex);
    v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
    mutex_unlock(&dev->dev_mutex);
    diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
    index e715184..33fa891 100644
    --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
    +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
    @@ -49,6 +49,18 @@ struct sunxi_cedrus_dev {
    struct reset_control *rstc;

    char *base;
    +
    + unsigned int mbh_buf;
    + unsigned int dcac_buf;
    + unsigned int ncf_buf;
    +
    + void *mbh_buf_virt;
    + void *dcac_buf_virt;
    + void *ncf_buf_virt;
    +
    + unsigned int mbh_buf_size;
    + unsigned int dcac_buf_size;
    + unsigned int ncf_buf_size;
    };

    struct sunxi_cedrus_fmt {
    @@ -72,6 +84,7 @@ struct sunxi_cedrus_ctx {
    struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];

    struct v4l2_ctrl *mpeg2_frame_hdr_ctrl;
    + struct v4l2_ctrl *mpeg4_frame_hdr_ctrl;
    };

    static inline void sunxi_cedrus_write(struct sunxi_cedrus_dev *vpu,
    diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
    index 38e8a3a..8ce635d 100644
    --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
    +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
    @@ -53,6 +53,11 @@ static struct sunxi_cedrus_fmt formats[] = {
    .types = SUNXI_CEDRUS_OUTPUT,
    .num_planes = 1,
    },
    + {
    + .fourcc = V4L2_PIX_FMT_MPEG4_FRAME,
    + .types = SUNXI_CEDRUS_OUTPUT,
    + .num_planes = 1,
    + },
    };

    #define NUM_FORMATS ARRAY_SIZE(formats)
    @@ -129,6 +134,10 @@ void device_run(void *priv)
    struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr =
    ctx->mpeg2_frame_hdr_ctrl->p_new.p;
    process_mpeg2(ctx, in_buf, out_luma, out_chroma, frame_hdr);
    + } else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG4_FRAME) {
    + struct v4l2_ctrl_mpeg4_frame_hdr *frame_hdr =
    + ctx->mpeg4_frame_hdr_ctrl->p_new.p;
    + process_mpeg4(ctx, in_buf, out_luma, out_chroma, frame_hdr);
    } else {
    v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
    v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
    @@ -306,8 +315,32 @@ static int vidioc_s_fmt(struct sunxi_cedrus_ctx *ctx, struct v4l2_format *f)

    switch (f->type) {
    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
    + if (ctx->vpu_src_fmt && ctx->vpu_src_fmt->fourcc ==
    + V4L2_PIX_FMT_MPEG4_FRAME && dev->mbh_buf_virt) {
    + dma_free_coherent(dev->dev, dev->mbh_buf_size,
    + dev->mbh_buf_virt, dev->mbh_buf);
    + dma_free_coherent(dev->dev, dev->dcac_buf_size,
    + dev->dcac_buf_virt, dev->dcac_buf);
    + dma_free_coherent(dev->dev, dev->ncf_buf_size,
    + dev->ncf_buf_virt, dev->ncf_buf);
    + }
    +
    ctx->vpu_src_fmt = find_format(f);
    ctx->src_fmt = *pix_fmt_mp;
    +
    + if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG4_FRAME) {
    + dev->mbh_buf_size = pix_fmt_mp->height * 2048;
    + dev->dcac_buf_size = 2 * pix_fmt_mp->width *
    + pix_fmt_mp->height;
    + dev->ncf_buf_size = 4 * 1024;
    +
    + dev->mbh_buf_virt = dma_alloc_coherent(dev->dev,
    + dev->mbh_buf_size, &dev->mbh_buf, GFP_KERNEL);
    + dev->dcac_buf_virt = dma_alloc_coherent(dev->dev,
    + dev->dcac_buf_size, &dev->dcac_buf, GFP_KERNEL);
    + dev->ncf_buf_virt = dma_alloc_coherent(dev->dev,
    + dev->ncf_buf_size, &dev->ncf_buf, GFP_KERNEL);
    + }
    break;
    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
    fmt = find_format(f);
    diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
    index 78625e5..d5f8b47 100644
    --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
    +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
    @@ -32,5 +32,8 @@ void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu);
    void process_mpeg2(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
    dma_addr_t out_luma, dma_addr_t out_chroma,
    struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr);
    +void process_mpeg4(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
    + dma_addr_t out_luma, dma_addr_t out_chroma,
    + struct v4l2_ctrl_mpeg4_frame_hdr *frame_hdr);

    #endif /* SUNXI_CEDRUS_HW_H_ */
    diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c
    new file mode 100644
    index 0000000..656fb6f
    --- /dev/null
    +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c
    @@ -0,0 +1,140 @@
    +/*
    + * Sunxi Cedrus codec driver
    + *
    + * Copyright (C) 2016 Florent Revest
    + * Florent Revest <florent.revest@free-electrons.com>
    + *
    + * Based on reverse engineering efforts of the 'Cedrus' project
    + * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
    + *
    + * 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 "sunxi_cedrus_common.h"
    +
    +#include <media/videobuf2-dma-contig.h>
    +
    +#define VOP_I 0
    +#define VOP_P 1
    +#define VOP_B 2
    +#define VOP_S 3
    +
    +void process_mpeg4(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
    + dma_addr_t out_luma, dma_addr_t out_chroma,
    + struct v4l2_ctrl_mpeg4_frame_hdr *frame_hdr)
    +{
    + struct sunxi_cedrus_dev *dev = ctx->dev;
    +
    + u16 width = DIV_ROUND_UP(frame_hdr->width, 16);
    + u16 height = DIV_ROUND_UP(frame_hdr->height, 16);
    +
    + u32 vop_header = 0;
    + u32 vld_len = frame_hdr->slice_len - frame_hdr->slice_pos;
    +
    + struct vb2_buffer *fwd_vb2_buf, *bwd_vb2_buf;
    + dma_addr_t fwd_luma = 0, fwd_chroma = 0, bwd_luma = 0, bwd_chroma = 0;
    +
    + /*
    + * The VPU is only able to handle bus addresses so we have to subtract
    + * the RAM offset to the physcal addresses
    + */
    + fwd_vb2_buf = ctx->dst_bufs[frame_hdr->forward_index];
    + if (fwd_vb2_buf) {
    + fwd_luma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 0);
    + fwd_chroma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 1);
    + fwd_luma -= PHYS_OFFSET;
    + fwd_chroma -= PHYS_OFFSET;
    + }
    +
    + bwd_vb2_buf = ctx->dst_bufs[frame_hdr->backward_index];
    + if (bwd_vb2_buf) {
    + bwd_luma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 0);
    + bwd_chroma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 1);
    + bwd_chroma -= PHYS_OFFSET;
    + bwd_luma -= PHYS_OFFSET;
    + }
    +
    + /* Activates MPEG engine */
    + sunxi_cedrus_write(dev, VE_CTRL_MPEG, VE_CTRL);
    +
    + /* Quantization parameter */
    + sunxi_cedrus_write(dev, frame_hdr->quant_scale, VE_MPEG_QP_INPUT);
    +
    + /* Intermediate buffers needed by the VPU */
    + sunxi_cedrus_write(dev, dev->mbh_buf - PHYS_OFFSET, VE_MPEG_MBH_ADDR);
    + sunxi_cedrus_write(dev, dev->dcac_buf - PHYS_OFFSET, VE_MPEG_DCAC_ADDR);
    + sunxi_cedrus_write(dev, dev->ncf_buf - PHYS_OFFSET, VE_MPEG_NCF_ADDR);
    +
    + /* Image's dimensions */
    + sunxi_cedrus_write(dev, width << 8 | height, VE_MPEG_SIZE);
    + sunxi_cedrus_write(dev, width << 20 | height << 4, VE_MPEG_FRAME_SIZE);
    +
    + /* MPEG VOP's header */
    + vop_header |= (frame_hdr->vop_fields.vop_coding_type == VOP_B) << 28;
    + vop_header |= frame_hdr->vol_fields.quant_type << 24;
    + vop_header |= frame_hdr->vol_fields.quarter_sample << 23;
    + vop_header |= frame_hdr->vol_fields.resync_marker_disable << 22;
    + vop_header |= frame_hdr->vop_fields.vop_coding_type << 18;
    + vop_header |= frame_hdr->vop_fields.vop_rounding_type << 17;
    + vop_header |= frame_hdr->vop_fields.intra_dc_vlc_thr << 8;
    + vop_header |= frame_hdr->vop_fields.top_field_first << 7;
    + vop_header |= frame_hdr->vop_fields.alternate_vertical_scan_flag << 6;
    + if (frame_hdr->vop_fields.vop_coding_type != VOP_I)
    + vop_header |= frame_hdr->vop_fcode_forward << 3;
    + if (frame_hdr->vop_fields.vop_coding_type == VOP_B)
    + vop_header |= frame_hdr->vop_fcode_backward << 0;
    + sunxi_cedrus_write(dev, vop_header, VE_MPEG_VOP_HDR);
    +
    + /* Enable interrupt and an unknown control flag */
    + if (frame_hdr->vop_fields.vop_coding_type == VOP_P)
    + sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG4_P, VE_MPEG_CTRL);
    + else
    + sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG4, VE_MPEG_CTRL);
    +
    + /* Temporal distances of B frames */
    + if (frame_hdr->vop_fields.vop_coding_type == VOP_B) {
    + u32 trbtrd = (frame_hdr->trb << 16) | frame_hdr->trd;
    +
    + sunxi_cedrus_write(dev, trbtrd, VE_MPEG_TRBTRD_FRAME);
    + sunxi_cedrus_write(dev, 0, VE_MPEG_TRBTRD_FIELD);
    + }
    +
    + /* Don't rotate or scale buffer */
    + sunxi_cedrus_write(dev, VE_NO_SDROT_CTRL, VE_MPEG_SDROT_CTRL);
    +
    + /* Macroblock number */
    + sunxi_cedrus_write(dev, 0, VE_MPEG_MBA);
    +
    + /* Clear previous status */
    + sunxi_cedrus_write(dev, 0xffffffff, VE_MPEG_STATUS);
    +
    + /* Forward and backward prediction buffers (cached in dst_bufs) */
    + sunxi_cedrus_write(dev, fwd_luma, VE_MPEG_FWD_LUMA);
    + sunxi_cedrus_write(dev, fwd_chroma, VE_MPEG_FWD_CHROMA);
    + sunxi_cedrus_write(dev, bwd_luma, VE_MPEG_BACK_LUMA);
    + sunxi_cedrus_write(dev, bwd_chroma, VE_MPEG_BACK_CHROMA);
    +
    + /* Output luma and chroma buffers */
    + sunxi_cedrus_write(dev, out_luma, VE_MPEG_REC_LUMA);
    + sunxi_cedrus_write(dev, out_chroma, VE_MPEG_REC_CHROMA);
    + sunxi_cedrus_write(dev, out_luma, VE_MPEG_ROT_LUMA);
    + sunxi_cedrus_write(dev, out_chroma, VE_MPEG_ROT_CHROMA);
    +
    + /* Input offset and length in bits */
    + sunxi_cedrus_write(dev, frame_hdr->slice_pos, VE_MPEG_VLD_OFFSET);
    + sunxi_cedrus_write(dev, vld_len, VE_MPEG_VLD_LEN);
    +
    + /* Input beginning and end addresses */
    + sunxi_cedrus_write(dev, VE_MPEG_VLD_ADDR_VAL(in_buf), VE_MPEG_VLD_ADDR);
    + sunxi_cedrus_write(dev, in_buf + VBV_SIZE - 1, VE_MPEG_VLD_END);
    +
    + /* Starts the MPEG engine */
    + sunxi_cedrus_write(dev, VE_TRIG_MPEG4(width, height), VE_MPEG_TRIGGER);
    +}
    --
    2.7.4
    \
     
     \ /
      Last update: 2016-09-17 09:57    [W:4.163 / U:0.380 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site