On Fri Jan 9 11:15:30 2026 -0500, Detlev Casanova wrote:
> This decoder variant is found in Rockchip RK3588 SoC family.
> 
> Like for rkvdec on rk3399, it supports the NV12, NV15, NV16 and NV20
> output formats and level up to 6.0.
> 
> The maximum width and height have been significantly increased
> supporting up to 65520 pixels for both.
> 
> Support for named register sections is added for this variant and future
> ones.
> 
> Fluster score for JVT-AVC_V1 is 129/135.
> 
> Tested-by: Diederik de Haas <[email protected]>  # Rock 5B
> Reviewed-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Detlev Casanova <[email protected]>
> Signed-off-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/platform/rockchip/rkvdec/Makefile    |   1 +
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c | 469 +++++++++++++++++++++
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h | 430 +++++++++++++++++++
 drivers/media/platform/rockchip/rkvdec/rkvdec.c    | 158 ++++++-
 drivers/media/platform/rockchip/rkvdec/rkvdec.h    |  12 +
 5 files changed, 1067 insertions(+), 3 deletions(-)

---

diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile 
b/drivers/media/platform/rockchip/rkvdec/Makefile
index 3d75103e536d..7bfd95151e40 100644
--- a/drivers/media/platform/rockchip/rkvdec/Makefile
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
@@ -8,4 +8,5 @@ rockchip-vdec-y += \
                   rkvdec-hevc.o \
                   rkvdec-hevc-common.o \
                   rkvdec-rcb.o \
+                  rkvdec-vdpu381-h264.o \
                   rkvdec-vp9.o
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c 
b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
new file mode 100644
index 000000000000..cd0aa3f3a13d
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip VDPU381 Video Decoder H264 backend
+ *
+ * Copyright (C) 2024 Collabora, Ltd.
+ *  Detlev Casanova <[email protected]>
+ */
+
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-cabac.h"
+#include "rkvdec-rcb.h"
+#include "rkvdec-h264-common.h"
+#include "rkvdec-vdpu381-regs.h"
+
+struct rkvdec_sps {
+       u16 seq_parameter_set_id:                       4;
+       u16 profile_idc:                                8;
+       u16 constraint_set3_flag:                       1;
+       u16 chroma_format_idc:                          2;
+       u16 bit_depth_luma:                             3;
+       u16 bit_depth_chroma:                           3;
+       u16 qpprime_y_zero_transform_bypass_flag:       1;
+       u16 log2_max_frame_num_minus4:                  4;
+       u16 max_num_ref_frames:                         5;
+       u16 pic_order_cnt_type:                         2;
+       u16 log2_max_pic_order_cnt_lsb_minus4:          4;
+       u16 delta_pic_order_always_zero_flag:           1;
+       u16 pic_width_in_mbs:                           12;
+       u16 pic_height_in_mbs:                          12;
+       u16 frame_mbs_only_flag:                        1;
+       u16 mb_adaptive_frame_field_flag:               1;
+       u16 direct_8x8_inference_flag:                  1;
+       u16 mvc_extension_enable:                       1;
+       u16 num_views:                                  2;
+
+       u16 reserved_bits:                              12;
+       u16 reserved[11];
+} __packed;
+
+struct rkvdec_pps {
+       u16 pic_parameter_set_id:                               8;
+       u16 pps_seq_parameter_set_id:                           5;
+       u16 entropy_coding_mode_flag:                           1;
+       u16 bottom_field_pic_order_in_frame_present_flag:       1;
+       u16 num_ref_idx_l0_default_active_minus1:               5;
+       u16 num_ref_idx_l1_default_active_minus1:               5;
+       u16 weighted_pred_flag:                                 1;
+       u16 weighted_bipred_idc:                                2;
+       u16 pic_init_qp_minus26:                                7;
+       u16 pic_init_qs_minus26:                                6;
+       u16 chroma_qp_index_offset:                             5;
+       u16 deblocking_filter_control_present_flag:             1;
+       u16 constrained_intra_pred_flag:                        1;
+       u16 redundant_pic_cnt_present:                          1;
+       u16 transform_8x8_mode_flag:                            1;
+       u16 second_chroma_qp_index_offset:                      5;
+       u16 scaling_list_enable_flag:                           1;
+       u32 scaling_list_address;
+       u16 is_longterm;
+
+       u8 reserved[3];
+} __packed;
+
+struct rkvdec_sps_pps {
+       struct rkvdec_sps sps;
+       struct rkvdec_pps pps;
+} __packed;
+
+/* Data structure describing auxiliary buffer format. */
+struct rkvdec_h264_priv_tbl {
+       s8 cabac_table[4][464][2];
+       struct rkvdec_h264_scaling_list scaling_list;
+       struct rkvdec_sps_pps param_set[256];
+       struct rkvdec_rps rps;
+};
+
+struct rkvdec_h264_ctx {
+       struct rkvdec_aux_buf priv_tbl;
+       struct rkvdec_h264_reflists reflists;
+       struct rkvdec_vdpu381_regs_h264 regs;
+};
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+                           struct rkvdec_h264_run *run)
+{
+       struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+       const struct v4l2_ctrl_h264_sps *sps = run->sps;
+       const struct v4l2_ctrl_h264_pps *pps = run->pps;
+       const struct v4l2_ctrl_h264_decode_params *dec_params = 
run->decode_params;
+       const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+       struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
+       struct rkvdec_sps_pps *hw_ps;
+       dma_addr_t scaling_list_address;
+       u32 scaling_distance;
+       u32 i;
+
+       /*
+        * HW read the SPS/PPS information from PPS packet index by PPS id.
+        * offset from the base can be calculated by PPS_id * 32 (size per PPS
+        * packet unit). so the driver copy SPS/PPS information to the exact PPS
+        * packet unit for HW accessing.
+        */
+       hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
+       memset(hw_ps, 0, sizeof(*hw_ps));
+
+       /* write sps */
+       hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id;
+       hw_ps->sps.profile_idc = sps->profile_idc;
+       hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 
3));
+       hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
+       hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8;
+       hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8;
+       hw_ps->sps.qpprime_y_zero_transform_bypass_flag =
+               !!(sps->flags & 
V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
+       hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
+       hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames;
+       hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type;
+       hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 =
+               sps->log2_max_pic_order_cnt_lsb_minus4;
+       hw_ps->sps.delta_pic_order_always_zero_flag =
+               !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
+       hw_ps->sps.mvc_extension_enable = 1;
+       hw_ps->sps.num_views = 1;
+
+       /*
+        * Use the SPS values since they are already in macroblocks
+        * dimensions, height can be field height (halved) if
+        * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
+        * decoding smaller images into larger allocation which can be used
+        * to implementing SVC spatial layer support.
+        */
+       hw_ps->sps.pic_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1;
+       hw_ps->sps.pic_height_in_mbs = sps->pic_height_in_map_units_minus1 + 1;
+       hw_ps->sps.frame_mbs_only_flag =
+               !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
+       hw_ps->sps.mb_adaptive_frame_field_flag =
+               !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
+       hw_ps->sps.direct_8x8_inference_flag =
+               !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
+
+       /* write pps */
+       hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id;
+       hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id;
+       hw_ps->pps.entropy_coding_mode_flag =
+               !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
+       hw_ps->pps.bottom_field_pic_order_in_frame_present_flag =
+               !!(pps->flags & 
V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
+       hw_ps->pps.num_ref_idx_l0_default_active_minus1 =
+               pps->num_ref_idx_l0_default_active_minus1;
+       hw_ps->pps.num_ref_idx_l1_default_active_minus1 =
+               pps->num_ref_idx_l1_default_active_minus1;
+       hw_ps->pps.weighted_pred_flag =
+               !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
+       hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc;
+       hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26;
+       hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26;
+       hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset;
+       hw_ps->pps.deblocking_filter_control_present_flag =
+               !!(pps->flags & 
V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
+       hw_ps->pps.constrained_intra_pred_flag =
+               !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
+       hw_ps->pps.redundant_pic_cnt_present =
+               !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
+       hw_ps->pps.transform_8x8_mode_flag =
+               !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
+       hw_ps->pps.second_chroma_qp_index_offset = 
pps->second_chroma_qp_index_offset;
+       hw_ps->pps.scaling_list_enable_flag =
+               !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT);
+
+       /*
+        * To be on the safe side, program the scaling matrix address
+        */
+       scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
+       scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance;
+       hw_ps->pps.scaling_list_address = scaling_list_address;
+
+       for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+               if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+                       hw_ps->pps.is_longterm |= (1 << i);
+       }
+}
+
+static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
+{
+       struct rkvdec_dev *rkvdec = ctx->dev;
+       struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+
+       rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
+                          &h264_ctx->regs.common,
+                          sizeof(h264_ctx->regs.common));
+       rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
+                          &h264_ctx->regs.h264_param,
+                          sizeof(h264_ctx->regs.h264_param));
+       rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
+                          &h264_ctx->regs.common_addr,
+                          sizeof(h264_ctx->regs.common_addr));
+       rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
+                          &h264_ctx->regs.h264_addr,
+                          sizeof(h264_ctx->regs.h264_addr));
+       rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
+                          &h264_ctx->regs.h264_highpoc,
+                          sizeof(h264_ctx->regs.h264_highpoc));
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+                            struct rkvdec_h264_run *run)
+{
+       const struct v4l2_ctrl_h264_decode_params *dec_params = 
run->decode_params;
+       const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+       struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+       dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
+       const struct v4l2_pix_format_mplane *dst_fmt;
+       struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+       struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+       struct rkvdec_vdpu381_regs_h264 *regs = &h264_ctx->regs;
+       const struct v4l2_format *f;
+       dma_addr_t rlc_addr;
+       dma_addr_t dst_addr;
+       u32 hor_virstride;
+       u32 ver_virstride;
+       u32 y_virstride;
+       u32 offset;
+       u32 pixels;
+       u32 i;
+
+       memset(regs, 0, sizeof(*regs));
+
+       /* Set H264 mode */
+       regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_H264;
+
+       /* Set config */
+       regs->common.reg011_important_en.buf_empty_en = 1;
+       regs->common.reg011_important_en.dec_clkgate_e = 1;
+       regs->common.reg011_important_en.dec_timeout_e = 1;
+       regs->common.reg011_important_en.pix_range_det_e = 1;
+
+       /*
+        * Even though the scan list address can be set in RPS,
+        * with some frames, it will try to use the address set in the register.
+        */
+       regs->common.reg012_secondary_en.scanlist_addr_valid_en = 1;
+
+       /* Set IDR flag */
+       regs->common.reg013_en_mode_set.cur_pic_is_idr =
+               !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC);
+
+       /* Set input stream length */
+       regs->common.reg016_stream_len = 
vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+       /* Set max slice number */
+       regs->common.reg017_slice_number.slice_num = MAX_SLICE_NUMBER;
+
+       /* Set strides */
+       f = &ctx->decoded_fmt;
+       dst_fmt = &f->fmt.pix_mp;
+       hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+       ver_virstride = dst_fmt->height;
+       y_virstride = hor_virstride * ver_virstride;
+
+       regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16;
+       regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16;
+       regs->common.reg020_y_stride.y_virstride = y_virstride / 16;
+
+       /* Activate block gating */
+       regs->common.reg026_block_gating_en.inter_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0;
+       regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.intra_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.mc_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.transd_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.sram_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.cru_auto_gating_e = 1;
+       regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1;
+
+       /* Set timeout threshold */
+       pixels = dst_fmt->height * dst_fmt->width;
+       if (pixels < RKVDEC_1080P_PIXELS)
+               regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p;
+       else if (pixels < RKVDEC_4K_PIXELS)
+               regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K;
+       else if (pixels < RKVDEC_8K_PIXELS)
+               regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K;
+       else
+               regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX;
+
+       /* Set TOP and BOTTOM POCs */
+       regs->h264_param.reg065_cur_top_poc = dec_params->top_field_order_cnt;
+       regs->h264_param.reg066_cur_bot_poc = 
dec_params->bottom_field_order_cnt;
+
+       /* Set ref pic address & poc */
+       for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+               struct vb2_buffer *vb_buf = run->ref_buf[i];
+               dma_addr_t buf_dma;
+
+               /*
+                * If a DPB entry is unused or invalid, address of current 
destination
+                * buffer is returned.
+                */
+               if (!vb_buf)
+                       vb_buf = &dst_buf->vb2_buf;
+
+               buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+
+               /* Set reference addresses */
+               regs->h264_addr.reg164_180_ref_base[i] = buf_dma;
+
+               /* Set COLMV addresses */
+               regs->h264_addr.reg182_198_colmv_base[i] = buf_dma + 
ctx->colmv_offset;
+
+               struct rkvdec_vdpu381_h264_ref_info *ref_info =
+                       &regs->h264_param.reg099_102_ref_info_regs[i / 
4].ref_info[i % 4];
+
+               ref_info->ref_field =
+                       !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD);
+               ref_info->ref_colmv_use_flag =
+                       !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE);
+               ref_info->ref_topfield_used =
+                       !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF);
+               ref_info->ref_botfield_used =
+                       !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF);
+
+               regs->h264_param.reg067_098_ref_poc[i * 2] =
+                       dpb[i].top_field_order_cnt;
+               regs->h264_param.reg067_098_ref_poc[i * 2 + 1] =
+                       dpb[i].bottom_field_order_cnt;
+       }
+
+       /* Set rlc base address (input stream) */
+       rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+       regs->common_addr.rlc_base = rlc_addr;
+       regs->common_addr.rlcwrite_base = rlc_addr;
+
+       /* Set output base address */
+       dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+       regs->common_addr.decout_base = dst_addr;
+       regs->common_addr.error_ref_base = dst_addr;
+
+       /* Set colmv address */
+       regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset;
+
+       /* Set RCB addresses */
+       for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++)
+               regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i);
+
+       /* Set hw pps address */
+       offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
+       regs->h264_addr.reg161_pps_base = priv_start_addr + offset;
+
+       /* Set hw rps address */
+       offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
+       regs->h264_addr.reg163_rps_base = priv_start_addr + offset;
+
+       /* Set cabac table */
+       offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
+       regs->h264_addr.reg199_cabactbl_base = priv_start_addr + offset;
+
+       offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
+       regs->h264_addr.reg181_scanlist_addr = priv_start_addr + offset;
+
+       rkvdec_write_regs(ctx);
+}
+
+static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
+{
+       struct rkvdec_dev *rkvdec = ctx->dev;
+       struct rkvdec_h264_priv_tbl *priv_tbl;
+       struct rkvdec_h264_ctx *h264_ctx;
+       struct v4l2_ctrl *ctrl;
+       int ret;
+
+       ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+                             V4L2_CID_STATELESS_H264_SPS);
+       if (!ctrl)
+               return -EINVAL;
+
+       ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+       if (ret)
+               return ret;
+
+       h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL);
+       if (!h264_ctx)
+               return -ENOMEM;
+
+       priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+                                     &h264_ctx->priv_tbl.dma, GFP_KERNEL);
+       if (!priv_tbl) {
+               ret = -ENOMEM;
+               goto err_free_ctx;
+       }
+
+       h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
+       h264_ctx->priv_tbl.cpu = priv_tbl;
+       memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
+              sizeof(rkvdec_h264_cabac_table));
+
+       ctx->priv = h264_ctx;
+       return 0;
+
+err_free_ctx:
+       kfree(h264_ctx);
+       return ret;
+}
+
+static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
+{
+       struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+       struct rkvdec_dev *rkvdec = ctx->dev;
+
+       dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+                         h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
+       kfree(h264_ctx);
+}
+
+static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
+{
+       struct v4l2_h264_reflist_builder reflist_builder;
+       struct rkvdec_dev *rkvdec = ctx->dev;
+       struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+       struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
+       struct rkvdec_h264_run run;
+
+       rkvdec_h264_run_preamble(ctx, &run);
+
+       /* Build the P/B{0,1} ref lists. */
+       v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
+                                      run.sps, run.decode_params->dpb);
+       v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
+       v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
+                                   h264_ctx->reflists.b1);
+
+       assemble_hw_scaling_list(&run, &tbl->scaling_list);
+       assemble_hw_pps(ctx, &run);
+       lookup_ref_buf_idx(ctx, &run);
+       assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
+
+       config_registers(ctx, &run);
+
+       rkvdec_run_postamble(ctx, &run.base);
+
+       rkvdec_schedule_watchdog(rkvdec, 
h264_ctx->regs.common.reg032_timeout_threshold);
+
+       /* Start decoding! */
+       writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
+
+       return 0;
+}
+
+static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+       if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
+               return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+
+       return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops = {
+       .adjust_fmt = rkvdec_h264_adjust_fmt,
+       .get_image_fmt = rkvdec_h264_get_image_fmt,
+       .start = rkvdec_h264_start,
+       .stop = rkvdec_h264_stop,
+       .run = rkvdec_h264_run,
+       .try_ctrl = rkvdec_h264_try_ctrl,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h 
b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h
new file mode 100644
index 000000000000..6da36031df2d
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h
@@ -0,0 +1,430 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip VDPU381 Video Decoder driver registers description
+ *
+ * Copyright (C) 2024 Collabora, Ltd.
+ *  Detlev Casanova <[email protected]>
+ */
+
+#include <linux/types.h>
+
+#ifndef _RKVDEC_REGS_H_
+#define _RKVDEC_REGS_H_
+
+#define OFFSET_COMMON_REGS             (8 * sizeof(u32))
+#define OFFSET_CODEC_PARAMS_REGS       (64 * sizeof(u32))
+#define OFFSET_COMMON_ADDR_REGS                (128 * sizeof(u32))
+#define OFFSET_CODEC_ADDR_REGS         (160 * sizeof(u32))
+#define OFFSET_POC_HIGHBIT_REGS                (200 * sizeof(u32))
+
+#define VDPU381_MODE_HEVC      0
+#define VDPU381_MODE_H264      1
+#define VDPU381_MODE_VP9       2
+#define VDPU381_MODE_AVS2      3
+
+#define MAX_SLICE_NUMBER       0x3fff
+
+#define RKVDEC_TIMEOUT_1080p           (0xefffff)
+#define RKVDEC_TIMEOUT_4K              (0x2cfffff)
+#define RKVDEC_TIMEOUT_8K              (0x4ffffff)
+#define RKVDEC_TIMEOUT_MAX             (0xffffffff)
+
+#define VDPU381_REG_DEC_E              0x028
+#define VDPU381_DEC_E_BIT              1
+
+#define VDPU381_REG_IMPORTANT_EN       0x02c
+#define VDPU381_DEC_IRQ_DISABLE        BIT(4)
+
+#define VDPU381_REG_STA_INT            0x380
+#define VDPU381_STA_INT_DEC_RDY_STA    BIT(2)
+#define VDPU381_STA_INT_ERROR          BIT(4)
+#define VDPU381_STA_INT_TIMEOUT                BIT(5)
+#define VDPU381_STA_INT_SOFTRESET_RDY  BIT(9)
+
+/* base: OFFSET_COMMON_REGS */
+struct rkvdec_vdpu381_regs_common {
+       struct {
+               u32 in_endian           : 1;
+               u32 in_swap32_e         : 1;
+               u32 in_swap64_e         : 1;
+               u32 str_endian          : 1;
+               u32 str_swap32_e        : 1;
+               u32 str_swap64_e        : 1;
+               u32 out_endian          : 1;
+               u32 out_swap32_e        : 1;
+               u32 out_cbcr_swap       : 1;
+               u32 out_swap64_e        : 1;
+               u32 reserved            : 22;
+       } reg008_in_out;
+
+       struct {
+               u32 dec_mode    : 10;
+               u32 reserved    : 22;
+       } reg009_dec_mode;
+
+       struct {
+               u32 dec_e       : 1;
+               u32 reserved    : 31;
+       } reg010_dec_e;
+
+       struct {
+               u32 reserved0                   : 1;
+               u32 dec_clkgate_e               : 1;
+               u32 dec_e_strmd_clkgate_dis     : 1;
+               u32 reserved1                   : 1;
+
+               u32 dec_irq_dis                 : 1;
+               u32 dec_timeout_e               : 1;
+               u32 buf_empty_en                : 1;
+               u32 reserved2                   : 3;
+
+               u32 dec_e_rewrite_valid         : 1;
+               u32 reserved3                   : 9;
+               u32 softrst_en_p                : 1;
+               u32 force_softreset_valid       : 1;
+               u32 reserved4                   : 2;
+               u32 pix_range_det_e             : 1;
+               u32 reserved5                   : 7;
+       } reg011_important_en;
+
+       struct {
+               u32 reserved0                   : 1;
+               u32 colmv_compress_en           : 1;
+               u32 fbc_e                       : 1;
+               u32 reserved1                   : 1;
+
+               u32 buspr_slot_disable          : 1;
+               u32 error_info_en               : 1;
+               u32 collect_info_en             : 1;
+               u32 error_auto_rst_disable      : 1;
+
+               u32 scanlist_addr_valid_en      : 1;
+               u32 scale_down_en               : 1;
+               u32 error_cfg_wr_disable        : 1;
+               u32 reserved2                   : 21;
+       } reg012_secondary_en;
+
+       struct {
+               u32 reserved0                   : 1;
+               u32 req_timeout_rst_sel         : 1;
+               u32 reserved1                   : 1;
+               u32 dec_commonirq_mode          : 1;
+               u32 reserved2                   : 2;
+               u32 stmerror_waitdecfifo_empty  : 1;
+               u32 reserved3                   : 5;
+               u32 allow_not_wr_unref_bframe   : 1;
+               u32 fbc_output_wr_disable       : 1;
+               u32 reserved4                   : 4;
+               u32 error_mode                  : 1;
+               u32 reserved5                   : 2;
+               u32 ycacherd_prior              : 1;
+               u32 reserved6                   : 2;
+               u32 cur_pic_is_idr              : 1;
+               u32 reserved7                   : 1;
+               u32 right_auto_rst_disable      : 1;
+               u32 frame_end_err_rst_flag      : 1;
+               u32 rd_prior_mode               : 1;
+               u32 rd_ctrl_prior_mode          : 1;
+               u32 reserved8                   : 1;
+               u32 filter_outbuf_mode          : 1;
+       } reg013_en_mode_set;
+
+       struct {
+               u32 fbc_force_uncompress        : 1;
+
+               u32 reserved0                   : 2;
+               u32 allow_16x8_cp_flag          : 1;
+               u32 reserved1                   : 2;
+
+               u32 fbc_h264_exten_4or8_flag    : 1;
+               u32 reserved2                   : 25;
+       } reg014_fbc_param_set;
+
+       struct {
+               u32 rlc_mode_direct_write       : 1;
+               u32 rlc_mode                    : 1;
+               u32 reserved0                   : 3;
+
+               u32 strm_start_bit              : 7;
+               u32 reserved1                   : 20;
+       } reg015_stream_param_set;
+
+       u32 reg016_stream_len;
+
+       struct {
+               u32 slice_num   : 25;
+               u32 reserved    : 7;
+       } reg017_slice_number;
+
+       struct {
+               u32 y_hor_virstride     : 16;
+               u32 reserved            : 16;
+       } reg018_y_hor_stride;
+
+       struct {
+               u32 uv_hor_virstride    : 16;
+               u32 reserved            : 16;
+       } reg019_uv_hor_stride;
+
+       struct {
+               u32 y_virstride         : 28;
+               u32 reserved            : 4;
+       } reg020_y_stride;
+
+       struct {
+               u32 inter_error_prc_mode                : 1;
+               u32 error_intra_mode                    : 1;
+               u32 error_deb_en                        : 1;
+               u32 picidx_replace                      : 5;
+               u32 error_spread_e                      : 1;
+               u32 reserved0                           : 3;
+               u32 error_inter_pred_cross_slice        : 1;
+               u32 reserved1                           : 11;
+               u32 roi_error_ctu_cal_en                : 1;
+               u32 reserved2                           : 7;
+       } reg021_error_ctrl_set;
+
+       struct {
+               u32 roi_x_ctu_offset_st : 12;
+               u32 reserved0           : 4;
+               u32 roi_y_ctu_offset_st : 12;
+               u32 reserved1           : 4;
+       } reg022_err_roi_ctu_offset_start;
+
+       struct {
+               u32 roi_x_ctu_offset_end        : 12;
+               u32 reserved0                   : 4;
+               u32 roi_y_ctu_offset_end        : 12;
+               u32 reserved1                   : 4;
+       } reg023_err_roi_ctu_offset_end;
+
+       struct {
+               u32 cabac_err_en_lowbits        : 32;
+       } reg024_cabac_error_en_lowbits;
+
+       struct {
+               u32 cabac_err_en_highbits       : 30;
+               u32 reserved                    : 2;
+       } reg025_cabac_error_en_highbits;
+
+       struct {
+               u32 inter_auto_gating_e         : 1;
+               u32 filterd_auto_gating_e       : 1;
+               u32 strmd_auto_gating_e         : 1;
+               u32 mcp_auto_gating_e           : 1;
+               u32 busifd_auto_gating_e        : 1;
+               u32 reserved0                   : 3;
+               u32 dec_ctrl_auto_gating_e      : 1;
+               u32 intra_auto_gating_e         : 1;
+               u32 mc_auto_gating_e            : 1;
+               u32 transd_auto_gating_e        : 1;
+               u32 reserved1                   : 4;
+               u32 sram_auto_gating_e          : 1;
+               u32 cru_auto_gating_e           : 1;
+               u32 reserved2                   : 13;
+               u32 reg_cfg_gating_en           : 1;
+       } reg026_block_gating_en;
+
+       struct {
+               u32 core_safe_x_pixels  : 16;
+               u32 core_safe_y_pixels  : 16;
+       } reg027_core_safe_pixels;
+
+       struct {
+               u32 vp9_wr_prob_idx             : 3;
+               u32 reserved0                   : 1;
+               u32 vp9_rd_prob_idx             : 3;
+               u32 reserved1                   : 1;
+
+               u32 ref_req_advance_flag        : 1;
+               u32 colmv_req_advance_flag      : 1;
+               u32 poc_only_highbit_flag       : 1;
+               u32 poc_arb_flag                : 1;
+
+               u32 reserved2                   : 4;
+               u32 film_idx                    : 10;
+               u32 reserved3                   : 2;
+               u32 pu_req_mismatch_dis         : 1;
+               u32 colmv_req_mismatch_dis      : 1;
+               u32 reserved4                   : 2;
+       } reg028_multiply_core_ctrl;
+
+       struct {
+               u32 scale_down_hor_ratio        : 2;
+               u32 reserved0                   : 6;
+               u32 scale_down_vrz_ratio        : 2;
+               u32 reserved1                   : 22;
+       } reg029_scale_down_ctrl;
+
+       struct {
+               u32 y_scale_down_tile8x8_hor_stride     : 20;
+               u32 reserved0                           : 12;
+       } reg030_y_scale_down_tile8x8_hor_stride;
+
+       struct {
+               u32 uv_scale_down8x8_tile_hor_stride    : 20;
+               u32 reserved0                           : 12;
+       } reg031_uv_scale_down_tile8x8_hor_stride;
+
+       u32 reg032_timeout_threshold;
+} __packed;
+
+/* base: OFFSET_COMMON_ADDR_REGS */
+struct rkvdec_vdpu381_regs_common_addr {
+       u32 rlc_base;
+       u32 rlcwrite_base;
+       u32 decout_base;
+       u32 colmv_cur_base;
+       u32 error_ref_base;
+       u32 rcb_base[10];
+} __packed;
+
+struct rkvdec_vdpu381_h26x_set {
+       u32 h26x_frame_orslice          : 1;
+       u32 h26x_rps_mode               : 1;
+       u32 h26x_stream_mode            : 1;
+       u32 h26x_stream_lastpacket      : 1;
+       u32 h264_firstslice_flag        : 1;
+       u32 reserved                    : 27;
+} __packed;
+
+/* base: OFFSET_CODEC_PARAMS_REGS */
+struct rkvdec_vdpu381_regs_h264_params {
+       struct rkvdec_vdpu381_h26x_set reg064_h26x_set;
+
+       u32 reg065_cur_top_poc;
+       u32 reg066_cur_bot_poc;
+       u32 reg067_098_ref_poc[32];
+
+       struct rkvdec_vdpu381_h264_info {
+               struct rkvdec_vdpu381_h264_ref_info {
+                       u32 ref_field           : 1;
+                       u32 ref_topfield_used   : 1;
+                       u32 ref_botfield_used   : 1;
+                       u32 ref_colmv_use_flag  : 1;
+                       u32 reserved            : 4;
+               } __packed ref_info[4];
+       } __packed reg099_102_ref_info_regs[4];
+
+       u32 reserved_103_111[9];
+
+       struct {
+               u32 avs2_ref_error_field        : 1;
+               u32 avs2_ref_error_topfield     : 1;
+               u32 ref_error_topfield_used     : 1;
+               u32 ref_error_botfield_used     : 1;
+               u32 reserved                    : 28;
+       } reg112_error_ref_info;
+} __packed;
+
+struct rkvdec_vdpu381_regs_hevc_params {
+       struct rkvdec_vdpu381_h26x_set reg064_h26x_set;
+
+       u32 reg065_cur_top_poc;
+       u32 reg066_cur_bot_poc;
+       u32 reg067_082_ref_poc[16];
+
+       u32 reserved_083_098[16];
+
+       struct {
+               u32 hevc_ref_valid_0    : 1;
+               u32 hevc_ref_valid_1    : 1;
+               u32 hevc_ref_valid_2    : 1;
+               u32 hevc_ref_valid_3    : 1;
+               u32 reserve0            : 4;
+               u32 hevc_ref_valid_4    : 1;
+               u32 hevc_ref_valid_5    : 1;
+               u32 hevc_ref_valid_6    : 1;
+               u32 hevc_ref_valid_7    : 1;
+               u32 reserve1            : 4;
+               u32 hevc_ref_valid_8    : 1;
+               u32 hevc_ref_valid_9    : 1;
+               u32 hevc_ref_valid_10   : 1;
+               u32 hevc_ref_valid_11   : 1;
+               u32 reserve2            : 4;
+               u32 hevc_ref_valid_12   : 1;
+               u32 hevc_ref_valid_13   : 1;
+               u32 hevc_ref_valid_14   : 1;
+               u32 reserve3            : 5;
+       } reg099_hevc_ref_valid;
+
+       u32 reserved_100_102[3];
+
+       struct {
+               u32 ref_pic_layer_same_with_cur : 16;
+               u32 reserve                     : 16;
+       } reg103_hevc_mvc0;
+
+       struct {
+               u32 poc_lsb_not_present_flag        : 1;
+               u32 num_direct_ref_layers           : 6;
+               u32 reserve0                        : 1;
+
+               u32 num_reflayer_pics               : 6;
+               u32 default_ref_layers_active_flag  : 1;
+               u32 max_one_active_ref_layer_flag   : 1;
+
+               u32 poc_reset_info_present_flag     : 1;
+               u32 vps_poc_lsb_aligned_flag        : 1;
+               u32 mvc_poc15_valid_flag            : 1;
+               u32 reserve1                        : 13;
+       } reg104_hevc_mvc1;
+
+       u32 reserved_105_111[7];
+
+       struct {
+               u32 avs2_ref_error_field        : 1;
+               u32 avs2_ref_error_topfield     : 1;
+               u32 ref_error_topfield_used     : 1;
+               u32 ref_error_botfield_used     : 1;
+               u32 reserve                     : 28;
+       } reg112_hevc_ref_info;
+
+} __packed;
+
+/* base: OFFSET_CODEC_ADDR_REGS */
+struct rkvdec_vdpu381_regs_h26x_addr {
+       u32 reserved_160;
+       u32 reg161_pps_base;
+       u32 reserved_162;
+       u32 reg163_rps_base;
+       u32 reg164_180_ref_base[16];
+       u32 reg181_scanlist_addr;
+       u32 reg182_198_colmv_base[16];
+       u32 reg199_cabactbl_base;
+} __packed;
+
+struct rkvdec_vdpu381_regs_h26x_highpoc {
+       struct {
+               u32 ref0_poc_highbit    : 4;
+               u32 ref1_poc_highbit    : 4;
+               u32 ref2_poc_highbit    : 4;
+               u32 ref3_poc_highbit    : 4;
+               u32 ref4_poc_highbit    : 4;
+               u32 ref5_poc_highbit    : 4;
+               u32 ref6_poc_highbit    : 4;
+               u32 ref7_poc_highbit    : 4;
+       } reg200_203_ref_poc_highbit[4];
+       struct {
+               u32 cur_poc_highbit     : 4;
+               u32 reserved            : 28;
+       } reg204_cur_poc_highbit;
+} __packed;
+
+struct rkvdec_vdpu381_regs_h264 {
+       struct rkvdec_vdpu381_regs_common               common;
+       struct rkvdec_vdpu381_regs_h264_params          h264_param;
+       struct rkvdec_vdpu381_regs_common_addr          common_addr;
+       struct rkvdec_vdpu381_regs_h26x_addr            h264_addr;
+       struct rkvdec_vdpu381_regs_h26x_highpoc         h264_highpoc;
+} __packed;
+
+struct rkvdec_vdpu381_regs_hevc {
+       struct rkvdec_vdpu381_regs_common               common;
+       struct rkvdec_vdpu381_regs_hevc_params          hevc_param;
+       struct rkvdec_vdpu381_regs_common_addr          common_addr;
+       struct rkvdec_vdpu381_regs_h26x_addr            hevc_addr;
+       struct rkvdec_vdpu381_regs_h26x_highpoc         hevc_highpoc;
+} __packed;
+
+#endif /* __RKVDEC_REGS_H__ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c 
b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index af2eced90026..da192c866e2e 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -29,6 +29,7 @@
 
 #include "rkvdec.h"
 #include "rkvdec-regs.h"
+#include "rkvdec-vdpu381-regs.h"
 #include "rkvdec-rcb.h"
 
 static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1,
@@ -90,6 +91,9 @@ static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx,
 {
        v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
                            pix_mp->width, pix_mp->height);
+
+       ctx->colmv_offset = pix_mp->plane_fmt[0].sizeimage;
+
        pix_mp->plane_fmt[0].sizeimage += 128 *
                DIV_ROUND_UP(pix_mp->width, 16) *
                DIV_ROUND_UP(pix_mp->height, 16);
@@ -269,6 +273,53 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = {
        .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs),
 };
 
