[RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec

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

 



This patch introduces the support of MPEG2 video decoding to the
sunxi-cedrus video decoder driver.

Signed-off-by: Florent Revest <florent.revest@xxxxxxxxxxxxxxxxxx>
---
 drivers/media/platform/sunxi-cedrus/Makefile       |   2 +-
 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c |  26 +++-
 .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |   2 +
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c |  15 +-
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  |  17 ++-
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |   4 +
 .../platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c     | 152 +++++++++++++++++++++
 7 files changed, 211 insertions(+), 7 deletions(-)
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c

diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
index 14c2f7a..2d495a2 100644
--- a/drivers/media/platform/sunxi-cedrus/Makefile
+++ b/drivers/media/platform/sunxi-cedrus/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
-				    sunxi_cedrus_dec.o
+				    sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
index 17af34c..d1c957a 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
@@ -46,14 +46,31 @@ static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
 	struct sunxi_cedrus_ctx *ctx =
 		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
 
-	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
-	return -EINVAL;
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
+		/* This is kept in memory and used directly. */
+		break;
+	default:
+		v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
 static const struct v4l2_ctrl_ops sunxi_cedrus_ctrl_ops = {
 	.s_ctrl = sunxi_cedrus_s_ctrl,
 };
 
+static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg2_frame_hdr = {
+	.ops = &sunxi_cedrus_ctrl_ops,
+	.id = V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR,
+	.type = V4L2_CTRL_TYPE_PRIVATE,
+	.name = "MPEG2 Frame Header Parameters",
+	.max_reqs = VIDEO_MAX_FRAME,
+	.elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr),
+};
+
 /*
  * File operations
  */
@@ -78,6 +95,10 @@ static int sunxi_cedrus_open(struct file *file)
 	hdl = &ctx->hdl;
 	v4l2_ctrl_handler_init(hdl, 1);
 
+	ctx->mpeg2_frame_hdr_ctrl = v4l2_ctrl_new_custom(hdl,
+			&sunxi_cedrus_ctrl_mpeg2_frame_hdr, NULL);
+	ctx->mpeg2_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
+
 	if (hdl->error) {
 		rc = hdl->error;
 		v4l2_ctrl_handler_free(hdl);
@@ -117,6 +138,7 @@ static int sunxi_cedrus_release(struct file *file)
 	v4l2_fh_del(&ctx->fh);
 	v4l2_fh_exit(&ctx->fh);
 	v4l2_ctrl_handler_free(&ctx->hdl);
+	ctx->mpeg2_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 6b8d87a..e715184 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
@@ -70,6 +70,8 @@ struct sunxi_cedrus_ctx {
 	struct v4l2_ctrl_handler hdl;
 
 	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
+
+	struct v4l2_ctrl *mpeg2_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 71ef34b..38e8a3a 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
@@ -48,6 +48,11 @@ static struct sunxi_cedrus_fmt formats[] = {
 		.depth = 8,
 		.num_planes = 2,
 	},
+	{
+		.fourcc = V4L2_PIX_FMT_MPEG2_FRAME,
+		.types	= SUNXI_CEDRUS_OUTPUT,
+		.num_planes = 1,
+	},
 };
 
 #define NUM_FORMATS ARRAY_SIZE(formats)
@@ -120,8 +125,14 @@ void device_run(void *priv)
 		 V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
 		 V4L2_BUF_FLAG_BFRAME   | V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
 
-	v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
-	v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
+	if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG2_FRAME) {
+		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 {
+		v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
+		v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
+	}
 }
 
 /*
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
index 72b9df4..160de17 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
@@ -47,6 +47,13 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
 	int val;
 	unsigned long flags;
 
+	/* Disable MPEG interrupts and stop the MPEG engine */
+	val = sunxi_cedrus_read(vpu, VE_MPEG_CTRL);
+	sunxi_cedrus_write(vpu, val & (~0xf), VE_MPEG_CTRL);
+	val = sunxi_cedrus_read(vpu, VE_MPEG_STATUS);
+	sunxi_cedrus_write(vpu, 0x0000c00f, VE_MPEG_STATUS);
+	sunxi_cedrus_write(vpu, VE_CTRL_REINIT, VE_CTRL);
+
 	curr_ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
 
 	if (!curr_ctx) {
@@ -57,9 +64,15 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
 	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
 	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
 
+	/* First bit of MPEG_STATUS means success */
 	spin_lock_irqsave(&vpu->irqlock, flags);
-	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
-	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+	if (val & 0x1) {
+		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+	} else {
+		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
+		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+	}
 	spin_unlock_irqrestore(&vpu->irqlock, flags);
 
 	v4l2_m2m_job_finish(vpu->m2m_dev, curr_ctx->fh.m2m_ctx);
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
index 3c9199b..78625e5 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
@@ -29,4 +29,8 @@ struct sunxi_cedrus_ctx;
 int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu);
 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);
