build/patch/kernel/meson64-dev/0046-media-meson-vdec-add-MPEG4-decoding-support.patch
2019-01-21 01:18:20 -05:00

312 lines
9.2 KiB
Diff

From 0078aba06651c06450fe3358ce75c88a696fd333 Mon Sep 17 00:00:00 2001
From: Maxime Jourdan <mjourdan@baylibre.com>
Date: Wed, 29 Aug 2018 16:01:55 +0200
Subject: [PATCH] media: meson: vdec: add MPEG4 decoding support
Add support for V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_XVID and
V4L2_PIX_FMT_H.263
---
drivers/media/platform/meson/vdec/Makefile | 2 +-
.../media/platform/meson/vdec/codec_mpeg4.c | 139 ++++++++++++++++++
.../media/platform/meson/vdec/codec_mpeg4.h | 13 ++
.../media/platform/meson/vdec/vdec_platform.c | 91 ++++++++++++
4 files changed, 244 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg4.c
create mode 100644 drivers/media/platform/meson/vdec/codec_mpeg4.h
diff --git a/drivers/media/platform/meson/vdec/Makefile b/drivers/media/platform/meson/vdec/Makefile
index 01dc9603abdd..bb7a134e2728 100644
--- a/drivers/media/platform/meson/vdec/Makefile
+++ b/drivers/media/platform/meson/vdec/Makefile
@@ -3,6 +3,6 @@
meson-vdec-objs = esparser.o vdec.o vdec_ctrls.o vdec_helpers.o vdec_platform.o
meson-vdec-objs += vdec_1.o
-meson-vdec-objs += codec_mpeg12.o codec_h264.o
+meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_mpeg4.o
obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o
diff --git a/drivers/media/platform/meson/vdec/codec_mpeg4.c b/drivers/media/platform/meson/vdec/codec_mpeg4.c
new file mode 100644
index 000000000000..1d574e576112
--- /dev/null
+++ b/drivers/media/platform/meson/vdec/codec_mpeg4.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+ */
+
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vdec_helpers.h"
+#include "dos_regs.h"
+
+#define SIZE_WORKSPACE SZ_1M
+/* Offset added by firmware, to substract from workspace paddr */
+#define DCAC_BUFF_START_IP 0x02b00000
+
+/* map firmware registers to known MPEG4 functions */
+#define MREG_BUFFERIN AV_SCRATCH_8
+#define MREG_BUFFEROUT AV_SCRATCH_9
+#define MP4_NOT_CODED_CNT AV_SCRATCH_A
+#define MP4_OFFSET_REG AV_SCRATCH_C
+#define MEM_OFFSET_REG AV_SCRATCH_F
+#define MREG_FATAL_ERROR AV_SCRATCH_L
+
+#define BUF_IDX_MASK GENMASK(2, 0)
+#define INTERLACE_FLAG BIT(7)
+#define TOP_FIELD_FIRST_FLAG BIT(6)
+
+struct codec_mpeg4 {
+ /* Buffer for the MPEG4 Workspace */
+ void *workspace_vaddr;
+ dma_addr_t workspace_paddr;
+};
+
+static int codec_mpeg4_can_recycle(struct amvdec_core *core)
+{
+ return !amvdec_read_dos(core, MREG_BUFFERIN);
+}
+
+static void codec_mpeg4_recycle(struct amvdec_core *core, u32 buf_idx)
+{
+ amvdec_write_dos(core, MREG_BUFFERIN, ~BIT(buf_idx));
+}
+
+static int codec_mpeg4_start(struct amvdec_session *sess) {
+ struct amvdec_core *core = sess->core;
+ struct codec_mpeg4 *mpeg4 = sess->priv;
+ int ret;
+
+ mpeg4 = kzalloc(sizeof(*mpeg4), GFP_KERNEL);
+ if (!mpeg4)
+ return -ENOMEM;
+
+ /* Allocate some memory for the MPEG4 decoder's state */
+ mpeg4->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE,
+ &mpeg4->workspace_paddr,
+ GFP_KERNEL);
+ if (!mpeg4->workspace_vaddr) {
+ dev_err(core->dev, "Failed to request MPEG4 Workspace\n");
+ ret = -ENOMEM;
+ goto free_mpeg4;
+ }
+
+ /* Canvas regs: AV_SCRATCH_0-AV_SCRATCH_4;AV_SCRATCH_G-AV_SCRATCH_J */
+ amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_0, AV_SCRATCH_G, 0 },
+ (u32[]){ 4, 4, 0 });
+
+ amvdec_write_dos(core, MEM_OFFSET_REG,
+ mpeg4->workspace_paddr - DCAC_BUFF_START_IP);
+ amvdec_write_dos(core, PSCALE_CTRL, 0);
+ amvdec_write_dos(core, MP4_NOT_CODED_CNT, 0);
+ amvdec_write_dos(core, MREG_BUFFERIN, 0);
+ amvdec_write_dos(core, MREG_BUFFEROUT, 0);
+ amvdec_write_dos(core, MREG_FATAL_ERROR, 0);
+ amvdec_write_dos(core, MDEC_PIC_DC_THRESH, 0x404038aa);
+
+ sess->keyframe_found = 1;
+ sess->priv = mpeg4;
+
+ return 0;
+
+free_mpeg4:
+ kfree(mpeg4);
+ return ret;
+}
+
+static int codec_mpeg4_stop(struct amvdec_session *sess)
+{
+ struct codec_mpeg4 *mpeg4 = sess->priv;
+ struct amvdec_core *core = sess->core;
+
+ if (mpeg4->workspace_vaddr) {
+ dma_free_coherent(core->dev, SIZE_WORKSPACE,
+ mpeg4->workspace_vaddr,
+ mpeg4->workspace_paddr);
+ mpeg4->workspace_vaddr = 0;
+ }
+
+ return 0;
+}
+
+static irqreturn_t codec_mpeg4_isr(struct amvdec_session *sess)
+{
+ struct amvdec_core *core = sess->core;
+ u32 reg;
+ u32 buffer_index;
+ u32 field = V4L2_FIELD_NONE;
+
+ reg = amvdec_read_dos(core, MREG_FATAL_ERROR);
+ if (reg == 1) {
+ dev_err(core->dev, "mpeg4 fatal error\n");
+ amvdec_abort(sess);
+ return IRQ_HANDLED;
+ }
+
+ reg = amvdec_read_dos(core, MREG_BUFFEROUT);
+ if (!reg)
+ goto end;
+
+ buffer_index = reg & BUF_IDX_MASK;
+ if (reg & INTERLACE_FLAG)
+ field = (reg & TOP_FIELD_FIRST_FLAG) ?
+ V4L2_FIELD_INTERLACED_TB :
+ V4L2_FIELD_INTERLACED_BT;
+
+ amvdec_dst_buf_done_idx(sess, buffer_index, -1, field);
+ amvdec_write_dos(core, MREG_BUFFEROUT, 0);
+
+end:
+ amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1);
+ return IRQ_HANDLED;
+}
+
+struct amvdec_codec_ops codec_mpeg4_ops = {
+ .start = codec_mpeg4_start,
+ .stop = codec_mpeg4_stop,
+ .isr = codec_mpeg4_isr,
+ .can_recycle = codec_mpeg4_can_recycle,
+ .recycle = codec_mpeg4_recycle,
+};
diff --git a/drivers/media/platform/meson/vdec/codec_mpeg4.h b/drivers/media/platform/meson/vdec/codec_mpeg4.h
new file mode 100644
index 000000000000..b91b26413185
--- /dev/null
+++ b/drivers/media/platform/meson/vdec/codec_mpeg4.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr>
+ */
+
+#ifndef __MESON_VDEC_CODEC_MPEG4_H_
+#define __MESON_VDEC_CODEC_MPEG4_H_
+
+#include "vdec.h"
+
+extern struct amvdec_codec_ops codec_mpeg4_ops;
+
+#endif
\ No newline at end of file
diff --git a/drivers/media/platform/meson/vdec/vdec_platform.c b/drivers/media/platform/meson/vdec/vdec_platform.c
index baecf5921d56..80b43fd5d01f 100644
--- a/drivers/media/platform/meson/vdec/vdec_platform.c
+++ b/drivers/media/platform/meson/vdec/vdec_platform.c
@@ -10,9 +10,40 @@
#include "vdec_1.h"
#include "codec_mpeg12.h"
#include "codec_h264.h"
+#include "codec_mpeg4.h"
static const struct amvdec_format vdec_formats_gxbb[] = {
{
+ .pixfmt = V4L2_PIX_FMT_MPEG4,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/vmpeg4_mc_5",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H263,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/h263_mc",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
+ .pixfmt = V4L2_PIX_FMT_XVID,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/vmpeg4_mc_5",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
.pixfmt = V4L2_PIX_FMT_H264,
.min_buffers = 2,
.max_buffers = 24,
@@ -49,6 +80,36 @@ static const struct amvdec_format vdec_formats_gxbb[] = {
static const struct amvdec_format vdec_formats_gxl[] = {
{
+ .pixfmt = V4L2_PIX_FMT_MPEG4,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/vmpeg4_mc_5",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H263,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/h263_mc",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
+ .pixfmt = V4L2_PIX_FMT_XVID,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/vmpeg4_mc_5",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
.pixfmt = V4L2_PIX_FMT_H264,
.min_buffers = 2,
.max_buffers = 24,
@@ -85,6 +146,36 @@ static const struct amvdec_format vdec_formats_gxl[] = {
static const struct amvdec_format vdec_formats_gxm[] = {
{
+ .pixfmt = V4L2_PIX_FMT_MPEG4,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/vmpeg4_mc_5",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
+ .pixfmt = V4L2_PIX_FMT_H263,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/h263_mc",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
+ .pixfmt = V4L2_PIX_FMT_XVID,
+ .min_buffers = 8,
+ .max_buffers = 8,
+ .max_width = 1920,
+ .max_height = 1080,
+ .vdec_ops = &vdec_1_ops,
+ .codec_ops = &codec_mpeg4_ops,
+ .firmware_path = "meson/gx/vmpeg4_mc_5",
+ .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 },
+ }, {
.pixfmt = V4L2_PIX_FMT_H264,
.min_buffers = 2,
.max_buffers = 24,