From e222a1cf4bd62745407a9404565d76b8a5770f12 Mon Sep 17 00:00:00 2001 From: Maxime Jourdan 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 +- drivers/media/platform/meson/vdec/codec_mpeg4.c | 139 ++++++++++++++++++++++ drivers/media/platform/meson/vdec/codec_mpeg4.h | 13 ++ drivers/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 01dc960..bb7a134 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 0000000..1d574e5 --- /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 + */ + +#include +#include + +#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 0000000..b91b26413 --- /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 + */ + +#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 baecf59..80b43fd 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,