+
 #endif /* SUNXI_CEDRUS_HW_H_ */
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
new file mode 100644
index 0000000..9381c63
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
@@ -0,0 +1,152 @@
+/*
+ * 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>
+
+static const u8 mpeg_default_intra_quant[64] = {
+	 8, 16, 16, 19, 16, 19, 22, 22,
+	22, 22, 22, 22, 26, 24, 26, 27,
+	27, 27, 26, 26, 26, 26, 27, 27,
+	27, 29, 29, 29, 34, 34, 34, 29,
+	29, 29, 27, 27, 29, 29, 32, 32,
+	34, 34, 37, 38, 37, 35, 35, 34,
+	35, 38, 38, 40, 40, 40, 48, 48,
+	46, 46, 56, 56, 58, 69, 69, 83
+};
+
+#define m_iq(i) (((64 + i) << 8) | mpeg_default_intra_quant[i])
+
+static const u8 mpeg_default_non_intra_quant[64] = {
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16
+};
+
+#define m_niq(i) ((i << 8) | mpeg_default_non_intra_quant[i])
+
+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)
+{
+	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 pic_header = 0;
+	u32 vld_len = frame_hdr->slice_len - frame_hdr->slice_pos;
+	int i;
+
+	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);
+
+	/* Upload quantization matrices */
+	for (i = 0; i < 64; i++) {
+		sunxi_cedrus_write(dev, m_iq(i),  VE_MPEG_IQ_MIN_INPUT);
+		sunxi_cedrus_write(dev, m_niq(i), VE_MPEG_IQ_MIN_INPUT);
+	}
+
+	/* 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 picture's header */
+	pic_header |= (frame_hdr->picture_coding_type        & 0xf) << 28;
+	pic_header |= (frame_hdr->f_code[0][0]               & 0xf) << 24;
+	pic_header |= (frame_hdr->f_code[0][1]               & 0xf) << 20;
+	pic_header |= (frame_hdr->f_code[1][0]               & 0xf) << 16;
+	pic_header |= (frame_hdr->f_code[1][1]               & 0xf) << 12;
+	pic_header |= (frame_hdr->intra_dc_precision         & 0x3) << 10;
+	pic_header |= (frame_hdr->picture_structure          & 0x3) << 8;
+	pic_header |= (frame_hdr->top_field_first            & 0x1) << 7;
+	pic_header |= (frame_hdr->frame_pred_frame_dct       & 0x1) << 6;
+	pic_header |= (frame_hdr->concealment_motion_vectors & 0x1) << 5;
+	pic_header |= (frame_hdr->q_scale_type               & 0x1) << 4;
+	pic_header |= (frame_hdr->intra_vlc_format           & 0x1) << 3;
+	pic_header |= (frame_hdr->alternate_scan             & 0x1) << 2;
+	sunxi_cedrus_write(dev, pic_header, VE_MPEG_PIC_HDR);
+
+	/* Enable interrupt and an unknown control flag */
+	sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG2, VE_MPEG_CTRL);
+
+	/* Macroblock address */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_MBA);
+
+	/* Clear previous errors */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_ERROR);
+
+	/* Unknown register */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_CTR_MB);
+
+	/* 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 */
+	if (frame_hdr->type == MPEG2)
+		sunxi_cedrus_write(dev, VE_TRIG_MPEG2, VE_MPEG_TRIGGER);
+	else
+		sunxi_cedrus_write(dev, VE_TRIG_MPEG1, 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