This patch introduces the support of MPEG4 video decoding to the sunxi-cedrus video decoder driver. Signed-off-by: Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx> --- 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@xxxxxxxxxxxxxxxxxx> + * + * Based on reverse engineering efforts of the 'Cedrus' project + * Copyright (c) 2013-2014 Jens Kuske <jenskuske@xxxxxxxxx> + * + * 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 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html