[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251027-b4-s4-vdec-upstream-v1-3-620401813b5d@amlogic.com>
Date: Mon, 27 Oct 2025 13:42:39 +0800
From: Zhentao Guo via B4 Relay <devnull+zhentao.guo.amlogic.com@...nel.org>
To: Mauro Carvalho Chehab <mchehab@...nel.org>,
Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Neil Armstrong <neil.armstrong@...aro.org>,
Kevin Hilman <khilman@...libre.com>, Jerome Brunet <jbrunet@...libre.com>,
Martin Blumenstingl <martin.blumenstingl@...glemail.com>
Cc: linux-media@...r.kernel.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
linux-amlogic@...ts.infradead.org, Zhentao Guo <zhentao.guo@...ogic.com>
Subject: [PATCH 3/3] decoder: Add V4L2 stateless H.264 decoder driver
From: Zhentao Guo <zhentao.guo@...ogic.com>
This patch introduces initial driver support for Amlogic's new
video acceleration hardware architecture, designed for video
stream decoding. The driver is designed to support the
V4L2 M2M stateless decoder API. In phase 1, it supports H.264
bitstreams decoding.
Signed-off-by: Zhentao Guo <zhentao.guo@...ogic.com>
---
MAINTAINERS | 7 +
drivers/media/platform/amlogic/Kconfig | 2 +
drivers/media/platform/amlogic/Makefile | 1 +
drivers/media/platform/amlogic/vdec/Kconfig | 15 +
drivers/media/platform/amlogic/vdec/Makefile | 4 +
drivers/media/platform/amlogic/vdec/aml_vdec.c | 759 +++++++++
drivers/media/platform/amlogic/vdec/aml_vdec.h | 31 +
.../platform/amlogic/vdec/aml_vdec_canvas_utils.c | 154 ++
.../platform/amlogic/vdec/aml_vdec_canvas_utils.h | 22 +
drivers/media/platform/amlogic/vdec/aml_vdec_drv.c | 263 +++
drivers/media/platform/amlogic/vdec/aml_vdec_drv.h | 194 +++
drivers/media/platform/amlogic/vdec/aml_vdec_hw.c | 652 +++++++
drivers/media/platform/amlogic/vdec/aml_vdec_hw.h | 182 ++
.../platform/amlogic/vdec/aml_vdec_platform.c | 37 +
.../platform/amlogic/vdec/aml_vdec_platform.h | 62 +
drivers/media/platform/amlogic/vdec/h264.c | 1790 ++++++++++++++++++++
drivers/media/platform/amlogic/vdec/h264.h | 300 ++++
drivers/media/platform/amlogic/vdec/reg_defines.h | 175 ++
18 files changed, 4650 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 5ea78444f035..f400b5dabf30 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1352,6 +1352,13 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml
F: drivers/spi/spi-amlogic-spisg.c
+AMLOGIC VCODEC DRIVER
+M: Zhentao Guo <zhentao.guo@...ogic.com>
+L: linux-media@...r.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/amlogic,vcodec-dec.yaml
+F: drivers/media/platform/amlogic/vdec/
+
AMPHENOL CHIPCAP 2 DRIVER
M: Javier Carrasco <javier.carrasco.cruz@...il.com>
L: linux-hwmon@...r.kernel.org
diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig
index 458acf3d5fa8..8132f5b9282b 100644
--- a/drivers/media/platform/amlogic/Kconfig
+++ b/drivers/media/platform/amlogic/Kconfig
@@ -4,3 +4,5 @@ comment "Amlogic media platform drivers"
source "drivers/media/platform/amlogic/c3/Kconfig"
source "drivers/media/platform/amlogic/meson-ge2d/Kconfig"
+
+source "drivers/media/platform/amlogic/vdec/Kconfig"
diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platform/amlogic/Makefile
index c744afcd1b9e..7409de674c0b 100644
--- a/drivers/media/platform/amlogic/Makefile
+++ b/drivers/media/platform/amlogic/Makefile
@@ -2,3 +2,4 @@
obj-y += c3/
obj-y += meson-ge2d/
+obj-y += vdec/
diff --git a/drivers/media/platform/amlogic/vdec/Kconfig b/drivers/media/platform/amlogic/vdec/Kconfig
new file mode 100644
index 000000000000..05c4568e058b
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+
+config VIDEO_AMLOGIC_VCODEC
+ tristate "Amlogic Video Codec Driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on V4L_MEM2MEM_DRIVERS
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_H264
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Amlogic video codec driver.
+ This driver is designed to support V4L2 M2M STATELESS
+ interface.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/vdec/Makefile b/drivers/media/platform/amlogic/vdec/Makefile
new file mode 100644
index 000000000000..f645f7ca5e2d
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+aml-vdec-drv-objs := aml_vdec.o aml_vdec_drv.o aml_vdec_hw.o aml_vdec_platform.o h264.o aml_vdec_canvas_utils.o\
+
+obj-$(CONFIG_VIDEO_AMLOGIC_VCODEC) += aml-vdec-drv.o
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec.c b/drivers/media/platform/amlogic/vdec/aml_vdec.c
new file mode 100644
index 000000000000..e8a66bde0397
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec.c
@@ -0,0 +1,759 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "aml_vdec.h"
+#include "aml_vdec_hw.h"
+#include "aml_vdec_platform.h"
+
+#define VCODEC_DRV_NAME "aml-vdec-drv"
+
+static const struct aml_vdec_v4l2_ctrl controls[] = {
+ {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_STATELESS_H264_SPS,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_STATELESS_H264_PPS,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_STATELESS_H264_DECODE_MODE,
+ .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_STATELESS_H264_START_CODE,
+ .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ },
+ }, {
+ .codec_type = CODEC_TYPE_H264,
+ .cfg = {
+ .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+ },
+ }
+};
+
+static struct aml_video_fmt aml_video_formats[] = {
+ {
+ .name = "H.264",
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .type = AML_FMT_DEC,
+ .codec_type = CODEC_TYPE_H264,
+ .num_planes = 1,
+ .stepwise = {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2},
+ },
+ {
+ .name = "NV21M",
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .type = AML_FMT_FRAME,
+ .codec_type = CODEC_TYPE_FRAME,
+ .num_planes = 2,
+ .stepwise = {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2},
+ },
+ {
+ .name = "NV21",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .type = AML_FMT_FRAME,
+ .codec_type = CODEC_TYPE_FRAME,
+ .num_planes = 1,
+ .stepwise = {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2},
+ },
+ {
+ .name = "NV12M",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .type = AML_FMT_FRAME,
+ .codec_type = CODEC_TYPE_FRAME,
+ .num_planes = 2,
+ .stepwise = {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2},
+
+ },
+ {
+ .name = "NV12",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .type = AML_FMT_FRAME,
+ .codec_type = CODEC_TYPE_FRAME,
+ .num_planes = 1,
+ .stepwise = {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2},
+ },
+};
+
+void aml_vdec_set_default_params(struct aml_vdec_ctx *ctx)
+{
+ struct aml_q_data *q_data = NULL;
+
+ ctx->m2m_ctx->q_lock = &ctx->v4l2_intf_lock;
+
+ ctx->pic_info.colorspace = V4L2_COLORSPACE_DEFAULT;
+ ctx->pic_info.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ ctx->pic_info.quantization = V4L2_QUANTIZATION_DEFAULT;
+ ctx->pic_info.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ q_data = &ctx->q_data[AML_Q_DATA_SRC];
+ memset(q_data, 0, sizeof(struct aml_q_data));
+ q_data->visible_width = AML_VDEC_MIN_W;
+ q_data->visible_height = AML_VDEC_MIN_H;
+ q_data->coded_width = AML_VDEC_MIN_W;
+ q_data->coded_height = AML_VDEC_MIN_H;
+ q_data->filed_flag = V4L2_FIELD_NONE;
+ q_data->bytesperline[0] = 0;
+ q_data->sizeimage[0] = (1024 * 1024);
+ q_data->fmt = &aml_video_formats[DEFAULT_OUT_IDX];
+
+ q_data = &ctx->q_data[AML_Q_DATA_DST];
+ memset(q_data, 0, sizeof(struct aml_q_data));
+ q_data->visible_width = AML_VDEC_MIN_W;
+ q_data->visible_height = AML_VDEC_MIN_H;
+ q_data->coded_width = AML_VDEC_MIN_W;
+ q_data->coded_height = AML_VDEC_MIN_H;
+ q_data->filed_flag = V4L2_FIELD_NONE;
+ q_data->bytesperline[0] = q_data->coded_width;
+ q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height;
+ q_data->bytesperline[1] = q_data->coded_width;
+ q_data->sizeimage[1] = q_data->sizeimage[0] / 2;
+ q_data->fmt = &aml_video_formats[DEFAULT_CAP_IDX];
+}
+
+int aml_vdec_ctrls_setup(struct aml_vdec_ctx *ctx)
+{
+ int i;
+ int ctrls_size = sizeof(controls) / sizeof(struct aml_vdec_v4l2_ctrl);
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, ctrls_size);
+ for (i = 0; i < ctrls_size; i++) {
+ v4l2_ctrl_new_custom(&ctx->ctrl_handler, &controls[i].cfg, NULL);
+ if (ctx->ctrl_handler.error) {
+ pr_debug("add ctrl for (%d) failed%d\n",
+ controls[i].cfg.id, ctx->ctrl_handler.error);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return ctx->ctrl_handler.error;
+ }
+ }
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ return v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+}
+
+static void m2mops_vdec_device_run(void *m2m_priv)
+{
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)m2m_priv;
+ struct aml_vdec_dev *dev = ctx->dev;
+ struct vb2_v4l2_buffer *src, *dst;
+ struct media_request *src_req;
+ const char *fw_path = dev->pvdec_data->fw_path[ctx->curr_dec_type];
+
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ pr_debug("device run : src buf : %d dst buf %d\n", src->vb2_buf.index, dst->vb2_buf.index);
+
+ src_req = src->vb2_buf.req_obj.req;
+ if (src_req)
+ v4l2_ctrl_request_setup(src_req, &ctx->ctrl_handler);
+
+ dos_enable();
+ /* incase of bus hang in stop_streaming */
+ ctx->dos_clk_en = 1;
+ aml_vdec_reset_core();
+ load_firmware(dev->dec_hw, fw_path);
+
+ if (ctx->codec_ops->run)
+ ctx->codec_ops->run(ctx);
+
+ v4l2_m2m_buf_copy_metadata(src, dst);
+ if (src_req)
+ v4l2_ctrl_request_complete(src_req, &ctx->ctrl_handler);
+
+ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, VB2_BUF_STATE_DONE);
+}
+
+const struct v4l2_m2m_ops aml_vdec_m2m_ops = {
+ .device_run = m2mops_vdec_device_run,
+};
+
+static int vidioc_vdec_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, VCODEC_DRV_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "platform:" VCODEC_DRV_NAME, sizeof(cap->card));
+
+ return 0;
+}
+
+static int vidioc_vdec_enum_fmt(struct v4l2_fmtdesc *f, bool is_output)
+{
+ struct aml_video_fmt *fmt;
+ int fmt_size = sizeof(aml_video_formats) / sizeof(struct aml_video_fmt);
+ int i = 0, j = 0;
+
+ for (; i < fmt_size; i++) {
+ fmt = &aml_video_formats[i];
+ if (is_output && fmt->type != AML_FMT_DEC)
+ continue;
+ if (!is_output && fmt->type != AML_FMT_FRAME)
+ continue;
+
+ if (j == f->index) {
+ f->pixelformat = fmt->fourcc;
+ strscpy(f->description, fmt->name,
+ sizeof(f->description));
+ if (strlen(fmt->name) >= sizeof(f->description)) {
+ pr_err("fmt name string is too long!\n");
+ f->description[sizeof(f->description) - 1] = '\0';
+ }
+ return 0;
+ }
+ ++j;
+ }
+ return -EINVAL;
+}
+
+static struct aml_q_data *aml_vdec_get_qdata_by_type(struct aml_vdec_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &ctx->q_data[AML_Q_DATA_SRC];
+
+ return &ctx->q_data[AML_Q_DATA_DST];
+}
+
+static struct aml_video_fmt *aml_vdec_get_video_fmt(u32 format)
+{
+ struct aml_video_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < (sizeof(aml_video_formats) / sizeof(struct aml_video_fmt)); k++) {
+ fmt = &aml_video_formats[k];
+ if (fmt->fourcc == format)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int aml_vdec_init_dec_inst(struct aml_vdec_ctx *ctx,
+ struct aml_video_fmt *fmt_out)
+{
+ struct aml_vdec_dev *dev = ctx->dev;
+ int ret = -1;
+
+ if (!fmt_out)
+ return ret;
+
+ if (fmt_out->codec_type == CODEC_TYPE_FRAME) {
+ pr_debug("capture type no need to set\n");
+ return 0;
+ }
+
+ ctx->codec_ops = &dev->pvdec_data->codec_ops[fmt_out->codec_type];
+ if (ctx->codec_ops->init) {
+ ret = ctx->codec_ops->init(ctx);
+ if (ret < 0)
+ return ret;
+ }
+ ctx->curr_dec_type = fmt_out->codec_type;
+ pr_info("%s set curr_dec_type = %d\n", __func__, ctx->curr_dec_type);
+
+ return ret;
+}
+
+static void set_pic_info(struct aml_vdec_ctx *ctx,
+ struct v4l2_pix_format_mplane *pix_mp,
+ enum v4l2_buf_type type)
+{
+ struct aml_q_data *q_data;
+
+ q_data = aml_vdec_get_qdata_by_type(ctx, type);
+
+ ctx->pic_info.colorspace = pix_mp->colorspace;
+ ctx->pic_info.ycbcr_enc = pix_mp->ycbcr_enc;
+ ctx->pic_info.quantization = pix_mp->quantization;
+ ctx->pic_info.xfer_func = pix_mp->xfer_func;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage;
+ ctx->pic_info.output_pix_fmt = pix_mp->pixelformat;
+ ctx->pic_info.coded_width = ALIGN(pix_mp->width, 64);
+ ctx->pic_info.coded_height = ALIGN(pix_mp->height, 64);
+ ctx->pic_info.fb_size[0] =
+ ctx->pic_info.coded_width * ctx->pic_info.coded_height;
+ ctx->pic_info.fb_size[1] = ctx->pic_info.fb_size[0] / 2;
+ ctx->pic_info.plane_num = 1;
+ } else {
+ ctx->pic_info.plane_num = q_data->fmt->num_planes;
+ ctx->pic_info.cap_pix_fmt = pix_mp->pixelformat;
+ q_data->coded_width = ctx->pic_info.coded_width;
+ q_data->coded_height = ctx->pic_info.coded_height;
+ q_data->sizeimage[0] = ctx->pic_info.fb_size[0];
+ q_data->bytesperline[0] = ctx->pic_info.coded_width;
+ if (q_data->fmt->num_planes > 1) {
+ q_data->sizeimage[1] = ctx->pic_info.fb_size[1];
+ q_data->bytesperline[1] = ctx->pic_info.coded_width;
+ } else {
+ q_data->sizeimage[0] += ctx->pic_info.fb_size[1];
+ }
+ }
+}
+
+static int vidioc_vdec_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct aml_video_fmt *fmt;
+ struct aml_vdec_dev *dev = video_drvdata(file);
+ u32 max_h, max_w;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ max_h = dev->pvdec_data->dec_fmt->max_height;
+ max_w = dev->pvdec_data->dec_fmt->max_width;
+
+ fmt = aml_vdec_get_video_fmt(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = fmt->stepwise;
+ fsize->stepwise.max_height = max_h;
+ fsize->stepwise.max_width = max_w;
+
+ return 0;
+}
+
+static int vdec_try_fmt_mp(struct aml_vdec_ctx *ctx, struct v4l2_format *f,
+ const struct aml_video_fmt *fmt_mp)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct aml_q_data *q_data;
+ struct aml_vdec_dev *dev = ctx->dev;
+ u32 max_h, max_w;
+ int i;
+
+ max_h = dev->pvdec_data->dec_fmt->max_height;
+ max_w = dev->pvdec_data->dec_fmt->max_width;
+
+ pix_mp->field = V4L2_FIELD_NONE;
+ q_data = aml_vdec_get_qdata_by_type(ctx, f->type);
+
+ pix_mp->height = clamp(pix_mp->height, AML_VDEC_MIN_H, max_h);
+ pix_mp->width = clamp(pix_mp->width, AML_VDEC_MIN_H, max_w);
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ pix_mp->num_planes = q_data->fmt->num_planes;
+ pix_mp->pixelformat = q_data->fmt->fourcc;
+ pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0];
+ pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0];
+ } else {
+ v4l2_fill_pixfmt_mp(pix_mp, fmt_mp->fourcc, pix_mp->width,
+ pix_mp->height);
+ }
+
+ for (i = 0; i < pix_mp->num_planes; i++)
+ memset(&pix_mp->plane_fmt[i].reserved[0], 0x0,
+ sizeof(pix_mp->plane_fmt[0].reserved));
+
+ memset(pix_mp->reserved, 0x0, sizeof(pix_mp->reserved));
+ pix_mp->flags = 0;
+
+ return 0;
+}
+
+static int vdec_s_fmt(struct aml_vdec_ctx *ctx, struct v4l2_format *f)
+{
+ struct aml_q_data *q_data;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct aml_video_fmt *fmt =
+ aml_vdec_get_video_fmt(f->fmt.pix_mp.pixelformat);
+
+ q_data = aml_vdec_get_qdata_by_type(ctx, f->type);
+
+ if (fmt) /* default fmt was set in fopen */
+ q_data->fmt = fmt;
+
+ vdec_try_fmt_mp(ctx, f, q_data->fmt);
+ set_pic_info(ctx, pix_mp, f->type);
+
+ return 0;
+}
+
+static int vdec_g_fmt(struct aml_vdec_ctx *ctx, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct aml_q_data *q_data;
+
+ q_data = aml_vdec_get_qdata_by_type(ctx, f->type);
+
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->colorspace = ctx->pic_info.colorspace;
+ pix_mp->ycbcr_enc = ctx->pic_info.ycbcr_enc;
+ pix_mp->quantization = ctx->pic_info.quantization;
+ pix_mp->xfer_func = ctx->pic_info.xfer_func;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ pix_mp->height = q_data->coded_height;
+ pix_mp->width = q_data->coded_width;
+ pix_mp->pixelformat = q_data->fmt->fourcc;
+ pix_mp->num_planes = q_data->fmt->num_planes;
+ pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0];
+ pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0];
+ } else {
+ if (ctx->pic_info.coded_width != 0 && ctx->pic_info.coded_height != 0) {
+ pix_mp->width = ctx->pic_info.coded_width;
+ pix_mp->height = ctx->pic_info.coded_height;
+ } else {
+ pix_mp->height = q_data->coded_height;
+ pix_mp->width = q_data->coded_height;
+ }
+ v4l2_fill_pixfmt_mp(pix_mp, q_data->fmt->fourcc, pix_mp->width,
+ pix_mp->height);
+ }
+
+ return 0;
+}
+
+static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(priv);
+ const struct aml_video_fmt *fmt_mp;
+ struct aml_q_data *q_data;
+
+ q_data = aml_vdec_get_qdata_by_type(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ fmt_mp = aml_vdec_get_video_fmt(f->fmt.pix_mp.pixelformat);
+ if (!fmt_mp)
+ fmt_mp = q_data->fmt;
+
+ return vdec_try_fmt_mp(ctx, f, fmt_mp);
+}
+
+static int vidioc_try_fmt_out_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(priv);
+ const struct aml_video_fmt *fmt_mp;
+ struct aml_q_data *q_data = aml_vdec_get_qdata_by_type(ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ fmt_mp = aml_vdec_get_video_fmt(f->fmt.pix_mp.pixelformat);
+ if (!fmt_mp)
+ fmt_mp = q_data->fmt;
+
+ return vdec_try_fmt_mp(ctx, f, fmt_mp);
+}
+
+static int vidioc_vdec_s_fmt_out_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(priv);
+
+ return vdec_s_fmt(ctx, f);
+}
+
+static int vidioc_vdec_s_fmt_cap_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(priv);
+
+ return vdec_s_fmt(ctx, f);
+}
+
+static int vidioc_vdec_g_fmt_out_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(priv);
+
+ return vdec_g_fmt(ctx, f);
+}
+
+static int vidioc_vdec_g_fmt_cap_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(priv);
+
+ return vdec_g_fmt(ctx, f);
+}
+
+static int vidioc_vdec_enum_fmt_out_mplane(struct file *file,
+ void *priv, struct v4l2_fmtdesc *f)
+{
+ return vidioc_vdec_enum_fmt(f, 1);
+}
+
+static int vidioc_vdec_enum_fmt_cap_mplane(struct file *file,
+ void *priv, struct v4l2_fmtdesc *f)
+{
+ return vidioc_vdec_enum_fmt(f, 0);
+}
+
+const struct v4l2_ioctl_ops aml_vdec_ioctl_ops = {
+ .vidioc_querycap = vidioc_vdec_querycap,
+ .vidioc_enum_framesizes = vidioc_vdec_enum_framesizes,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_vdec_enum_fmt_cap_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_vdec_s_fmt_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_vdec_g_fmt_cap_mplane,
+
+ .vidioc_enum_fmt_vid_out = vidioc_vdec_enum_fmt_out_mplane,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_out_mplane,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_vdec_s_fmt_out_mplane,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_vdec_g_fmt_out_mplane,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd,
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+};
+
+static void aml_vdec_release_instance(struct aml_vdec_ctx *ctx)
+{
+ if (ctx->codec_ops && ctx->codec_ops->exit)
+ ctx->codec_ops->exit(ctx);
+}
+
+static int vb2ops_vdec_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct aml_vdec_ctx *ctx = vb2_get_drv_priv(vq);
+ struct aml_q_data *q_data;
+ unsigned int i;
+
+ q_data = aml_vdec_get_qdata_by_type(ctx, vq->type);
+ if (!q_data) {
+ pr_err("not supported vq type\n");
+ return -EINVAL;
+ }
+
+ if (*nplanes) {
+ if (*nplanes != q_data->fmt->num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < *nplanes; i++) {
+ if (sizes[i] < q_data->sizeimage[i]) {
+ pr_err("not supported sizeimage\n");
+ return -EINVAL;
+ }
+ alloc_devs[i] = &ctx->dev->plat_dev->dev;
+ }
+ } else {
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ *nplanes = q_data->fmt->num_planes;
+ else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ *nplanes = 1;
+
+ for (i = 0; i < *nplanes; i++) {
+ alloc_devs[i] = &ctx->dev->plat_dev->dev;
+ sizes[i] = q_data->sizeimage[i];
+ }
+ }
+
+ if (*nplanes) {
+ pr_debug("type: %d, plane: %d, buf cnt: %d, size: [Y: %u, C: %u]\n",
+ vq->type, *nplanes, *nbuffers, sizes[0], sizes[1]);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb)
+{
+ struct aml_vdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct aml_q_data *q_data;
+ unsigned int sizeimage = 0;
+ int i;
+
+ q_data = aml_vdec_get_qdata_by_type(ctx, vb->type);
+ if (!q_data) {
+ pr_err("not supported vq type\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < q_data->fmt->num_planes; i++) {
+ sizeimage = q_data->sizeimage[i];
+ if (vb2_plane_size(vb, i) < sizeimage)
+ return -EINVAL;
+
+ if (V4L2_TYPE_IS_CAPTURE(vb->type))
+ vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
+ }
+
+ return 0;
+}
+
+static int vb2_ops_vdec_buf_init(struct vb2_buffer *vb)
+{
+ return 0;
+}
+
+static void vb2_ops_vdec_buf_queue(struct vb2_buffer *vb)
+{
+ struct aml_vdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vb2_v4l2 = to_vb2_v4l2_buffer(vb);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb2_v4l2);
+}
+
+static void vb2_ops_vdec_buf_finish(struct vb2_buffer *vb)
+{
+}
+
+static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct aml_vdec_ctx *ctx = vb2_get_drv_priv(q);
+ struct aml_q_data *q_data;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ ctx->is_output_streamon = 1;
+ q_data = aml_vdec_get_qdata_by_type(ctx, q->type);
+ if (aml_vdec_init_dec_inst(ctx, q_data->fmt) < 0)
+ return -EINVAL;
+ } else {
+ ctx->is_cap_streamon = 1;
+ }
+
+ return 0;
+}
+
+static void vb2ops_vdec_stop_streaming(struct vb2_queue *q)
+{
+ struct aml_vdec_ctx *ctx = vb2_get_drv_priv(q);
+ struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL;
+
+ aml_vdec_release_instance(ctx);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx)))
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ ctx->is_output_streamon = 0;
+ } else {
+ while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx)))
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+ ctx->is_cap_streamon = 0;
+ }
+}
+
+static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb)
+{
+ struct aml_vdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_handler);
+}
+
+static const struct vb2_ops aml_vdec_vb2_ops = {
+ .queue_setup = vb2ops_vdec_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = vb2ops_vdec_start_streaming,
+ .stop_streaming = vb2ops_vdec_stop_streaming,
+
+ .buf_init = vb2_ops_vdec_buf_init,
+ .buf_prepare = vb2ops_vdec_buf_prepare,
+ .buf_out_validate = vb2ops_vdec_out_buf_validate,
+ .buf_queue = vb2_ops_vdec_buf_queue,
+ .buf_finish = vb2_ops_vdec_buf_finish,
+ .buf_request_complete = vb2ops_vdec_buf_request_complete,
+};
+
+int aml_vdec_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)priv;
+ struct aml_vdec_dev *dev = ctx->dev;
+ int ret = 0;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &aml_vdec_vb2_ops;
+ src_vq->lock = &ctx->v4l2_intf_lock;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->supports_requests = true;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ ret = vb2_queue_init(src_vq);
+ if (ret) {
+ v4l2_info(&dev->v4l2_dev,
+ "Failed to initialize videobuf2 queue(output)");
+ return ret;
+ }
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->ops = &aml_vdec_vb2_ops;
+ dst_vq->lock = &ctx->v4l2_intf_lock;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ v4l2_info(&dev->v4l2_dev,
+ "Failed to initialize videobuf2 queue(capture)");
+ vb2_queue_release(src_vq);
+ }
+
+ return ret;
+}
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec.h b/drivers/media/platform/amlogic/vdec/aml_vdec.h
new file mode 100644
index 000000000000..a9ff93f25043
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef _AML_VDEC_H_
+#define _AML_VDEC_H_
+
+#include "aml_vdec_drv.h"
+
+#define DEFAULT_OUT_IDX 0 /* set default output format to h264 type */
+#define DEFAULT_CAP_IDX 2 /* set default capture format to NV21 */
+
+/**
+ * struct aml_vdec_v4l2_ctrl - helper type to declare supported ctrls
+ * @codec_type: codec id this control belong to (CODEC_TYPE_H264, etc.)
+ * @cfg: control configuration
+ */
+struct aml_vdec_v4l2_ctrl {
+ unsigned int codec_type;
+ struct v4l2_ctrl_config cfg;
+};
+
+extern const struct v4l2_m2m_ops aml_vdec_m2m_ops;
+extern const struct v4l2_ioctl_ops aml_vdec_ioctl_ops;
+
+int aml_vdec_ctrls_setup(struct aml_vdec_ctx *ctx);
+int aml_vdec_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+void aml_vdec_set_default_params(struct aml_vdec_ctx *ctx);
+#endif
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_canvas_utils.c b/drivers/media/platform/amlogic/vdec/aml_vdec_canvas_utils.c
new file mode 100644
index 000000000000..066958118b00
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_canvas_utils.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#include "aml_vdec_canvas_utils.h"
+
+#define CANVAS_REG_OFFSET (0x12 << 2)
+
+/*
+ * DMC_CAV_LUT_DATAL/DMC_CAV_LUT_DATAH
+ * high 32bits of canvas data which need to be configured
+ * to canvas memory.
+ * 64bits CANVAS look up table
+ * bit 61 : 58 Endian control.
+ * bit 61 : 1 : switch 2 64bits data inside 128bits boundary.
+ * 0 : no change.
+ * bit 60 : 1 : switch 2 32bits data inside 64bits data boundary.
+ * 0 : no change.
+ * bit 59 : 1 : switch 2 16bits data inside 32bits data boundary.
+ * 0 : no change.
+ * bit 58 : 1 : switch 2 8bits data inside 16bits data boundary.
+ * 0 : no change.
+ * bit 57 : 56. Canvas block mode. 2 : 64x32, 1: 32x32;
+ * 0 : linear mode.
+ * bit 55 : canvas Y direction wrap control.
+ * 1: wrap back in y. 0: not wrap back.
+ * bit 54 : canvas X direction wrap control.
+ * 1: wrap back in X. 0: not wrap back.
+ * bit 53 : 41. canvas Hight.
+ * bit 40 : 29. canvas Width, unit: 8bytes. must in 32bytes boundary.
+ * that means last 2 bits must be 0.
+ * bit 28 : 0. canvas start address. unit. 8 bytes. must be in
+ * 32bytes boundary. that means last 2bits must be 0.
+ */
+
+#define CANVAS_WADDR_LBIT 0
+#define CANVAS_WIDTH_LBIT 29
+#define CANVAS_HEIGHT_HBIT (41 - 32)
+#define CANVAS_WRAPX_HBIT (54 - 32)
+#define CANVAS_WRAPY_HBIT (55 - 32)
+#define CANVAS_BLKMODE_HBIT (56 - 32)
+#define CANVAS_ENDIAN_HBIT (58 - 32)
+
+/* canvas regs */
+#define DC_CAV_LUT_DATAL 0x12
+#define DC_CAV_LUT_DATAH 0x13
+#define DC_CAV_LUT_ADDR 0x14
+#define DC_CAV_LUT_RDATAL 0x15
+#define DC_CAV_LUT_RDATAH 0x16
+
+#define CANVAS_ADDR_LMASK 0x1fffffff
+#define CANVAS_WIDTH_LMASK 0x7
+#define CANVAS_WIDTH_LWID 3
+#define CANVAS_WIDTH_HMASK 0x1ff
+#define CANVAS_WIDTH_HBIT 0
+#define CANVAS_HEIGHT_MASK 0x1fff
+#define CANVAS_HEIGHT_BIT 9
+#define CANVAS_ADDR_NOWRAP 0x00
+#define CANVAS_ADDR_WRAPX 0x01
+#define CANVAS_ADDR_WRAPY 0x02
+#define CANVAS_BLKMODE_MASK 0x03
+
+#define CANVAS_BLKMODE_LINEAR 0x00
+#define CANVAS_BLKMODE_32X32 0x01
+#define CANVAS_BLKMODE_64X32 0x02
+
+#define CANVAS_LUT_WR_EN (0x2 << 8)
+#define CANVAS_LUT_RD_EN (0x1 << 8)
+
+#define CANVAS_ADDR_BITS_MASK GENMASK(28, 0)
+#define CANVAS_WIDTH_L_MASK GENMASK(31, 29)
+#define CANVAS_WIDTH_H_MASK GENMASK(8, 0)
+#define CANVAS_HEIGHT_H_MASK GENMASK(21, 9)
+#define CANVAS_WRAP_H_MASK GENMASK(23, 23)
+#define CANVAS_BLOCK_H_MASK GENMASK(25, 24)
+#define CANVAS_BITS_CTRL_MASK GENMASK(29, 26)
+
+static struct vdec_canvas_s canvas;
+
+static void write_canvas_reg(u32 addr, u32 val)
+{
+ int offset = -DC_CAV_LUT_DATAL;
+
+ return writel(val, canvas.regs_base + ((addr + offset) << 2));
+}
+
+static int aml_vdec_canvas_alloc(u8 *canvas_index)
+{
+ int i;
+ unsigned long flags;
+
+ *canvas_index = -1;
+ spin_lock_irqsave(&canvas.canvas_lock, flags);
+ for (i = 0; i < CANVAS_MAX_SIZE; i++) {
+ if (i >= 0x10 && i <= 0x15) /* 0x10~0x15 is used by RDMA */
+ continue;
+ if (canvas.canvas_used[i] == 0) {
+ *canvas_index = i;
+ canvas.canvas_used[i] = 1;
+ spin_unlock_irqrestore(&canvas.canvas_lock, flags);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&canvas.canvas_lock, flags);
+
+ return -1;
+}
+
+static void aml_vdec_canvas_free(u8 canvas_index)
+{
+ unsigned long flags;
+
+ if (canvas_index < 0 || canvas_index > CANVAS_MAX_SIZE)
+ return;
+
+ spin_lock_irqsave(&canvas.canvas_lock, flags);
+ if (canvas.canvas_used[canvas_index])
+ canvas.canvas_used[canvas_index] = 0;
+
+ spin_unlock_irqrestore(&canvas.canvas_lock, flags);
+}
+
+static void aml_vdec_config_canvas(u8 canvas_index,
+ ulong addr, u32 width, u32 height,
+ u32 wrap, u32 blkmode, u32 endian)
+{
+ u32 data_h, data_l;
+
+ data_h = FIELD_PREP(CANVAS_WIDTH_H_MASK,
+ (((width + 7) >> 3) >> CANVAS_WIDTH_LWID)) |
+ FIELD_PREP(CANVAS_HEIGHT_H_MASK, height) |
+ FIELD_PREP(CANVAS_WRAP_H_MASK, wrap) |
+ FIELD_PREP(CANVAS_BLOCK_H_MASK, blkmode) |
+ FIELD_PREP(CANVAS_BITS_CTRL_MASK, endian);
+
+ data_l = FIELD_PREP(CANVAS_ADDR_BITS_MASK, ((addr + 7) >> 3)) |
+ FIELD_PREP(CANVAS_WIDTH_L_MASK, ((width + 7) >> 3));
+
+ write_canvas_reg(DC_CAV_LUT_DATAL, data_l);
+ write_canvas_reg(DC_CAV_LUT_DATAH, data_h);
+ write_canvas_reg(DC_CAV_LUT_ADDR, CANVAS_LUT_WR_EN | canvas_index);
+}
+
+void aml_vdec_canvas_register(struct aml_vdec_hw *hw)
+{
+ canvas.regs_base = hw->regs[DMC_BUS] + CANVAS_REG_OFFSET;
+ memset(canvas.canvas_used, 0, CANVAS_MAX_SIZE);
+ spin_lock_init(&canvas.canvas_lock);
+
+ hw->hw_ops.canvas_alloc = aml_vdec_canvas_alloc;
+ hw->hw_ops.canvas_free = aml_vdec_canvas_free;
+ hw->hw_ops.config_canvas = aml_vdec_config_canvas;
+}
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_canvas_utils.h b/drivers/media/platform/amlogic/vdec/aml_vdec_canvas_utils.h
new file mode 100644
index 000000000000..55757fe5f95d
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_canvas_utils.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+#include "reg_defines.h"
+#include "aml_vdec_hw.h"
+
+#define CANVAS_MAX_SIZE 63
+
+/**
+ * struct vdec_canvas_s - helper to decoder canvas, which config the w/h, buffer addr of frames
+ * @regs_base: base addr of canvas regs.
+ * @canvas_used: used flag.
+ * @canvas_lock: spinlock for canvas.
+ */
+struct vdec_canvas_s {
+ void __iomem *regs_base;
+ u8 canvas_used[CANVAS_MAX_SIZE];
+ spinlock_t canvas_lock;
+};
+
+void aml_vdec_canvas_register(struct aml_vdec_hw *hw);
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_drv.c b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.c
new file mode 100644
index 000000000000..ef2c779839d8
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#include "aml_vdec.h"
+#include "aml_vdec_hw.h"
+#include "aml_vdec_platform.h"
+
+#define AML_VDEC_DRV_NAME "aml-vdec-drv"
+
+static int fops_vcodec_open(struct file *file)
+{
+ struct aml_vdec_dev *dec_dev = video_drvdata(file);
+ struct aml_vdec_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mutex_lock(&dec_dev->dev_mutex);
+ dec_dev->dec_ctx = ctx;
+ ctx->dev = dec_dev;
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh, file);
+ dec_dev->filp = file;
+ mutex_init(&ctx->v4l2_intf_lock);
+ init_waitqueue_head(&ctx->queue);
+ ctx->int_cond = 0;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dec_dev->m2m_dev_dec, ctx,
+ &aml_vdec_queue_init);
+ if (IS_ERR((__force void *)ctx->m2m_ctx)) {
+ ret = PTR_ERR((__force void *)ctx->m2m_ctx);
+ v4l2_err(&dec_dev->v4l2_dev, "Failed to v4l2_m2m_ctx_init() (%d)", ret);
+ goto err_m2m_ctx_init;
+ }
+
+ ctx->fh.m2m_ctx = ctx->m2m_ctx;
+ ret = aml_vdec_ctrls_setup(ctx);
+ if (ret) {
+ v4l2_err(&dec_dev->v4l2_dev, "Failed to init all ctrls (%d)", ret);
+ goto err_ctrls_setup;
+ }
+
+ aml_vdec_set_default_params(ctx);
+ mutex_unlock(&dec_dev->dev_mutex);
+
+ return ret;
+
+err_ctrls_setup:
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+err_m2m_ctx_init:
+ v4l2_fh_del(&ctx->fh, file);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mutex_unlock(&dec_dev->dev_mutex);
+
+ return ret;
+}
+
+static int fops_vcodec_release(struct file *file)
+{
+ struct aml_vdec_dev *dec_dev = video_drvdata(file);
+ struct aml_vdec_ctx *ctx = fh_to_dec_ctx(file->private_data);
+
+ mutex_lock(&dec_dev->dev_mutex);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_fh_del(&ctx->fh, file);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mutex_unlock(&dec_dev->dev_mutex);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations aml_vdec_fops = {
+ .owner = THIS_MODULE,
+ .open = fops_vcodec_open,
+ .release = fops_vcodec_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct video_device dec_dev = {
+ .name = "aml_dev_drv",
+ .fops = &aml_vdec_fops,
+ .ioctl_ops = &aml_vdec_ioctl_ops,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_M2M,
+ .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING,
+};
+
+static const struct media_device_ops aml_m2m_media_ops = {
+ .req_validate = vb2_request_validate,
+ .req_queue = v4l2_m2m_request_queue,
+};
+
+static int aml_vdec_drv_probe(struct platform_device *pdev)
+{
+ struct aml_vdec_dev *dev;
+ struct video_device *vfd_dec;
+ struct aml_vdec_hw *hw;
+ int ret = 0;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->plat_dev = pdev;
+ mutex_init(&dev->dev_mutex);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "v4l2_device_register err\n");
+
+ vfd_dec = video_device_alloc();
+ if (!vfd_dec) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_device_alloc;
+ }
+ *vfd_dec = dec_dev;
+ vfd_dec->v4l2_dev = &dev->v4l2_dev;
+ vfd_dec->lock = &dev->dev_mutex;
+ video_set_drvdata(vfd_dec, dev);
+ dev->vfd = vfd_dec;
+ platform_set_drvdata(pdev, dev);
+
+ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw) {
+ ret = -ENOMEM;
+ goto err_dec_mem_init;
+ }
+ dev->dec_hw = hw;
+
+ dev->pvdec_data = of_device_get_match_data(&pdev->dev);
+ ret = dev->pvdec_data->req_hw_resource(dev);
+ if (ret < 0)
+ goto err_hw_init;
+
+ dev->m2m_dev_dec = v4l2_m2m_init(&aml_vdec_m2m_ops);
+ if (IS_ERR((__force void *)dev->m2m_dev_dec)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem dec device\n");
+ ret = PTR_ERR((__force void *)dev->m2m_dev_dec);
+ goto err_hw_init;
+ }
+
+ ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device");
+ goto err_vid_dev_register;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd_dec->name, video_device_node_name(vfd_dec));
+
+ dev->mdev.dev = &pdev->dev;
+ strscpy(dev->mdev.model, AML_VDEC_DRV_NAME, sizeof(dev->mdev.model));
+ media_device_init(&dev->mdev);
+ dev->mdev.ops = &aml_m2m_media_ops;
+ dev->v4l2_dev.mdev = &dev->mdev;
+
+ ret = v4l2_m2m_register_media_controller(dev->m2m_dev_dec, vfd_dec,
+ MEDIA_ENT_F_PROC_VIDEO_DECODER);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n");
+ goto error_m2m_mc_register;
+ }
+
+ ret = media_device_register(&dev->mdev);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register media device");
+ goto err_media_dev_register;
+ }
+ vdec_enable(dev->dec_hw);
+ return 0;
+
+err_media_dev_register:
+ v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec);
+error_m2m_mc_register:
+ media_device_cleanup(&dev->mdev);
+err_vid_dev_register:
+ v4l2_m2m_release(dev->m2m_dev_dec);
+err_hw_init:
+ kfree(hw);
+ dev->dec_hw = NULL;
+err_dec_mem_init:
+ video_device_release(vfd_dec);
+err_device_alloc:
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+}
+
+static void aml_vdec_drv_remove(struct platform_device *pdev)
+{
+ struct aml_vdec_dev *dev = platform_get_drvdata(pdev);
+
+ vdec_disable(dev->dec_hw);
+
+ if (media_devnode_is_registered(dev->mdev.devnode)) {
+ media_device_unregister(&dev->mdev);
+ media_device_cleanup(&dev->mdev);
+ }
+
+ if (dev->m2m_dev_dec)
+ v4l2_m2m_release(dev->m2m_dev_dec);
+ if (dev->vfd)
+ video_unregister_device(dev->vfd);
+ if (dev->dec_hw)
+ dev->pvdec_data->destroy_hw_resource(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ pr_debug("aml v4l2 decoder driver remove\n");
+}
+
+static const struct of_device_id aml_vdec_match[] = {
+ {.compatible = "amlogic,s4-vcodec-dec", .data = &aml_vdec_s4_pdata},
+ {},
+};
+
+static struct platform_driver aml_vcodec_dec_driver = {
+ .probe = aml_vdec_drv_probe,
+ .remove = aml_vdec_drv_remove,
+ .driver = {
+ .name = AML_VDEC_DRV_NAME,
+ .of_match_table = aml_vdec_match,
+ },
+};
+
+static int __init aml_vdec_init(void)
+{
+ pr_debug("aml v4l2 decoder module init\n");
+
+ if (platform_driver_register(&aml_vcodec_dec_driver)) {
+ pr_err("failed to register aml v4l2 decoder\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit aml_vdec_exit(void)
+{
+ pr_debug("aml v4l2 decoder module exit\n");
+ platform_driver_unregister(&aml_vcodec_dec_driver);
+}
+
+module_init(aml_vdec_init);
+module_exit(aml_vdec_exit);
+
+MODULE_DESCRIPTION("Amlogic V4L2 decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h
new file mode 100644
index 000000000000..f3318cceff3f
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef _AML_VDEC_DRV_H_
+#define _AML_VDEC_DRV_H_
+
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define AML_VCODEC_MAX_PLANES 3
+#define AML_VDEC_MIN_W 64U
+#define AML_VDEC_MIN_H 64U
+#define AML_VDEC_1080P_MAX_H 1088U
+#define AML_VDEC_1080P_MAX_W 1920U
+
+struct aml_vdec_ctx;
+/**
+ * enum aml_fmt_type - Type of format type
+ */
+enum aml_fmt_type {
+ AML_FMT_DEC = 0,
+ AML_FMT_FRAME = 1,
+};
+
+/**
+ * enum aml_codec_type - Type of codec format
+ */
+enum aml_codec_type {
+ CODEC_TYPE_H264 = 0,
+ CODEC_TYPE_FRAME,
+};
+
+/**
+ * enum aml_q_type - Type of queue : cap or output
+ */
+enum aml_q_type {
+ AML_Q_DATA_SRC = 0,
+ AML_Q_DATA_DST = 1,
+};
+
+/**
+ * struct aml_video_fmt - aml video decoder fmt information
+ * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*.
+ * @type: Curr queue type: capture or output.
+ * @codec_type: Codec mode related. See aml_codec_type.
+ * @num_planes: Num planes of the format.
+ * @name: Name of the format.
+ * @stepwise: Supported range of frame sizes (only for bitstream formats).
+ */
+struct aml_video_fmt {
+ u32 fourcc;
+ enum aml_fmt_type type;
+ enum aml_codec_type codec_type;
+ u32 num_planes;
+ const u8 *name;
+ struct v4l2_frmsize_stepwise stepwise;
+};
+
+/**
+ * struct aml_q_data - aml video queue information
+ * @visible_width: Width for display.
+ * @visible_height: Height for display.
+ * @coded_width: Width for decode, which is 64/32 aligned.
+ * @coded_height: Height for decode, which is 64/32 aligned.
+ * @filed_flag: Field pic flag.
+ * @bytesperline: Byte num of each pixel line.
+ * @sizeimage: Size of frame in bytes.
+ * @fmt: Format for curr queue. See struct aml_video_fmt.
+ */
+struct aml_q_data {
+ u32 visible_width;
+ u32 visible_height;
+ u32 coded_width;
+ u32 coded_height;
+ u32 filed_flag;
+ u32 bytesperline[AML_VCODEC_MAX_PLANES];
+ u32 sizeimage[AML_VCODEC_MAX_PLANES];
+ struct aml_video_fmt *fmt;
+};
+
+/**
+ * struct aml_vdec_dev - driver data
+ * @plat_dev: Platform device for the current driver.
+ * @v4l2_dev: V4L2 device to register video devices for.
+ * @m2m_dev_dec: Mem2mem device associated to this device.
+ * @vfd: Video_device associated to this device.
+ * @mdev: Media_device associated to this device.
+ * @dec_ctx: Decoder context. See struct aml_vdec_ctx.
+ * @dec_hw: Decoder hardware resources. See struct aml_vdec_hw.
+ * @pvdec_data: Decoder platform data. See struct aml_dev_platform_data.
+ * @dev_mutex: video_device lock.
+ * @filp: v4l2 file handle pointer.
+ */
+struct aml_vdec_dev {
+ struct platform_device *plat_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev_dec;
+ struct video_device *vfd;
+ struct media_device mdev;
+
+ struct aml_vdec_ctx *dec_ctx;
+ struct aml_vdec_hw *dec_hw;
+ const struct aml_dev_platform_data *pvdec_data;
+
+ struct mutex dev_mutex;
+ struct file *filp;
+};
+
+/**
+ * struct dec_pic_info - pic information description
+ * @colorspace: enum v4l2_colorspace; supplemental to pixelformat
+ * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
+ * @xfer_func: enum v4l2_xfer_func, colorspace transfer function
+ * @quantization: enum v4l2_quantization, colorspace quantization
+ * @cap_pix_fmt: Pixel format for capture queue.
+ * @output_pix_fmt: Pixel format for output queue.
+ * @coded_width: Width for decode.
+ * @coded_height: Height for decode.
+ * @fb_size: Frame buffer size for Y or UV.
+ * @plane_num: Num for planes for curr format.
+ */
+struct dec_pic_info {
+ enum v4l2_colorspace colorspace;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_xfer_func xfer_func;
+ enum v4l2_quantization quantization;
+ u32 cap_pix_fmt;
+ u32 output_pix_fmt;
+ u32 coded_width;
+ u32 coded_height;
+ u32 fb_size[2];
+ u32 plane_num;
+};
+
+/**
+ * struct aml_vdec_ctx - driver instance context
+ * @dev: pointer to the aml_vdec_dev of the device.
+ * @fh: struct v4l2 fh.
+ * @m2m_ctx: pointer to v4l2_m2m_ctx context.
+ * @ctrl_handler: V4L2 ctrl handler.
+ * @v4l2_intf_lock: Mutex lock for v4l2 interface.
+ * @codec_ops: Codec operation functions. See struct aml_codec_ops.
+ * @int_cond: Variable used by the waitqueue.
+ * @queue: Waitqueue to wait for the current decode context finish.
+ * @q_data: feature supported by the current decoder instance.
+ * @is_cap_streamon: indicates if the current capture stream is on.
+ * @is_output_streamon: indicates if the current output stream is on.
+ * @dos_clk_en: indicates if dos clk is enabled.
+ * @pic_info: Pic information for curr decoder context. See struct dec_pic_info.
+ * @curr_dec_type: Current decoder type. (CODEC_TYPE_H264, etc.)
+ * @codec_priv: Pointer to current decoder instance.
+ */
+struct aml_vdec_ctx {
+ struct aml_vdec_dev *dev;
+ struct v4l2_fh fh;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct mutex v4l2_intf_lock;
+
+ const struct aml_codec_ops *codec_ops;
+ int int_cond;
+ wait_queue_head_t queue;
+ struct aml_q_data q_data[2];
+
+ bool is_cap_streamon;
+ bool is_output_streamon;
+ bool dos_clk_en;
+
+ struct dec_pic_info pic_info;
+ u32 curr_dec_type;
+ void *codec_priv;
+};
+
+static inline struct aml_vdec_ctx *fh_to_dec_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct aml_vdec_ctx, fh);
+}
+
+static inline struct aml_vdec_ctx *ctrl_to_dec_ctx(struct v4l2_ctrl_handler *ctrl)
+{
+ return container_of(ctrl, struct aml_vdec_ctx, ctrl_handler);
+}
+
+#endif
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_hw.c b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.c
new file mode 100644
index 000000000000..4db0e3076761
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "aml_vdec_drv.h"
+#include "aml_vdec_hw.h"
+#include "aml_vdec_platform.h"
+#include "aml_vdec_canvas_utils.h"
+
+#define MHz (1000000)
+#define MC_SIZE (4096 * 16)
+
+static void __iomem *reg_base[MAX_REG_BUS];
+
+static struct pm_pd_s vdec_domain_data[] = {
+ [VDEC_1] = {.name = "pwrc-vdec", },
+ [VDEC_HEVC] = {.name = "pwrc-hevc", },
+};
+
+u32 read_dos_reg(u32 addr)
+{
+ return readl(reg_base[DOS_BUS] + (addr << 2));
+}
+
+void write_dos_reg(u32 addr, int val)
+{
+ writel(val, reg_base[DOS_BUS] + (addr << 2));
+}
+
+u32 read_dmc_reg(u32 addr)
+{
+ return readl(reg_base[DMC_BUS] + (addr << 2));
+}
+
+void write_dmc_reg(u32 addr, int val)
+{
+ writel(val, reg_base[DMC_BUS] + (addr << 2));
+}
+
+void dos_reg_set_mask(u32 addr, u32 mask)
+{
+ u32 r;
+
+ r = read_dos_reg(addr);
+ write_dos_reg(addr, (r | mask));
+}
+
+void dos_reg_clear_mask(u32 addr, u32 mask)
+{
+ u32 r;
+
+ r = read_dos_reg(addr);
+ write_dos_reg(addr, (r & (~mask)));
+}
+
+void dos_reg_write_bits(u32 reg, u32 val, int start, int len)
+{
+ u32 to_val = read_dos_reg(reg);
+ u32 mask = (((1L << (len)) - 1) << (start));
+
+ to_val &= ~mask;
+ to_val |= (val << start) & mask;
+ write_dos_reg(reg, to_val);
+}
+
+void dos_enable(void)
+{
+ WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10);
+
+ WRITE_VREG(GCLK_EN, 0x3ff);
+
+ CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, (1 << 31));
+}
+
+void aml_vdec_reset_core(void)
+{
+ unsigned int mask = 0;
+
+ mask = (1 << 21);
+
+ write_dmc_reg(0x0, (read_dmc_reg(0x0) & ~mask));
+ usleep_range(60, 70);
+ WRITE_VREG(DOS_SW_RESET0, (1 << 3) | (1 << 4) |
+ (1 << 5) | (1 << 7) | (1 << 8) | (1 << 9));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+ CLEAR_VREG_MASK(VDEC_ASSIST_MMC_CTRL1, 1 << 3);
+ CLEAR_VREG_MASK(MDEC_PIC_DC_MUX_CTRL, 1 << 31);
+ WRITE_VREG(MDEC_EXTIF_CFG1, 0);
+
+ write_dmc_reg(0x0, (read_dmc_reg(0x0) | mask));
+}
+
+void aml_start_vdec_hw(void)
+{
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+
+ WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+
+ WRITE_VREG(MPSR, 0x0001);
+}
+
+void aml_stop_vdec_hw(void)
+{
+ ulong timeout = jiffies + HZ / 10;
+
+ WRITE_VREG(MPSR, 0);
+ WRITE_VREG(CPSR, 0);
+
+ while (RD_VREG(IMEM_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ timeout = jiffies + HZ / 10;
+ while (RD_VREG(LMEM_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ timeout = jiffies + HZ / 80;
+ while (RD_VREG(WRRSP_LMEM) & 0xfff) {
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s, ctrl %x, rsp %x, pc %x status %x,%x\n",
+ __func__, RD_VREG(LMEM_DMA_CTRL),
+ RD_VREG(WRRSP_LMEM), RD_VREG(MPC_E),
+ RD_VREG(AV_SCRATCH_J), RD_VREG(AV_SCRATCH_9));
+ break;
+ }
+ }
+
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+
+ WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+ RD_VREG(DOS_SW_RESET0);
+}
+
+int load_firmware(struct aml_vdec_hw *hw, const char *path)
+{
+ const struct firmware *fw;
+ struct device *dev = hw->dev;
+ static u8 *mc_addr;
+ static dma_addr_t mc_addr_map;
+ int fw_head_len;
+ ulong timeout;
+ int ret;
+
+ ret = request_firmware(&fw, path, dev);
+ if (ret < 0) {
+ pr_info("request_firmware %s failed ret %d\n", path, ret);
+ return -EINVAL;
+ }
+
+ if (fw->size > MC_SIZE) {
+ pr_info("fw %s oversize\n", path);
+ ret = -EINVAL;
+ goto release_firmware;
+ }
+
+ fw_head_len = 512;
+ mc_addr = dma_alloc_coherent(dev, MC_SIZE, &mc_addr_map, GFP_KERNEL);
+ if (!mc_addr) {
+ pr_info("no mem for fw %s\n", path);
+ ret = -ENOMEM;
+ goto release_firmware;
+ }
+ memset(mc_addr, 0, MC_SIZE);
+ memcpy(mc_addr, ((u8 *)fw->data + fw_head_len),
+ (fw->size - fw_head_len));
+
+ WRITE_VREG(MPSR, 0);
+ WRITE_VREG(CPSR, 0);
+
+ timeout = RD_VREG(MPSR);
+ timeout = RD_VREG(MPSR);
+ timeout = jiffies + HZ;
+
+ WRITE_VREG(IMEM_DMA_ADR, mc_addr_map);
+ WRITE_VREG(IMEM_DMA_COUNT, 0x1000);
+ WRITE_VREG(IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
+
+ while (RD_VREG(IMEM_DMA_CTRL) & 0x8000) {
+ if (time_before(jiffies, timeout)) {
+ schedule();
+ } else {
+ pr_info("vdec load mc error\n");
+ ret = -EBUSY;
+ break;
+ }
+ }
+
+ /* Only h264 needs this step */
+ if (hw->hw_ops.load_firmware_ex) {
+ ret = hw->hw_ops.load_firmware_ex(hw->curr_ctx,
+ mc_addr,
+ (fw->size - fw_head_len));
+ if (ret < 0) {
+ ret = -EINVAL;
+ goto free_dma_mem;
+ }
+ }
+
+free_dma_mem:
+ dma_free_coherent(dev, MC_SIZE, mc_addr, mc_addr_map);
+release_firmware:
+ release_firmware(fw);
+ return ret;
+}
+
+static int vdec_clock_gate_init(struct aml_vdec_hw *hw)
+{
+ int i;
+
+ hw->gates[VDEC].name = "vdec";
+ hw->gates[VDEC_MUX].name = "clk_vdec_mux";
+ hw->gates[HEVCF_MUX].name = "clk_hevcf_mux";
+
+ for (i = 0; i < DOS_CLK_MAX; i++) {
+ hw->gates[i].clk = devm_clk_get(hw->dev, hw->gates[i].name);
+ hw->gates[i].ref_count = 0;
+ mutex_init(&hw->gates[i].mutex);
+ }
+
+ return 0;
+}
+
+static int vdec_gate_clk(struct gate_switch_node *gate_node, bool enable)
+{
+ int ret = 0;
+
+ mutex_lock(&gate_node->mutex);
+ if (enable) {
+ gate_node->ref_count++;
+ if (gate_node->ref_count == 1) {
+ ret = clk_prepare_enable(gate_node->clk);
+ pr_debug("the %-15s clock on, ref cnt: %d ret = %d\n",
+ gate_node->name, gate_node->ref_count, ret);
+ }
+ } else {
+ gate_node->ref_count--;
+ if (!gate_node->ref_count) {
+ clk_disable_unprepare(gate_node->clk);
+ pr_debug("the %-15s clock off, ref cnt: %d ret = %d\n",
+ gate_node->name, gate_node->ref_count, ret);
+ }
+ }
+ mutex_unlock(&gate_node->mutex);
+
+ return 0;
+}
+
+static int vdec_switch_gate(struct aml_vdec_hw *hw,
+ const char *name, bool enable)
+{
+ int i;
+
+ for (i = 0; i < DOS_CLK_MAX; i++) {
+ if (!strcmp(name, hw->gates[i].name)) {
+ if (hw->gates[i].clk)
+ vdec_gate_clk(&hw->gates[i], enable);
+ else
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+static int vdec_set_rate(struct aml_vdec_hw *hw,
+ const char *name, unsigned long hz)
+{
+ int i;
+
+ for (i = 0; i < DOS_CLK_MAX; i++) {
+ if (!strcmp(name, hw->gates[i].name)) {
+ if (hw->gates[i].clk && hw->gates[i].ref_count) {
+ clk_set_rate(hw->gates[i].clk, hz);
+ pr_debug("after set, vdec mux clock is %lu Hz\n",
+ clk_get_rate(hw->gates[i].clk));
+ } else {
+ return -ENODEV;
+ }
+ }
+ }
+ return 0;
+}
+
+static void pm_vdec_clock_switch(struct aml_vdec_hw *hw, int id, bool on)
+{
+ int ret = 0;
+
+ if (id == VDEC_1) {
+ ret = vdec_switch_gate(hw, "clk_vdec_mux", on);
+ vdec_set_rate(hw, "clk_vdec_mux", 499999992);
+ } else if (id == VDEC_HEVC) {
+ /* enable hevc clock */
+ ret = vdec_switch_gate(hw, "clk_hevcf_mux", on);
+ if (ret == -ENODEV)
+ ret = vdec_switch_gate(hw, "clk_hevc_mux", on);
+ }
+
+ if (ret < 0)
+ pr_debug("clk %d, unreachable ret %d\n", id, ret);
+}
+
+static void pm_vdec_power_switch(struct pm_pd_s *pd, int id, bool on)
+{
+ struct device *dev = pd[id].dev;
+ int ret;
+
+ if (!dev) {
+ pr_debug("no dev %d, maybe always on\n", id);
+ return;
+ }
+
+ if (on)
+ ret = pm_runtime_get_sync(dev);
+ else
+ ret = pm_runtime_put_sync(dev);
+
+ pr_debug("dev: %p link %p the %-15s power %s ret %d\n",
+ dev, pd[id].link, pd[id].name, on ? "on" : "off", ret);
+}
+
+static int pm_vdec_power_domain_init(struct aml_vdec_hw *hw)
+{
+ int i, err;
+ const struct power_manager_s *pm = hw->pm;
+ struct pm_pd_s *pd = pm->pd_data;
+
+ mutex_init(&hw->pm_mutex);
+
+ for (i = 0; i < VDEC_MAX; i++) {
+ pd[i].dev = dev_pm_domain_attach_by_name(hw->dev, pd[i].name);
+ if (IS_ERR_OR_NULL(pd[i].dev)) {
+ err = PTR_ERR(pd[i].dev);
+ pr_debug("Get %s failed, pm-domain: %d\n",
+ pd[i].name, err);
+ continue;
+ }
+
+ pd[i].link = device_link_add(hw->dev, pd[i].dev,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (IS_ERR_OR_NULL(pd[i].link)) {
+ pr_err("Adding %s device link failed!\n", pd[i].name);
+ return -ENODEV;
+ }
+
+ pr_debug("power domain: name: %s, dev: %p, link: %p\n",
+ pd[i].name, pd[i].dev, pd[i].link);
+ }
+
+ return 0;
+}
+
+static void pm_vdec_power_domain_release(struct aml_vdec_hw *hw)
+{
+ int i;
+ const struct power_manager_s *pm = hw->pm;
+ struct pm_pd_s *pd = pm->pd_data;
+
+ for (i = 0; i < VDEC_MAX; i++) {
+ if (!IS_ERR_OR_NULL(pd[i].link))
+ device_link_del(pd[i].link);
+
+ if (!IS_ERR_OR_NULL(pd[i].dev))
+ dev_pm_domain_detach(pd[i].dev, true);
+ }
+}
+
+static void dos_local_config(bool is_on, int id)
+{
+ if (is_on) {
+ usleep_range(20, 100);
+
+ switch (id) {
+ case VDEC_1:
+ WRITE_VREG(DOS_MEM_PD_VDEC, 0);
+ WRITE_VREG(DOS_SW_RESET0, 0xfffffffc);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_SW_RESET0, 0);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_MEM_PD_VDEC, 0);
+ break;
+ case VDEC_HEVC:
+ WRITE_VREG(DOS_MEM_PD_HEVC, 0);
+ WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_SW_RESET3, 0);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_MEM_PD_HEVC, 0);
+ break;
+ default:
+ pr_debug("%s on, not found id %d\n", __func__, id);
+ break;
+ }
+ } else {
+ switch (id) {
+ case VDEC_1:
+ WRITE_VREG(DOS_SW_RESET0, 0xfffffffc);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_SW_RESET0, 0);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_MEM_PD_VDEC, 0xffffffffUL);
+ break;
+ case VDEC_HEVC:
+ WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_SW_RESET3, 0);
+ usleep_range(20, 100);
+ WRITE_VREG(DOS_MEM_PD_HEVC, 0xffffffffUL);
+ break;
+ default:
+ pr_debug("%s off, not found id %d\n", __func__, id);
+ break;
+ }
+ }
+
+ pr_debug("%s end, id %d, is_on %d\n", __func__, id, is_on);
+}
+
+static void pm_vdec_power_domain_power_on(struct aml_vdec_hw *hw, int id)
+{
+ const struct power_manager_s *pm = hw->pm;
+
+ pm_vdec_clock_switch(hw, id, true);
+ pm_vdec_power_switch(pm->pd_data, id, true);
+
+ dos_local_config(1, id);
+}
+
+static void pm_vdec_power_domain_power_off(struct aml_vdec_hw *hw, int id)
+{
+ const struct power_manager_s *pm = hw->pm;
+
+ dos_local_config(0, id);
+
+ pm_vdec_clock_switch(hw, id, false);
+ pm_vdec_power_switch(pm->pd_data, id, false);
+}
+
+static bool pm_vdec_power_domain_power_state(struct aml_vdec_hw *hw, int id)
+{
+ if (hw->pm->pd_data[id].dev)
+ return pm_runtime_active(hw->pm->pd_data[id].dev);
+ else
+ return false;
+}
+
+static void vdec_poweron(struct aml_vdec_hw *hw, enum vdec_type_e core)
+{
+ if (core >= VDEC_MAX)
+ return;
+
+ mutex_lock(&hw->pm_mutex);
+ if (!hw->pm->pd_data[core].dev)
+ goto out;
+
+ hw->pm->pd_data[core].ref_count++;
+ if (hw->pm->pd_data[core].ref_count > 1)
+ goto out;
+
+ if (hw->pm->power_state(hw, core))
+ goto out;
+
+ hw->pm->power_on(hw, core);
+
+out:
+ mutex_unlock(&hw->pm_mutex);
+}
+
+static void vdec_poweroff(struct aml_vdec_hw *hw, enum vdec_type_e core)
+{
+ if (core >= VDEC_MAX)
+ return;
+
+ mutex_lock(&hw->pm_mutex);
+ if (hw->pm->pd_data[core].ref_count == 0)
+ goto out;
+
+ hw->pm->pd_data[core].ref_count--;
+ if (hw->pm->pd_data[core].ref_count > 0)
+ goto out;
+
+ hw->pm->power_off(hw, core);
+
+out:
+ mutex_unlock(&hw->pm_mutex);
+}
+
+int vdec_enable(struct aml_vdec_hw *hw)
+{
+ vdec_switch_gate(hw, "vdec", 1);
+ vdec_poweron(hw, VDEC_1);
+
+ return 0;
+}
+
+int vdec_disable(struct aml_vdec_hw *hw)
+{
+ vdec_poweroff(hw, VDEC_1);
+ vdec_switch_gate(hw, "vdec", 0);
+
+ return 0;
+}
+
+static const struct power_manager_s pm[] = {
+ [AML_PM_PD] = {
+ .pd_data = vdec_domain_data,
+ .init = pm_vdec_power_domain_init,
+ .release = pm_vdec_power_domain_release,
+ .power_on = pm_vdec_power_domain_power_on,
+ .power_off = pm_vdec_power_domain_power_off,
+ .power_state = pm_vdec_power_domain_power_state,
+ },
+};
+
+static irqreturn_t vdec_irq_handler(int irq, void *priv)
+{
+ struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv;
+ struct aml_vdec_hw *hw = dev->dec_hw;
+ irqreturn_t ret;
+
+ if (hw->hw_ops.irq_handler)
+ ret = hw->hw_ops.irq_handler(irq, priv);
+
+ return ret;
+}
+
+static irqreturn_t vdec_threaded_isr_handler(int irq, void *priv)
+{
+ struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv;
+ struct aml_vdec_hw *hw = dev->dec_hw;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ if (hw->hw_ops.irq_threaded_func)
+ ret = hw->hw_ops.irq_threaded_func(irq, priv);
+
+ return ret;
+}
+
+struct aml_vdec_hw *vdec_get_hw(void *priv)
+{
+ struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv;
+
+ return dev->dec_hw;
+}
+
+int dev_request_hw_resources(void *priv)
+{
+ struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv;
+ struct aml_vdec_hw *hw;
+ struct platform_device *pdev;
+ struct resource res;
+ u32 res_size;
+ int i;
+ int ret = -1;
+
+ if (!dev || !dev->dec_hw) {
+ pr_err("%s param invalid\n", __func__);
+ return -1;
+ }
+ pdev = dev->plat_dev;
+ hw = dev->dec_hw;
+ hw->dev = &pdev->dev;
+
+ hw->dec_irq = platform_get_irq(pdev, VDEC_IRQ_1);
+ if (hw->dec_irq < 0) {
+ dev_err(&pdev->dev, "get irq failed\n");
+ return hw->dec_irq;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, hw->dec_irq, vdec_irq_handler,
+ vdec_threaded_isr_handler, IRQF_ONESHOT,
+ "vdec-1", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install irq %d (%d)",
+ hw->dec_irq, ret);
+ return -1;
+ }
+
+ for (i = 0; i < MAX_REG_BUS; i++) {
+ if (of_address_to_resource(pdev->dev.of_node, i, &res)) {
+ pr_err("of_address_to_resource %d failed\n", i);
+ return -EINVAL;
+ }
+
+ res_size = resource_size(&res);
+ hw->regs[i] = ioremap(res.start, res_size);
+ reg_base[i] = hw->regs[i];
+
+ pr_debug("%s, res start %llx, end %llx, iomap: %p\n",
+ __func__, (unsigned long long)res.start,
+ (unsigned long long)res.end, reg_base[i]);
+ }
+
+ hw->pm = &pm[dev->pvdec_data->power_type];
+ if (hw->pm->init) {
+ ret = hw->pm->init(hw);
+ if (ret < 0) {
+ pr_err("power mgr init failed!\n");
+ return ret;
+ }
+ }
+ vdec_clock_gate_init(hw);
+
+ aml_vdec_canvas_register(hw);
+
+ pr_debug("##Amlogic hw resource init OK##\n");
+
+ return 0;
+}
+
+void dev_destroy_hw_resources(void *priv)
+{
+ struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv;
+ struct aml_vdec_hw *hw;
+
+ if (!dev || !dev->dec_hw) {
+ pr_err("%s param invalid\n", __func__);
+ return;
+ }
+ hw = dev->dec_hw;
+
+ if (hw->pm->release)
+ hw->pm->release(hw);
+
+ kfree(hw);
+ dev->dec_hw = NULL;
+ pr_debug("##Amlogic hw resource release OK##\n");
+}
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_hw.h b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.h
new file mode 100644
index 000000000000..ad1e613a2cf8
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef _AML_VDEC_HW_H_
+#define _AML_VDEC_HW_H_
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include "reg_defines.h"
+
+#define VDEC_FIFO_ALIGN 8
+#define VLD_PADDING_SIZE 1024
+
+#define RD_VREG(addr) read_dos_reg(addr)
+#define WRITE_VREG(addr, val) write_dos_reg(addr, val)
+#define WRITE_VREG_BITS(r, val, start, len) dos_reg_write_bits(r, val, start, len)
+#define CLEAR_VREG_MASK(r, mask) dos_reg_clear_mask(r, mask)
+#define SET_VREG_MASK(r, mask) dos_reg_set_mask(r, mask)
+
+/**
+ * struct aml_vdec_hw_ops - codec mode specific operations for hw
+ * @load_firmware_ex: Load firmware for current dec specific.
+ * @irq_handler: mandatory call when the ISR triggers
+ * @irq_threaded_func: mandatory call for the threaded ISR
+ * @canvas_alloc: Alloc canvas for curr frame
+ * @canvas_free: Release canvas.
+ * @config_canvas: Config for curr frame, such as w/h, Y/UV start addr etc.
+ */
+struct aml_vdec_hw_ops {
+ int (*load_firmware_ex)(void *priv, const u8 *data, u32 len);
+ irqreturn_t (*irq_handler)(int irq, void *priv);
+ irqreturn_t (*irq_threaded_func)(int irq, void *priv);
+ int (*canvas_alloc)(u8 *canvas_index);
+ void (*canvas_free)(u8 canvas_index);
+ void (*config_canvas)(u8 canvas_index,
+ ulong addr, u32 width, u32 height,
+ u32 wrap, u32 blkmode, u32 endian);
+};
+
+/**
+ * enum vdec_type_e - Type of decoder hardware.
+ */
+enum vdec_type_e {
+ VDEC_1 = 0,
+ VDEC_HEVC,
+ VDEC_MAX,
+};
+
+/**
+ * enum vdec_irq_num - Definition of the irq.
+ */
+enum vdec_irq_num {
+ VDEC_IRQ_0 = 0,
+ VDEC_IRQ_1,
+ VDEC_IRQ_2,
+ VDEC_IRQ_MAX,
+};
+
+/**
+ * enum vdec_type_e - Type of decoder clock.
+ */
+enum clk_type_e {
+ VDEC = 0,
+ VDEC_MUX,
+ HEVCF_MUX,
+ DOS_CLK_MAX,
+};
+
+/**
+ * enum aml_power_type_e - Type of decoder power.
+ */
+enum aml_power_type_e {
+ AML_PM_PD = 0,
+};
+
+/**
+ * enum mm_bus_e - Type of decoder hardware bus.
+ */
+enum mm_bus_e {
+ DOS_BUS = 0,
+ DMC_BUS,
+ MAX_REG_BUS
+};
+
+/**
+ * struct gate_switch_node - clock node definition
+ * @clk: Pointer to clk instance.
+ * @name: Clock name used.
+ * @mutex: Mutex lock for multi decoder instance.
+ * @ref_count: Curr clk instance ref count.
+ */
+struct gate_switch_node {
+ struct clk *clk;
+ const char *name;
+ struct mutex mutex;
+ int ref_count;
+};
+
+/**
+ * struct pm_pd_s - power domain definition
+ * @name: Power domain name.
+ * @dev: Pointer to device structure.
+ * @mutex: Pointer to device_link structure.
+ * @ref_count: Curr power domain instance ref count.
+ */
+struct pm_pd_s {
+ u8 *name;
+ struct device *dev;
+ struct device_link *link;
+ int ref_count;
+};
+
+/**
+ * struct aml_vdec_hw - decoder hardware resources definition
+ * @pdev: Pointer to struct platform_device.
+ * @dev: Pointer to struct device.
+ * @regs: Reg base for dos/dmc hardware.
+ * @pm_mutex: Mutex for pm->pd_data.
+ * @pm: Pointer to struct power_manager_s.
+ * @hw_ops: Hardware resource operation functions. See struct aml_vdec_hw_ops.
+ * @gates: Clk instance used by curr decoder context.
+ * @dec_irq: Irq registered.
+ * @curr_ctx: Pointer to curr decoder context.
+ */
+struct aml_vdec_hw {
+ struct platform_device *pdev;
+ struct device *dev;
+ void __iomem *regs[MAX_REG_BUS];
+ struct mutex pm_mutex;
+ const struct power_manager_s *pm;
+ struct aml_vdec_hw_ops hw_ops;
+ struct gate_switch_node gates[DOS_CLK_MAX];
+ int dec_irq;
+ void *curr_ctx;
+};
+
+/**
+ * struct power_manager_s - Power manager & opertion function
+ * @pd_data: Pointer to struct pm_pd_s
+ * @init: Power manager init.
+ * @release: Power manager release.
+ * @power_on: Power on for decoder hw.
+ * @power_off: Power off for decoder hw.
+ * @power_state: Query the power state.
+ */
+struct power_manager_s {
+ struct pm_pd_s *pd_data;
+ int (*init)(struct aml_vdec_hw *hw);
+ void (*release)(struct aml_vdec_hw *hw);
+ void (*power_on)(struct aml_vdec_hw *hw, int id);
+ void (*power_off)(struct aml_vdec_hw *hw, int id);
+ bool (*power_state)(struct aml_vdec_hw *hw, int id);
+};
+
+int dev_request_hw_resources(void *priv);
+void dev_destroy_hw_resources(void *priv);
+struct aml_vdec_hw *vdec_get_hw(void *priv);
+u32 read_dos_reg(u32 reg_addr);
+void write_dos_reg(u32 addr, int val);
+void dos_reg_write_bits(u32 reg, u32 val, int start, int len);
+void dos_reg_clear_mask(u32 addr, u32 mask);
+void dos_reg_set_mask(u32 addr, u32 mask);
+u32 read_dmc_reg(u32 addr);
+void write_dmc_reg(u32 addr, int val);
+int vdec_enable(struct aml_vdec_hw *hw);
+int vdec_disable(struct aml_vdec_hw *hw);
+void dos_enable(void);
+void aml_start_vdec_hw(void);
+void aml_stop_vdec_hw(void);
+int load_firmware(struct aml_vdec_hw *hw, const char *path);
+void aml_vdec_reset_core(void);
+
+#endif
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c
new file mode 100644
index 000000000000..f1f8ba1f4ff6
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#include "aml_vdec_platform.h"
+#include "aml_vdec_drv.h"
+#include "aml_vdec_hw.h"
+#include "h264.h"
+
+#define VIDEO_DEC_H264 "s4_h264_multi.bin"
+
+const struct aml_codec_ops aml_S4_dec_ops[] = {
+ [CODEC_TYPE_H264] = {
+ .init = aml_h264_init,
+ .exit = aml_h264_exit,
+ .run = aml_h264_dec_run,
+ },
+};
+
+static const struct aml_video_dec_fmt aml_S4_dec_fmts = {
+ .max_height = AML_VDEC_1080P_MAX_H,
+ .max_width = AML_VDEC_1080P_MAX_W,
+ .align = 32,
+ .is_10_bit_support = 0,
+};
+
+const struct aml_dev_platform_data aml_vdec_s4_pdata = {
+ .dec_fmt = &aml_S4_dec_fmts,
+ .codec_ops = aml_S4_dec_ops,
+ .power_type = AML_PM_PD,
+ .req_hw_resource = dev_request_hw_resources,
+ .destroy_hw_resource = dev_destroy_hw_resources,
+ .fw_path = {
+ VIDEO_DEC_H264,
+ },
+};
diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h
new file mode 100644
index 000000000000..ff0933f6f074
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef AML_VDEC_PLATFORM_H_
+#define AML_VDEC_PLATFORM_H_
+
+#include <linux/videodev2.h>
+
+#define MAX_DEC_FORMAT 3
+
+/**
+ * struct aml_codec_ops - codec mode specific operations
+ * @init: Used for decoder initialization.
+ * @exit: If needed, can be used to undo the .init phase.
+ * @run: Start a single decoding job. Called from atomic context.
+ * Caller should ensure that a pair of buffers is ready and the
+ * hardware is powered on and clk is enabled. Returns zero if OK,
+ * a negative value in error cases.
+ */
+struct aml_codec_ops {
+ int (*init)(void *priv);
+ void (*exit)(void *priv);
+ int (*run)(void *priv);
+};
+
+/**
+ * struct aml_video_dec_fmt - codec format required by platform
+ * @max_height: Max decode frame height of current platform.
+ * @max_width: Max decode frame width of current platform.
+ * @align: Align requirement of the current platform.
+ * @is_10_bit_support: Whether the platform supports 10 bit.
+ */
+struct aml_video_dec_fmt {
+ u32 max_height;
+ u32 max_width;
+ int align;
+ int is_10_bit_support;
+};
+
+/**
+ * struct aml_dev_platform_data - compatible data for each chip.
+ * @dec_fmt: Support dec format.
+ * @codec_ops: Codec operation function.
+ * @req_hw_resource: Operation function to request the hardware resource.
+ * @destroy_hw_resource: Operation function to release the hardware resource.
+ * @power_type: Type of power that the current chip need. See aml_power_type_e.
+ * @fw_path: Path of the firmware.bin.
+ */
+struct aml_dev_platform_data {
+ const struct aml_video_dec_fmt *dec_fmt;
+ const struct aml_codec_ops *codec_ops;
+ int (*req_hw_resource)(void *priv);
+ void (*destroy_hw_resource)(void *priv);
+ int power_type;
+ const char *fw_path[MAX_DEC_FORMAT];
+};
+
+extern const struct aml_dev_platform_data aml_vdec_s4_pdata;
+
+#endif
diff --git a/drivers/media/platform/amlogic/vdec/h264.c b/drivers/media/platform/amlogic/vdec/h264.c
new file mode 100644
index 000000000000..ebfd10885eaa
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/h264.c
@@ -0,0 +1,1790 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#include <media/v4l2-h264.h>
+#include <linux/vmalloc.h>
+#include "aml_vdec.h"
+#include "aml_vdec_hw.h"
+#include "h264.h"
+
+#define INVALID_POC 0xffffffff
+
+#define H264_SLICE_HEADER_DONE 0x1
+#define H264_SLICE_DATA_DONE 0x2
+
+#define DECODER_TIMEOUT_MS 500
+
+#define COL_BUFFER_MARGIN 2
+
+struct vdec_h264_stateless_ctrl_ref {
+ const struct v4l2_ctrl_h264_decode_params *decode;
+ const struct v4l2_ctrl_h264_scaling_matrix *scaling;
+ const struct v4l2_ctrl_h264_sps *sps;
+ const struct v4l2_ctrl_h264_pps *pps;
+};
+
+enum SliceType {
+ P_SLICE = 0,
+ B_SLICE = 1,
+ I_SLICE = 2,
+ SP_SLICE = 3,
+ SI_SLICE = 4,
+ MAX_SLICE_TYPES = 5
+};
+
+/* Used by firmware */
+union param {
+ struct {
+ unsigned short data[RPM_END - RPM_BEGIN];
+ } l;
+ struct {
+ unsigned short dump[DPB_OFFSET];
+ unsigned short dpb_base[FRAME_IN_DPB << 3];
+
+ unsigned short dpb_max_buffer_frame;
+ unsigned short actual_dpb_size;
+
+ unsigned short colocated_buf_status;
+
+ unsigned short num_forward_short_term_reference_pic;
+ unsigned short num_short_term_reference_pic;
+ unsigned short num_reference_pic;
+
+ unsigned short current_dpb_index;
+ unsigned short current_decoded_frame_num;
+ unsigned short current_reference_frame_num;
+
+ unsigned short l0_size;
+ unsigned short l1_size;
+ /**
+ * [6:5] : nal_ref_idc
+ * [4:0] : nal_unit_type
+ */
+ unsigned short NAL_info_mmco;
+ /**
+ * [1:0] : 00 - top field, 01 - bottom field,
+ * 10 - frame, 11 - mbaff frame
+ */
+ unsigned short picture_structure_mmco;
+ unsigned short frame_num;
+ unsigned short pic_order_cnt_lsb;
+
+ unsigned short num_ref_idx_l0_active_minus1;
+ unsigned short num_ref_idx_l1_active_minus1;
+
+ unsigned short PrevPicOrderCntLsb;
+ unsigned short PreviousFrameNum;
+
+ /* 32 bits variables */
+ unsigned short delta_pic_order_cnt_bottom[2];
+ unsigned short delta_pic_order_cnt_0[2];
+ unsigned short delta_pic_order_cnt_1[2];
+
+ unsigned short PrevPicOrderCntMsb[2];
+ unsigned short PrevFrameNumOffset[2];
+
+ unsigned short frame_pic_order_cnt[2];
+ unsigned short top_field_pic_order_cnt[2];
+ unsigned short bottom_field_pic_order_cnt[2];
+
+ unsigned short colocated_mv_addr_start[2];
+ unsigned short colocated_mv_addr_end[2];
+ unsigned short colocated_mv_wr_addr[2];
+ } dpb;
+ struct {
+ unsigned short dump[MMCO_OFFSET];
+
+ /* array base address for offset_for_ref_frame */
+ unsigned short offset_for_ref_frame_base[128];
+
+ /**
+ * 0 - Index in DPB
+ * 1 - Picture Flag
+ * [2] : 0 - short term reference,
+ * 1 - long term reference
+ * [1] : bottom field
+ * [0] : top field
+ * 2 - Picture Number (short term or long term) low 16 bits
+ * 3 - Picture Number (short term or long term) high 16 bits
+ */
+ unsigned short reference_base[128];
+
+ /* command and parameter, until command is 3 */
+ unsigned short l0_reorder_cmd[REORDER_CMD_MAX];
+ unsigned short l1_reorder_cmd[REORDER_CMD_MAX];
+
+ /* command and parameter, until command is 0 */
+ unsigned short mmco_cmd[44];
+
+ unsigned short l0_base[40];
+ unsigned short l1_base[40];
+ } mmco;
+ struct {
+ /* from ucode lmem, do not change this struct */
+ } p;
+};
+
+struct h264_decode_buf_spec {
+ struct v4l2_h264_dpb_entry *dpb;
+ u32 canvas_pos;
+ u32 dpb_index;
+ u32 poc;
+ int col_buf_index;
+ u8 y_canvas_index;
+ u8 u_canvas_index;
+ u8 v_canvas_index;
+ u8 used;
+ u8 long_term_flag;
+ dma_addr_t y_dma_addr;
+ dma_addr_t c_dma_addr;
+};
+
+#define REORDERING_COMMAND_MAX_SIZE 33
+struct slice {
+ int frame_num;
+ /*modification */
+ int slice_type;
+ int num_ref_idx_l0;
+ int num_ref_idx_l1;
+ int ref_pic_list_reordering_flag[2];
+ int modification_of_pic_nums_idc[2][REORDERING_COMMAND_MAX_SIZE];
+ int abs_diff_pic_num_minus1[2][REORDERING_COMMAND_MAX_SIZE];
+ int long_term_pic_idx[2][REORDERING_COMMAND_MAX_SIZE];
+ unsigned char dec_ref_pic_marking_buffer_valid;
+};
+
+struct aml_h264_ctx {
+ struct aml_vdec_ctx *v4l2_ctx;
+ u8 init_flag;
+ u8 new_pic_flag;
+ u8 mc_cpu_loaded;
+ u8 param_set;
+ u8 colocated_buf_num;
+ u8 reg_iqidct_control_init_flag;
+ u32 reg_iqidct_control;
+ u32 reg_vcop_ctrl_reg;
+ u32 reg_rv_ai_mb_count;
+ u32 vld_dec_control;
+ u32 save_avscratch_f;
+ u32 seq_info;
+ u32 decode_pic_count;
+ union param dpb_param;
+ u32 dec_status;
+ struct slice mslice;
+ struct h264_decode_buf_spec curr_spec;
+ struct h264_decode_buf_spec ref_list0[V4L2_H264_NUM_DPB_ENTRIES + 1];
+ struct h264_decode_buf_spec ref_list1[V4L2_H264_NUM_DPB_ENTRIES + 1];
+ struct h264_decode_buf_spec ref_list0_unreordered[V4L2_H264_NUM_DPB_ENTRIES + 1];
+ struct h264_decode_buf_spec ref_list1_unreordered[V4L2_H264_NUM_DPB_ENTRIES + 1];
+ u8 list_size[2];
+ dma_addr_t lmem_phy_addr;
+ void *lmem_addr;
+ dma_addr_t mc_cpu_paddr;
+ void *mc_cpu_vaddr;
+ dma_addr_t cma_alloc_addr;
+ void *cma_alloc_vaddr;
+ dma_addr_t collated_cma_addr;
+ dma_addr_t collated_cma_addr_end;
+ void *collated_cma_vaddr;
+ dma_addr_t workspace_offset;
+ void *workspace_vaddr;
+ u32 col_buf_alloc_size;
+ u32 one_col_buf_size;
+ u32 colocated_buf_map;
+ int colocated_buf_poc[32];
+
+ u32 frame_width;
+ u32 frame_height;
+ u32 mb_width;
+ u32 mb_height;
+ u32 mb_total;
+ u32 max_num_ref_frames;
+
+ struct vdec_h264_stateless_ctrl_ref ctrl_ref;
+};
+
+static inline int get_flag(u32 flag, u32 mask)
+{
+ return (flag & mask) ? 1 : 0;
+}
+
+static inline void write_lmem(unsigned short *base, u32 offset, u32 value)
+{
+ base[offset] = value;
+}
+
+static inline uint32_t spec2canvas(struct h264_decode_buf_spec *buf_spec)
+{
+ return (buf_spec->v_canvas_index << 16) |
+ (buf_spec->u_canvas_index << 8) |
+ (buf_spec->y_canvas_index << 0);
+}
+
+static struct h264_decode_buf_spec *find_spec_by_dpb_index(struct aml_h264_ctx *h264_ctx, int index)
+{
+ int i;
+
+ for (i = 0; i < h264_ctx->list_size[0]; i++) {
+ if (index == h264_ctx->ref_list0[i].dpb_index)
+ return &h264_ctx->ref_list0[i];
+ }
+
+ for (i = 0; i < h264_ctx->list_size[1]; i++) {
+ if (index == h264_ctx->ref_list1[i].dpb_index)
+ return &h264_ctx->ref_list1[i];
+ }
+ return NULL;
+}
+
+static int h264_prepare_input(struct aml_vdec_ctx *ctx)
+{
+ struct vb2_v4l2_buffer *src;
+ struct vb2_buffer *vb;
+ dma_addr_t src_dma;
+ u32 payload_size;
+ int dummy;
+
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ if (!src) {
+ pr_info("no input buffer available!\n");
+ return -1;
+ }
+ vb = &src->vb2_buf;
+ payload_size = vb2_get_plane_payload(vb, 0);
+ src_dma = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+ /* reset VLD fifo for all vdec */
+ WRITE_VREG(DOS_SW_RESET0, (1 << 5) | (1 << 4) | (1 << 3));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+ WRITE_VREG(POWER_CTL_VLD, 1 << 4);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_START_PTR, src_dma);
+ WRITE_VREG(VLD_MEM_VIFIFO_END_PTR, (src_dma + payload_size));
+ WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
+ round_down(src_dma, VDEC_FIFO_ALIGN));
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_RP,
+ round_down(src_dma, VDEC_FIFO_ALIGN));
+ dummy = payload_size + VLD_PADDING_SIZE;
+ WRITE_VREG(VLD_MEM_VIFIFO_WP,
+ round_down((src_dma + dummy), VDEC_FIFO_ALIGN));
+
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 3);
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
+ (0x11 << 16) | (1 << 10) | (7 << 3));
+
+ WRITE_VREG(AV_SCRATCH_1, 0x0);
+ WRITE_VREG(H264_DECODE_INFO, (1 << 13));
+ WRITE_VREG(H264_DECODE_SIZE, payload_size);
+ WRITE_VREG(VIFF_BIT_CNT, payload_size * 8);
+
+ return 0;
+}
+
+static void config_sps_params(struct aml_h264_ctx *h264_ctx,
+ unsigned short *sps_base,
+ const struct v4l2_ctrl_h264_sps *sps)
+{
+ u32 cfg_tmp = 0;
+ u32 frame_size;
+ u32 offset = 0;
+ unsigned short data_tmp[0x100];
+ int i, ii;
+
+ memset(sps_base, 0, 0x100);
+
+ h264_ctx->frame_width = (sps->pic_width_in_mbs_minus1 + 1) << 4;
+ h264_ctx->frame_height = (sps->pic_height_in_map_units_minus1 + 1) << 4;
+
+ data_tmp[offset] = PARAM_BASE_VAL;
+ offset += 2;
+
+ data_tmp[offset++] = GET_SPS_PROFILE_IDC(sps->profile_idc);
+
+ data_tmp[offset++] = GET_SPS_SEQ_PARAM_SET_ID(sps->seq_parameter_set_id) |
+ GET_SPS_LEVEL_IDC(sps->level_idc);
+
+ if (sps->profile_idc >= 100) {
+ data_tmp[offset++] = GET_SPS_CHROMA_FORMAT_IDC(sps->chroma_format_idc);
+
+ data_tmp[offset++] = ((sps->chroma_format_idc ^ 1) << 1);
+ }
+
+ data_tmp[offset++] = GET_SPS_LOG2_MAX_FRAME_NUM(sps->log2_max_frame_num_minus4);
+ data_tmp[offset++] = GET_SPS_PIC_ORDER_TYPE(sps->pic_order_cnt_type);
+
+ if (sps->pic_order_cnt_type == 0) {
+ data_tmp[offset++] =
+ GET_SPS_PIC_ORDER_CNT_LSB(sps->log2_max_pic_order_cnt_lsb_minus4);
+ } else if (sps->pic_order_cnt_type == 1) {
+ data_tmp[offset++] =
+ get_flag(sps->flags,
+ V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
+ data_tmp[offset++] =
+ GET_SPS_OFFSET_FOR_NONREF_PIC_LOW(sps->offset_for_non_ref_pic);
+ data_tmp[offset++] =
+ GET_SPS_OFFSET_FOR_NONREF_PIC_HIGH(sps->offset_for_non_ref_pic);
+ data_tmp[offset++] =
+ GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_LOW(sps->offset_for_top_to_bottom_field);
+ data_tmp[offset++] =
+ GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_HIGH(sps->offset_for_top_to_bottom_field);
+ data_tmp[offset++] = sps->num_ref_frames_in_pic_order_cnt_cycle;
+ }
+
+ data_tmp[offset++] = GET_SPS_NUM_REF_FRAMES(sps->max_num_ref_frames) |
+ GET_SPS_GAPS_ALLOWED_FLAG(get_flag(sps->flags,
+ V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED));
+
+ data_tmp[offset++] = GET_SPS_PIC_WIDTH_IN_MBS(sps->pic_width_in_mbs_minus1);
+
+ data_tmp[offset++] = GET_SPS_PIC_HEIGHT_IN_MBS(sps->pic_height_in_map_units_minus1);
+ data_tmp[offset++] =
+ GET_SPS_DIRECT_8X8_FLAGS
+ (get_flag(sps->flags,
+ V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)) |
+ GET_SPS_MB_ADAPTIVE_FRAME_FIELD_FLAGS
+ (get_flag(sps->flags,
+ V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)) |
+ GET_SPS_FRAME_MBS_ONLY_FLAGS
+ (get_flag(sps->flags,
+ V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY));
+
+ for (i = 0; i < 0x100; i += 4) {
+ for (ii = 0; ii < 4; ii++)
+ sps_base[i + 3 - ii] = data_tmp[i + ii];
+ }
+
+ frame_size = (sps->pic_width_in_mbs_minus1 + 1) * (sps->pic_height_in_map_units_minus1 + 1);
+ cfg_tmp = (get_flag(sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) << 31) |
+ (sps->max_num_ref_frames << 24) | (frame_size << 8) |
+ (sps->pic_width_in_mbs_minus1 + 1);
+ WRITE_VREG(AV_SCRATCH_1, cfg_tmp);
+ h264_ctx->seq_info = cfg_tmp;
+
+ cfg_tmp = 0;
+ cfg_tmp = (get_flag(sps->flags, V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) << 15) |
+ (sps->chroma_format_idc);
+ WRITE_VREG(AV_SCRATCH_2, cfg_tmp);
+
+ cfg_tmp = 0;
+ cfg_tmp = (sps->max_num_ref_frames << 8) | (sps->level_idc);
+ WRITE_VREG(AV_SCRATCH_B, cfg_tmp);
+
+ cfg_tmp = ((sps->level_idc & 0xff) << 7) |
+ (get_flag(sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) << 2);
+ WRITE_VREG(NAL_SEARCH_CTL, RD_VREG(NAL_SEARCH_CTL) | cfg_tmp);
+
+ h264_ctx->mb_width = (sps->pic_width_in_mbs_minus1 + 4) & 0xfffffffc;
+ h264_ctx->mb_height = (sps->pic_height_in_map_units_minus1 + 4) & 0xfffffffc;
+ h264_ctx->mb_total = h264_ctx->mb_width * h264_ctx->mb_height;
+ h264_ctx->max_num_ref_frames = sps->max_num_ref_frames;
+}
+
+static void config_pps_params(struct aml_h264_ctx *h264_ctx,
+ unsigned short *pps_base,
+ const struct v4l2_ctrl_h264_pps *pps)
+{
+ u32 offset = 0;
+ unsigned short data_tmp[0x100];
+ u32 max_reference_size = V4L2_H264_NUM_DPB_ENTRIES;
+ u32 max_list_size;
+ int i, ii;
+
+ memset(pps_base, 0, 0x100);
+
+ data_tmp[offset++] = PARAM_BASE_VAL;
+
+ data_tmp[offset++] =
+ GET_PPS_PIC_PARAM_SET_ID(pps->pic_parameter_set_id) |
+ GET_PPS_SEQ_PARAM_SET_ID(pps->seq_parameter_set_id) |
+ GET_PPS_ENTROPY_CODING_MODE_FLAG
+ (get_flag(pps->flags,
+ V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE)) |
+ GET_PPS_PIC_ORDER_PRESENT_FLAG
+ (get_flag(pps->flags,
+ V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT));
+
+ data_tmp[offset++] =
+ GET_PPS_WEIGHTED_BIPRED_IDC(pps->weighted_bipred_idc) |
+ GET_PPS_WEIGHTED_PRED_FLAG(get_flag(pps->flags,
+ V4L2_H264_PPS_FLAG_WEIGHTED_PRED)) |
+ GET_PPS_NUM_IDX_REF_L1_MINUS1(pps->num_ref_idx_l1_default_active_minus1) |
+ GET_PPS_NUM_IDX_REF_L0_MINUS1(pps->num_ref_idx_l0_default_active_minus1);
+
+ data_tmp[offset++] = GET_PPS_INIT_QS_MINUS26(pps->pic_init_qs_minus26) |
+ GET_PPS_INIT_QP_MINUS26(pps->pic_init_qp_minus26);
+
+ data_tmp[offset] =
+ GET_PPS_CHROMA_QP_INDEX_OFFSET(pps->chroma_qp_index_offset) |
+ GET_PPS_DEBLOCK_FILTER_CTRL_PRESENT_FLAG
+ (get_flag(pps->flags,
+ V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)) |
+ GET_PPS_CONSTRAIN_INTRA_PRED_FLAG
+ (get_flag(pps->flags,
+ V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)) |
+ GET_PPS_REDUNDANT_PIC_CNT_PRESENT_FLAG
+ (get_flag(pps->flags,
+ V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT));
+ if (get_flag
+ (pps->flags,
+ V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE |
+ V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT))
+ data_tmp[offset] |= (1 << 11);
+ offset++;
+
+ data_tmp[offset++] =
+ GET_PPS_SCALING_MATRIX_PRESENT_FLAG(get_flag
+ (pps->flags,
+ V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) |
+ GET_PPS_TRANSFORM_8X8_FLAG(get_flag
+ (pps->flags,
+ V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE));
+
+ data_tmp[offset++] =
+ GET_PPS_GET_SECOND_CHROMA_QP_OFFSET(pps->second_chroma_qp_index_offset);
+
+ max_list_size = (pps->num_ref_idx_l1_default_active_minus1 + 1) +
+ (pps->num_ref_idx_l0_default_active_minus1 + 1);
+
+ h264_ctx->max_num_ref_frames = max_list_size > h264_ctx->max_num_ref_frames ?
+ max_list_size : h264_ctx->max_num_ref_frames;
+
+ WRITE_VREG(AV_SCRATCH_0, ((h264_ctx->max_num_ref_frames + 1) << 24) |
+ (max_reference_size << 16) | (max_reference_size << 8));
+
+ for (i = 0; i < 0x100; i += 4) {
+ for (ii = 0; ii < 4; ii++)
+ pps_base[i + 3 - ii] = data_tmp[i + ii];
+ }
+}
+
+static void h264_config_params(struct aml_vdec_ctx *ctx)
+{
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)ctx->codec_priv;
+ unsigned short *p_sps_base, *p_pps_base;
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+ const struct v4l2_ctrl_h264_sps *sps = ctrls->sps;
+ const struct v4l2_ctrl_h264_pps *pps = ctrls->pps;
+
+ p_sps_base = (unsigned short *)(h264_ctx->workspace_vaddr +
+ MEM_SPS_BASE + sps->seq_parameter_set_id * 0x400);
+ p_pps_base = (unsigned short *)(h264_ctx->workspace_vaddr +
+ MEM_PPS_BASE + pps->pic_parameter_set_id * 0x200);
+
+ config_sps_params(h264_ctx, p_sps_base, sps);
+ config_pps_params(h264_ctx, p_pps_base, pps);
+}
+
+static void config_decode_canvas(struct aml_vdec_hw *hw,
+ struct h264_decode_buf_spec *buf_spec,
+ u32 mb_width, u32 mb_height)
+{
+ int canvas_alloc_result = 0;
+ int blkmode = 0x0;
+
+ canvas_alloc_result = hw->hw_ops.canvas_alloc(&buf_spec->y_canvas_index);
+ canvas_alloc_result = hw->hw_ops.canvas_alloc(&buf_spec->u_canvas_index);
+ buf_spec->v_canvas_index = buf_spec->u_canvas_index;
+
+ if (!canvas_alloc_result) {
+ /* config y canvas */
+ hw->hw_ops.config_canvas(buf_spec->y_canvas_index,
+ buf_spec->y_dma_addr, mb_width << 4,
+ mb_height << 4, 0, blkmode, 7);
+ WRITE_VREG(VDEC_ASSIST_CANVAS_BLK32, (1 << 11) | /* canvas_blk32_wr */
+ (blkmode << 10) | /* canvas_blk32 */
+ (1 << 8) | /* canvas_index_wr */
+ (buf_spec->y_canvas_index << 0) /* canvas index */
+ );
+
+ /* config uv canvas */
+ hw->hw_ops.config_canvas(buf_spec->u_canvas_index,
+ buf_spec->c_dma_addr, mb_width << 4,
+ mb_height << 3, 0, blkmode, 7);
+ WRITE_VREG(VDEC_ASSIST_CANVAS_BLK32, (1 << 11) | /* canvas_blk32_wr */
+ (blkmode << 10) | /* canvas_blk32 */
+ (1 << 8) | /* canvas_index_wr */
+ (buf_spec->u_canvas_index << 0) /* canvas index */
+ );
+
+ WRITE_VREG(ANC0_CANVAS_ADDR + buf_spec->canvas_pos,
+ spec2canvas(buf_spec));
+ }
+}
+
+static int allocate_colocate_buf(struct aml_h264_ctx *h264_ctx, int poc)
+{
+ int i;
+
+ for (i = 0; i < h264_ctx->colocated_buf_num; i++) {
+ if (((h264_ctx->colocated_buf_map >> i) & 0x1) == 0) {
+ h264_ctx->colocated_buf_map |= (1 << i);
+ break;
+ }
+ }
+
+ if (i == h264_ctx->colocated_buf_num)
+ return -1;
+
+ h264_ctx->colocated_buf_poc[i] = poc;
+ pr_debug("%s colocated_buf_index %d poc %d\n", __func__, i,
+ h264_ctx->colocated_buf_poc[i]);
+
+ return i;
+}
+
+static void release_colocate_buf(struct aml_h264_ctx *h264_ctx, int index)
+{
+ if (index >= 0) {
+ if (index >= h264_ctx->colocated_buf_num) {
+ pr_debug
+ ("%s error, index %d is bigger than buf count %d\n",
+ __func__, index, h264_ctx->max_num_ref_frames);
+ } else {
+ if (((h264_ctx->colocated_buf_map >> index) & 0x1) == 0x1) {
+ h264_ctx->colocated_buf_map &= (~(1 << index));
+ pr_debug
+ ("%s colocated_buf_index %d released poc %d\n",
+ __func__, index,
+ h264_ctx->colocated_buf_poc[index]);
+ }
+ h264_ctx->colocated_buf_poc[index] = INVALID_POC;
+ }
+ }
+}
+
+static int get_col_buf_index_by_poc(struct aml_h264_ctx *h264_ctx, int poc)
+{
+ int idx;
+
+ for (idx = 0; idx < h264_ctx->colocated_buf_num; idx++) {
+ if (h264_ctx->colocated_buf_poc[idx] == poc)
+ break;
+ }
+
+ if (idx == h264_ctx->colocated_buf_num)
+ idx = -1;
+
+ return idx;
+}
+
+static int alloc_colocate_cma(struct aml_h264_ctx *h264_ctx,
+ struct aml_vdec_ctx *ctx)
+{
+ int alloc_size = 0;
+ int i;
+ struct aml_vdec_hw *hw;
+
+ if (h264_ctx->collated_cma_vaddr)
+ return 0;
+
+ hw = vdec_get_hw(ctx->dev);
+ if (!hw)
+ return -1;
+
+ /* 96 :col buf size for each mb */
+ h264_ctx->one_col_buf_size = h264_ctx->mb_total * 96;
+ alloc_size = PAGE_ALIGN(h264_ctx->one_col_buf_size *
+ (h264_ctx->max_num_ref_frames + COL_BUFFER_MARGIN));
+ h264_ctx->collated_cma_vaddr = dma_alloc_coherent(hw->dev, alloc_size,
+ &h264_ctx->collated_cma_addr, GFP_KERNEL);
+ if (!h264_ctx->collated_cma_vaddr)
+ return -ENOMEM;
+
+ pr_debug
+ ("collated_cma_addr = 0x%llx, one_col_buf_size = %x alloc_size = %x\n",
+ h264_ctx->collated_cma_addr, h264_ctx->one_col_buf_size,
+ alloc_size);
+ h264_ctx->collated_cma_addr_end =
+ h264_ctx->collated_cma_addr + alloc_size;
+ memset(h264_ctx->collated_cma_vaddr, 0, alloc_size);
+ h264_ctx->col_buf_alloc_size = alloc_size;
+ h264_ctx->colocated_buf_map = 0;
+ h264_ctx->colocated_buf_num = h264_ctx->max_num_ref_frames + COL_BUFFER_MARGIN;
+
+ /* 32 : max index of co-locate buffer */
+ for (i = 0; i < 32; i++)
+ h264_ctx->colocated_buf_poc[i] = INVALID_POC;
+
+ return 0;
+}
+
+static void config_p_reflist(struct aml_h264_ctx *h264_ctx,
+ struct v4l2_h264_reference *v4l2_p0_reflist,
+ u32 list_size)
+{
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+ struct v4l2_ctrl_h264_decode_params *decode =
+ (struct v4l2_ctrl_h264_decode_params *)ctrls->decode;
+ struct v4l2_h264_dpb_entry *dpb = decode->dpb;
+ u8 index;
+ int i;
+
+ for (i = 0; i < list_size; i++) {
+ index = v4l2_p0_reflist[i].index;
+ h264_ctx->ref_list0[i].used = 1;
+ h264_ctx->ref_list0[i].dpb = &dpb[index];
+ h264_ctx->ref_list0[i].poc = dpb[index].top_field_order_cnt;
+ h264_ctx->ref_list0[i].long_term_flag =
+ dpb[index].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ? true : false;
+ h264_ctx->ref_list0[i].dpb_index = index;
+ }
+ h264_ctx->list_size[0] = i;
+}
+
+static void config_b_reflist(struct aml_h264_ctx *h264_ctx,
+ struct v4l2_h264_reference *v4l2_b0_reflist,
+ struct v4l2_h264_reference *v4l2_b1_reflist,
+ u32 list_size)
+{
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+ struct v4l2_ctrl_h264_decode_params *decode =
+ (struct v4l2_ctrl_h264_decode_params *)ctrls->decode;
+ struct v4l2_h264_dpb_entry *dpb = decode->dpb;
+ u8 index;
+ int i, j;
+
+ for (i = 0; i < list_size; i++) {
+ index = v4l2_b0_reflist[i].index;
+ if (dpb[index].top_field_order_cnt > decode->top_field_order_cnt) {
+ h264_ctx->list_size[0] = i;
+ break;
+ }
+ h264_ctx->ref_list0[i].used = 1;
+ h264_ctx->ref_list0[i].dpb = &dpb[index];
+ h264_ctx->ref_list0[i].poc = dpb[index].top_field_order_cnt;
+ h264_ctx->ref_list0[i].long_term_flag =
+ dpb[index].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ? true : false;
+ h264_ctx->ref_list0[i].col_buf_index =
+ get_col_buf_index_by_poc(h264_ctx, dpb[index].top_field_order_cnt);
+ h264_ctx->ref_list0[i].dpb_index = index;
+ }
+
+ for (j = 0; j < list_size; j++) {
+ index = v4l2_b1_reflist[j].index;
+ if (dpb[index].top_field_order_cnt < decode->top_field_order_cnt) {
+ h264_ctx->list_size[1] = j;
+ break;
+ }
+ h264_ctx->ref_list1[j].used = 1;
+ h264_ctx->ref_list1[j].dpb = &dpb[index];
+ h264_ctx->ref_list1[j].poc = dpb[index].top_field_order_cnt;
+ h264_ctx->ref_list1[j].long_term_flag =
+ dpb[index].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ? true : false;
+ h264_ctx->ref_list1[j].col_buf_index =
+ get_col_buf_index_by_poc(h264_ctx, dpb[index].top_field_order_cnt);
+ h264_ctx->ref_list1[j].dpb_index = index;
+ }
+
+ if ((h264_ctx->list_size[1] + h264_ctx->list_size[0]) < list_size)
+ pr_info("ref list incorrect list0 %d list0 %d list_size%d\n",
+ h264_ctx->list_size[0], h264_ctx->list_size[1], list_size);
+}
+
+static int poc_is_in_dpb(int poc, const struct v4l2_h264_dpb_entry *dpb)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
+ if (poc == dpb[i].top_field_order_cnt) {
+ ret = 1;
+ pr_debug("%s find poc %d col index %x inDPB\n", __func__, poc, i);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int get_ref_list_size(struct aml_h264_ctx *h264_ctx, int cur_list)
+{
+ unsigned short override_flag = h264_ctx->dpb_param.l.data[REF_IDC_OVERRIDE_FLAG];
+ int num_ref_idx_lx_active_minus1;
+
+ if (cur_list == 0) {
+ num_ref_idx_lx_active_minus1 =
+ h264_ctx->ctrl_ref.pps->num_ref_idx_l0_default_active_minus1;
+ if (override_flag)
+ num_ref_idx_lx_active_minus1 =
+ h264_ctx->dpb_param.dpb.num_ref_idx_l0_active_minus1;
+ } else {
+ num_ref_idx_lx_active_minus1 =
+ h264_ctx->ctrl_ref.pps->num_ref_idx_l1_default_active_minus1;
+ }
+ pr_debug("%s get list %d size %d\n", __func__, cur_list, num_ref_idx_lx_active_minus1 + 1);
+
+ return num_ref_idx_lx_active_minus1 + 1;
+}
+
+static int get_refidx_by_picnum(struct aml_h264_ctx *h264_ctx, int pic_num,
+ int curr_list)
+{
+ int i;
+ struct h264_decode_buf_spec *ref_list;
+
+ if (curr_list == 0)
+ ref_list = &h264_ctx->ref_list0[0];
+ else
+ ref_list = &h264_ctx->ref_list1[0];
+
+ for (i = 0; ref_list[i].dpb; i++) {
+ if (pic_num == ref_list[i].dpb->pic_num)
+ return i;
+ }
+
+ return -1;
+}
+
+static struct h264_decode_buf_spec *get_st_refpic_by_num(struct aml_h264_ctx *h264_ctx,
+ int pic_num, int curr_list)
+{
+ int i;
+ struct h264_decode_buf_spec *ref_list;
+
+ if (curr_list == 0)
+ ref_list = &h264_ctx->ref_list0_unreordered[0];
+ else
+ ref_list = &h264_ctx->ref_list1_unreordered[0];
+
+ for (i = 0; ref_list[i].dpb; i++) {
+ if (pic_num == ref_list[i].dpb->pic_num && ref_list[i].long_term_flag == 0)
+ return &ref_list[i];
+ }
+
+ return NULL;
+}
+
+static struct h264_decode_buf_spec *get_lt_refpic_by_num(struct aml_h264_ctx *h264_ctx,
+ int pic_num, int curr_list)
+{
+ int i;
+ struct h264_decode_buf_spec *ref_list;
+
+ if (curr_list == 0)
+ ref_list = &h264_ctx->ref_list0_unreordered[0];
+ else
+ ref_list = &h264_ctx->ref_list1_unreordered[0];
+
+ for (i = 0; ref_list[i].dpb; i++) {
+ if (pic_num == ref_list[i].dpb->pic_num && ref_list[i].long_term_flag == 1)
+ return &ref_list[i];
+ }
+
+ return NULL;
+}
+
+static void reorder_short_term(struct slice *curr_slice, int cur_list,
+ int pic_num_lx, int *ref_idx_lx)
+{
+ struct aml_h264_ctx *h264_ctx = container_of(curr_slice, struct aml_h264_ctx, mslice);
+ int c_idx, n_idx;
+ int num_ref_idx_lx_active;
+ struct h264_decode_buf_spec *pic_lx = NULL;
+ struct h264_decode_buf_spec *ref_list_reordered;
+
+ if (cur_list == 0)
+ ref_list_reordered = &h264_ctx->ref_list0[0];
+ else
+ ref_list_reordered = &h264_ctx->ref_list1[0];
+
+ num_ref_idx_lx_active = get_ref_list_size(h264_ctx, cur_list);
+
+ /* find short-term ref frame with pic_num is pic_num_lx */
+ pic_lx = get_st_refpic_by_num(h264_ctx, pic_num_lx, cur_list);
+ if (!pic_lx) {
+ pr_debug("cannot find st pic_lx for %d\n", pic_num_lx);
+ return;
+ }
+
+ if (*ref_idx_lx == get_refidx_by_picnum(h264_ctx, pic_num_lx, cur_list)) {
+ pr_debug("no need to move pic lx %d\n", *ref_idx_lx);
+ *ref_idx_lx = *ref_idx_lx + 1;
+ return;
+ }
+
+ for (c_idx = num_ref_idx_lx_active; c_idx > *ref_idx_lx; c_idx--)
+ memcpy(&ref_list_reordered[c_idx], &ref_list_reordered[c_idx - 1],
+ sizeof(struct h264_decode_buf_spec));
+
+ memcpy(&ref_list_reordered[*ref_idx_lx], pic_lx, sizeof(struct h264_decode_buf_spec));
+ pr_debug("%s 1 : RefPicListX[%d ] = pic %p pic_num(%d)\n", __func__,
+ *ref_idx_lx, pic_lx, ref_list_reordered[*ref_idx_lx].dpb->pic_num);
+ *ref_idx_lx = *ref_idx_lx + 1;
+
+ n_idx = *ref_idx_lx;
+ for (c_idx = *ref_idx_lx; c_idx <= num_ref_idx_lx_active; c_idx++) {
+ if (ref_list_reordered[c_idx].long_term_flag || !ref_list_reordered[c_idx].dpb ||
+ ref_list_reordered[c_idx].dpb->pic_num != pic_num_lx)
+ memcpy(&ref_list_reordered[n_idx++], &ref_list_reordered[c_idx],
+ sizeof(struct h264_decode_buf_spec));
+ }
+
+ h264_ctx->list_size[cur_list] = num_ref_idx_lx_active;
+}
+
+static void reorder_long_term(struct slice *curr_slice, int cur_list,
+ int lt_pic_num, int *ref_idx_lx)
+{
+ struct aml_h264_ctx *h264_ctx = container_of(curr_slice, struct aml_h264_ctx, mslice);
+ int num_ref_idx_lx_active;
+ int c_idx, n_idx;
+ struct h264_decode_buf_spec *ref_list;
+ struct h264_decode_buf_spec *pic_lt = NULL;
+
+ if (cur_list == 0)
+ ref_list = &h264_ctx->ref_list0[0];
+ else
+ ref_list = &h264_ctx->ref_list1[0];
+
+ num_ref_idx_lx_active = get_ref_list_size(h264_ctx, cur_list);
+
+ /* find long-term ref frame with pic_num is lt_pic_num */
+ pic_lt = get_lt_refpic_by_num(h264_ctx, lt_pic_num, cur_list);
+ if (!pic_lt) {
+ pr_debug("cannot find lt pic_lx for %d\n", lt_pic_num);
+ return;
+ }
+
+ if (*ref_idx_lx == get_refidx_by_picnum(h264_ctx, lt_pic_num, cur_list)) {
+ pr_debug("no need to move pic lx %d\n", *ref_idx_lx);
+ *ref_idx_lx = *ref_idx_lx + 1;
+ return;
+ }
+
+ for (c_idx = num_ref_idx_lx_active; c_idx > *ref_idx_lx; c_idx--)
+ memcpy(&ref_list[c_idx], &ref_list[c_idx - 1], sizeof(struct h264_decode_buf_spec));
+
+ memcpy(&ref_list[*ref_idx_lx], pic_lt, sizeof(struct h264_decode_buf_spec));
+ pr_debug("%s 1 : RefPicListX[%d ] = pic %p pic_num(%d)\n", __func__,
+ *ref_idx_lx, pic_lt, ref_list[*ref_idx_lx].dpb->pic_num);
+ *ref_idx_lx = *ref_idx_lx + 1;
+
+ n_idx = *ref_idx_lx;
+ /* Pointer dpb is NULL means this is a dummy frame store */
+ for (c_idx = *ref_idx_lx; c_idx <= num_ref_idx_lx_active; c_idx++) {
+ if (!ref_list[c_idx].long_term_flag || !ref_list[c_idx].dpb ||
+ ref_list[c_idx].dpb->pic_num != lt_pic_num)
+ memcpy(&ref_list[n_idx++], &ref_list[c_idx],
+ sizeof(struct h264_decode_buf_spec));
+ }
+
+ h264_ctx->list_size[cur_list] = num_ref_idx_lx_active;
+}
+
+static void get_modification_cmd(unsigned short *reorder_cmd, struct slice *curr_slice, int list)
+{
+ int i, j, val;
+
+ val = curr_slice->ref_pic_list_reordering_flag[list];
+ if (val) {
+ i = 0;
+ j = 0;
+ do {
+ curr_slice->modification_of_pic_nums_idc[list][i] =
+ reorder_cmd[j++];
+ if (j >= REORDER_CMD_MAX) {
+ curr_slice->modification_of_pic_nums_idc[list][i] = 0;
+ break;
+ }
+
+ val = curr_slice->modification_of_pic_nums_idc[list][i];
+ if (val == 0 || val == 1)
+ curr_slice->abs_diff_pic_num_minus1[list][i] = reorder_cmd[j++];
+ else if (val == 2)
+ curr_slice->long_term_pic_idx[list][i] = reorder_cmd[j++];
+
+ i++;
+
+ if (i >= REORDERING_COMMAND_MAX_SIZE) {
+ curr_slice->ref_pic_list_reordering_flag[list] = 0;
+ break;
+ };
+ if (j > REORDER_CMD_MAX) {
+ curr_slice->ref_pic_list_reordering_flag[list] = 0;
+ break;
+ };
+ } while (val != 3);
+ }
+}
+
+static void reorder_pics(struct aml_h264_ctx *h264_ctx, struct slice *curr_slice, int cur_list)
+{
+ int *modification_of_pic_nums_idc =
+ curr_slice->modification_of_pic_nums_idc[cur_list];
+ int *abs_diff_pic_num_minus1 =
+ curr_slice->abs_diff_pic_num_minus1[cur_list];
+ int *long_term_pic_idx = curr_slice->long_term_pic_idx[cur_list];
+ int pic_num_lx_nowarp, pic_num_lx_pred, pic_num_lx;
+ int curr_pic_num = curr_slice->frame_num;
+ int max_pic_num = 1 << (4 + h264_ctx->ctrl_ref.sps->log2_max_frame_num_minus4);
+ int ref_idx_lx = 0;
+ int nowarp_tmp = 0;
+ int i;
+
+ pic_num_lx_pred = curr_pic_num;
+ for (i = 0; i < REORDERING_COMMAND_MAX_SIZE && modification_of_pic_nums_idc[i] != 3; i++) {
+ if (modification_of_pic_nums_idc[i] > 3) {
+ pr_info("error, Invalid modification_of_pic_nums_idc command\n");
+ break;
+ }
+
+ if (modification_of_pic_nums_idc[i] < 2) {
+ if (modification_of_pic_nums_idc[i] == 0) {
+ nowarp_tmp = pic_num_lx_pred - (abs_diff_pic_num_minus1[i] + 1);
+ pic_num_lx_nowarp = nowarp_tmp + (nowarp_tmp < 0 ? max_pic_num : 0);
+ } else if (modification_of_pic_nums_idc[i] == 1) {
+ nowarp_tmp = pic_num_lx_pred + (abs_diff_pic_num_minus1[i] + 1);
+ pic_num_lx_nowarp = nowarp_tmp -
+ (nowarp_tmp > max_pic_num ? max_pic_num : 0);
+ }
+ pic_num_lx_pred = pic_num_lx_nowarp;
+ if (pic_num_lx_nowarp > curr_pic_num)
+ pic_num_lx = pic_num_lx_nowarp - max_pic_num;
+ else
+ pic_num_lx = pic_num_lx_nowarp;
+
+ reorder_short_term(curr_slice, cur_list, pic_num_lx, &ref_idx_lx);
+ } else {
+ reorder_long_term(curr_slice, cur_list, long_term_pic_idx[i], &ref_idx_lx);
+ }
+ }
+}
+
+static void copy_ref_list(struct aml_h264_ctx *h264_ctx, int curr_list)
+{
+ if (curr_list == 0)
+ memcpy(h264_ctx->ref_list0_unreordered, h264_ctx->ref_list0,
+ sizeof(h264_ctx->ref_list0));
+ else
+ memcpy(h264_ctx->ref_list1_unreordered, h264_ctx->ref_list0,
+ sizeof(h264_ctx->ref_list1));
+}
+
+static void h264_reorder_reflists(struct aml_h264_ctx *h264_ctx)
+{
+ unsigned short *reorder_cmd;
+ struct slice *curr_slice = &h264_ctx->mslice;
+
+ memset(curr_slice, 0, sizeof(struct slice));
+ /* parsed by ucode */
+ curr_slice->slice_type = h264_ctx->dpb_param.l.data[SLICE_TYPE];
+ curr_slice->num_ref_idx_l0 = h264_ctx->dpb_param.dpb.num_ref_idx_l0_active_minus1 + 1;
+ curr_slice->num_ref_idx_l1 = h264_ctx->dpb_param.dpb.num_ref_idx_l1_active_minus1 + 1;
+ curr_slice->frame_num = h264_ctx->ctrl_ref.decode->frame_num;
+
+ if (curr_slice->slice_type != I_SLICE && curr_slice->slice_type != SI_SLICE) {
+ reorder_cmd = &h264_ctx->dpb_param.mmco.l0_reorder_cmd[0];
+ /* 3:parsed by ucode, means no reorder needed */
+ if (reorder_cmd[0] != 3)
+ curr_slice->ref_pic_list_reordering_flag[0] = 1;
+ else
+ curr_slice->ref_pic_list_reordering_flag[0] = 0;
+
+ get_modification_cmd(reorder_cmd, curr_slice, 0);
+ }
+
+ if (curr_slice->slice_type == B_SLICE) {
+ reorder_cmd = &h264_ctx->dpb_param.mmco.l1_reorder_cmd[0];
+ /* 3:parsed by ucode, means no reorder needed */
+ if (reorder_cmd[0] != 3)
+ curr_slice->ref_pic_list_reordering_flag[1] = 1;
+ else
+ curr_slice->ref_pic_list_reordering_flag[1] = 0;
+
+ get_modification_cmd(reorder_cmd, curr_slice, 1);
+ }
+
+ if (curr_slice->slice_type != I_SLICE &&
+ curr_slice->slice_type != SI_SLICE &&
+ curr_slice->ref_pic_list_reordering_flag[0] != 0) {
+ copy_ref_list(h264_ctx, 0);
+ reorder_pics(h264_ctx, curr_slice, 0);
+ }
+
+ if (curr_slice->slice_type == B_SLICE && curr_slice->ref_pic_list_reordering_flag[1] != 0) {
+ copy_ref_list(h264_ctx, 1);
+ reorder_pics(h264_ctx, curr_slice, 1);
+ }
+}
+
+static void h264_config_ref_lists(struct aml_vdec_ctx *ctx)
+{
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)ctx->codec_priv;
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+ struct v4l2_ctrl_h264_decode_params *decode =
+ (struct v4l2_ctrl_h264_decode_params *)ctrls->decode;
+ struct v4l2_ctrl_h264_sps *sps = (struct v4l2_ctrl_h264_sps *)ctrls->sps;
+ const struct v4l2_h264_dpb_entry *dpb = decode->dpb;
+ struct v4l2_h264_reflist_builder builder;
+ struct v4l2_h264_reference v4l2_p0_reflist[V4L2_H264_REF_LIST_LEN];
+ struct v4l2_h264_reference v4l2_b0_reflist[V4L2_H264_REF_LIST_LEN];
+ struct v4l2_h264_reference v4l2_b1_reflist[V4L2_H264_REF_LIST_LEN];
+
+ if (decode->flags == V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC)
+ return;
+
+ v4l2_h264_init_reflist_builder(&builder, decode, sps, dpb);
+ pr_debug("%s num_valid = %d", __func__, builder.num_valid);
+
+ if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_PFRAME) {
+ v4l2_h264_build_p_ref_list(&builder, v4l2_p0_reflist);
+ config_p_reflist(h264_ctx, v4l2_p0_reflist, builder.num_valid);
+ } else if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME) {
+ v4l2_h264_build_b_ref_lists(&builder, v4l2_b0_reflist, v4l2_b1_reflist);
+ config_b_reflist(h264_ctx, v4l2_b0_reflist, v4l2_b1_reflist, builder.num_valid);
+ }
+}
+
+static void clear_unused_col_buf(struct aml_h264_ctx *h264_ctx,
+ struct v4l2_ctrl_h264_decode_params *decode)
+{
+ int i, col_poc;
+
+ /* flush all col buffers when IDR */
+ if (decode->flags == V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) {
+ /* 32 : max index of co-locate buffer */
+ for (i = 0; i < 32; i++)
+ release_colocate_buf(h264_ctx, i);
+ return;
+ }
+
+ for (i = 0; i < h264_ctx->colocated_buf_num; i++) {
+ col_poc = h264_ctx->colocated_buf_poc[i];
+ if (col_poc != INVALID_POC && (poc_is_in_dpb(col_poc, decode->dpb) != 1))
+ release_colocate_buf(h264_ctx, i);
+ }
+}
+
+static void h264_config_decode_spec(struct aml_vdec_hw *hw, struct aml_vdec_ctx *ctx)
+{
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)hw->curr_ctx;
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+ struct v4l2_ctrl_h264_decode_params *decode =
+ (struct v4l2_ctrl_h264_decode_params *)ctrls->decode;
+ struct h264_decode_buf_spec *buf_spec;
+ struct vb2_buffer *vb;
+ struct vb2_v4l2_buffer *vb2_v4l2;
+ struct vb2_queue *vq;
+ int i;
+
+ clear_unused_col_buf(h264_ctx, decode);
+
+ vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ vb = &vb2_v4l2->vb2_buf;
+
+ h264_ctx->curr_spec.y_dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (ctx->pic_info.plane_num > 1)
+ h264_ctx->curr_spec.c_dma_addr = vb2_dma_contig_plane_dma_addr(vb, 1);
+ else
+ h264_ctx->curr_spec.c_dma_addr =
+ h264_ctx->curr_spec.y_dma_addr + ctx->pic_info.fb_size[0];
+ h264_ctx->curr_spec.canvas_pos = 0;
+ h264_ctx->curr_spec.col_buf_index =
+ allocate_colocate_buf(h264_ctx, decode->top_field_order_cnt);
+ h264_ctx->curr_spec.poc = decode->top_field_order_cnt;
+ config_decode_canvas(hw, &h264_ctx->curr_spec, h264_ctx->mb_width, h264_ctx->mb_height);
+
+ h264_config_ref_lists(ctx);
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) {
+ struct v4l2_h264_dpb_entry *dpb = &decode->dpb[i];
+
+ if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
+ continue;
+
+ buf_spec = find_spec_by_dpb_index(h264_ctx, i);
+ if (buf_spec) {
+ buf_spec->canvas_pos = i + 1;
+ vb = vb2_find_buffer(vq, dpb->reference_ts);
+ if (!vb) {
+ pr_err("ref pic for ts %llu lost\n", dpb->reference_ts);
+ continue;
+ }
+
+ buf_spec->y_dma_addr =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (ctx->pic_info.plane_num > 1)
+ buf_spec->c_dma_addr = vb2_dma_contig_plane_dma_addr(vb, 1);
+ else
+ buf_spec->c_dma_addr =
+ buf_spec->y_dma_addr + ctx->pic_info.fb_size[0];
+ config_decode_canvas(hw, buf_spec, h264_ctx->mb_width, h264_ctx->mb_height);
+ pr_debug
+ ("config canvas for poc %d canvas %d y_dma_addr 0x%llx\n",
+ buf_spec->dpb->top_field_order_cnt,
+ buf_spec->canvas_pos, buf_spec->y_dma_addr);
+ }
+ }
+
+ h264_reorder_reflists(h264_ctx);
+}
+
+static int h264_config_decode_buf(struct aml_vdec_hw *hw, struct aml_vdec_ctx *ctx)
+{
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)hw->curr_ctx;
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+ struct v4l2_ctrl_h264_decode_params *decode =
+ (struct v4l2_ctrl_h264_decode_params *)ctrls->decode;
+ unsigned long canvas_adr;
+ unsigned int ref_cfg;
+ unsigned int ref_cfg_once = 0;
+ unsigned int type_cfg = 0x3; /* 0x3: frame type */
+ unsigned int colocate_adr_offset, colocate_wr_adr;
+ unsigned int info0;
+ unsigned int info1;
+ unsigned int info2;
+ int i, j;
+ int h264_buffer_info_data_write_count;
+ u8 canvas_pos;
+ u8 use_mode_8x8_flag;
+
+ WRITE_VREG(H264_CURRENT_POC_IDX_RESET, 0);
+ WRITE_VREG(H264_CURRENT_POC, decode->top_field_order_cnt);
+ WRITE_VREG(H264_CURRENT_POC, decode->top_field_order_cnt);
+ WRITE_VREG(H264_CURRENT_POC, decode->bottom_field_order_cnt);
+ WRITE_VREG(CURR_CANVAS_CTRL, h264_ctx->curr_spec.canvas_pos << 24);
+ canvas_adr = RD_VREG(CURR_CANVAS_CTRL) & 0xffffff;
+ pr_debug("canvas_pos = %d canvas_adr 0x%lx\n", h264_ctx->curr_spec.canvas_pos, canvas_adr);
+
+ WRITE_VREG(REC_CANVAS_ADDR, canvas_adr);
+ WRITE_VREG(DBKR_CANVAS_ADDR, canvas_adr);
+ WRITE_VREG(DBKW_CANVAS_ADDR, canvas_adr);
+
+ WRITE_VREG(H264_BUFFER_INFO_INDEX, 16);
+ info0 = 0xf480;
+ info1 = decode->top_field_order_cnt;
+ info2 = decode->bottom_field_order_cnt;
+ if (decode->bottom_field_order_cnt < decode->top_field_order_cnt)
+ info0 |= 0x100;
+
+ WRITE_VREG(H264_BUFFER_INFO_DATA, info0 | 0xf);
+ WRITE_VREG(H264_BUFFER_INFO_DATA, info1);
+ WRITE_VREG(H264_BUFFER_INFO_DATA, info2);
+
+ for (j = 0; j < V4L2_H264_NUM_DPB_ENTRIES; j++) {
+ struct v4l2_h264_dpb_entry *dpb = &decode->dpb[j];
+
+ info0 = 0;
+ info1 = 0;
+ info2 = 0;
+ if (dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) {
+ info0 = 0xf480;
+ if (decode->bottom_field_order_cnt < decode->top_field_order_cnt)
+ info0 |= 0x100;
+ info1 = dpb->top_field_order_cnt;
+ info2 = dpb->bottom_field_order_cnt;
+ if (dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ info0 |= ((1 << 5) | (1 << 4));
+ }
+ WRITE_VREG(H264_BUFFER_INFO_DATA, info0);
+ WRITE_VREG(H264_BUFFER_INFO_DATA, info1);
+ WRITE_VREG(H264_BUFFER_INFO_DATA, info2);
+ }
+
+ WRITE_VREG(H264_BUFFER_INFO_INDEX, 0);
+ /* when frame width <= 256, Disable DDR_BYTE64_CACHE */
+ if (ctx->pic_info.coded_width <= 256) {
+ SET_VREG_MASK(IQIDCT_CONTROL, (1 << 16));
+ WRITE_VREG(DCAC_DDR_BYTE64_CTL,
+ (RD_VREG(DCAC_DDR_BYTE64_CTL) & (~0xf)) | 0xa);
+ } else {
+ CLEAR_VREG_MASK(IQIDCT_CONTROL, (1 << 16));
+ }
+
+ ref_cfg = 0;
+ j = 0;
+
+ if (h264_ctx->list_size[0] > 0) {
+ for (i = 0; i < h264_ctx->list_size[0]; i++) {
+ canvas_pos = h264_ctx->ref_list0[i].canvas_pos;
+ /* bit 0:3 canvas_pos bit 5:6 frame struct cfg */
+ ref_cfg_once = (canvas_pos & 0x1f) | (type_cfg << 5);
+ ref_cfg <<= 8;
+ ref_cfg |= ref_cfg_once;
+ j++;
+
+ if (j == 4) {
+ WRITE_VREG(H264_BUFFER_INFO_DATA, ref_cfg);
+ pr_debug("H264_BUFFER_INFO_DATA: %x\n", ref_cfg);
+ h264_buffer_info_data_write_count++;
+ j = 0;
+ }
+ }
+
+ if (j != 0) {
+ while (j != 4) {
+ ref_cfg <<= 8;
+ ref_cfg |= ref_cfg_once;
+ j++;
+ }
+ WRITE_VREG(H264_BUFFER_INFO_DATA, ref_cfg);
+ pr_debug("H264_BUFFER_INFO_DATA: %x\n", ref_cfg);
+ h264_buffer_info_data_write_count++;
+ }
+ ref_cfg = (ref_cfg_once << 24) | (ref_cfg_once << 16) |
+ (ref_cfg_once << 8) | ref_cfg_once;
+ for (j = h264_buffer_info_data_write_count; j < 8; j++)
+ WRITE_VREG(H264_BUFFER_INFO_DATA, ref_cfg);
+ }
+
+ WRITE_VREG(H264_BUFFER_INFO_INDEX, 8);
+ j = 0;
+ ref_cfg = 0;
+
+ if (h264_ctx->list_size[1] > 0) {
+ for (i = 0; i < h264_ctx->list_size[1]; i++) {
+ canvas_pos = h264_ctx->ref_list1[i].canvas_pos;
+ ref_cfg_once = (canvas_pos & 0x1f) | (type_cfg << 5);
+ ref_cfg <<= 8;
+ ref_cfg |= ref_cfg_once;
+ j++;
+
+ if (j == 4) {
+ WRITE_VREG(H264_BUFFER_INFO_DATA, ref_cfg);
+ pr_debug("H264_BUFFER_INFO_DATA: %x\n", ref_cfg);
+ j = 0;
+ }
+ }
+ }
+
+ if (j != 0) {
+ while (j != 4) {
+ ref_cfg <<= 8;
+ ref_cfg |= ref_cfg_once;
+ j++;
+ }
+ pr_debug("H264_BUFFER_INFO_DATA: %x\n", ref_cfg);
+ WRITE_VREG(H264_BUFFER_INFO_DATA, ref_cfg);
+ }
+
+ if (get_flag(ctrls->sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) &&
+ get_flag(ctrls->sps->flags, V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE))
+ use_mode_8x8_flag = 1;
+ else
+ use_mode_8x8_flag = 0;
+
+ while ((RD_VREG(H264_CO_MB_RW_CTL) >> 11) & 0x1)
+ continue;
+
+ /* col buf for curr frame */
+ colocate_adr_offset = 0;
+ if (h264_ctx->curr_spec.col_buf_index >= 0 &&
+ h264_ctx->curr_spec.col_buf_index < h264_ctx->colocated_buf_num) {
+ colocate_wr_adr = h264_ctx->collated_cma_addr +
+ ((h264_ctx->one_col_buf_size * h264_ctx->curr_spec.col_buf_index) >>
+ (use_mode_8x8_flag ? 2 : 0));
+ if (colocate_wr_adr + h264_ctx->one_col_buf_size >
+ h264_ctx->collated_cma_addr_end) {
+ pr_err
+ ("Error, colocate buf is not enough, index is %d\n",
+ h264_ctx->curr_spec.col_buf_index);
+ return -1;
+ }
+ WRITE_VREG(H264_CO_MB_WR_ADDR,
+ (colocate_wr_adr + colocate_adr_offset));
+ pr_debug("col buffer addr = 0x%x col_buf_index %d\n",
+ (colocate_wr_adr + colocate_adr_offset),
+ h264_ctx->curr_spec.col_buf_index);
+ } else {
+ WRITE_VREG(H264_CO_MB_WR_ADDR, 0xffffffff);
+ pr_debug("col buffer addr = 0xffffffff\n");
+ }
+
+ if (h264_ctx->list_size[1] > 0) {
+ struct h264_decode_buf_spec *colocate_pic = &h264_ctx->ref_list1[0];
+ struct h264_decode_buf_spec *curr_pic = &h264_ctx->curr_spec;
+ int l10_structure = 2; /* for pic struct == FRAME, default to 2 */
+ int cur_colocate_ref_type;
+ unsigned int colocate_rd_adr;
+ unsigned int colocate_rd_adr_offset = 0;
+ unsigned int val;
+
+ cur_colocate_ref_type =
+ (abs(curr_pic->poc - colocate_pic->dpb->top_field_order_cnt) <
+ abs(curr_pic->poc - colocate_pic->dpb->bottom_field_order_cnt)) ?
+ 0 : 1;
+
+ if (colocate_pic->col_buf_index >= 0 &&
+ colocate_pic->col_buf_index < h264_ctx->colocated_buf_num) {
+ colocate_rd_adr = h264_ctx->collated_cma_addr +
+ ((h264_ctx->one_col_buf_size * colocate_pic->col_buf_index) >>
+ (use_mode_8x8_flag ? 2 : 0));
+ if (colocate_rd_adr + h264_ctx->one_col_buf_size >
+ h264_ctx->collated_cma_addr_end) {
+ pr_err
+ ("Error, colocate rd buf is not enough, index is %d\n",
+ colocate_pic->col_buf_index);
+ return -1;
+ }
+ val = ((colocate_rd_adr_offset + colocate_rd_adr) >> 3) |
+ (cur_colocate_ref_type << 29) |
+ (l10_structure << 30);
+ WRITE_VREG(H264_CO_MB_RD_ADDR, val);
+ } else {
+ pr_err
+ ("Error, reference pic has no colocated buf poc %d\n",
+ curr_pic->poc);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void h264_release_decode_spec(struct aml_vdec_hw *hw, struct aml_vdec_ctx *ctx)
+{
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)hw->curr_ctx;
+ int i;
+ struct h264_decode_buf_spec *buf;
+
+ hw->hw_ops.canvas_free(h264_ctx->curr_spec.y_canvas_index);
+ hw->hw_ops.canvas_free(h264_ctx->curr_spec.v_canvas_index);
+
+ if (h264_ctx->list_size[0] > 0) {
+ for (i = 0; i < h264_ctx->list_size[0]; i++) {
+ buf = &h264_ctx->ref_list0[i];
+ if (buf->used) {
+ buf->canvas_pos = 0;
+ buf->dpb = NULL;
+ hw->hw_ops.canvas_free(buf->y_canvas_index);
+ hw->hw_ops.canvas_free(buf->u_canvas_index);
+ buf->used = 0;
+ }
+ }
+ h264_ctx->list_size[0] = 0;
+ }
+
+ if (h264_ctx->list_size[1] > 0) {
+ for (i = 0; i < h264_ctx->list_size[1]; i++) {
+ buf = &h264_ctx->ref_list1[i];
+ if (buf->used) {
+ buf->canvas_pos = 0;
+ buf->dpb = NULL;
+ hw->hw_ops.canvas_free(buf->y_canvas_index);
+ hw->hw_ops.canvas_free(buf->u_canvas_index);
+ buf->used = 0;
+ }
+ }
+ h264_ctx->list_size[1] = 0;
+ }
+}
+
+static void save_reg_status(struct aml_h264_ctx *h264_ctx)
+{
+ h264_ctx->reg_iqidct_control = RD_VREG(IQIDCT_CONTROL);
+ h264_ctx->reg_iqidct_control_init_flag = 1;
+ h264_ctx->reg_vcop_ctrl_reg = RD_VREG(VCOP_CTRL_REG);
+ h264_ctx->reg_rv_ai_mb_count = RD_VREG(RV_AI_MB_COUNT);
+ h264_ctx->vld_dec_control = RD_VREG(VLD_DECODE_CONTROL);
+}
+
+static irqreturn_t h264_isr(int irq, void *priv)
+{
+ WRITE_VREG(VDEC_ASSIST_MBOX1_CLR_REG, 1);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t h264_threaded_isr_func(int irq, void *priv)
+{
+ int dec_status = RD_VREG(DPB_STATUS_REG);
+ struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv;
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)dev->dec_hw->curr_ctx;
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)dev->dec_ctx;
+ struct aml_vdec_hw *hw = (struct aml_vdec_hw *)dev->dec_hw;
+ unsigned short *p = (unsigned short *)h264_ctx->lmem_addr;
+ int i, ii;
+
+ h264_ctx->dec_status = dec_status;
+ pr_debug
+ ("%s, dec_status 0x%x VIFF_BIT_CNT 0x%x MBY_MBX 0x%x VLD_SHIFT_STATUS 0x%x\n",
+ __func__, dec_status, RD_VREG(VIFF_BIT_CNT), RD_VREG(MBY_MBX),
+ RD_VREG(VLD_SHIFT_STATUS));
+
+ h264_ctx->save_avscratch_f = RD_VREG(AV_SCRATCH_F);
+
+ switch (dec_status) {
+ case H264_SLICE_HEADER_DONE:
+ for (i = 0; i < 0x400; i += 4)
+ for (ii = 0; ii < 4; ii++)
+ h264_ctx->dpb_param.l.data[i + ii] = p[i + 3 - ii];
+ save_reg_status(h264_ctx);
+ if (h264_ctx->new_pic_flag == 1)
+ h264_config_decode_spec(hw, ctx);
+
+ if (h264_config_decode_buf(hw, ctx) < 0) {
+ h264_release_decode_spec(hw, ctx);
+ ctx->int_cond = 1;
+ wake_up_interruptible(&ctx->queue);
+ goto irq_handled;
+ }
+ if (h264_ctx->new_pic_flag == 1) {
+ WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_NEWPIC);
+ pr_debug("action decode new pic\n");
+ h264_ctx->new_pic_flag = 0;
+ } else {
+ WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_SLICE);
+ pr_debug("action decode new slice\n");
+ }
+ break;
+ case H264_SLICE_DATA_DONE:
+ h264_release_decode_spec(hw, ctx);
+ h264_ctx->decode_pic_count++;
+ ctx->int_cond = 1;
+ wake_up_interruptible(&ctx->queue);
+ break;
+ default:
+ h264_release_decode_spec(hw, ctx);
+ ctx->int_cond = 1;
+ wake_up_interruptible(&ctx->queue);
+ break;
+ }
+irq_handled:
+ return IRQ_HANDLED;
+}
+
+static int h264_restore_hw_ctx(struct aml_vdec_ctx *ctx)
+{
+ struct aml_h264_ctx *h264_ctx =
+ (struct aml_h264_ctx *)ctx->codec_priv;
+
+ WRITE_VREG(POWER_CTL_VLD,
+ (RD_VREG(POWER_CTL_VLD) | (0 << 10) | (1 << 9) | (1 << 6)));
+
+ WRITE_VREG(PSCALE_CTRL, 0);
+
+ /* clear mailbox interrupt */
+ WRITE_VREG(VDEC_ASSIST_MBOX1_CLR_REG, 1);
+
+ /* enable mailbox interrupt */
+ WRITE_VREG(VDEC_ASSIST_MBOX1_MASK, 1);
+
+ SET_VREG_MASK(MDEC_PIC_DC_CTRL, (1 << 17));
+ if (ctx->q_data[AML_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_NV21 ||
+ ctx->q_data[AML_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_NV21M)
+ SET_VREG_MASK(MDEC_PIC_DC_CTRL, (1 << 16));
+ else
+ CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, (1 << 16));
+
+ SET_VREG_MASK(MDEC_PIC_DC_CTRL, (0xbf << 24));
+ CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, (0xbf << 24));
+ CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, (1 << 31));
+
+ CLEAR_VREG_MASK(MDEC_PIC_DC_MUX_CTRL, 1 << 31);
+ WRITE_VREG(MDEC_EXTIF_CFG1, 0);
+ WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa);
+
+ WRITE_VREG(DPB_STATUS_REG, 0);
+
+ WRITE_VREG(LMEM_DUMP_ADR, h264_ctx->lmem_phy_addr);
+ WRITE_VREG(FRAME_COUNTER_REG, h264_ctx->decode_pic_count);
+ WRITE_VREG(AV_SCRATCH_8, h264_ctx->workspace_offset);
+ WRITE_VREG(AV_SCRATCH_G, h264_ctx->mc_cpu_paddr);
+
+ WRITE_VREG(AV_SCRATCH_F, ((h264_ctx->save_avscratch_f & 0xffffffc3) | (1 << 4)));
+ CLEAR_VREG_MASK(AV_SCRATCH_F, 1 << 6);
+
+ WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa);
+
+ if (h264_ctx->reg_iqidct_control_init_flag == 0)
+ WRITE_VREG(IQIDCT_CONTROL, 0x200);
+
+ if (h264_ctx->reg_iqidct_control)
+ WRITE_VREG(IQIDCT_CONTROL, h264_ctx->reg_iqidct_control);
+
+ if (h264_ctx->reg_vcop_ctrl_reg)
+ WRITE_VREG(VCOP_CTRL_REG, h264_ctx->reg_vcop_ctrl_reg);
+
+ if (h264_ctx->vld_dec_control)
+ WRITE_VREG(VLD_DECODE_CONTROL, h264_ctx->vld_dec_control);
+
+ pr_debug
+ ("IQIDCT_CONTROL = 0x%x, VCOP_CTRL_REG 0x%x VLD_DECODE_CONTROL 0x%x\n",
+ RD_VREG(IQIDCT_CONTROL), RD_VREG(VCOP_CTRL_REG),
+ RD_VREG(VLD_DECODE_CONTROL));
+
+ return 0;
+}
+
+static void *aml_h264_get_ctrl(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(hdl, id);
+ return ctrl ? ctrl->p_cur.p : NULL;
+}
+
+static int aml_h264_get_stateless_ctrl_ref(struct aml_h264_ctx *h264_ctx)
+{
+ struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx;
+ struct vdec_h264_stateless_ctrl_ref *ctrls = &h264_ctx->ctrl_ref;
+
+ ctrls->sps =
+ (struct v4l2_ctrl_h264_sps *)aml_h264_get_ctrl(&ctx->ctrl_handler,
+ V4L2_CID_STATELESS_H264_SPS);
+ if (WARN_ON(!ctrls->sps))
+ return -EINVAL;
+
+ ctrls->pps =
+ (struct v4l2_ctrl_h264_pps *)aml_h264_get_ctrl(&ctx->ctrl_handler,
+ V4L2_CID_STATELESS_H264_PPS);
+ if (WARN_ON(!ctrls->pps))
+ return -EINVAL;
+
+ ctrls->decode =
+ (struct v4l2_ctrl_h264_decode_params *)aml_h264_get_ctrl(&ctx->ctrl_handler,
+ V4L2_CID_STATELESS_H264_DECODE_PARAMS);
+ if (WARN_ON(!ctrls->decode))
+ return -EINVAL;
+
+ ctrls->scaling =
+ (struct v4l2_ctrl_h264_scaling_matrix *)aml_h264_get_ctrl(&ctx->ctrl_handler,
+ V4L2_CID_STATELESS_H264_SCALING_MATRIX);
+ if (WARN_ON(!ctrls->scaling))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void copy_mc_cpu_fw(void *mc_cpu_addr, const u8 *data)
+{
+ /*header */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_HEADER,
+ data + 0x4000, MC_SWAP_SIZE);
+ /*data */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_DATA,
+ data + 0x2000, MC_SWAP_SIZE);
+ /*mmco */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MMCO,
+ data + 0x6000, MC_SWAP_SIZE);
+ /*list */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_LIST,
+ data + 0x3000, MC_SWAP_SIZE);
+ /*slice */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_SLICE,
+ data + 0x5000, MC_SWAP_SIZE);
+ /*main */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MAIN, data, 0x2000);
+ /*data */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MAIN + 0x2000,
+ data + 0x2000, 0x1000);
+ /*slice */
+ memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MAIN + 0x3000,
+ data + 0x5000, 0x1000);
+}
+
+static int aml_h264_load_fw_ext(void *priv, const u8 *data, u32 len)
+{
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)priv;
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)h264_ctx->v4l2_ctx;
+ struct aml_vdec_hw *dec_hw;
+
+ if (h264_ctx->mc_cpu_loaded)
+ return 0;
+
+ dec_hw = vdec_get_hw(ctx->dev);
+ if (!dec_hw)
+ return -1;
+
+ if (len > MC_TOTAL_SIZE) {
+ pr_info("size of mc_cpu_fw id invalid\n");
+ return -1;
+ }
+
+ h264_ctx->mc_cpu_vaddr = dma_alloc_coherent(dec_hw->dev, MC_TOTAL_SIZE,
+ &h264_ctx->mc_cpu_paddr,
+ GFP_KERNEL);
+ if (!h264_ctx->mc_cpu_vaddr)
+ return -ENOMEM;
+
+ copy_mc_cpu_fw(h264_ctx->mc_cpu_vaddr, data);
+
+ h264_ctx->mc_cpu_loaded = true;
+
+ pr_debug("h264 mccpu fw loaded\n");
+
+ return 0;
+}
+
+int aml_h264_init(void *priv)
+{
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)priv;
+ struct aml_vdec_hw *dec_hw;
+ struct aml_h264_ctx *h264_ctx;
+ int ret = 0;
+
+ h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL);
+ if (!h264_ctx)
+ return -ENOMEM;
+
+ h264_ctx->v4l2_ctx = ctx;
+ dec_hw = vdec_get_hw(ctx->dev);
+ if (!dec_hw)
+ return -1;
+
+ h264_ctx->mc_cpu_loaded = false;
+ dec_hw->hw_ops.irq_handler = h264_isr;
+ dec_hw->hw_ops.irq_threaded_func = h264_threaded_isr_func;
+ dec_hw->hw_ops.load_firmware_ex = aml_h264_load_fw_ext;
+
+ h264_ctx->lmem_addr = dma_alloc_coherent(dec_hw->dev, LMEM_DUMP_SIZE,
+ &h264_ctx->lmem_phy_addr,
+ GFP_KERNEL);
+ if (!h264_ctx->lmem_addr) {
+ ret = -ENOMEM;
+ goto err_alloc_lmem;
+ }
+
+ h264_ctx->cma_alloc_vaddr =
+ dma_alloc_coherent(dec_hw->dev, V_BUF_ADDR_OFFSET,
+ &h264_ctx->cma_alloc_addr, GFP_KERNEL);
+ if (!h264_ctx->cma_alloc_vaddr) {
+ ret = -ENOMEM;
+ goto err_alloc_workspace;
+ }
+
+ h264_ctx->workspace_offset = h264_ctx->cma_alloc_addr + DCAC_READ_MARGIN;
+ h264_ctx->workspace_vaddr = h264_ctx->cma_alloc_vaddr + DCAC_READ_MARGIN;
+
+ ctx->codec_priv = h264_ctx;
+ dec_hw->curr_ctx = h264_ctx;
+ h264_ctx->col_buf_alloc_size = 0;
+ h264_ctx->init_flag = 0;
+ h264_ctx->new_pic_flag = 0;
+ h264_ctx->param_set = 0;
+ h264_ctx->reg_iqidct_control_init_flag = 0;
+ h264_ctx->decode_pic_count = 0;
+
+ return 0;
+
+err_alloc_workspace:
+ dma_free_coherent(dec_hw->dev, LMEM_DUMP_SIZE,
+ h264_ctx->lmem_addr, h264_ctx->lmem_phy_addr);
+err_alloc_lmem:
+ kfree(h264_ctx);
+
+ return ret;
+}
+
+void aml_h264_exit(void *priv)
+{
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)priv;
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)ctx->codec_priv;
+ struct aml_vdec_hw *dec_hw;
+
+ if (!h264_ctx) {
+ pr_info("h264 decoder is already destroyed or not created!\n");
+ return;
+ }
+ dec_hw = vdec_get_hw(ctx->dev);
+ h264_ctx->param_set = 0;
+
+ if (ctx->dos_clk_en)
+ aml_stop_vdec_hw();
+
+ if (h264_ctx->collated_cma_vaddr) {
+ dma_free_coherent(dec_hw->dev, h264_ctx->col_buf_alloc_size,
+ h264_ctx->collated_cma_vaddr,
+ h264_ctx->collated_cma_addr);
+ h264_ctx->col_buf_alloc_size = 0;
+ }
+
+ if (h264_ctx->mc_cpu_vaddr) {
+ dma_free_coherent(dec_hw->dev, MC_TOTAL_SIZE,
+ h264_ctx->mc_cpu_vaddr,
+ h264_ctx->mc_cpu_paddr);
+ h264_ctx->mc_cpu_loaded = false;
+ }
+
+ if (h264_ctx->lmem_addr)
+ dma_free_coherent(dec_hw->dev, LMEM_DUMP_SIZE,
+ h264_ctx->lmem_addr, h264_ctx->lmem_phy_addr);
+
+ if (h264_ctx->cma_alloc_vaddr)
+ dma_free_coherent(dec_hw->dev, V_BUF_ADDR_OFFSET,
+ h264_ctx->cma_alloc_vaddr,
+ h264_ctx->cma_alloc_addr);
+
+ kfree(ctx->codec_priv);
+ dec_hw->curr_ctx = NULL;
+ ctx->codec_priv = NULL;
+}
+
+static void config_decode_mode(struct aml_h264_ctx *h264_ctx)
+{
+ WRITE_VREG(H264_DECODE_MODE, 0x1); /*decode mode framebase*/
+ WRITE_VREG(HEAD_PADDING_REG, 0);
+ WRITE_VREG(H264_DECODE_SEQINFO, h264_ctx->seq_info);
+ WRITE_VREG(INIT_FLAG_REG, 1);
+}
+
+int aml_h264_dec_run(void *priv)
+{
+ struct aml_vdec_ctx *ctx = (struct aml_vdec_ctx *)priv;
+ struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)ctx->codec_priv;
+ int ret = -1;
+ int i;
+
+ ret = aml_h264_get_stateless_ctrl_ref(h264_ctx);
+ if (ret < 0) {
+ pr_err("not ctrl ref for h264 decoder\n");
+ return ret;
+ }
+
+ h264_config_params(ctx);
+
+ if (h264_prepare_input(ctx) < 0)
+ return ret;
+
+ if (alloc_colocate_cma(h264_ctx, ctx) < 0)
+ return ret;
+
+ h264_restore_hw_ctx(ctx);
+
+ config_decode_mode(h264_ctx);
+ /* enable stream input hardware */
+ SET_VREG_MASK(VLD_MEM_VIFIFO_CONTROL, (1 << 2) | (1 << 1));
+ /* enable hardware timer */
+ WRITE_VREG(NAL_SEARCH_CTL, RD_VREG(NAL_SEARCH_CTL) | (1 << 16));
+ WRITE_VREG(MDEC_EXTIF_CFG2, RD_VREG(MDEC_EXTIF_CFG2) | 0x20);
+ WRITE_VREG(NAL_SEARCH_CTL, RD_VREG(NAL_SEARCH_CTL) & (~0x2));
+ CLEAR_VREG_MASK(VDEC_ASSIST_MMC_CTRL1, 1 << 3);
+
+ aml_start_vdec_hw();
+ h264_ctx->init_flag = 1;
+ h264_ctx->new_pic_flag = 1;
+
+ WRITE_VREG(DPB_STATUS_REG, H264_ACTION_SEARCH_HEAD);
+
+ ret = wait_event_interruptible_timeout(ctx->queue, ctx->int_cond,
+ msecs_to_jiffies(DECODER_TIMEOUT_MS));
+ ctx->int_cond = 0;
+ if (!ret) {
+ ret = -1;
+ pr_err("dec timeout=%u\n", DECODER_TIMEOUT_MS);
+ for (i = 0; i < 16; i++) { /* 16 : show ucode PC 16 times when timeout */
+ pr_info("decoder timeout, pc 0x%x\n", RD_VREG(MPC_E));
+ usleep_range(10, 20);
+ }
+ } else if (-ERESTARTSYS == ret) {
+ ret = -1;
+ pr_err("dec inter fail\n");
+ }
+
+ aml_stop_vdec_hw();
+ h264_ctx->init_flag = 0;
+
+ return ret;
+}
diff --git a/drivers/media/platform/amlogic/vdec/h264.h b/drivers/media/platform/amlogic/vdec/h264.h
new file mode 100644
index 000000000000..3d3a35a641c9
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/h264.h
@@ -0,0 +1,300 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+#ifndef _H264_H_
+#define _H264_H_
+
+#define RPM_BEGIN 0x0
+#define FRAME_IN_DPB 24
+#define RPM_END 0x400
+#define DPB_OFFSET 0x100
+#define MMCO_OFFSET 0x200
+#define SPS_OFFSET 0x100
+#define PPS_OFFSET 0x300
+#define PARAM_BASE_VAL 0x414d
+#define MEM_MMCO_BASE 0x01c3000
+#define MEM_SPS_BASE 0x01c3c00
+#define MEM_PPS_BASE 0x01cbc00
+#define MC_TOTAL_SIZE ((20 + 16) * SZ_1K)
+#define MC_SWAP_SIZE (4 * SZ_1K)
+#define LMEM_DUMP_SIZE 4096
+#define V_BUF_ADDR_OFFSET (0x200000 + 0x8000 + 0x20000 + 0x1000)
+#define DCAC_READ_MARGIN (64 * 1024)
+#define MC_OFFSET_HEADER 0x0000
+#define MC_OFFSET_DATA 0x1000
+#define MC_OFFSET_MMCO 0x2000
+#define MC_OFFSET_LIST 0x3000
+#define MC_OFFSET_SLICE 0x4000
+#define MC_OFFSET_MAIN 0x5000
+
+/* Rename the dos regs */
+#define H264_DECODE_INFO M4_CONTROL_REG
+#define INIT_FLAG_REG AV_SCRATCH_2
+#define HEAD_PADDING_REG AV_SCRATCH_3
+#define UCODE_WATCHDOG_REG AV_SCRATCH_7
+#define LMEM_DUMP_ADR AV_SCRATCH_L
+#define DEBUG_REG1 AV_SCRATCH_M
+#define DEBUG_REG2 AV_SCRATCH_N
+#define FRAME_COUNTER_REG AV_SCRATCH_I
+#define RPM_CMD_REG AV_SCRATCH_A
+#define H264_DECODE_SIZE AV_SCRATCH_E
+#define H264_DECODE_MODE AV_SCRATCH_4
+#define H264_DECODE_SEQINFO AV_SCRATCH_5
+/**
+ * NAL_SEARCH_CTL: bit 0, enable itu_t35
+ * NAL_SEARCH_CTL: bit 1, enable mmu
+ * NAL_SEARCH_CTL: bit 2, detect frame_mbs_only_flag whether switch resolution
+ * NAL_SEARCH_CTL: bit 3, recover the correct sps pps
+ * NAL_SEARCH_CTL: bit 7-14,level_idc
+ * NAL_SEARCH_CTL: bit 15,bitstream_restriction_flag
+ */
+#define NAL_SEARCH_CTL AV_SCRATCH_9
+#define DPB_STATUS_REG AV_SCRATCH_J
+#define ERROR_STATUS_REG AV_SCRATCH_9
+
+#define H264_BUFFER_INFO_INDEX PMV3_X /* 0xc24 */
+#define H264_BUFFER_INFO_DATA PMV2_X /* 0xc22 */
+#define H264_CURRENT_POC_IDX_RESET LAST_SLICE_MV_ADDR /* 0xc30 */
+#define H264_CURRENT_POC LAST_MVY /* 0xc32 shared with conceal MV */
+#define H264_CO_MB_WR_ADDR VLD_C38
+#define H264_CO_MB_RD_ADDR VLD_C39
+#define H264_CO_MB_RW_CTL VLD_C3D
+#define MBY_MBX MB_MOTION_MODE
+
+#define H264_ACTION_SEARCH_HEAD 0xf0
+#define H264_ACTION_DECODE_SLICE 0xf1
+#define H264_ACTION_CONFIG_DONE 0xf2
+#define H264_ACTION_DECODE_NEWPIC 0xf3
+#define H264_ACTION_DECODE_START 0xff
+
+/* RPM memory definition */
+#define FIXED_FRAME_RATE_FLAG 0X21
+#define OFFSET_DELIMITER_LO 0x2f
+#define OFFSET_DELIMITER_HI 0x30
+#define REF_IDC_OVERRIDE_FLAG 0x35
+#define SLICE_IPONLY_BREAK 0X5C
+#define PREV_MAX_REFERENCE_FRAME_NUM 0X5D
+#define EOS 0X5E
+#define FRAME_PACKING_TYPE 0X5F
+#define OLD_POC_PAR_1 0X60
+#define OLD_POC_PAR_2 0X61
+#define PREV_MBX 0X62
+#define PREV_MBY 0X63
+#define ERROR_SKIP_MB_NUM 0X64
+#define ERROR_MB_STATUS 0X65
+#define L0_PIC0_STATUS 0X66
+#define TIMEOUT_COUNTER 0X67
+#define BUFFER_SIZE 0X68
+#define BUFFER_SIZE_HI 0X69
+#define CROPPING_LEFT_RIGHT 0X6A
+#define CROPPING_TOP_BOTTOM 0X6B
+/**
+ * sps_flags2:
+ * bit 3, bitstream_restriction_flag
+ * bit 2, pic_struct_present_flag
+ * bit 1, vcl_hrd_parameters_present_flag
+ * bit 0, nal_hrd_parameters_present_flag
+ */
+#define SPS_FLAGS2 0x6C
+#define NUM_REORDER_FRAMES 0x6D
+#define MAX_BUFFER_FRAME 0X6E
+
+#define NON_CONFORMING_STREAM 0X70
+#define RECOVERY_POINT 0X71
+#define POST_CANVAS 0X72
+#define POST_CANVAS_H 0X73
+#define SKIP_PIC_COUNT 0X74
+#define TARGET_NUM_SCALING_LIST 0X75
+#define FF_POST_ONE_FRAME 0X76
+#define PREVIOUS_BIT_CNT 0X77
+#define MB_NOT_SHIFT_COUNT 0X78
+#define PIC_STATUS 0X79
+#define FRAME_COUNTER 0X7A
+#define NEW_SLICE_TYPE 0X7B
+#define NEW_PICTURE_STRUCTURE 0X7C
+#define NEW_FRAME_NUM 0X7D
+#define NEW_IDR_PIC_ID 0X7E
+#define IDR_PIC_ID 0X7F
+
+/* h264 LOCAL */
+#define NAL_UNIT_TYPE 0X80
+#define NAL_REF_IDC 0X81
+#define SLICE_TYPE 0X82
+#define LOG2_MAX_FRAME_NUM 0X83
+#define FRAME_MBS_ONLY_FLAG 0X84
+#define PIC_ORDER_CNT_TYPE 0X85
+#define LOG2_MAX_PIC_ORDER_CNT_LSB 0X86
+#define PIC_ORDER_PRESENT_FLAG 0X87
+#define REDUNDANT_PIC_CNT_PRESENT_FLAG 0X88
+#define PIC_INIT_QP_MINUS26 0X89
+#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG 0X8A
+#define NUM_SLICE_GROUPS_MINUS1 0X8B
+#define MODE_8X8_FLAGS 0X8C
+#define ENTROPY_CODING_MODE_FLAG 0X8D
+#define SLICE_QUANT 0X8E
+#define TOTAL_MB_HEIGHT 0X8F
+#define PICTURE_STRUCTURE 0X90
+#define TOP_INTRA_TYPE 0X91
+#define RV_AI_STATUS 0X92
+#define AI_READ_START 0X93
+#define AI_WRITE_START 0X94
+#define AI_CUR_BUFFER 0X95
+#define AI_DMA_BUFFER 0X96
+#define AI_READ_OFFSET 0X97
+#define AI_WRITE_OFFSET 0X98
+#define AI_WRITE_OFFSET_SAVE 0X99
+#define RV_AI_BUFF_START 0X9A
+#define I_PIC_MB_COUNT 0X9B
+#define AI_WR_DCAC_DMA_CTRL 0X9C
+#define SLICE_MB_COUNT 0X9D
+#define PICTYPE 0X9E
+#define SLICE_GROUP_MAP_TYPE 0X9F
+#define MB_TYPE 0XA0
+#define MB_AFF_ADDED_DMA 0XA1
+#define PREVIOUS_MB_TYPE 0XA2
+#define WEIGHTED_PRED_FLAG 0XA3
+#define WEIGHTED_BIPRED_IDC 0XA4
+/* bit 3:2 - PICTURE_STRUCTURE
+ * bit 1 - MB_ADAPTIVE_FRAME_FIELD_FLAG
+ * bit 0 - FRAME_MBS_ONLY_FLAG
+ */
+#define MBFF_INFO 0XA5
+#define TOP_INTRA_TYPE_TOP 0XA6
+#define RV_AI_BUFF_INC 0xA7
+#define DEFAULT_MB_INFO_LO 0xA8
+/* 0 -- no need to read
+ * 1 -- need to wait Left
+ * 2 -- need to read Intra
+ * 3 -- need to read back MV
+ */
+#define NEED_READ_TOP_INFO 0xA9
+/* 0 -- idle
+ * 1 -- wait Left
+ * 2 -- reading top Intra
+ * 3 -- reading back MV
+ */
+#define READ_TOP_INFO_STATE 0xAA
+#define DCAC_MBX 0xAB
+#define TOP_MB_INFO_OFFSET 0xAC
+#define TOP_MB_INFO_RD_IDX 0xAD
+#define TOP_MB_INFO_WR_IDX 0xAE
+
+#define VLD_NO_WAIT 0
+#define VLD_WAIT_BUFFER 1
+#define VLD_WAIT_HOST 2
+#define VLD_WAIT_GAP 3
+
+#define VLD_WAITING 0xAF
+
+#define MB_X_NUM 0xB0
+#define MB_HEIGHT 0xB2
+#define MBX 0xB3
+#define TOTAL_MBY 0xB4
+#define INTR_MSK_SAVE 0xB5
+#define NEED_DISABLE_PPE 0xB6
+#define IS_NEW_PICTURE 0XB7
+#define PREV_NAL_REF_IDC 0XB8
+#define PREV_NAL_UNIT_TYPE 0XB9
+#define FRAME_MB_COUNT 0XBA
+#define SLICE_GROUP_UCODE 0XBB
+#define SLICE_GROUP_CHANGE_RATE 0XBC
+#define SLICE_GROUP_CHANGE_CYCLE_LEN 0XBD
+#define DELAY_LENGTH 0XBE
+#define PICTURE_STRUCT 0XBF
+#define DCAC_PREVIOUS_MB_TYPE 0xC1
+
+#define TIME_STAMP 0XC2
+#define H_TIME_STAMP 0XC3
+#define VPTS_MAP_ADDR 0XC4
+#define H_VPTS_MAP_ADDR 0XC5
+#define PIC_INSERT_FLAG 0XC7
+#define TIME_STAMP_START 0XC8
+#define TIME_STAMP_END 0XDF
+#define OFFSET_FOR_NON_REF_PIC 0XE0
+#define OFFSET_FOR_TOP_TO_BOTTOM_FIELD 0XE2
+#define MAX_REFERENCE_FRAME_NUM 0XE4
+#define FRAME_NUM_GAP_ALLOWED 0XE5
+#define NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE 0XE6
+#define PROFILE_IDC_MMCO 0XE7
+#define LEVEL_IDC_MMCO 0XE8
+#define FRAME_SIZE_IN_MB 0XE9
+#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG 0XEA
+#define PPS_NUM_REF_IDX_L0_ACTIVE_MINUS1 0XEB
+#define PPS_NUM_REF_IDX_L1_ACTIVE_MINUS1 0XEC
+#define CURRENT_SPS_ID 0XED
+#define CURRENT_PPS_ID 0XEE
+/* bit 0 - sequence parameter set may change
+ * bit 1 - picture parameter set may change
+ * bit 2 - new dpb just inited
+ * bit 3 - IDR picture not decoded yet
+ * bit 5:4 - 0: mb level code loaded 1: picture
+ * level code loaded 2: slice level code loaded
+ */
+#define DECODE_STATUS 0XEF
+#define FIRST_MB_IN_SLICE 0XF0
+#define PREV_MB_WIDTH 0XF1
+#define PREV_FRAME_SIZE_IN_MB 0XF2
+/* bit 0 - aspect_ratio_info_present_flag
+ * bit 1 - timing_info_present_flag
+ * bit 2 - nal_hrd_parameters_present_flag
+ * bit 3 - vcl_hrd_parameters_present_flag
+ * bit 4 - pic_struct_present_flag
+ * bit 5 - bitstream_restriction_flag
+ */
+#define VUI_STATUS 0XF4
+#define ASPECT_RATIO_IDC 0XF5
+#define ASPECT_RATIO_SAR_WIDTH 0XF6
+#define ASPECT_RATIO_SAR_HEIGHT 0XF7
+#define NUM_UNITS_IN_TICK 0XF8
+#define TIME_SCALE 0XFA
+#define CURRENT_PIC_INFO 0XFC
+#define DPB_BUFFER_INFO 0XFD
+#define REFERENCE_POOL_INFO 0XFE
+#define REFERENCE_LIST_INFO 0XFF
+
+#define REORDER_CMD_MAX 66
+
+/* config parameters to DDR lmem */
+#define GET_SPS_PROFILE_IDC(x) (((x) & 0xff) << 8)
+#define GET_SPS_LEVEL_IDC(x) ((x) & 0xff)
+#define GET_SPS_SEQ_PARAM_SET_ID(x) (((x) & 0x1f) << 8)
+#define GET_SPS_CHROMA_FORMAT_IDC(x) ((x) << 8)
+#define GET_SPS_NUM_REF_FRAMES(x) ((x) & 0xff)
+#define GET_SPS_GAPS_ALLOWED_FLAG(x) ((x) << 8)
+#define GET_SPS_LOG2_MAX_FRAME_NUM(x) ((x) + 4)
+#define GET_SPS_PIC_ORDER_CNT_LSB(x) ((x) + 4)
+#define GET_SPS_PIC_ORDER_TYPE(x) (x)
+#define GET_SPS_OFFSET_FOR_NONREF_PIC_HIGH(x) (((x) & 0xffff0000) >> 16)
+#define GET_SPS_OFFSET_FOR_NONREF_PIC_LOW(x) ((x) & 0xffff)
+#define GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_HIGH(x) (((x) & 0xffff0000) >> 16)
+#define GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_LOW(x) ((x) & 0xffff)
+#define GET_SPS_PIC_WIDTH_IN_MBS(x) ((x) + 1)
+#define GET_SPS_PIC_HEIGHT_IN_MBS(x) ((x) + 1)
+#define GET_SPS_DIRECT_8X8_FLAGS(x) (((x) & 0x1) << 2)
+#define GET_SPS_MB_ADAPTIVE_FRAME_FIELD_FLAGS(x) (((x) & 0x1) << 1)
+#define GET_SPS_FRAME_MBS_ONLY_FLAGS(x) ((x) & 0x1)
+
+#define GET_PPS_PIC_PARAM_SET_ID(x) ((x) & 0xff)
+#define GET_PPS_SEQ_PARAM_SET_ID(x) (((x) & 0x1f) << 8)
+#define GET_PPS_ENTROPY_CODING_MODE_FLAG(x) (((x) & 0x1) << 13)
+#define GET_PPS_PIC_ORDER_PRESENT_FLAG(x) (((x) & 0x1) << 14)
+#define GET_PPS_NUM_IDX_REF_L0_MINUS1(x) ((x) & 0x1f)
+#define GET_PPS_NUM_IDX_REF_L1_MINUS1(x) (((x) & 0x1f) << 5)
+#define GET_PPS_WEIGHTED_PRED_FLAG(x) (((x) & 0x1) << 10)
+#define GET_PPS_WEIGHTED_BIPRED_IDC(x) (((x) & 0x3) << 11)
+#define GET_PPS_INIT_QS_MINUS26(x) (((x) & 0xff) << 8)
+#define GET_PPS_INIT_QP_MINUS26(x) ((x) & 0xff)
+#define GET_PPS_CHROMA_QP_INDEX_OFFSET(x) ((x) & 0xff)
+#define GET_PPS_DEBLOCK_FILTER_CTRL_PRESENT_FLAG(x) (((x) & 0x1) << 8)
+#define GET_PPS_CONSTRAIN_INTRA_PRED_FLAG(x) (((x) & 0x1) << 9)
+#define GET_PPS_REDUNDANT_PIC_CNT_PRESENT_FLAG(x) (((x) & 0x1) << 10)
+#define GET_PPS_SCALING_MATRIX_PRESENT_FLAG(x) (((x) & 0x1) << 1)
+#define GET_PPS_TRANSFORM_8X8_FLAG(x) ((x) & 0x1)
+#define GET_PPS_GET_SECOND_CHROMA_QP_OFFSET(x) (x)
+
+int aml_h264_init(void *priv);
+void aml_h264_exit(void *priv);
+int aml_h264_dec_run(void *priv);
+
+#endif
diff --git a/drivers/media/platform/amlogic/vdec/reg_defines.h b/drivers/media/platform/amlogic/vdec/reg_defines.h
new file mode 100644
index 000000000000..6b1900ecbedb
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/reg_defines.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef _REG_DEFINES_H_
+#define _REG_DEFINES_H_
+
+#define VDEC_ASSIST_MMC_CTRL0 0x0001
+#define VDEC_ASSIST_MMC_CTRL1 0x0002
+
+#define VDEC_ASSIST_CANVAS_BLK32 0x0005
+
+#define VDEC_ASSIST_MBOX1_CLR_REG 0x0075
+#define VDEC_ASSIST_MBOX1_MASK 0x0076
+
+#define MPSR 0x0301
+#define MPC_P 0x0306
+#define MPC_D 0x0307
+#define MPC_E 0x0308
+#define MPC_W 0x0309
+#define CPSR 0x0321
+#define IMEM_DMA_CTRL 0x0340
+#define IMEM_DMA_ADR 0x0341
+#define IMEM_DMA_COUNT 0x0342
+#define WRRSP_IMEM 0x0343
+#define LMEM_DMA_CTRL 0x0350
+#define WRRSP_LMEM 0x0353
+
+#define PSCALE_CTRL 0x0911
+#define GCLK_EN 0x0983
+#define MDEC_PIC_DC_CTRL 0x098e
+#define MDEC_PIC_DC_MUX_CTRL 0x098d
+#define ANC0_CANVAS_ADDR 0x0990
+#define ANC1_CANVAS_ADDR 0x0991
+#define ANC2_CANVAS_ADDR 0x0992
+#define ANC3_CANVAS_ADDR 0x0993
+#define ANC4_CANVAS_ADDR 0x0994
+#define ANC5_CANVAS_ADDR 0x0995
+#define ANC6_CANVAS_ADDR 0x0996
+#define ANC7_CANVAS_ADDR 0x0997
+#define ANC8_CANVAS_ADDR 0x0998
+#define ANC9_CANVAS_ADDR 0x0999
+#define ANC10_CANVAS_ADDR 0x099a
+#define ANC11_CANVAS_ADDR 0x099b
+#define ANC12_CANVAS_ADDR 0x099c
+#define ANC13_CANVAS_ADDR 0x099d
+#define ANC14_CANVAS_ADDR 0x099e
+#define ANC15_CANVAS_ADDR 0x099f
+#define ANC16_CANVAS_ADDR 0x09a0
+#define ANC17_CANVAS_ADDR 0x09a1
+#define ANC18_CANVAS_ADDR 0x09a2
+#define ANC19_CANVAS_ADDR 0x09a3
+#define ANC20_CANVAS_ADDR 0x09a4
+#define ANC21_CANVAS_ADDR 0x09a5
+#define ANC22_CANVAS_ADDR 0x09a6
+#define ANC23_CANVAS_ADDR 0x09a7
+#define ANC24_CANVAS_ADDR 0x09a8
+#define ANC25_CANVAS_ADDR 0x09a9
+#define ANC26_CANVAS_ADDR 0x09aa
+#define ANC27_CANVAS_ADDR 0x09ab
+#define ANC28_CANVAS_ADDR 0x09ac
+#define ANC29_CANVAS_ADDR 0x09ad
+#define ANC30_CANVAS_ADDR 0x09ae
+#define ANC31_CANVAS_ADDR 0x09af
+#define DBKR_CANVAS_ADDR 0x09b0
+#define DBKW_CANVAS_ADDR 0x09b1
+#define REC_CANVAS_ADDR 0x09b2
+#define CURR_CANVAS_CTRL 0x09b3
+#define MDEC_PIC_DC_THRESH 0x09b8
+#define AV_SCRATCH_0 0x09c0
+#define AV_SCRATCH_1 0x09c1
+#define AV_SCRATCH_2 0x09c2
+#define AV_SCRATCH_3 0x09c3
+#define AV_SCRATCH_4 0x09c4
+#define AV_SCRATCH_5 0x09c5
+#define AV_SCRATCH_6 0x09c6
+#define AV_SCRATCH_7 0x09c7
+#define AV_SCRATCH_8 0x09c8
+#define AV_SCRATCH_9 0x09c9
+#define AV_SCRATCH_A 0x09ca
+#define AV_SCRATCH_B 0x09cb
+#define AV_SCRATCH_C 0x09cc
+#define AV_SCRATCH_D 0x09cd
+#define AV_SCRATCH_E 0x09ce
+#define AV_SCRATCH_F 0x09cf
+#define AV_SCRATCH_G 0x09d0
+#define AV_SCRATCH_H 0x09d1
+#define AV_SCRATCH_I 0x09d2
+#define AV_SCRATCH_J 0x09d3
+#define AV_SCRATCH_K 0x09d4
+#define AV_SCRATCH_L 0x09d5
+#define AV_SCRATCH_M 0x09d6
+#define AV_SCRATCH_N 0x09d7
+#define WRRSP_VLD 0x09da
+#define MDEC_DOUBLEW_CFG0 0x09db
+#define MDEC_DOUBLEW_CFG1 0x09dc
+#define MDEC_DOUBLEW_CFG2 0x09dd
+#define MDEC_DOUBLEW_CFG3 0x09de
+#define MDEC_DOUBLEW_CFG4 0x09df
+#define MDEC_DOUBLEW_CFG5 0x09e0
+#define MDEC_DOUBLEW_CFG6 0x09e1
+#define MDEC_DOUBLEW_CFG7 0x09e2
+#define MDEC_DOUBLEW_STATUS 0x09e3
+#define MDEC_EXTIF_CFG0 0x09e4
+
+#define MDEC_EXTIF_CFG1 0x09e5
+#define MDEC_EXTIF_CFG2 0x09e6
+
+#define POWER_CTL_VLD 0x0c08
+#define VLD_DECODE_CONTROL 0x0c18
+
+#define PMV1_X 0x0c20
+#define PMV1_Y 0x0c21
+#define PMV2_X 0x0c22
+#define PMV2_Y 0x0c23
+#define PMV3_X 0x0c24
+#define PMV3_Y 0x0c25
+#define PMV4_X 0x0c26
+#define PMV4_Y 0x0c27
+#define M4_TABLE_SELECT 0x0c28
+#define M4_CONTROL_REG 0x0c29
+#define BLOCK_NUM 0x0c2a
+#define PATTERN_CODE 0x0c2b
+#define MB_INFO 0x0c2c
+#define VLD_DC_PRED 0x0c2d
+#define VLD_ERROR_MASK 0x0c2e
+#define VLD_DC_PRED_C 0x0c2f
+#define LAST_SLICE_MV_ADDR 0x0c30
+#define LAST_MVX 0x0c31
+#define LAST_MVY 0x0c32
+
+#define MB_MOTION_MODE 0x0c07
+#define VIFF_BIT_CNT 0x0c1a
+#define M4_CONTROL_REG 0x0c29
+#define VLD_C38 0x0c38
+#define VLD_C39 0x0c39
+#define VLD_SHIFT_STATUS 0x0c3b
+#define VLD_C3D 0x0c3d
+#define VLD_MEM_VIFIFO_START_PTR 0x0c40
+#define VLD_MEM_VIFIFO_CURR_PTR 0x0c41
+#define VLD_MEM_VIFIFO_END_PTR 0x0c42
+#define VLD_MEM_VIFIFO_BYTES_AVAIL 0x0c43
+#define VLD_MEM_VIFIFO_CONTROL 0x0c44
+#define VLD_MEM_VIFIFO_WP 0x0c45
+#define VLD_MEM_VIFIFO_RP 0x0c46
+#define VLD_MEM_VIFIFO_LEVEL 0x0c47
+#define VLD_MEM_VIFIFO_BUF_CNTL 0x0c48
+
+#define VCOP_CTRL_REG 0x0e00
+#define RV_AI_MB_COUNT 0x0e0c
+#define IQIDCT_CONTROL 0x0e0e
+#define DCAC_DDR_BYTE64_CTL 0x0e1d
+
+#define VDEC2_IMEM_DMA_CTRL 0x2340
+#define VDEC2_IMEM_DMA_ADR 0x2341
+#define VDEC2_IMEM_DMA_COUNT 0x2342
+
+#define DOS_SW_RESET0 0x3f00
+#define DOS_GCLK_EN0 0x3f01
+#define DOS_GCLK_EN1 0x3f09
+#define DOS_GCLK_EN3 0x3f35
+
+#define DOS_MEM_PD_VDEC 0x3f30
+#define DOS_MEM_PD_VDEC2 0x3f31
+#define DOS_MEM_PD_HCODEC 0x3f32
+/*add from M8M2*/
+#define DOS_MEM_PD_HEVC 0x3f33
+
+#define DOS_SW_RESET3 0x3f34
+#define DOS_GCLK_EN3 0x3f35
+#define DOS_HEVC_INT_EN 0x3f36
+
+#endif
+
--
2.42.0
Powered by blists - more mailing lists