+static const struct rkvdec_ctrl_desc vdpu38x_h264_ctrl_descs[] = {
+       {
+               .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
+       },
+       {
+               .cfg.id = V4L2_CID_STATELESS_H264_SPS,
+               .cfg.ops = &rkvdec_ctrl_ops,
+       },
+       {
+               .cfg.id = V4L2_CID_STATELESS_H264_PPS,
+       },
+       {
+               .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
+       },
+       {
+               .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE,
+               .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+               .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+               .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+       },
+       {
+               .cfg.id = V4L2_CID_STATELESS_H264_START_CODE,
+               .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+               .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+               .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+       },
+       {
+               .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+               .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE,
+               .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA,
+               .cfg.menu_skip_mask =
+                       BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) |
+                       BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE),
+               .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+       },
+       {
+               .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+               .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+               .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0,
+       },
+};
+
+static const struct rkvdec_ctrls vdpu38x_h264_ctrls = {
+       .ctrls = vdpu38x_h264_ctrl_descs,
+       .num_ctrls = ARRAY_SIZE(vdpu38x_h264_ctrl_descs),
+};
+
 static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = {
        {
                .fourcc = V4L2_PIX_FMT_NV12,
@@ -382,6 +433,25 @@ static const struct rkvdec_coded_fmt_desc 
rk3288_coded_fmts[] = {
        }
 };
 
+static const struct rkvdec_coded_fmt_desc vdpu381_coded_fmts[] = {
+       {
+               .fourcc = V4L2_PIX_FMT_H264_SLICE,
+               .frmsize = {
+                       .min_width = 64,
+                       .max_width =  65520,
+                       .step_width = 64,
+                       .min_height = 64,
+                       .max_height =  65520,
+                       .step_height = 16,
+               },
+               .ctrls = &vdpu38x_h264_ctrls,
+               .ops = &rkvdec_vdpu381_h264_fmt_ops,
+               .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts),
+               .decoded_fmts = rkvdec_h264_decoded_fmts,
+               .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+       },
+};
+
 static const struct rkvdec_coded_fmt_desc *
 rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index)
 {
@@ -946,6 +1016,20 @@ void rkvdec_memcpy_toio(void __iomem *dst, void *src, 
size_t len)
 #endif
 }
 
