Re: [PATCH v6 15/17] media: rkvdec: Add H264 support for the VDPU383 variant
From: Jonas Karlman <jonas@kwiboo.se>
Date: 2025-12-18 00:18:23
Also in:
linux-media, linux-rockchip, lkml
Hi Detlev, On 12/17/2025 6:40 PM, Detlev Casanova wrote:
quoted hunk ↗ jump to hunk
This variant is used on the RK3576 SoC. The moving vectors size requirements are slightly different so support for a colmv_size function per variant is added. Also, the link registers are used to start the decoder and read IRQ status. Per variant support for named register sections is added. The fluster score is 128/135 for JVT-AVC_V1, with MPS_MW_A failing in addition to the usual ones. The other test suites are not supported yet. Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com> --- .../media/platform/rockchip/rkvdec/Makefile | 1 + .../rockchip/rkvdec/rkvdec-vdpu383-h264.c | 582 ++++++++++++++++++ .../rockchip/rkvdec/rkvdec-vdpu383-regs.h | 281 +++++++++ .../media/platform/rockchip/rkvdec/rkvdec.c | 118 +++- .../media/platform/rockchip/rkvdec/rkvdec.h | 8 + 5 files changed, 984 insertions(+), 6 deletions(-) create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.hdiff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile index 7bfd95151e40..a58d4aede2fe 100644 --- a/drivers/media/platform/rockchip/rkvdec/Makefile +++ b/drivers/media/platform/rockchip/rkvdec/Makefile@@ -9,4 +9,5 @@ rockchip-vdec-y += \ rkvdec-hevc-common.o \ rkvdec-rcb.o \ rkvdec-vdpu381-h264.o \ + rkvdec-vdpu383-h264.o \ rkvdec-vp9.odiff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c new file mode 100644 index 000000000000..d8419085bdd8 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
[snip]
+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;I recall some older mail thread that we should not have to validate sps in start ops. It is validated in try_ctrl and is expected to always be in a valid state. This can most likely be dropped, it was removed from the HEVC start ops.
+
+ 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_run run;
+ struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
+ u32 watchdog_time;
+ u64 timeout_threshold;
+ unsigned long axi_rate;
+
+ 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);
+
+ /* Set watchdog at 2 times the hardware timeout threshold */
+ timeout_threshold = h264_ctx->regs.common.reg013_core_timeout_threshold;
+ axi_rate = clk_get_rate(rkvdec->axi_clk);
+
+ if (axi_rate)
+ watchdog_time = 2 * (1000 * timeout_threshold) / axi_rate;
+ else
+ watchdog_time = 2000;
+ schedule_delayed_work(&rkvdec->watchdog_work,
+ msecs_to_jiffies(watchdog_time));
+
+ /* Start decoding! */
+ writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
+ writel(0, rkvdec->link + VDPU383_LINK_IP_ENABLE);
+ writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
+
+ 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_vdpu383_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,
+};[snip]
quoted hunk ↗ jump to hunk
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c index 1ddae1b23434..ceb56516bd90 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c@@ -9,6 +9,7 @@ * Copyright (C) 2011 Samsung Electronics Co., Ltd. */ +#include <linux/hw_bitfield.h> #include <linux/clk.h> #include <linux/genalloc.h> #include <linux/interrupt.h>@@ -30,6 +31,7 @@ #include "rkvdec.h" #include "rkvdec-regs.h" #include "rkvdec-vdpu381-regs.h" +#include "rkvdec-vdpu383-regs.h" #include "rkvdec-rcb.h" static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1,@@ -86,17 +88,27 @@ static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc, return false; } +static u32 rkvdec_colmv_size(u16 width, u16 height) +{ + return 128 * DIV_ROUND_UP(width, 16) * DIV_ROUND_UP(height, 16); +} + +static u32 vdpu383_colmv_size(u16 width, u16 height) +{ + return ALIGN(width, 64) * ALIGN(height, 16); +} + static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp) { + const struct rkvdec_variant *variant = ctx->dev->variant; + 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); + pix_mp->plane_fmt[0].sizeimage += variant->ops->colmv_size(pix_mp->width, pix_mp->height);
nit: The rest of this function use line breake to keep line lengt below or around 80 chars, this long line feel out of place. Maybe: pix_mp->plane_fmt[0].sizeimage += variant->ops->colmv_size(pix_mp->width, pix_mp->height);
quoted hunk ↗ jump to hunk
} static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f,@@ -391,6 +403,26 @@ static const struct rkvdec_coded_fmt_desc vdpu381_coded_fmts[] = { }, }; +static const struct rkvdec_coded_fmt_desc vdpu383_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 = &rkvdec_h264_ctrls, + .ops = &rkvdec_vdpu383_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, + .capability = RKVDEC_CAPABILITY_H264, + }, +}; + static bool rkvdec_is_capable(struct rkvdec_ctx *ctx, unsigned int capability) { return (ctx->dev->variant->capabilities & capability) == capability;@@ -1294,6 +1326,35 @@ static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx) return IRQ_HANDLED; } +static irqreturn_t vdpu383_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->link + VDPU383_LINK_STA_INT); + writel(FIELD_PREP_WM16(VDPU383_STA_INT_ALL, 0), rkvdec->link + VDPU383_LINK_STA_INT); + /* On vdpu383, the interrupts must be disabled */ + writel(FIELD_PREP_WM16(VDPU383_INT_EN_IRQ | VDPU383_INT_EN_LINE_IRQ, 0), + rkvdec->link + VDPU383_LINK_INT_EN); + + if (status & VDPU383_STA_INT_DEC_RDY_STA) { + state = VB2_BUF_STATE_DONE; + } else { + state = VB2_BUF_STATE_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;@@ -1374,8 +1435,22 @@ static const struct rcb_size_info vdpu381_rcb_sizes[] = { {67, PIC_HEIGHT}, // filtc col }; +static const struct rcb_size_info vdpu383_rcb_sizes[] = { + {6, PIC_WIDTH}, // streamd + {6, PIC_WIDTH}, // streamd_tile + {12, PIC_WIDTH}, // inter + {12, PIC_WIDTH}, // inter_tile + {16, PIC_WIDTH}, // intra + {10, PIC_WIDTH}, // intra_tile + {120, PIC_WIDTH}, // filterd + {120, PIC_WIDTH}, // filterd_protect + {120, PIC_WIDTH}, // filterd_tile_row + {180, PIC_HEIGHT}, // filterd_tile_col +}; + static const struct rkvdec_variant_ops rk3399_variant_ops = { .irq_handler = rk3399_irq_handler, + .colmv_size = rkvdec_colmv_size, }; static const struct rkvdec_variant rk3288_rkvdec_variant = {@@ -1409,6 +1484,7 @@ static const struct rkvdec_variant rk3399_rkvdec_variant = { static const struct rkvdec_variant_ops vdpu381_variant_ops = { .irq_handler = vdpu381_irq_handler, + .colmv_size = rkvdec_colmv_size, }; static const struct rkvdec_variant rk3588_vdpu381_variant = {@@ -1417,6 +1493,22 @@ static const struct rkvdec_variant rk3588_vdpu381_variant = { .rcb_sizes = vdpu381_rcb_sizes, .num_rcb_sizes = ARRAY_SIZE(vdpu381_rcb_sizes), .ops = &vdpu381_variant_ops, + .named_regs = true,
Why is this added here in this patch ? Should probably be added in prior patch with the lookup for link regs added in this patch ?
quoted hunk ↗ jump to hunk
+ .capabilities = RKVDEC_CAPABILITY_H264, +}; + +static const struct rkvdec_variant_ops vdpu383_variant_ops = { + .irq_handler = vdpu383_irq_handler, + .colmv_size = vdpu383_colmv_size, +}; + +static const struct rkvdec_variant rk3576_vdpu383_variant = { + .coded_fmts = vdpu383_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(vdpu383_coded_fmts), + .rcb_sizes = vdpu383_rcb_sizes, + .num_rcb_sizes = ARRAY_SIZE(vdpu383_rcb_sizes), + .ops = &vdpu383_variant_ops, + .named_regs = true, .capabilities = RKVDEC_CAPABILITY_H264, };@@ -1437,6 +1529,10 @@ static const struct of_device_id of_rkvdec_match[] = { .compatible = "rockchip,rk3588-vdec", .data = &rk3588_vdpu381_variant, }, + { + .compatible = "rockchip,rk3576-vdec", + .data = &rk3576_vdpu383_variant, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_rkvdec_match);@@ -1472,9 +1568,19 @@ static int rkvdec_probe(struct platform_device *pdev) 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->named_regs) { + rkvdec->regs = devm_platform_ioremap_resource_byname(pdev, "function"); + if (IS_ERR(rkvdec->regs)) + return PTR_ERR(rkvdec->regs); + + rkvdec->link = devm_platform_ioremap_resource_byname(pdev, "link"); + if (IS_ERR(rkvdec->link)) + return PTR_ERR(rkvdec->link); + } else { + rkvdec->regs = devm_platform_ioremap_resource(pdev, 0); + 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 1ad9a4fc31db..2f924c924828 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h@@ -77,6 +77,7 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf) struct rkvdec_variant_ops { irqreturn_t (*irq_handler)(struct rkvdec_ctx *ctx); + u32 (*colmv_size)(u16 width, u16 height); }; struct rkvdec_variant {@@ -86,6 +87,7 @@ struct rkvdec_variant { const struct rcb_size_info *rcb_sizes; size_t num_rcb_sizes; const struct rkvdec_variant_ops *ops; + bool named_regs;
nit: Maybe use_named_regs or similar ? Maybe this should be inverted as I suspect all new variants are expected to use named regs ?
quoted hunk ↗ jump to hunk
unsigned int capabilities; unsigned int quirks; };@@ -138,6 +140,7 @@ struct rkvdec_dev { unsigned int num_clocks; struct clk *axi_clk; void __iomem *regs; + void __iomem *link; struct mutex vdev_lock; /* serializes ioctls */ struct delayed_work watchdog_work; struct gen_pool *sram_pool;@@ -182,10 +185,15 @@ void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len); 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 */
nit: This comment and the one above should be added in prior patch. Regards, Jonas
extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops; +/* VDPU383 ops */ +extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops; + #endif /* RKVDEC_H_ */