On Fri Jan 9 11:15:26 2026 -0500, Detlev Casanova wrote:
> The RCB (Rows and Cols Buffers) are a set of buffers used by other
> variations of the decoder to store temporary data.
> 
> Those variation come with a dedicated SRAM area used to store those
> buffers for better performances.
> 
> The buffer sizes are either the width or height of the frame being
> decoded multiplied by a documented factor and can be stored either
> in SRAM or RAM.
> A fallback to RAM is provided if the SRAM is full (e.g.: multiple
> streams are being decoded at the same time).
> 
> To manage the different kind of allocation, an enum is added to the
> rkvdec_aux_buf struct to specify how the buffer was allocated, and
> so, how to free it.
> 
> This commit is in preparation of other variants support.
> 
> 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 +
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.c    | 179 +++++++++++++++++++++
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.h    |  29 ++++
 drivers/media/platform/rockchip/rkvdec/rkvdec.c    |  27 +++-
 drivers/media/platform/rockchip/rkvdec/rkvdec.h    |  13 ++
 5 files changed, 247 insertions(+), 2 deletions(-)

---

diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile 
b/drivers/media/platform/rockchip/rkvdec/Makefile
index 1b4bc44be23e..3d75103e536d 100644
--- a/drivers/media/platform/rockchip/rkvdec/Makefile
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
@@ -7,4 +7,5 @@ rockchip-vdec-y += \
                   rkvdec-h264-common.o \
                   rkvdec-hevc.o \
                   rkvdec-hevc-common.o \
+                  rkvdec-rcb.o \
                   rkvdec-vp9.o
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c 
b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
new file mode 100644
index 000000000000..fdcf1f177379
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip video decoder Rows and Cols Buffers manager
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ *  Detlev Casanova <[email protected]>
+ */
+
+#include "rkvdec.h"
+#include "rkvdec-rcb.h"
+
+#include <linux/iommu.h>
+#include <linux/genalloc.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+struct rkvdec_rcb_config {
+       struct rkvdec_aux_buf *rcb_bufs;
+       size_t rcb_count;
+};
+
+static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info,
+                             unsigned int width, unsigned int height)
+{
+       return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height 
: width);
+}
+
+dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id)
+{
+       return ctx->rcb_config->rcb_bufs[id].dma;
+}
+
+size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id)
+{
+       return ctx->rcb_config->rcb_bufs[id].size;
+}
+
+int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx)
+{
+       return ctx->rcb_config->rcb_count;
+}
+
+void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
+{
+       struct rkvdec_dev *dev = ctx->dev;
+       struct rkvdec_rcb_config *cfg = ctx->rcb_config;
+       unsigned long virt_addr;
+       int i;
+
+       if (!cfg)
+               return;
+
+       for (i = 0; i < cfg->rcb_count; i++) {
+               size_t rcb_size = cfg->rcb_bufs[i].size;
+
+               if (!cfg->rcb_bufs[i].cpu)
+                       continue;
+
+               switch (cfg->rcb_bufs[i].type) {
+               case RKVDEC_ALLOC_SRAM:
+                       virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu;
+
+                       if (dev->iommu_domain)
+                               iommu_unmap(dev->iommu_domain, virt_addr, 
rcb_size);
+                       gen_pool_free(dev->sram_pool, virt_addr, rcb_size);
+                       break;
+               case RKVDEC_ALLOC_DMA:
+                       dma_free_coherent(dev->dev,
+                                         rcb_size,
+                                         cfg->rcb_bufs[i].cpu,
+                                         cfg->rcb_bufs[i].dma);
+                       break;
+               }
+       }
+
+       if (cfg->rcb_bufs)
+               devm_kfree(dev->dev, cfg->rcb_bufs);
+
+       devm_kfree(dev->dev, cfg);
+}
+
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
+                       const struct rcb_size_info *size_info,
+                       size_t rcb_count)
+{
+       int ret, i;
+       u32 width, height;
+       struct rkvdec_dev *rkvdec = ctx->dev;
+       struct rkvdec_rcb_config *cfg;
+
+       if (!size_info || !rcb_count) {
+               ctx->rcb_config = NULL;
+               return 0;
+       }
+
+       ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), 
GFP_KERNEL);
+       if (!ctx->rcb_config)
+               return -ENOMEM;
+
+       cfg = ctx->rcb_config;
+
+       cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * 
rcb_count, GFP_KERNEL);
+       if (!cfg->rcb_bufs) {
+               ret = -ENOMEM;
+               goto err_alloc;
+       }
+
+       width = ctx->decoded_fmt.fmt.pix_mp.width;
+       height = ctx->decoded_fmt.fmt.pix_mp.height;
+
+       for (i = 0; i < rcb_count; i++) {
+               void *cpu = NULL;
+               dma_addr_t dma;
+               size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height);
+               enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM;
+
+               /* Try allocating an SRAM buffer */
+               if (ctx->dev->sram_pool) {
+                       if (rkvdec->iommu_domain)
+                               rcb_size = ALIGN(rcb_size, SZ_4K);
+
+                       cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool,
+                                                       rcb_size,
+                                                       &dma,
+                                                       SZ_4K);
+               }
+
+               /* If an IOMMU is used, map the SRAM address through it */
+               if (cpu && rkvdec->iommu_domain) {
+                       unsigned long virt_addr = (unsigned long)cpu;
+                       phys_addr_t phys_addr = dma;
+
+                       ret = iommu_map(rkvdec->iommu_domain, virt_addr, 
phys_addr,
+                                       rcb_size, IOMMU_READ | IOMMU_WRITE, 0);
+                       if (ret) {
+                               gen_pool_free(ctx->dev->sram_pool,
+                                             (unsigned long)cpu,
+                                             rcb_size);
+                               cpu = NULL;
+                               goto ram_fallback;
+                       }
+
+                       /*
+                        * The registers will be configured with the virtual
+                        * address so that it goes through the IOMMU
+                        */
+                       dma = virt_addr;
+               }
+
+ram_fallback:
+               /* Fallback to RAM */
+               if (!cpu) {
+                       cpu = dma_alloc_coherent(ctx->dev->dev,
+                                                rcb_size,
+                                                &dma,
+                                                GFP_KERNEL);
+                       alloc_type = RKVDEC_ALLOC_DMA;
+               }
+
+               if (!cpu) {
+                       ret = -ENOMEM;
+                       goto err_alloc;
+               }
+
+               cfg->rcb_bufs[i].cpu = cpu;
+               cfg->rcb_bufs[i].dma = dma;
+               cfg->rcb_bufs[i].size = rcb_size;
+               cfg->rcb_bufs[i].type = alloc_type;
+
+               cfg->rcb_count += 1;
+       }
+
+       return 0;
+
+err_alloc:
+       rkvdec_free_rcb(ctx);
+
+       return ret;
+}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h 
b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
new file mode 100644
index 000000000000..30e8002555c8
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip video decoder Rows and Cols Buffers manager
+ *
+ * Copyright (C) 2025 Collabora, Ltd.
+ *  Detlev Casanova <[email protected]>
+ */
+
+#include <linux/types.h>
+
+struct rkvdec_ctx;
+
+enum rcb_axis {
+       PIC_WIDTH = 0,
+       PIC_HEIGHT = 1
+};
+
+struct rcb_size_info {
+       u8 multiplier;
+       enum rcb_axis axis;
+};
+
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
+                       const struct rcb_size_info *size_info,
+                       size_t rcb_count);
+dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id);
+size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id);
+int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx);
+void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c 
b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 92b1c7b62bd2..ff6a09e45462 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/genalloc.h>
 #include <linux/interrupt.h>
 #include <linux/iommu.h>
 #include <linux/module.h>
@@ -28,6 +29,7 @@
 
 #include "rkvdec.h"
 #include "rkvdec-regs.h"
+#include "rkvdec-rcb.h"
 
 static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1,
                                   enum rkvdec_image_fmt fmt2)
