[RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux