[PATCH RFC v3 2/4] decoder: Add V4L2 stateless H.264 decoder driver
From: Zhentao Guo via B4 Relay <devnull+zhentao.guo.amlogic.com@kernel.org>
Date: 2026-01-21 10:31:19
Also in:
b4-sent, linux-amlogic, linux-media, lkml
Subsystem:
media input infrastructure (v4l/dvb), the rest · Maintainers:
Mauro Carvalho Chehab, Linus Torvalds
From: Zhentao Guo <redacted> Add initial support for V4L2 stateless video decoder driver on Amlogic S4(S805X2) platform. In phase 1, it supports 8bit H.264 bitstreams decoding. Currently only progressive streams are supported. Signed-off-by: Zhentao Guo <redacted> --- MAINTAINERS | 7 + drivers/media/platform/amlogic/Kconfig | 1 + drivers/media/platform/amlogic/Makefile | 1 + drivers/media/platform/amlogic/vdec/Kconfig | 16 + drivers/media/platform/amlogic/vdec/Makefile | 4 + drivers/media/platform/amlogic/vdec/TODO | 7 + drivers/media/platform/amlogic/vdec/aml_vdec.c | 734 +++++++ drivers/media/platform/amlogic/vdec/aml_vdec.h | 33 + drivers/media/platform/amlogic/vdec/aml_vdec_drv.c | 239 +++ drivers/media/platform/amlogic/vdec/aml_vdec_drv.h | 172 ++ drivers/media/platform/amlogic/vdec/aml_vdec_hw.c | 596 ++++++ drivers/media/platform/amlogic/vdec/aml_vdec_hw.h | 158 ++ .../platform/amlogic/vdec/aml_vdec_platform.c | 85 + .../platform/amlogic/vdec/aml_vdec_platform.h | 50 + drivers/media/platform/amlogic/vdec/h264.c | 2129 ++++++++++++++++++++ drivers/media/platform/amlogic/vdec/h264.h | 299 +++ drivers/media/platform/amlogic/vdec/reg_defines.h | 177 ++ 17 files changed, 4708 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 14a06f856b81..077c628a46e0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS@@ -1369,6 +1369,13 @@ S: Maintained F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml F: drivers/spi/spi-amlogic-spisg.c +AMLOGIC VDEC DRIVER +M: Zhentao Guo <zhentao.guo@amlogic.com> +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/amlogic,s4-vcodec-dec.yaml +F: drivers/media/platform/amlogic/vdec/ + AMPHENOL CHIPCAP 2 DRIVER M: Javier Carrasco <javier.carrasco.cruz@gmail.com> L: linux-hwmon@vger.kernel.org
diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig
index 458acf3d5fa8..7c541ac0d0c3 100644
--- a/drivers/media/platform/amlogic/Kconfig
+++ b/drivers/media/platform/amlogic/Kconfig@@ -4,3 +4,4 @@ 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..158fd31ac6a2
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/Kconfig@@ -0,0 +1,16 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR MIT) + +config VIDEO_AMLOGIC_VDEC + tristate "Amlogic Video Decoder 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 + select MESON_CANVAS + help + This is a v4l2 driver for Amlogic video decoder 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..9a4e3102a9b8
--- /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\ + +obj-$(CONFIG_VIDEO_AMLOGIC_VDEC) += aml-vdec-drv.o
diff --git a/drivers/media/platform/amlogic/vdec/TODO b/drivers/media/platform/amlogic/vdec/TODO
new file mode 100644
index 000000000000..54c60145770e
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/TODO@@ -0,0 +1,7 @@ +TODO list for Amlogic V4L2 stateless decoder driver: + +1. Support decoding for HEVC, VP9, AV1, and MPEG-2. +2. Support more SoCs, including the new T7/S7 series and legacy SoCs (e.g., GXL, SM1, G12B). +3. Support 10-bit decoding and P010 output. + Note: P010 output requires hardware support. +4. Support interlaced stream decoding for H.264, HEVC, and MPEG-2.
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..bbcb3978dd6b
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec.c@@ -0,0 +1,734 @@ +// 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 const struct aml_dec_type dec_type_name[] = { + { + .codec_type = CODEC_TYPE_H264, + .name = "H264", + }, +}; + +static const char *dec_type_to_name(unsigned int type) +{ + int i; + int size = ARRAY_SIZE(dec_type_name); + + for (i = 0; i < size; i++) { + if (dec_type_name[i].codec_type == type) + return dec_type_name[i].name; + } + + return "ERR"; +} + +int aml_vdec_ctrls_setup(struct aml_vdec_ctx *ctx) +{ + int i; + int ctrls_size = ARRAY_SIZE(controls); + + 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) { + dev_info(&ctx->dev->plat_dev->dev, "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 = m2m_priv; + struct aml_vdec_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct media_request *src_req; + int ret = 0; + const char *fw_path = dev->pvdec_data->fw_path[ctx->curr_dec_type]; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + dev_dbg(&dev->plat_dev->dev, "device run : src buf : %d dst buf %d\n", + src_buf->vb2_buf.index, dst_buf->vb2_buf.index); + if (WARN_ON_ONCE(!ctx->codec_ops->run)) + goto err_cancel_job; + + src_req = src_buf->vb2_buf.req_obj.req; + if (src_req) + v4l2_ctrl_request_setup(src_req, &ctx->ctrl_handler); + dos_enable(dev->dec_hw); + /* incase of bus hang in stop_streaming */ + ctx->dos_clk_en = 1; + aml_vdec_reset_core(dev->dec_hw); + + if (load_firmware(dev->dec_hw, fw_path)) + goto err_cancel_job; + + ret = ctx->codec_ops->run(ctx); + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); + if (src_req) + v4l2_ctrl_request_complete(src_req, &ctx->ctrl_handler); + if (ret < 0) + goto err_cancel_job; + + return; + +err_cancel_job: + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + VB2_BUF_STATE_ERROR); +} + +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 aml_vdec_ctx *ctx, + struct v4l2_fmtdesc *f, bool is_output) +{ + struct aml_vdec_dev *dev = ctx->dev; + const struct aml_video_fmt *fmt; + int i = 0, j = 0; + + for (; i < dev->pvdec_data->num_fmts; i++) { + fmt = &dev->pvdec_data->dec_fmt[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)); + return 0; + } + ++j; + } + return -EINVAL; +} + +static const struct aml_video_fmt *aml_vdec_get_video_fmt(struct aml_vdec_dev + *dev, u32 format) +{ + const struct aml_video_fmt *fmt; + unsigned int k; + + for (k = 0; k < dev->pvdec_data->num_fmts; k++) { + fmt = &dev->pvdec_data->dec_fmt[k]; + if (fmt->fourcc == format) + return fmt; + } + + return NULL; +} + +static int aml_vdec_init_dec_inst(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_dev *dev = ctx->dev; + struct aml_video_fmt *fmt_out = &ctx->dec_fmt[AML_FMT_SRC]; + int ret = -1; + + 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; + dev_info(&dev->plat_dev->dev, "%s set curr_dec_type %s\n", + __func__, dec_type_to_name(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) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) { + 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; + } +} + +static int vidioc_vdec_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + const struct aml_video_fmt *fmt; + struct aml_vdec_dev *dev = video_drvdata(file); + + if (fsize->index != 0) + return -EINVAL; + + fmt = aml_vdec_get_video_fmt(dev, fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = fmt->stepwise; + + return 0; +} + +static int vdec_try_fmt_mp(struct aml_vdec_ctx *ctx, enum v4l2_buf_type type, + struct v4l2_pix_format_mplane *pix_mp) +{ + struct aml_video_fmt *dec_fmt; + int i, align; + + if (V4L2_TYPE_IS_OUTPUT(type)) + dec_fmt = &ctx->dec_fmt[AML_FMT_SRC]; + else + dec_fmt = &ctx->dec_fmt[AML_FMT_DST]; + + pix_mp->field = V4L2_FIELD_NONE; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + pix_mp->num_planes = dec_fmt->num_planes; + pix_mp->pixelformat = dec_fmt->fourcc; + if (!pix_mp->plane_fmt[0].sizeimage) + pix_mp->plane_fmt[0].sizeimage = + (pix_mp->height * pix_mp->width * 3) / 2; + } + + align = ctx->dev->pvdec_data->dec_fmt->align; + pix_mp->height = ALIGN(pix_mp->height, align); + pix_mp->width = ALIGN(pix_mp->width, align); + + v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, + &dec_fmt->stepwise); + dev_dbg(&ctx->dev->plat_dev->dev, + "%s type %d four_cc %d pix_mp->width %d pix_mp->height %d\n", + __func__, type, dec_fmt->fourcc, pix_mp->width, pix_mp->height); + + v4l2_fill_pixfmt_mp(pix_mp, dec_fmt->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; + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s type %d fmt %d num_plane %d sizeimage0 %d sizeimage1 %d\n", + __func__, type, pix_mp->pixelformat, pix_mp->num_planes, + pix_mp->plane_fmt[0].sizeimage, pix_mp->plane_fmt[1].sizeimage); + + return 0; +} + +static int vdec_s_fmt_output(struct aml_vdec_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct aml_video_fmt *out_fmt; + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (vb2_is_busy(vq) && + pix_mp->pixelformat != ctx->pix_fmt[AML_FMT_SRC].pixelformat) + return -EBUSY; + + out_fmt = aml_vdec_get_video_fmt(ctx->dev, pix_mp->pixelformat); + if (out_fmt) + ctx->dec_fmt[AML_FMT_SRC] = *out_fmt; + else + dev_dbg(&ctx->dev->plat_dev->dev, + "%s fmt %d not supported, use default\n", __func__, + pix_mp->pixelformat); + + ret = vdec_try_fmt_mp(ctx, f->type, pix_mp); + set_pic_info(ctx, pix_mp, f->type); + + ctx->pix_fmt[AML_FMT_SRC] = *pix_mp; + ctx->pix_fmt[AML_FMT_DST] = *pix_mp; + + return ret; +} + +static int vdec_s_fmt_capture(struct aml_vdec_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct aml_video_fmt *cap_fmt; + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + cap_fmt = aml_vdec_get_video_fmt(ctx->dev, pix_mp->pixelformat); + if (cap_fmt) + ctx->dec_fmt[AML_FMT_DST] = *cap_fmt; + else + dev_dbg(&ctx->dev->plat_dev->dev, + "%s fmt %d not supported, use default\n", __func__, + pix_mp->pixelformat); + + ret = vdec_try_fmt_mp(ctx, f->type, pix_mp); + + ctx->pix_fmt[AML_FMT_DST] = *pix_mp; + + return ret; +} + +static void reset_output_fmts(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_dev *dev = ctx->dev; + const struct aml_video_fmt *out_fmt; + struct v4l2_pix_format_mplane fmt; + + /* reset default output fmt to V4L2_PIX_FMT_H264_SLICE */ + out_fmt = aml_vdec_get_video_fmt(dev, V4L2_PIX_FMT_H264_SLICE); + if (!out_fmt) + return; + + ctx->dec_fmt[AML_FMT_SRC] = *out_fmt; + + memset(&fmt, 0, sizeof(struct v4l2_pix_format_mplane)); + + fmt.height = out_fmt->stepwise.min_height; + fmt.width = out_fmt->stepwise.min_width; + /* reset bytesperline to 0 for output fmts */ + fmt.plane_fmt[0].bytesperline = 0; + fmt.colorspace = V4L2_COLORSPACE_DEFAULT; + fmt.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt.quantization = V4L2_QUANTIZATION_DEFAULT; + fmt.xfer_func = V4L2_XFER_FUNC_DEFAULT; + vdec_try_fmt_mp(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, &fmt); + + ctx->pix_fmt[AML_FMT_SRC] = fmt; +} + +static void reset_capture_fmts(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_dev *dev = ctx->dev; + const struct aml_video_fmt *cap_fmt; + struct v4l2_pix_format_mplane fmt; + + /* reset default output fmt to V4L2_PIX_FMT_NV12 */ + cap_fmt = aml_vdec_get_video_fmt(dev, V4L2_PIX_FMT_NV12); + if (!cap_fmt) + return; + + ctx->dec_fmt[AML_FMT_DST] = *cap_fmt; + + memset(&fmt, 0, sizeof(struct v4l2_pix_format_mplane)); + + fmt.height = cap_fmt->stepwise.min_height; + fmt.width = cap_fmt->stepwise.min_width; + fmt.colorspace = V4L2_COLORSPACE_DEFAULT; + fmt.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt.quantization = V4L2_QUANTIZATION_DEFAULT; + fmt.xfer_func = V4L2_XFER_FUNC_DEFAULT; + vdec_try_fmt_mp(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, &fmt); + + ctx->pix_fmt[AML_FMT_DST] = fmt; +} + +void aml_vdec_reset_fmts(struct aml_vdec_ctx *ctx) +{ + ctx->m2m_ctx->q_lock = &ctx->v4l2_intf_lock; + reset_output_fmts(ctx); + reset_capture_fmts(ctx); +} + +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; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) + *pix_mp = ctx->pix_fmt[AML_FMT_SRC]; + else + *pix_mp = ctx->pix_fmt[AML_FMT_DST]; + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s fmt %d num planes %d\n", __func__, pix_mp->pixelformat, + pix_mp->num_planes); + + return 0; +} + +static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vdec_try_fmt_mp(fh_to_dec_ctx(file), f->type, &f->fmt.pix_mp); +} + +static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vdec_try_fmt_mp(fh_to_dec_ctx(file), f->type, &f->fmt.pix_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(file); + + return vdec_s_fmt_output(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(file); + + return vdec_s_fmt_capture(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(file); + + 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(file); + + return vdec_g_fmt(ctx, f); +} + +static int vidioc_vdec_enum_fmt_out_mplane(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + struct aml_vdec_ctx *ctx = fh_to_dec_ctx(file); + + return vidioc_vdec_enum_fmt(ctx, f, 1); +} + +static int vidioc_vdec_enum_fmt_cap_mplane(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + struct aml_vdec_ctx *ctx = fh_to_dec_ctx(file); + + return vidioc_vdec_enum_fmt(ctx, 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 v4l2_pix_format_mplane *pix_fmt; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->pix_fmt[AML_FMT_SRC]; + else + pix_fmt = &ctx->pix_fmt[AML_FMT_DST]; + + if (*nplanes) { + if (*nplanes != pix_fmt->num_planes) + return -EINVAL; + + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < pix_fmt->plane_fmt[i].sizeimage) { + dev_err(&ctx->dev->plat_dev->dev, + "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 = pix_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] = pix_fmt->plane_fmt[i].sizeimage; + } + } + + if (*nplanes) { + dev_dbg(&ctx->dev->plat_dev->dev, + "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 v4l2_pix_format_mplane *pix_fmt; + unsigned int sizeimage = 0; + int i; + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + pix_fmt = &ctx->pix_fmt[AML_FMT_SRC]; + else + pix_fmt = &ctx->pix_fmt[AML_FMT_DST]; + + for (i = 0; i < pix_fmt->num_planes; i++) { + sizeimage = pix_fmt->plane_fmt[i].sizeimage; + if (vb2_plane_size(vb, i) < sizeimage) + return -EINVAL; + + if (V4L2_TYPE_IS_CAPTURE(vb->type)) { + vb2_set_plane_payload(vb, i, + pix_fmt->plane_fmt[i].sizeimage); + dev_dbg(&ctx->dev->plat_dev->dev, + "%s type: %d set plane: %d, sizeimage: %d\n", + __func__, vb->vb2_queue->type, i, + pix_fmt->plane_fmt[i].sizeimage); + } + } + + 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); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + ctx->is_output_streamon = 1; + if (aml_vdec_init_dec_inst(ctx) < 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, + .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..32f7fa245f7e
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec.h@@ -0,0 +1,33 @@ +/* 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" + +/** + * 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; +}; + +struct aml_dec_type { + unsigned int codec_type; + const char *name; +}; + +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_reset_fmts(struct aml_vdec_ctx *ctx); +#endif
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..55668d01b526
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.c@@ -0,0 +1,239 @@ +// 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_obj(*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(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_reset_fmts(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); + + 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 = devm_kzalloc(&pdev->dev, 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(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; + } + + 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: + 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); + dev->dec_hw = NULL; + } + v4l2_device_unregister(&dev->v4l2_dev); +} + +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, + }, +}; + +module_platform_driver(aml_vcodec_dec_driver); + +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..fa86233ea5b3
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h@@ -0,0 +1,172 @@ +/* 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_queue_type - Type of queue : cap or output + */ +enum aml_queue_type { + AML_FMT_SRC = 0, + AML_FMT_DST = 1, +}; + +/** + * struct aml_video_fmt - aml video decoder fmt information + * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. + * @align: Describe the align width/height required by hardware. + * @is_10_bit_support: If the curr platform support p010 output. + * @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; + int align; + int is_10_bit_support; + enum aml_fmt_type type; + enum aml_codec_type codec_type; + u32 num_planes; + const u8 *name; + struct v4l2_frmsize_stepwise stepwise; +}; + +/** + * 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 + * @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 { + 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. + * @pix_fmt: To store the V4L2 pixel format. + * @dec_fmt: To describe the decoding format supported by hardware platform. + * @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 v4l2_pix_format_mplane pix_fmt[2]; + struct aml_video_fmt dec_fmt[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 file *file) +{ + struct v4l2_fh *file_fh = file_to_v4l2_fh(file); + + return container_of(file_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..1729076de76c
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.c@@ -0,0 +1,596 @@ +// 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 <linux/iopoll.h> + +#include "aml_vdec_drv.h" +#include "aml_vdec_hw.h" +#include "aml_vdec_platform.h" + +#define MHz (1000000) +#define MC_SIZE (4096 * 16) + +static struct pm_pd_s vdec_domain_data[] = { + [VDEC_1] = {.name = "vdec", }, + [VDEC_HEVC] = {.name = "hevc", }, +}; + +u32 read_dos_reg(struct aml_vdec_hw *hw, u32 addr) +{ + u32 ret_val; + + regmap_read(hw->map[DOS_BUS], addr, &ret_val); + + return ret_val; +} + +static void dos_reg_write_bits(struct aml_vdec_hw *hw, u32 reg, u32 val, int start, int len) +{ + u32 mask = (((1L << (len)) - 1) << (start)); + + regmap_update_bits(hw->map[DOS_BUS], reg, mask, val); +} + +void dos_enable(struct aml_vdec_hw *hw) +{ + dos_reg_write_bits(hw, DOS_GCLK_EN0, 0x3ff, 0, 10); + + regmap_write(hw->map[DOS_BUS], GCLK_EN, 0x3ff); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 31), 0); +} + +void aml_vdec_reset_core(struct aml_vdec_hw *hw) +{ + unsigned int mask = 0; + + mask = (1 << 21); + + regmap_update_bits(hw->map[DMC_BUS], 0x0, mask, 0); + usleep_range(60, 70); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, + (1 << 3) | (1 << 4) | (1 << 5) | (1 << 7) | + (1 << 8) | (1 << 9)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + regmap_update_bits(hw->map[DOS_BUS], VDEC_ASSIST_MMC_CTRL1, 1 << 3, 0); + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_MUX_CTRL, 1 << 31, 0); + regmap_write(hw->map[DOS_BUS], MDEC_EXTIF_CFG1, 0); + + regmap_update_bits(hw->map[DMC_BUS], 0x0, mask, mask); +} + +void aml_start_vdec_hw(struct aml_vdec_hw *hw) +{ + u32 reg_read_val; + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, (1 << 12) | (1 << 11)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + + regmap_write(hw->map[DOS_BUS], MPSR, 0x0001); +} + +void aml_stop_vdec_hw(struct aml_vdec_hw *hw) +{ + u32 reg_val = 0; + int ret; + + regmap_write(hw->map[DOS_BUS], MPSR, 0); + regmap_write(hw->map[DOS_BUS], CPSR, 0); + + ret = read_poll_timeout_atomic(read_dos_reg, reg_val, + !(reg_val & 0x8000), + 10, 100000, true, + hw, IMEM_DMA_CTRL); + + ret = read_poll_timeout_atomic(read_dos_reg, reg_val, + !(reg_val & 0x8000), + 10, 100000, true, + hw, LMEM_DMA_CTRL); + + ret = read_poll_timeout_atomic(read_dos_reg, reg_val, + !(reg_val & 0xfff), + 10, 800000, true, + hw, WRRSP_LMEM); + if (ret) + dev_err(hw->dev, "%s, ctrl %x, rsp %x, pc %x status %x,%x\n", + __func__, read_dos_reg(hw, LMEM_DMA_CTRL), + read_dos_reg(hw, WRRSP_LMEM), read_dos_reg(hw, MPC_E), + read_dos_reg(hw, AV_SCRATCH_J), read_dos_reg(hw, AV_SCRATCH_9)); + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, (1 << 12) | (1 << 11)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); +} + +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) { + dev_info(dev, "request_firmware %s failed ret %d\n", path, ret); + return -EINVAL; + } + + if (fw->size > MC_SIZE) { + dev_info(dev, "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) { + dev_info(dev, "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)); + + regmap_write(hw->map[DOS_BUS], MPSR, 0); + regmap_write(hw->map[DOS_BUS], CPSR, 0); + + timeout = read_dos_reg(hw, MPSR); + timeout = read_dos_reg(hw, MPSR); + + timeout = jiffies + HZ; + + regmap_write(hw->map[DOS_BUS], IMEM_DMA_ADR, mc_addr_map); + regmap_write(hw->map[DOS_BUS], IMEM_DMA_COUNT, 0x1000); + regmap_write(hw->map[DOS_BUS], IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (read_dos_reg(hw, IMEM_DMA_CTRL) & 0x8000) { + if (time_before(jiffies, timeout)) { + schedule(); + } else { + dev_info(dev, "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) +{ + hw->gates[VDEC].id = "vdec"; + hw->gates[VDEC_MUX].id = "clk_vdec_mux"; + hw->gates[HEVCF_MUX].id = "clk_hevcf_mux"; + + return devm_clk_bulk_get(hw->dev, DOS_CLK_MAX, hw->gates); +} + +static struct clk_bulk_data *vdec_get_clk_by_name(struct aml_vdec_hw *hw, + const char *name) +{ + int i; + + for (i = 0; i < DOS_CLK_MAX; i++) { + if (!strcmp(name, hw->gates[i].id)) { + if (hw->gates[i].clk) + return &hw->gates[i]; + } + } + return NULL; +} + +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); + dev_dbg(hw->dev, "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)) { + dev_err(hw->dev, "Adding %s device link failed!\n", pd[i].name); + return -ENODEV; + } + + dev_dbg(hw->dev, "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(struct aml_vdec_hw *hw, bool is_on, int id) +{ + if (is_on) { + usleep_range(20, 100); + + switch (id) { + case VDEC_1: + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_VDEC, 0); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0xfffffffc); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_VDEC, 0); + break; + case VDEC_HEVC: + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_HEVC, 0); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0xffffffff); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_HEVC, 0); + break; + default: + dev_info(hw->dev, "%s on, not found id %d\n", __func__, id); + break; + } + } else { + switch (id) { + case VDEC_1: + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0xfffffffc); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_VDEC, 0xffffffffUL); + break; + case VDEC_HEVC: + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0xffffffff); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_HEVC, 0xffffffffUL); + break; + default: + dev_info(hw->dev, "%s off, not found id %d\n", __func__, id); + break; + } + } + + dev_dbg(hw->dev, "%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; + struct device *dev = pm->pd_data[id].dev; + struct clk_bulk_data *gate_node; + + if (id == VDEC_1) + gate_node = vdec_get_clk_by_name(hw, "clk_vdec_mux"); + else if (id == VDEC_HEVC) + gate_node = vdec_get_clk_by_name(hw, "clk_hevcf_mux"); + + if (gate_node) { + clk_prepare_enable(gate_node->clk); + if (id == VDEC_1) { + clk_set_rate(gate_node->clk, 499999992); + dev_dbg(hw->dev, "after set, vdec mux clock is %lu Hz\n", + clk_get_rate(gate_node->clk)); + } + dev_dbg(hw->dev, "the %-15s clock on\n", gate_node->id); + } else { + dev_info(hw->dev, "clk %d, unreachable\n", id); + } + + if (dev) { + pm_runtime_get_sync(dev); + dev_dbg(dev, "dev: %p link %p the %-15s power on\n", + dev, pm->pd_data[id].link, pm->pd_data[id].name); + } + + dos_local_config(hw, 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; + struct device *dev = pm->pd_data[id].dev; + struct clk_bulk_data *gate_node; + + dos_local_config(hw, 0, id); + + if (dev) { + pm_runtime_put_sync(dev); + dev_dbg(dev, "dev: %p link %p the %-15s power off\n", + dev, pm->pd_data[id].link, pm->pd_data[id].name); + } + + if (id == VDEC_1) + gate_node = vdec_get_clk_by_name(hw, "clk_vdec_mux"); + else if (id == VDEC_HEVC) + gate_node = vdec_get_clk_by_name(hw, "clk_hevcf_mux"); + + if (gate_node) { + clk_disable_unprepare(gate_node->clk); + dev_dbg(hw->dev, "the %-15s clock off\n", gate_node->id); + } else { + dev_info(hw->dev, "clk %d, unreachable\n", id); + } +} + +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_poweron(hw, VDEC_1); + + return 0; +} + +int vdec_disable(struct aml_vdec_hw *hw) +{ + vdec_poweroff(hw, VDEC_1); + + 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; +} + +static const struct regmap_config dos_regmap_conf = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x10000, +}; + +static const struct regmap_config dmc_regmap_conf = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x20, +}; + +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; + void __iomem *reg_base[MAX_REG_BUS]; + struct resource res; + int i; + int ret = -1; + + if (!dev || !dev->dec_hw) + 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)) { + dev_err(&pdev->dev, "of_address_to_resource %d failed\n", i); + return -EINVAL; + } + reg_base[i] = devm_ioremap_resource(&pdev->dev, &res); + + if (IS_ERR(reg_base[i])) + return PTR_ERR(reg_base[i]); + + if (i == DOS_BUS) { + hw->map[i] = devm_regmap_init_mmio(&pdev->dev, reg_base[i], + &dos_regmap_conf); + } else if (i == DMC_BUS) { + hw->map[i] = devm_regmap_init_mmio(&pdev->dev, reg_base[i], + &dmc_regmap_conf); + } + + if (IS_ERR(hw->map[i])) + return PTR_ERR(hw->map[i]); + + dev_dbg(&pdev->dev, "%s, res start %llx, end %llx, iomap: %p\n", + __func__, (unsigned long long)res.start, + (unsigned long long)res.end, reg_base[i]); + } + hw->canvas = meson_canvas_get(&pdev->dev); + if (IS_ERR(&pdev->dev)) + return PTR_ERR(&pdev->dev); + + hw->pm = &pm[dev->pvdec_data->power_type]; + if (hw->pm->init) { + ret = hw->pm->init(hw); + if (ret < 0) { + dev_err(&pdev->dev, "power mgr init failed!\n"); + return ret; + } + } + + ret = vdec_clock_gate_init(hw); + if (ret) { + dev_err(&pdev->dev, "clk bulk init failed!\n"); + return ret; + } + + dev_dbg(&pdev->dev, "##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) + return; + + hw = dev->dec_hw; + + if (hw->pm->release) + hw->pm->release(hw); + + dev_dbg(hw->dev, "##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..6aac89a6356c
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.h@@ -0,0 +1,158 @@ +/* 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 <linux/soc/amlogic/meson-canvas.h> +#include "reg_defines.h" + +#define VDEC_FIFO_ALIGN 8 +#define VLD_PADDING_SIZE 1024 + +/** + * 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 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; + struct regmap *map[MAX_REG_BUS]; + struct mutex pm_mutex; + struct meson_canvas *canvas; + const struct power_manager_s *pm; + struct aml_vdec_hw_ops hw_ops; + struct clk_bulk_data 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(struct aml_vdec_hw *hw, u32 reg_addr); +int vdec_enable(struct aml_vdec_hw *hw); +int vdec_disable(struct aml_vdec_hw *hw); +void dos_enable(struct aml_vdec_hw *hw); +void aml_start_vdec_hw(struct aml_vdec_hw *hw); +void aml_stop_vdec_hw(struct aml_vdec_hw *hw); +int load_firmware(struct aml_vdec_hw *hw, const char *path); +void aml_vdec_reset_core(struct aml_vdec_hw *hw); + +#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..5b43c72375e9
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c@@ -0,0 +1,85 @@ +// 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_hw.h" +#include "h264.h" + +#define VIDEO_DEC_H264 "s4_h264_multi.bin" + +static struct aml_video_fmt aml_s4_video_formats[] = { + { + .name = "H.264", + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .type = AML_FMT_DEC, + .align = 64, + .is_10_bit_support = 0, + .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, + .align = 64, + .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, + .align = 64, + .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, + .align = 64, + .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, + .align = 64, + .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}, + }, +}; + +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, + }, +}; + +const struct aml_dev_platform_data aml_vdec_s4_pdata = { + .codec_ops = aml_S4_dec_ops, + .dec_fmt = aml_s4_video_formats, + .num_fmts = ARRAY_SIZE(aml_s4_video_formats), + .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..6bde55891f4b
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h@@ -0,0 +1,50 @@ +/* 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> +#include "aml_vdec_drv.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_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_codec_ops *codec_ops; + const struct aml_video_fmt *dec_fmt; + int num_fmts; + 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..a2bb56f2609d
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/h264.c@@ -0,0 +1,2129 @@ +// 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 H264_MAX_COL_BUF 32 +#define H264_MAX_CANVAS_POS 26 + +#define DECODER_TIMEOUT_MS 500 + +#define COL_BUFFER_MARGIN 2 +#define COL_SIZE_FOR_ONE_MB 96 + +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 +}; + +#define I_Slice 2 +#define P_Slice 5 +#define B_Slice 6 +#define P_Slice_0 0 +#define B_Slice_1 1 +#define I_Slice_7 7 + +/* 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_canvas { + u32 canvas_pos; + int poc; +}; + +struct h264_decode_buf_spec { + struct v4l2_h264_dpb_entry *dpb; + u32 canvas_pos; + u32 dpb_index; + int 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 first_mb_in_slice; + 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]; + u32 canvas_pos_map; + struct h264_canvas ref_canvas[V4L2_H264_NUM_DPB_ENTRIES + 1]; + 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[H264_MAX_COL_BUF]; + + 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 list) +{ + int i; + int size; + struct h264_decode_buf_spec *ref_list; + + size = h264_ctx->list_size[list]; + if (list == 0) + ref_list = &h264_ctx->ref_list0[0]; + else + ref_list = &h264_ctx->ref_list1[0]; + + for (i = 0; i < size; i++) { + if (index == ref_list[i].dpb_index) + return &ref_list[i]; + } + + return NULL; +} + +static int h264_prepare_input(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_hw *hw = vdec_get_hw(ctx->dev); + 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) { + dev_info(hw->dev, "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); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 0); + /* reset VLD fifo for all vdec */ + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, + (1 << 5) | (1 << 4) | (1 << 3)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + regmap_write(hw->map[DOS_BUS], POWER_CTL_VLD, 1 << 4); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_START_PTR, src_dma); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_END_PTR, + (src_dma + payload_size)); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CURR_PTR, + round_down(src_dma, VDEC_FIFO_ALIGN)); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 1); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 0); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_BUF_CNTL, 2); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_RP, + round_down(src_dma, VDEC_FIFO_ALIGN)); + dummy = payload_size + VLD_PADDING_SIZE; + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_WP, + round_down((src_dma + dummy), VDEC_FIFO_ALIGN)); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_BUF_CNTL, 3); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_BUF_CNTL, 2); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, + (0x11 << 16) | (1 << 10) | (7 << 3)); + + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_1, 0x0); + regmap_write(hw->map[DOS_BUS], H264_DECODE_INFO, (1 << 13)); + regmap_write(hw->map[DOS_BUS], H264_DECODE_SIZE, payload_size); + regmap_write(hw->map[DOS_BUS], 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) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + struct aml_vdec_hw *hw = vdec_get_hw(ctx->dev); + 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); + regmap_write(hw->map[DOS_BUS], 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); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_2, cfg_tmp); + + cfg_tmp = 0; + cfg_tmp = (sps->max_num_ref_frames << 8) | (sps->level_idc); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_B, cfg_tmp); + + cfg_tmp = ((sps->level_idc & 0xff) << 7) | + (get_flag(sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) << 2); + regmap_write(hw->map[DOS_BUS], NAL_SEARCH_CTL, + read_dos_reg(hw, 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) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + struct aml_vdec_hw *hw = vdec_get_hw(ctx->dev); + 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; + + regmap_write(hw->map[DOS_BUS], 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); + + dev_dbg(&ctx->dev->plat_dev->dev, "%s sps id %d, pps id %d\n", + __func__, sps->seq_parameter_set_id, pps->pic_parameter_set_id); + + 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 = meson_canvas_alloc(hw->canvas, &buf_spec->y_canvas_index); + canvas_alloc_result = meson_canvas_alloc(hw->canvas, &buf_spec->u_canvas_index); + buf_spec->v_canvas_index = buf_spec->u_canvas_index; + + if (!canvas_alloc_result) { + /* config y canvas */ + meson_canvas_config(hw->canvas, + buf_spec->y_canvas_index, buf_spec->y_dma_addr, + mb_width << 4, mb_height << 4, + MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + regmap_write(hw->map[DOS_BUS], 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 */ + meson_canvas_config(hw->canvas, + buf_spec->u_canvas_index, buf_spec->c_dma_addr, + mb_width << 4, mb_height << 3, + MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + regmap_write(hw->map[DOS_BUS], 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 */ + ); + + regmap_write(hw->map[DOS_BUS], ANC0_CANVAS_ADDR + (buf_spec->canvas_pos << 2), + spec2canvas(buf_spec)); + } +} + +static int allocate_colocate_buf(struct aml_h264_ctx *h264_ctx, int poc) +{ + int i; + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + + 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; + dev_dbg(&ctx->dev->plat_dev->dev, "%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) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + + if (index >= 0) { + if (index >= h264_ctx->colocated_buf_num) { + dev_dbg + (&ctx->dev->plat_dev->dev, + "%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_poc[index] != INVALID_POC && + ((h264_ctx->colocated_buf_map >> index) & 0x1) == 0x1) { + h264_ctx->colocated_buf_map &= (~(1 << index)); + dev_dbg + (&ctx->dev->plat_dev->dev, + "%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; + + dev_dbg + (&ctx->dev->plat_dev->dev, + "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; + + for (i = 0; i < H264_MAX_COL_BUF; 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] = list_size; +} + +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 aml_vdec_ctx *ctx = h264_ctx->v4l2_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 v4l2_h264_dpb_entry *dpb = decode->dpb; + u8 index; + int i, j; + + h264_ctx->list_size[0] = list_size; + for (i = 0; i < list_size; i++) { + index = v4l2_b0_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].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; + } + + h264_ctx->list_size[1] = list_size; + for (j = 0; j < list_size; j++) { + index = v4l2_b1_reflist[j].index; + 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) + dev_info(&ctx->dev->plat_dev->dev, "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; + break; + } + } + + return ret; +} + +static int get_ref_list_size(struct aml_h264_ctx *h264_ctx, int cur_list) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + 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; + } + dev_dbg(&ctx->dev->plat_dev->dev, "%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); + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + 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) { + dev_dbg(&ctx->dev->plat_dev->dev, "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)) { + dev_dbg(&ctx->dev->plat_dev->dev, "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)); + dev_dbg(&ctx->dev->plat_dev->dev, "%s : 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); + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + 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) { + dev_dbg(&ctx->dev->plat_dev->dev, "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)) { + dev_dbg(&ctx->dev->plat_dev->dev, "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)); + dev_dbg(&ctx->dev->plat_dev->dev, "%s : 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) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + 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) { + dev_info(&ctx->dev->plat_dev->dev, "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; + + 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]; + struct slice *curr_slice = &h264_ctx->mslice; + + if (decode->flags == V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) + return; + + v4l2_h264_init_reflist_builder(&builder, decode, sps, dpb); + dev_dbg(&ctx->dev->plat_dev->dev, "%s num_valid = %d", __func__, + builder.num_valid); + + if (curr_slice->slice_type == P_SLICE && + (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 (curr_slice->slice_type == B_SLICE && + (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 int allocate_canvas_pos(struct aml_h264_ctx *h264_ctx, int poc) +{ + int i; + int ret = -1; + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + + for (i = 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + if (((h264_ctx->canvas_pos_map >> i) & 0x1) == 0) { + h264_ctx->canvas_pos_map |= (1 << i); + h264_ctx->ref_canvas[i].poc = poc; + h264_ctx->ref_canvas[i].canvas_pos = i; + ret = i; + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s i %d pos_poc %d\n", __func__, i, + h264_ctx->ref_canvas[i].poc); + break; + } + } + + return ret; +} + +static void release_canvas_pos(struct aml_h264_ctx *h264_ctx, int index) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + + if (index >= 0) { + if (index > V4L2_H264_NUM_DPB_ENTRIES) { + dev_dbg(&ctx->dev->plat_dev->dev, + "%s error, index %d is bigger than buf count %d\n", + __func__, index, h264_ctx->max_num_ref_frames); + } else { + if (h264_ctx->ref_canvas[index].poc != INVALID_POC && + ((h264_ctx->canvas_pos_map >> index) & 0x1) == + 0x1) { + h264_ctx->canvas_pos_map &= (~(1 << index)); + dev_dbg(&ctx->dev->plat_dev->dev, + "%s canvas_pos index %d released poc %d, canvas_pos_map 0x%x\n", + __func__, index, h264_ctx->ref_canvas[index].poc, + h264_ctx->canvas_pos_map); + h264_ctx->ref_canvas[index].poc = INVALID_POC; + h264_ctx->ref_canvas[index].canvas_pos = -1; + } + } + } +} + +static int get_canvas_pos_by_poc(struct aml_h264_ctx *h264_ctx, int poc) +{ + int i; + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + int ret_pos = -1; + + for (i = 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + if (h264_ctx->ref_canvas[i].poc == poc) { + ret_pos = h264_ctx->ref_canvas[i].canvas_pos; + dev_dbg(&ctx->dev->plat_dev->dev, "%s canvas_pos %d\n", + __func__, ret_pos); + return ret_pos; + } + } + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s error, no find canvas pos %d, poc %d\n", __func__, ret_pos, poc); + + return ret_pos; +} + +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); + for (i = 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) + release_canvas_pos(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); + } + + for (i = 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + col_poc = h264_ctx->ref_canvas[i].poc; + if (col_poc != INVALID_POC && + (poc_is_in_dpb(col_poc, decode->dpb) != 1)) + release_canvas_pos(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_l0, *buf_spec_l1; + 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 = + allocate_canvas_pos(h264_ctx, decode->top_field_order_cnt); + if (h264_ctx->curr_spec.canvas_pos < 0) + dev_err(&ctx->dev->plat_dev->dev, "curr_spec.canvas error\n"); + + if (decode->nal_ref_idc) + h264_ctx->curr_spec.col_buf_index = + allocate_colocate_buf(h264_ctx, + decode->top_field_order_cnt); + else + h264_ctx->curr_spec.col_buf_index = -1; + h264_ctx->curr_spec.poc = decode->top_field_order_cnt; + + 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)) + break; + + buf_spec_l0 = find_spec_by_dpb_index(h264_ctx, i, 0); + if (buf_spec_l0) { + buf_spec_l0->canvas_pos = + get_canvas_pos_by_poc(h264_ctx, + dpb->top_field_order_cnt); + if (buf_spec_l0->canvas_pos < 0) { + dev_err(&ctx->dev->plat_dev->dev, + "l0 canvas_pos %d error\n", + buf_spec_l0->canvas_pos); + continue; + } + vb = vb2_find_buffer(vq, dpb->reference_ts); + if (!vb) { + dev_err(&ctx->dev->plat_dev->dev, + "ref pic for ts %llu lost\n", dpb->reference_ts); + continue; + } + + buf_spec_l0->y_dma_addr = + vb2_dma_contig_plane_dma_addr(vb, 0); + if (ctx->pic_info.plane_num > 1) + buf_spec_l0->c_dma_addr = + vb2_dma_contig_plane_dma_addr(vb, 1); + else + buf_spec_l0->c_dma_addr = + buf_spec_l0->y_dma_addr + + ctx->pic_info.fb_size[0]; + dev_dbg(&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx c_dma_addr 0x%llx\n", + buf_spec_l0->dpb->top_field_order_cnt, + buf_spec_l0->canvas_pos, + buf_spec_l0->y_dma_addr, + buf_spec_l0->c_dma_addr); + } + + buf_spec_l1 = find_spec_by_dpb_index(h264_ctx, i, 1); + if (!buf_spec_l0 && buf_spec_l1) { + buf_spec_l1->canvas_pos = + get_canvas_pos_by_poc(h264_ctx, + dpb->top_field_order_cnt); + if (buf_spec_l1->canvas_pos < 0) { + dev_err(&ctx->dev->plat_dev->dev, + "l1 canvas_pos %d error\n", + buf_spec_l1->canvas_pos); + continue; + } + vb = vb2_find_buffer(vq, dpb->reference_ts); + if (!vb) { + dev_err(&ctx->dev->plat_dev->dev, + "ref pic for ts %llu lost\n", dpb->reference_ts); + continue; + } + + buf_spec_l1->y_dma_addr = + vb2_dma_contig_plane_dma_addr(vb, 0); + if (ctx->pic_info.plane_num > 1) + buf_spec_l1->c_dma_addr = + vb2_dma_contig_plane_dma_addr(vb, 1); + else + buf_spec_l1->c_dma_addr = + buf_spec_l1->y_dma_addr + + ctx->pic_info.fb_size[0]; + dev_dbg(&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx c_dma_addr 0x%llx\n", + buf_spec_l1->dpb->top_field_order_cnt, + buf_spec_l1->canvas_pos, + buf_spec_l1->y_dma_addr, + buf_spec_l1->c_dma_addr); + } else if (buf_spec_l0 && buf_spec_l1) { + memcpy(buf_spec_l1, buf_spec_l0, + sizeof(struct h264_decode_buf_spec)); + dev_dbg(&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx c_dma_addr 0x%llx\n", + buf_spec_l1->dpb->top_field_order_cnt, + buf_spec_l1->canvas_pos, + buf_spec_l1->y_dma_addr, + buf_spec_l1->c_dma_addr); + } + } +} + +static int get_poc_by_canvas_pos(struct aml_h264_ctx *h264_ctx, int canvas_pos) +{ + int i; + + for (i = 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + if (h264_ctx->ref_canvas[i].canvas_pos == canvas_pos) + return h264_ctx->ref_canvas[i].poc; + } + return -1; +} + +static struct v4l2_h264_dpb_entry *get_dpb_by_poc(struct v4l2_ctrl_h264_decode_params *decode, + int poc) +{ + int i; + + for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { + if (decode->dpb[i].top_field_order_cnt == poc) + return &decode->dpb[i]; + } + return NULL; +} + +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 int canvas_adr; + unsigned int ref_cfg; + unsigned int ref_cfg_once = 0; + struct slice *curr_slice = &h264_ctx->mslice; + unsigned int type_cfg = 0x3; /* 0x3: frame type */ + unsigned int colocate_adr_offset = 0; + unsigned int 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; + u32 reg_val; + + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC_IDX_RESET, 0); + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC, decode->top_field_order_cnt); + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC, decode->top_field_order_cnt); + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC, decode->bottom_field_order_cnt); + regmap_write(hw->map[DOS_BUS], CURR_CANVAS_CTRL, h264_ctx->curr_spec.canvas_pos << 24); + regmap_read(hw->map[DOS_BUS], CURR_CANVAS_CTRL, &canvas_adr); + canvas_adr &= 0xffffff; + dev_dbg(hw->dev, "canvas_pos = %d canvas_adr 0x%x\n", + h264_ctx->curr_spec.canvas_pos, canvas_adr); + + regmap_write(hw->map[DOS_BUS], REC_CANVAS_ADDR, canvas_adr); + regmap_write(hw->map[DOS_BUS], DBKR_CANVAS_ADDR, canvas_adr); + regmap_write(hw->map[DOS_BUS], DBKW_CANVAS_ADDR, canvas_adr); + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_INDEX, 16); + + for (j = 0; j < (V4L2_H264_NUM_DPB_ENTRIES + 1); j++) { + int poc; + struct v4l2_h264_dpb_entry *dpb = NULL; + + info0 = 0; + info1 = 0; + info2 = 0; + + poc = get_poc_by_canvas_pos(h264_ctx, j); + if (poc == decode->top_field_order_cnt) { + info0 = 0xf480 | 0xf; + 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; + } else { + dpb = get_dpb_by_poc(decode, poc); + if (dpb && (dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) { + info0 = 0xf480; + if (dpb->bottom_field_order_cnt < + dpb->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)); + } + } + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info0); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info1); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info2); + } + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_INDEX, 0); + /* when frame width <= 256, Disable DDR_BYTE64_CACHE */ + if (ctx->pic_info.coded_width <= 256) { + regmap_update_bits(hw->map[DOS_BUS], IQIDCT_CONTROL, (1 << 16), (1 << 16)); + regmap_write(hw->map[DOS_BUS], DCAC_DDR_BYTE64_CTL, + (read_dos_reg(hw, DCAC_DDR_BYTE64_CTL) & (~0xf)) | 0xa); + } else { + regmap_update_bits(hw->map[DOS_BUS], IQIDCT_CONTROL, (1 << 16), 0); + regmap_write(hw->map[DOS_BUS], DCAC_DDR_BYTE64_CTL, + (read_dos_reg(hw, DCAC_DDR_BYTE64_CTL) & (~0xf))); + } + + ref_cfg = 0; + j = 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) { + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, + ref_cfg); + dev_dbg(hw->dev, "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++; + } + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + dev_dbg(hw->dev, "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++) { + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + } + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_INDEX, 8); + j = 0; + ref_cfg = 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) { + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + dev_dbg(hw->dev, "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++; + } + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + regmap_write(hw->map[DOS_BUS], 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; + + read_poll_timeout(read_dos_reg, reg_val, + !(reg_val & 0x800), + 10, 0, true, hw, H264_CO_MB_RW_CTL); + + /* col buf for curr frame */ + colocate_adr_offset = COL_SIZE_FOR_ONE_MB; + if (use_mode_8x8_flag) + colocate_adr_offset >>= 2; + colocate_adr_offset *= curr_slice->first_mb_in_slice; + + 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_adr_offset > h264_ctx->one_col_buf_size || + colocate_wr_adr + h264_ctx->one_col_buf_size > + h264_ctx->collated_cma_addr_end) { + dev_err(hw->dev, + "Error, colocate buf is not enough, index is %d\n", + h264_ctx->curr_spec.col_buf_index); + return -1; + } + regmap_write(hw->map[DOS_BUS], H264_CO_MB_WR_ADDR, + (colocate_wr_adr + colocate_adr_offset)); + dev_dbg(hw->dev, "col buffer addr = 0x%x col_buf_index %d\n", + (colocate_wr_adr + colocate_adr_offset), + h264_ctx->curr_spec.col_buf_index); + } else { + regmap_write(hw->map[DOS_BUS], H264_CO_MB_WR_ADDR, 0xffffffff); + dev_dbg(hw->dev, "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; + colocate_rd_adr_offset = COL_SIZE_FOR_ONE_MB; + if (use_mode_8x8_flag) + colocate_rd_adr_offset >>= 2; + + colocate_rd_adr_offset *= curr_slice->first_mb_in_slice; + 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) { + dev_err(hw->dev, + "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); + regmap_write(hw->map[DOS_BUS], H264_CO_MB_RD_ADDR, val); + } else { + dev_err + (hw->dev, + "Error, reference pic has no colocated buf poc %d\n", + curr_pic->poc); + return -1; + } + } + + return 0; +} + +static void get_canvas_index(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; + + config_decode_canvas(hw, &h264_ctx->curr_spec, + h264_ctx->mb_width, h264_ctx->mb_height); + if (h264_ctx->list_size[0] > 0) { + for (i = 0; i < h264_ctx->list_size[0]; i++) { + buf = &h264_ctx->ref_list0[i]; + config_decode_canvas(hw, buf, h264_ctx->mb_width, + h264_ctx->mb_height); + } + } + + if (h264_ctx->list_size[1] > 0) { + for (i = 0; i < h264_ctx->list_size[1]; i++) { + buf = &h264_ctx->ref_list1[i]; + config_decode_canvas(hw, buf, h264_ctx->mb_width, + h264_ctx->mb_height); + } + } +} + +static void release_canvas_index(struct aml_vdec_hw *hw, + struct h264_decode_buf_spec *buf) +{ + if (buf->y_canvas_index >= 0) { + dev_dbg(hw->dev, "free y_canvas %d\n", buf->y_canvas_index); + meson_canvas_free(hw->canvas, buf->y_canvas_index); + buf->y_canvas_index = -1; + } + + if (buf->u_canvas_index >= 0) { + dev_dbg(hw->dev, "free uv_canvas_index %d\n", + buf->u_canvas_index); + meson_canvas_free(hw->canvas, buf->u_canvas_index); + buf->u_canvas_index = -1; + buf->v_canvas_index = -1; + } +} + +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; + + release_canvas_index(hw, &h264_ctx->curr_spec); + + 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->dpb = NULL; + release_canvas_index(hw, buf); + 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->dpb = NULL; + release_canvas_index(hw, buf); + buf->used = 0; + } + } + h264_ctx->list_size[1] = 0; + } +} + +static void save_reg_status(struct aml_h264_ctx *h264_ctx) +{ + struct aml_vdec_ctx *ctx = h264_ctx->v4l2_ctx; + struct aml_vdec_hw *hw = vdec_get_hw(ctx->dev); + + regmap_read(hw->map[DOS_BUS], IQIDCT_CONTROL, &h264_ctx->reg_iqidct_control); + h264_ctx->reg_iqidct_control_init_flag = 1; + regmap_read(hw->map[DOS_BUS], VCOP_CTRL_REG, &h264_ctx->reg_vcop_ctrl_reg); + regmap_read(hw->map[DOS_BUS], RV_AI_MB_COUNT, &h264_ctx->reg_rv_ai_mb_count); + regmap_read(hw->map[DOS_BUS], VLD_DECODE_CONTROL, &h264_ctx->vld_dec_control); +} + +static void h264_get_slice_params(struct aml_h264_ctx *h264_ctx) +{ + struct slice *curr_slice = &h264_ctx->mslice; + + memset(curr_slice, 0, sizeof(struct slice)); + /* parsed by ucode */ + switch (h264_ctx->dpb_param.l.data[SLICE_TYPE]) { + case I_Slice: + curr_slice->slice_type = I_SLICE; + break; + case P_Slice: + curr_slice->slice_type = P_SLICE; + break; + case B_Slice: + curr_slice->slice_type = B_SLICE; + break; + default: + curr_slice->slice_type = MAX_SLICE_TYPES; + break; + } + + curr_slice->first_mb_in_slice = + h264_ctx->dpb_param.l.data[FIRST_MB_IN_SLICE]; + 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; +} + +static irqreturn_t h264_isr(int irq, void *priv) +{ + struct aml_vdec_dev *dev = (struct aml_vdec_dev *)priv; + + regmap_write(dev->dec_hw->map[DOS_BUS], VDEC_ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t h264_threaded_isr_func(int irq, void *priv) +{ + u32 dec_status; + 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 = vdec_get_hw(ctx->dev); + unsigned short *p = (unsigned short *)h264_ctx->lmem_addr; + int i, ii; + + regmap_read(hw->map[DOS_BUS], DPB_STATUS_REG, &dec_status); + h264_ctx->dec_status = dec_status; + dev_dbg + (&dev->plat_dev->dev, + "%s, dec_status 0x%x VIFF_BIT_CNT 0x%x MBY_MBX 0x%x VLD_SHIFT_STATUS 0x%x\n", + __func__, dec_status, read_dos_reg(hw, VIFF_BIT_CNT), + read_dos_reg(hw, MBY_MBX), read_dos_reg(hw, VLD_SHIFT_STATUS)); + + regmap_read(hw->map[DOS_BUS], AV_SCRATCH_F, &h264_ctx->save_avscratch_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); + h264_get_slice_params(h264_ctx); + if (h264_ctx->mslice.first_mb_in_slice != 0) + h264_release_decode_spec(hw, ctx); + + h264_config_decode_spec(hw, ctx); + h264_reorder_reflists(h264_ctx); + get_canvas_index(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) { + regmap_write(hw->map[DOS_BUS], DPB_STATUS_REG, H264_ACTION_DECODE_NEWPIC); + dev_dbg(&dev->plat_dev->dev, "action decode new pic\n"); + h264_ctx->new_pic_flag = 0; + } else { + regmap_write(hw->map[DOS_BUS], DPB_STATUS_REG, H264_ACTION_DECODE_SLICE); + dev_dbg(&dev->plat_dev->dev, "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; + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + VB2_BUF_STATE_DONE); + wake_up_interruptible(&ctx->queue); + break; + default: + h264_release_decode_spec(hw, ctx); + ctx->int_cond = 1; + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + VB2_BUF_STATE_ERROR); + 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; + struct aml_vdec_hw *hw = vdec_get_hw(ctx->dev); + + regmap_write(hw->map[DOS_BUS], POWER_CTL_VLD, + (read_dos_reg(hw, POWER_CTL_VLD) | (0 << 10) | (1 << 9) | (1 << 6))); + + regmap_write(hw->map[DOS_BUS], PSCALE_CTRL, 0); + + /* clear mailbox interrupt */ + regmap_write(hw->map[DOS_BUS], VDEC_ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + regmap_write(hw->map[DOS_BUS], VDEC_ASSIST_MBOX1_MASK, 1); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 17), (1 << 17)); + if (ctx->dec_fmt[AML_FMT_DST].fourcc == V4L2_PIX_FMT_NV21 || + ctx->dec_fmt[AML_FMT_DST].fourcc == V4L2_PIX_FMT_NV21M) + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, + (1 << 16), (1 << 16)); + else + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 16), 0); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, + (0xbf << 24), (0xbf << 24)); + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (0xbf << 24), 0); + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 31), 0); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_MUX_CTRL, (1 << 31), 0); + regmap_write(hw->map[DOS_BUS], MDEC_EXTIF_CFG1, 0); + regmap_write(hw->map[DOS_BUS], MDEC_PIC_DC_THRESH, 0x404038aa); + + regmap_write(hw->map[DOS_BUS], DPB_STATUS_REG, 0); + + regmap_write(hw->map[DOS_BUS], LMEM_DUMP_ADR, h264_ctx->lmem_phy_addr); + regmap_write(hw->map[DOS_BUS], FRAME_COUNTER_REG, h264_ctx->decode_pic_count); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_8, h264_ctx->workspace_offset); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_G, h264_ctx->mc_cpu_paddr); + + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_F, + ((h264_ctx->save_avscratch_f & 0xffffffc3) | (1 << 4))); + regmap_update_bits(hw->map[DOS_BUS], AV_SCRATCH_F, (1 << 6), 0); + + regmap_write(hw->map[DOS_BUS], MDEC_PIC_DC_THRESH, 0x404038aa); + + if (h264_ctx->reg_iqidct_control_init_flag == 0) + regmap_write(hw->map[DOS_BUS], IQIDCT_CONTROL, 0x200); + + if (h264_ctx->reg_iqidct_control) + regmap_write(hw->map[DOS_BUS], IQIDCT_CONTROL, h264_ctx->reg_iqidct_control); + + if (h264_ctx->reg_vcop_ctrl_reg) + regmap_write(hw->map[DOS_BUS], VCOP_CTRL_REG, h264_ctx->reg_vcop_ctrl_reg); + + if (h264_ctx->vld_dec_control) + regmap_write(hw->map[DOS_BUS], VLD_DECODE_CONTROL, h264_ctx->vld_dec_control); + + dev_dbg + (hw->dev, + "IQIDCT_CONTROL = 0x%x, VCOP_CTRL_REG 0x%x VLD_DECODE_CONTROL 0x%x\n", + read_dos_reg(hw, IQIDCT_CONTROL), read_dos_reg(hw, VCOP_CTRL_REG), + read_dos_reg(hw, 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) { + dev_info(dec_hw->dev, "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; + + dev_dbg(dec_hw->dev, "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_obj(*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) { + dev_info(&ctx->dev->plat_dev->dev, + "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(dec_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_vdec_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx = (struct aml_h264_ctx *)ctx->codec_priv; + struct aml_vdec_hw *hw = vdec_get_hw(ctx->dev); + + regmap_write(hw->map[DOS_BUS], H264_DECODE_MODE, 0x1); /*decode mode framebase */ + regmap_write(hw->map[DOS_BUS], HEAD_PADDING_REG, 0); + regmap_write(hw->map[DOS_BUS], H264_DECODE_SEQINFO, h264_ctx->seq_info); + regmap_write(hw->map[DOS_BUS], 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; + struct aml_vdec_hw *dec_hw = vdec_get_hw(ctx->dev); + int ret = -1; + int i; + + ret = aml_h264_get_stateless_ctrl_ref(h264_ctx); + if (ret < 0) { + dev_err(&ctx->dev->plat_dev->dev, "not ctrl ref for h264 decoder\n"); + return ret; + } + + h264_ctx->new_pic_flag = 1; + 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(ctx); + /* enable stream input hardware */ + regmap_update_bits(dec_hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 0x6, 0x6); + /* enable hardware timer */ + regmap_write(dec_hw->map[DOS_BUS], NAL_SEARCH_CTL, + read_dos_reg(dec_hw, NAL_SEARCH_CTL) | (1 << 16)); + regmap_write(dec_hw->map[DOS_BUS], MDEC_EXTIF_CFG2, + read_dos_reg(dec_hw, MDEC_EXTIF_CFG2) | 0x20); + regmap_write(dec_hw->map[DOS_BUS], NAL_SEARCH_CTL, + read_dos_reg(dec_hw, NAL_SEARCH_CTL) & (~0x2)); + regmap_update_bits(dec_hw->map[DOS_BUS], VDEC_ASSIST_MMC_CTRL1, + (1 << 3), 0); + + aml_start_vdec_hw(dec_hw); + h264_ctx->init_flag = 1; + + regmap_write(dec_hw->map[DOS_BUS], 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; + dev_err(&ctx->dev->plat_dev->dev, "dec timeout=%u\n", DECODER_TIMEOUT_MS); + for (i = 0; i < 16; i++) { /* 16 : show ucode PC 16 times when timeout */ + dev_info(&ctx->dev->plat_dev->dev, "decoder timeout, pc 0x%x\n", + read_dos_reg(dec_hw, MPC_E)); + usleep_range(10, 20); + } + h264_release_decode_spec(dec_hw, ctx); + } else if (-ERESTARTSYS == ret) { + ret = -1; + h264_release_decode_spec(dec_hw, ctx); + dev_err(&ctx->dev->plat_dev->dev, "dec inter fail\n"); + } + + aml_stop_vdec_hw(dec_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..830ab3241a1e
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/h264.h@@ -0,0 +1,299 @@ +/* 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 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 REF_IDC_OVERRIDE_FLAG 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..ea50018a078d
--- /dev/null
+++ b/drivers/media/platform/amlogic/vdec/reg_defines.h@@ -0,0 +1,177 @@ +/* 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 REG_ALIGN(x) ((x) << 2) + +#define VDEC_ASSIST_MMC_CTRL0 REG_ALIGN(0x0001) +#define VDEC_ASSIST_MMC_CTRL1 REG_ALIGN(0x0002) + +#define VDEC_ASSIST_CANVAS_BLK32 REG_ALIGN(0x0005) + +#define VDEC_ASSIST_MBOX1_CLR_REG REG_ALIGN(0x0075) +#define VDEC_ASSIST_MBOX1_MASK REG_ALIGN(0x0076) + +#define MPSR REG_ALIGN(0x0301) +#define MPC_P REG_ALIGN(0x0306) +#define MPC_D REG_ALIGN(0x0307) +#define MPC_E REG_ALIGN(0x0308) +#define MPC_W REG_ALIGN(0x0309) +#define CPSR REG_ALIGN(0x0321) +#define IMEM_DMA_CTRL REG_ALIGN(0x0340) +#define IMEM_DMA_ADR REG_ALIGN(0x0341) +#define IMEM_DMA_COUNT REG_ALIGN(0x0342) +#define WRRSP_IMEM REG_ALIGN(0x0343) +#define LMEM_DMA_CTRL REG_ALIGN(0x0350) +#define WRRSP_LMEM REG_ALIGN(0x0353) + +#define PSCALE_CTRL REG_ALIGN(0x0911) +#define GCLK_EN REG_ALIGN(0x0983) +#define MDEC_PIC_DC_CTRL REG_ALIGN(0x098e) +#define MDEC_PIC_DC_MUX_CTRL REG_ALIGN(0x098d) +#define ANC0_CANVAS_ADDR REG_ALIGN(0x0990) +#define ANC1_CANVAS_ADDR REG_ALIGN(0x0991) +#define ANC2_CANVAS_ADDR REG_ALIGN(0x0992) +#define ANC3_CANVAS_ADDR REG_ALIGN(0x0993) +#define ANC4_CANVAS_ADDR REG_ALIGN(0x0994) +#define ANC5_CANVAS_ADDR REG_ALIGN(0x0995) +#define ANC6_CANVAS_ADDR REG_ALIGN(0x0996) +#define ANC7_CANVAS_ADDR REG_ALIGN(0x0997) +#define ANC8_CANVAS_ADDR REG_ALIGN(0x0998) +#define ANC9_CANVAS_ADDR REG_ALIGN(0x0999) +#define ANC10_CANVAS_ADDR REG_ALIGN(0x099a) +#define ANC11_CANVAS_ADDR REG_ALIGN(0x099b) +#define ANC12_CANVAS_ADDR REG_ALIGN(0x099c) +#define ANC13_CANVAS_ADDR REG_ALIGN(0x099d) +#define ANC14_CANVAS_ADDR REG_ALIGN(0x099e) +#define ANC15_CANVAS_ADDR REG_ALIGN(0x099f) +#define ANC16_CANVAS_ADDR REG_ALIGN(0x09a0) +#define ANC17_CANVAS_ADDR REG_ALIGN(0x09a1) +#define ANC18_CANVAS_ADDR REG_ALIGN(0x09a2) +#define ANC19_CANVAS_ADDR REG_ALIGN(0x09a3) +#define ANC20_CANVAS_ADDR REG_ALIGN(0x09a4) +#define ANC21_CANVAS_ADDR REG_ALIGN(0x09a5) +#define ANC22_CANVAS_ADDR REG_ALIGN(0x09a6) +#define ANC23_CANVAS_ADDR REG_ALIGN(0x09a7) +#define ANC24_CANVAS_ADDR REG_ALIGN(0x09a8) +#define ANC25_CANVAS_ADDR REG_ALIGN(0x09a9) +#define ANC26_CANVAS_ADDR REG_ALIGN(0x09aa) +#define ANC27_CANVAS_ADDR REG_ALIGN(0x09ab) +#define ANC28_CANVAS_ADDR REG_ALIGN(0x09ac) +#define ANC29_CANVAS_ADDR REG_ALIGN(0x09ad) +#define ANC30_CANVAS_ADDR REG_ALIGN(0x09ae) +#define ANC31_CANVAS_ADDR REG_ALIGN(0x09af) +#define DBKR_CANVAS_ADDR REG_ALIGN(0x09b0) +#define DBKW_CANVAS_ADDR REG_ALIGN(0x09b1) +#define REC_CANVAS_ADDR REG_ALIGN(0x09b2) +#define CURR_CANVAS_CTRL REG_ALIGN(0x09b3) +#define MDEC_PIC_DC_THRESH REG_ALIGN(0x09b8) +#define AV_SCRATCH_0 REG_ALIGN(0x09c0) +#define AV_SCRATCH_1 REG_ALIGN(0x09c1) +#define AV_SCRATCH_2 REG_ALIGN(0x09c2) +#define AV_SCRATCH_3 REG_ALIGN(0x09c3) +#define AV_SCRATCH_4 REG_ALIGN(0x09c4) +#define AV_SCRATCH_5 REG_ALIGN(0x09c5) +#define AV_SCRATCH_6 REG_ALIGN(0x09c6) +#define AV_SCRATCH_7 REG_ALIGN(0x09c7) +#define AV_SCRATCH_8 REG_ALIGN(0x09c8) +#define AV_SCRATCH_9 REG_ALIGN(0x09c9) +#define AV_SCRATCH_A REG_ALIGN(0x09ca) +#define AV_SCRATCH_B REG_ALIGN(0x09cb) +#define AV_SCRATCH_C REG_ALIGN(0x09cc) +#define AV_SCRATCH_D REG_ALIGN(0x09cd) +#define AV_SCRATCH_E REG_ALIGN(0x09ce) +#define AV_SCRATCH_F REG_ALIGN(0x09cf) +#define AV_SCRATCH_G REG_ALIGN(0x09d0) +#define AV_SCRATCH_H REG_ALIGN(0x09d1) +#define AV_SCRATCH_I REG_ALIGN(0x09d2) +#define AV_SCRATCH_J REG_ALIGN(0x09d3) +#define AV_SCRATCH_K REG_ALIGN(0x09d4) +#define AV_SCRATCH_L REG_ALIGN(0x09d5) +#define AV_SCRATCH_M REG_ALIGN(0x09d6) +#define AV_SCRATCH_N REG_ALIGN(0x09d7) +#define WRRSP_VLD REG_ALIGN(0x09da) +#define MDEC_DOUBLEW_CFG0 REG_ALIGN(0x09db) +#define MDEC_DOUBLEW_CFG1 REG_ALIGN(0x09dc) +#define MDEC_DOUBLEW_CFG2 REG_ALIGN(0x09dd) +#define MDEC_DOUBLEW_CFG3 REG_ALIGN(0x09de) +#define MDEC_DOUBLEW_CFG4 REG_ALIGN(0x09df) +#define MDEC_DOUBLEW_CFG5 REG_ALIGN(0x09e0) +#define MDEC_DOUBLEW_CFG6 REG_ALIGN(0x09e1) +#define MDEC_DOUBLEW_CFG7 REG_ALIGN(0x09e2) +#define MDEC_DOUBLEW_STATUS REG_ALIGN(0x09e3) +#define MDEC_EXTIF_CFG0 REG_ALIGN(0x09e4) + +#define MDEC_EXTIF_CFG1 REG_ALIGN(0x09e5) +#define MDEC_EXTIF_CFG2 REG_ALIGN(0x09e6) + +#define POWER_CTL_VLD REG_ALIGN(0x0c08) +#define VLD_DECODE_CONTROL REG_ALIGN(0x0c18) + +#define PMV1_X REG_ALIGN(0x0c20) +#define PMV1_Y REG_ALIGN(0x0c21) +#define PMV2_X REG_ALIGN(0x0c22) +#define PMV2_Y REG_ALIGN(0x0c23) +#define PMV3_X REG_ALIGN(0x0c24) +#define PMV3_Y REG_ALIGN(0x0c25) +#define PMV4_X REG_ALIGN(0x0c26) +#define PMV4_Y REG_ALIGN(0x0c27) +#define M4_TABLE_SELECT REG_ALIGN(0x0c28) +#define M4_CONTROL_REG REG_ALIGN(0x0c29) +#define BLOCK_NUM REG_ALIGN(0x0c2a) +#define PATTERN_CODE REG_ALIGN(0x0c2b) +#define MB_INFO REG_ALIGN(0x0c2c) +#define VLD_DC_PRED REG_ALIGN(0x0c2d) +#define VLD_ERROR_MASK REG_ALIGN(0x0c2e) +#define VLD_DC_PRED_C REG_ALIGN(0x0c2f) +#define LAST_SLICE_MV_ADDR REG_ALIGN(0x0c30) +#define LAST_MVX REG_ALIGN(0x0c31) +#define LAST_MVY REG_ALIGN(0x0c32) + +#define MB_MOTION_MODE REG_ALIGN(0x0c07) +#define VIFF_BIT_CNT REG_ALIGN(0x0c1a) +#define M4_CONTROL_REG REG_ALIGN(0x0c29) +#define VLD_C38 REG_ALIGN(0x0c38) +#define VLD_C39 REG_ALIGN(0x0c39) +#define VLD_SHIFT_STATUS REG_ALIGN(0x0c3b) +#define VLD_C3D REG_ALIGN(0x0c3d) +#define VLD_MEM_VIFIFO_START_PTR REG_ALIGN(0x0c40) +#define VLD_MEM_VIFIFO_CURR_PTR REG_ALIGN(0x0c41) +#define VLD_MEM_VIFIFO_END_PTR REG_ALIGN(0x0c42) +#define VLD_MEM_VIFIFO_BYTES_AVAIL REG_ALIGN(0x0c43) +#define VLD_MEM_VIFIFO_CONTROL REG_ALIGN(0x0c44) +#define VLD_MEM_VIFIFO_WP REG_ALIGN(0x0c45) +#define VLD_MEM_VIFIFO_RP REG_ALIGN(0x0c46) +#define VLD_MEM_VIFIFO_LEVEL REG_ALIGN(0x0c47) +#define VLD_MEM_VIFIFO_BUF_CNTL REG_ALIGN(0x0c48) + +#define VCOP_CTRL_REG REG_ALIGN(0x0e00) +#define RV_AI_MB_COUNT REG_ALIGN(0x0e0c) +#define IQIDCT_CONTROL REG_ALIGN(0x0e0e) +#define DCAC_DDR_BYTE64_CTL REG_ALIGN(0x0e1d) + +#define VDEC2_IMEM_DMA_CTRL REG_ALIGN(0x2340) +#define VDEC2_IMEM_DMA_ADR REG_ALIGN(0x2341) +#define VDEC2_IMEM_DMA_COUNT REG_ALIGN(0x2342) + +#define DOS_SW_RESET0 REG_ALIGN(0x3f00) +#define DOS_GCLK_EN0 REG_ALIGN(0x3f01) +#define DOS_GCLK_EN1 REG_ALIGN(0x3f09) +#define DOS_GCLK_EN3 REG_ALIGN(0x3f35) + +#define DOS_MEM_PD_VDEC REG_ALIGN(0x3f30) +#define DOS_MEM_PD_VDEC2 REG_ALIGN(0x3f31) +#define DOS_MEM_PD_HCODEC REG_ALIGN(0x3f32) +/*add from M8M2*/ +#define DOS_MEM_PD_HEVC REG_ALIGN(0x3f33) + +#define DOS_SW_RESET3 REG_ALIGN(0x3f34) +#define DOS_GCLK_EN3 REG_ALIGN(0x3f35) +#define DOS_HEVC_INT_EN REG_ALIGN(0x3f36) + +#endif +
--
2.42.0