@@ -778,6 +780,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, 
unsigned int count)
 {
        struct rkvdec_ctx *ctx = vb2_get_drv_priv(q);
        const struct rkvdec_coded_fmt_desc *desc;
+       const struct rkvdec_variant *variant = ctx->dev->variant;
        int ret;
 
        if (V4L2_TYPE_IS_CAPTURE(q->type))
@@ -787,13 +790,22 @@ static int rkvdec_start_streaming(struct vb2_queue *q, 
unsigned int count)
        if (WARN_ON(!desc))
                return -EINVAL;
 
+       ret = rkvdec_allocate_rcb(ctx, variant->rcb_sizes, 
variant->num_rcb_sizes);
+       if (ret)
+               return ret;
+
        if (desc->ops->start) {
                ret = desc->ops->start(ctx);
                if (ret)
-                       return ret;
+                       goto err_ops_start;
        }
 
        return 0;
+
+err_ops_start:
+       rkvdec_free_rcb(ctx);
+
+       return ret;
 }
 
 static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state)
@@ -829,6 +841,8 @@ static void rkvdec_stop_streaming(struct vb2_queue *q)
 
                if (desc->ops->stop)
                        desc->ops->stop(ctx);
+
+               rkvdec_free_rcb(ctx);
        }
 
        rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR);
@@ -1345,6 +1359,10 @@ static int rkvdec_probe(struct platform_device *pdev)
                return ret;
        }
 
+       rkvdec->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+       if (!rkvdec->sram_pool && rkvdec->variant->num_rcb_sizes > 0)
+               dev_info(&pdev->dev, "No sram node, RCB will be stored in 
RAM\n");
+
        pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
@@ -1353,7 +1371,8 @@ static int rkvdec_probe(struct platform_device *pdev)
        if (ret)
                goto err_disable_runtime_pm;
 
-       if (iommu_get_domain_for_dev(&pdev->dev)) {
+       rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev);
+       if (rkvdec->iommu_domain) {
                rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
 
                if (IS_ERR(rkvdec->empty_domain)) {
@@ -1367,6 +1386,10 @@ static int rkvdec_probe(struct platform_device *pdev)
 err_disable_runtime_pm:
        pm_runtime_dont_use_autosuspend(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
+
+       if (rkvdec->sram_pool)
+               gen_pool_destroy(rkvdec->sram_pool);
+
        return ret;
 }
 
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h 
b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index 8c4f96ba5cde..751f39afe7e2 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -19,12 +19,14 @@
 #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 RKVDEC_QUIRK_DISABLE_QOS       BIT(0)
 
 struct rkvdec_ctx;
+struct rkvdec_rcb_config;
 
 struct rkvdec_ctrl_desc {
        struct v4l2_ctrl_config cfg;
@@ -69,6 +71,8 @@ struct rkvdec_variant {
        unsigned int num_regs;
        const struct rkvdec_coded_fmt_desc *coded_fmts;
        size_t num_coded_fmts;
+       const struct rcb_size_info *rcb_sizes;
+       size_t num_rcb_sizes;
        unsigned int quirks;
 };
 
@@ -119,6 +123,8 @@ struct rkvdec_dev {
        void __iomem *regs;
        struct mutex vdev_lock; /* serializes ioctls */
        struct delayed_work watchdog_work;
+       struct gen_pool *sram_pool;
+       struct iommu_domain *iommu_domain;
        struct iommu_domain *empty_domain;
        const struct rkvdec_variant *variant;
 };
@@ -131,6 +137,7 @@ struct rkvdec_ctx {
        struct v4l2_ctrl_handler ctrl_hdl;
        struct rkvdec_dev *dev;
        enum rkvdec_image_fmt image_fmt;
+       struct rkvdec_rcb_config *rcb_config;
        void *priv;
 };
 
@@ -139,10 +146,16 @@ static inline struct rkvdec_ctx 
*file_to_rkvdec_ctx(struct file *filp)
        return container_of(file_to_v4l2_fh(filp), struct rkvdec_ctx, fh);
 }
 
+enum rkvdec_alloc_type {
+       RKVDEC_ALLOC_DMA  = 0,
+       RKVDEC_ALLOC_SRAM = 1,
+};
+
 struct rkvdec_aux_buf {
        void *cpu;
        dma_addr_t dma;
        size_t size;
+       enum rkvdec_alloc_type type;
 };
 
 void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to