From 7fe9eeaee3d92649de595694906140e40786451e Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Tue, 10 Jun 2025 14:53:27 -0400 Subject: [PATCH 074/113] DETLEV(v3): media: rkvdec: Add RCB and SRAM support 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. Signed-off-by: Detlev Casanova --- .../media/platform/rockchip/rkvdec/Makefile | 1 + .../platform/rockchip/rkvdec/rkvdec-rcb.c | 174 ++++++++++++++++++ .../platform/rockchip/rkvdec/rkvdec-rcb.h | 29 +++ .../media/platform/rockchip/rkvdec/rkvdec.c | 29 ++- .../media/platform/rockchip/rkvdec/rkvdec.h | 13 ++ 5 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile index d2ba7a7c15e5..3b34f39b17d6 100644 --- a/drivers/media/platform/rockchip/rkvdec/Makefile +++ b/drivers/media/platform/rockchip/rkvdec/Makefile @@ -6,4 +6,5 @@ rockchip-vdec-y += \ rkvdec-h264.o \ rkvdec-h264-common.o \ rkvdec-hevc.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..210e59db2c62 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip video decoder Rows and Cols Buffers manager + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova + */ + +#include "rkvdec.h" +#include "rkvdec-rcb.h" + +#include +#include +#include + +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 RKVDEC2_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 RKVDEC2_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; + + 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 = RKVDEC2_ALLOC_SRAM; + + /* Try allocating an SRAM buffer */ + if (ctx->dev->sram_pool) { + if (rkvdec->iommu_domain) + rcb_size = ALIGN(rcb_size, 0x1000); + + cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool, + rcb_size, + &dma, + 0x1000); + } + + /* 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 = RKVDEC2_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 + */ + +#include + +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 033c03051f2e..19deb1d34a18 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -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) @@ -770,6 +772,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_config *cfg = ctx->dev->config; int ret; if (V4L2_TYPE_IS_CAPTURE(q->type)) @@ -779,13 +782,22 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) if (WARN_ON(!desc)) return -EINVAL; + ret = rkvdec_allocate_rcb(ctx, cfg->rcb_size_info, cfg->rcb_num); + 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) @@ -821,6 +833,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); @@ -1339,8 +1353,6 @@ static int rkvdec_probe(struct platform_device *pdev) return ret; } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - irq = platform_get_irq(pdev, 0); if (irq <= 0) return -ENXIO; @@ -1353,6 +1365,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->config->rcb_num > 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); @@ -1361,7 +1377,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)) { @@ -1375,6 +1392,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 d8a1b3e8e0ef..86d1af5dee5e 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ #define RKVDEC_QUIRK_DISABLE_QOS BIT(0) struct rkvdec_ctx; +struct rkvdec_rcb_config; struct rkvdec_ctrl_desc { struct v4l2_ctrl_config cfg; @@ -117,6 +119,8 @@ struct rkvdec_coded_fmt_desc { struct rkvdec_config { struct rkvdec_coded_fmt_desc *coded_fmts; size_t coded_fmts_num; + struct rcb_size_info *rcb_size_info; + size_t rcb_num; }; struct rkvdec_dev { @@ -129,6 +133,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; const struct rkvdec_config *config; @@ -144,6 +150,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; }; @@ -152,10 +159,16 @@ static inline struct rkvdec_ctx *fh_to_rkvdec_ctx(struct v4l2_fh *fh) return container_of(fh, struct rkvdec_ctx, fh); } +enum rkvdec_alloc_type { + RKVDEC2_ALLOC_DMA = 0, + RKVDEC2_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); -- 2.34.1