+void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold)
+{
+       /* Set watchdog at 2 times the hardware timeout threshold */
+       u32 watchdog_time;
+       unsigned long axi_rate = clk_get_rate(rkvdec->axi_clk);
+
+       if (axi_rate)
+               watchdog_time = 2 * div_u64(1000 * (u64)timeout_threshold, 
axi_rate);
+       else
+               watchdog_time = 2000;
+
+       schedule_delayed_work(&rkvdec->watchdog_work, 
msecs_to_jiffies(watchdog_time));
+}
+
 static void rkvdec_device_run(void *priv)
 {
        struct rkvdec_ctx *ctx = priv;
@@ -1245,6 +1329,35 @@ static irqreturn_t rk3399_irq_handler(struct rkvdec_ctx 
*ctx)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
+{
+       struct rkvdec_dev *rkvdec = ctx->dev;
+       enum vb2_buffer_state state;
+       bool need_reset = 0;
+       u32 status;
+
+       status = readl(rkvdec->regs + VDPU381_REG_STA_INT);
+       writel(0, rkvdec->regs + VDPU381_REG_STA_INT);
+
+       if (status & VDPU381_STA_INT_DEC_RDY_STA) {
+               state = VB2_BUF_STATE_DONE;
+       } else {
+               state = VB2_BUF_STATE_ERROR;
+               if (status & (VDPU381_STA_INT_SOFTRESET_RDY |
+                             VDPU381_STA_INT_TIMEOUT |
+                             VDPU381_STA_INT_ERROR))
+                       rkvdec_iommu_restore(rkvdec);
+       }
+
+       if (need_reset)
+               rkvdec_iommu_restore(rkvdec);
+
+       if (cancel_delayed_work(&rkvdec->watchdog_work))
+               rkvdec_job_finish(ctx, state);
+
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
 {
        struct rkvdec_dev *rkvdec = priv;
@@ -1321,6 +1434,7 @@ static const struct rkvdec_variant rk3288_rkvdec_variant 
= {
        .coded_fmts = rk3288_coded_fmts,
        .num_coded_fmts = ARRAY_SIZE(rk3288_coded_fmts),
        .ops = &rk3399_variant_ops,
+       .has_single_reg_region = true,
 };
 
 static const struct rkvdec_variant rk3328_rkvdec_variant = {
@@ -1328,6 +1442,7 @@ static const struct rkvdec_variant rk3328_rkvdec_variant 
= {
        .coded_fmts = rkvdec_coded_fmts,
        .num_coded_fmts = ARRAY_SIZE(rkvdec_coded_fmts),
        .ops = &rk3399_variant_ops,
+       .has_single_reg_region = true,
        .quirks = RKVDEC_QUIRK_DISABLE_QOS,
 };
 
@@ -1336,6 +1451,32 @@ static const struct rkvdec_variant rk3399_rkvdec_variant 
= {
        .coded_fmts = rkvdec_coded_fmts,
        .num_coded_fmts = ARRAY_SIZE(rkvdec_coded_fmts),
        .ops = &rk3399_variant_ops,
+       .has_single_reg_region = true,
+};
+
+static const struct rcb_size_info vdpu381_rcb_sizes[] = {
+       {6,     PIC_WIDTH},     // intrar
+       {1,     PIC_WIDTH},     // transdr (Is actually 0.4*pic_width)
+       {1,     PIC_HEIGHT},    // transdc (Is actually 0.1*pic_height)
+       {3,     PIC_WIDTH},     // streamdr
+       {6,     PIC_WIDTH},     // interr
+       {3,     PIC_HEIGHT},    // interc
+       {22,    PIC_WIDTH},     // dblkr
+       {6,     PIC_WIDTH},     // saor
+       {11,    PIC_WIDTH},     // fbcr
+       {67,    PIC_HEIGHT},    // filtc col
+};
+
+static const struct rkvdec_variant_ops vdpu381_variant_ops = {
+       .irq_handler = vdpu381_irq_handler,
+};
+
+static const struct rkvdec_variant vdpu381_variant = {
+       .coded_fmts = vdpu381_coded_fmts,
+       .num_coded_fmts = ARRAY_SIZE(vdpu381_coded_fmts),
+       .rcb_sizes = vdpu381_rcb_sizes,
+       .num_rcb_sizes = ARRAY_SIZE(vdpu381_rcb_sizes),
+       .ops = &vdpu381_variant_ops,
 };
 
 static const struct of_device_id of_rkvdec_match[] = {
@@ -1351,6 +1492,10 @@ static const struct of_device_id of_rkvdec_match[] = {
                .compatible = "rockchip,rk3399-vdec",
                .data = &rk3399_rkvdec_variant,
        },
+       {
+               .compatible = "rockchip,rk3588-vdec",
+               .data = &vdpu381_variant,
+       },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, of_rkvdec_match);
@@ -1384,10 +1529,17 @@ static int rkvdec_probe(struct platform_device *pdev)
                return ret;
 
        rkvdec->num_clocks = ret;
+       rkvdec->axi_clk = devm_clk_get(&pdev->dev, "axi");
 
-       rkvdec->regs = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(rkvdec->regs))
-               return PTR_ERR(rkvdec->regs);
+       if (rkvdec->variant->has_single_reg_region) {
+               rkvdec->regs = devm_platform_ioremap_resource(pdev, 0);
+               if (IS_ERR(rkvdec->regs))
+                       return PTR_ERR(rkvdec->regs);
+       } else {
+               rkvdec->regs = devm_platform_ioremap_resource_byname(pdev, 
"function");
+               if (IS_ERR(rkvdec->regs))
+                       return PTR_ERR(rkvdec->regs);
+       }
 
        ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
        if (ret) {
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h 
b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index 7766a79caf68..4afa9cd690d5 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -25,6 +25,10 @@
 
 #define RKVDEC_QUIRK_DISABLE_QOS       BIT(0)
 
+#define RKVDEC_1080P_PIXELS            (1920 * 1088)
+#define RKVDEC_4K_PIXELS               (4096 * 2304)
+#define RKVDEC_8K_PIXELS               (7680 * 4320)
+
 struct rkvdec_ctx;
 struct rkvdec_rcb_config;
 
@@ -78,6 +82,7 @@ struct rkvdec_variant {
        const struct rcb_size_info *rcb_sizes;
        size_t num_rcb_sizes;
        const struct rkvdec_variant_ops *ops;
+       bool has_single_reg_region;
        unsigned int quirks;
 };
 
@@ -126,6 +131,7 @@ struct rkvdec_dev {
        struct device *dev;
        struct clk_bulk_data *clocks;
        unsigned int num_clocks;
+       struct clk *axi_clk;
        void __iomem *regs;
        struct mutex vdev_lock; /* serializes ioctls */
        struct delayed_work watchdog_work;
@@ -144,6 +150,7 @@ struct rkvdec_ctx {
        struct rkvdec_dev *dev;
        enum rkvdec_image_fmt image_fmt;
        struct rkvdec_rcb_config *rcb_config;
+       u32 colmv_offset;
        void *priv;
 };
 
@@ -167,11 +174,16 @@ struct rkvdec_aux_buf {
 void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
 void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
 void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len);
+void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 
timeout_threshold);
 
 void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx);
 
+/* RKVDEC ops */
 extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops;
 extern const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops;
 extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops;
 
+/* VDPU381 ops */
+extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops;
+
 #endif /* RKVDEC_H_ */
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to