lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