Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

Tested-by: Diederik de Haas <didi.debian@cknow.org> # Rock 5B
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>

authored by

Detlev Casanova and committed by
Hans Verkuil
e5640dbb ae2070ca

+247 -2
+1
drivers/media/platform/rockchip/rkvdec/Makefile
··· 7 7 rkvdec-h264-common.o \ 8 8 rkvdec-hevc.o \ 9 9 rkvdec-hevc-common.o \ 10 + rkvdec-rcb.o \ 10 11 rkvdec-vp9.o
+179
drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Rockchip video decoder Rows and Cols Buffers manager 4 + * 5 + * Copyright (C) 2025 Collabora, Ltd. 6 + * Detlev Casanova <detlev.casanova@collabora.com> 7 + */ 8 + 9 + #include "rkvdec.h" 10 + #include "rkvdec-rcb.h" 11 + 12 + #include <linux/iommu.h> 13 + #include <linux/genalloc.h> 14 + #include <linux/sizes.h> 15 + #include <linux/types.h> 16 + 17 + struct rkvdec_rcb_config { 18 + struct rkvdec_aux_buf *rcb_bufs; 19 + size_t rcb_count; 20 + }; 21 + 22 + static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info, 23 + unsigned int width, unsigned int height) 24 + { 25 + return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height : width); 26 + } 27 + 28 + dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id) 29 + { 30 + return ctx->rcb_config->rcb_bufs[id].dma; 31 + } 32 + 33 + size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id) 34 + { 35 + return ctx->rcb_config->rcb_bufs[id].size; 36 + } 37 + 38 + int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx) 39 + { 40 + return ctx->rcb_config->rcb_count; 41 + } 42 + 43 + void rkvdec_free_rcb(struct rkvdec_ctx *ctx) 44 + { 45 + struct rkvdec_dev *dev = ctx->dev; 46 + struct rkvdec_rcb_config *cfg = ctx->rcb_config; 47 + unsigned long virt_addr; 48 + int i; 49 + 50 + if (!cfg) 51 + return; 52 + 53 + for (i = 0; i < cfg->rcb_count; i++) { 54 + size_t rcb_size = cfg->rcb_bufs[i].size; 55 + 56 + if (!cfg->rcb_bufs[i].cpu) 57 + continue; 58 + 59 + switch (cfg->rcb_bufs[i].type) { 60 + case RKVDEC_ALLOC_SRAM: 61 + virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu; 62 + 63 + if (dev->iommu_domain) 64 + iommu_unmap(dev->iommu_domain, virt_addr, rcb_size); 65 + gen_pool_free(dev->sram_pool, virt_addr, rcb_size); 66 + break; 67 + case RKVDEC_ALLOC_DMA: 68 + dma_free_coherent(dev->dev, 69 + rcb_size, 70 + cfg->rcb_bufs[i].cpu, 71 + cfg->rcb_bufs[i].dma); 72 + break; 73 + } 74 + } 75 + 76 + if (cfg->rcb_bufs) 77 + devm_kfree(dev->dev, cfg->rcb_bufs); 78 + 79 + devm_kfree(dev->dev, cfg); 80 + } 81 + 82 + int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, 83 + const struct rcb_size_info *size_info, 84 + size_t rcb_count) 85 + { 86 + int ret, i; 87 + u32 width, height; 88 + struct rkvdec_dev *rkvdec = ctx->dev; 89 + struct rkvdec_rcb_config *cfg; 90 + 91 + if (!size_info || !rcb_count) { 92 + ctx->rcb_config = NULL; 93 + return 0; 94 + } 95 + 96 + ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL); 97 + if (!ctx->rcb_config) 98 + return -ENOMEM; 99 + 100 + cfg = ctx->rcb_config; 101 + 102 + cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL); 103 + if (!cfg->rcb_bufs) { 104 + ret = -ENOMEM; 105 + goto err_alloc; 106 + } 107 + 108 + width = ctx->decoded_fmt.fmt.pix_mp.width; 109 + height = ctx->decoded_fmt.fmt.pix_mp.height; 110 + 111 + for (i = 0; i < rcb_count; i++) { 112 + void *cpu = NULL; 113 + dma_addr_t dma; 114 + size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height); 115 + enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM; 116 + 117 + /* Try allocating an SRAM buffer */ 118 + if (ctx->dev->sram_pool) { 119 + if (rkvdec->iommu_domain) 120 + rcb_size = ALIGN(rcb_size, SZ_4K); 121 + 122 + cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool, 123 + rcb_size, 124 + &dma, 125 + SZ_4K); 126 + } 127 + 128 + /* If an IOMMU is used, map the SRAM address through it */ 129 + if (cpu && rkvdec->iommu_domain) { 130 + unsigned long virt_addr = (unsigned long)cpu; 131 + phys_addr_t phys_addr = dma; 132 + 133 + ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr, 134 + rcb_size, IOMMU_READ | IOMMU_WRITE, 0); 135 + if (ret) { 136 + gen_pool_free(ctx->dev->sram_pool, 137 + (unsigned long)cpu, 138 + rcb_size); 139 + cpu = NULL; 140 + goto ram_fallback; 141 + } 142 + 143 + /* 144 + * The registers will be configured with the virtual 145 + * address so that it goes through the IOMMU 146 + */ 147 + dma = virt_addr; 148 + } 149 + 150 + ram_fallback: 151 + /* Fallback to RAM */ 152 + if (!cpu) { 153 + cpu = dma_alloc_coherent(ctx->dev->dev, 154 + rcb_size, 155 + &dma, 156 + GFP_KERNEL); 157 + alloc_type = RKVDEC_ALLOC_DMA; 158 + } 159 + 160 + if (!cpu) { 161 + ret = -ENOMEM; 162 + goto err_alloc; 163 + } 164 + 165 + cfg->rcb_bufs[i].cpu = cpu; 166 + cfg->rcb_bufs[i].dma = dma; 167 + cfg->rcb_bufs[i].size = rcb_size; 168 + cfg->rcb_bufs[i].type = alloc_type; 169 + 170 + cfg->rcb_count += 1; 171 + } 172 + 173 + return 0; 174 + 175 + err_alloc: 176 + rkvdec_free_rcb(ctx); 177 + 178 + return ret; 179 + }
+29
drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Rockchip video decoder Rows and Cols Buffers manager 4 + * 5 + * Copyright (C) 2025 Collabora, Ltd. 6 + * Detlev Casanova <detlev.casanova@collabora.com> 7 + */ 8 + 9 + #include <linux/types.h> 10 + 11 + struct rkvdec_ctx; 12 + 13 + enum rcb_axis { 14 + PIC_WIDTH = 0, 15 + PIC_HEIGHT = 1 16 + }; 17 + 18 + struct rcb_size_info { 19 + u8 multiplier; 20 + enum rcb_axis axis; 21 + }; 22 + 23 + int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, 24 + const struct rcb_size_info *size_info, 25 + size_t rcb_count); 26 + dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id); 27 + size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id); 28 + int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx); 29 + void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
+25 -2
drivers/media/platform/rockchip/rkvdec/rkvdec.c
··· 10 10 */ 11 11 12 12 #include <linux/clk.h> 13 + #include <linux/genalloc.h> 13 14 #include <linux/interrupt.h> 14 15 #include <linux/iommu.h> 15 16 #include <linux/module.h> ··· 29 28 30 29 #include "rkvdec.h" 31 30 #include "rkvdec-regs.h" 31 + #include "rkvdec-rcb.h" 32 32 33 33 static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, 34 34 enum rkvdec_image_fmt fmt2) ··· 780 778 { 781 779 struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); 782 780 const struct rkvdec_coded_fmt_desc *desc; 781 + const struct rkvdec_variant *variant = ctx->dev->variant; 783 782 int ret; 784 783 785 784 if (V4L2_TYPE_IS_CAPTURE(q->type)) ··· 790 787 if (WARN_ON(!desc)) 791 788 return -EINVAL; 792 789 790 + ret = rkvdec_allocate_rcb(ctx, variant->rcb_sizes, variant->num_rcb_sizes); 791 + if (ret) 792 + return ret; 793 + 793 794 if (desc->ops->start) { 794 795 ret = desc->ops->start(ctx); 795 796 if (ret) 796 - return ret; 797 + goto err_ops_start; 797 798 } 798 799 799 800 return 0; 801 + 802 + err_ops_start: 803 + rkvdec_free_rcb(ctx); 804 + 805 + return ret; 800 806 } 801 807 802 808 static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state) ··· 841 829 842 830 if (desc->ops->stop) 843 831 desc->ops->stop(ctx); 832 + 833 + rkvdec_free_rcb(ctx); 844 834 } 845 835 846 836 rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR); ··· 1359 1345 return ret; 1360 1346 } 1361 1347 1348 + rkvdec->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); 1349 + if (!rkvdec->sram_pool && rkvdec->variant->num_rcb_sizes > 0) 1350 + dev_info(&pdev->dev, "No sram node, RCB will be stored in RAM\n"); 1351 + 1362 1352 pm_runtime_set_autosuspend_delay(&pdev->dev, 100); 1363 1353 pm_runtime_use_autosuspend(&pdev->dev); 1364 1354 pm_runtime_enable(&pdev->dev); ··· 1371 1353 if (ret) 1372 1354 goto err_disable_runtime_pm; 1373 1355 1374 - if (iommu_get_domain_for_dev(&pdev->dev)) { 1356 + rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev); 1357 + if (rkvdec->iommu_domain) { 1375 1358 rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev); 1376 1359 1377 1360 if (IS_ERR(rkvdec->empty_domain)) { ··· 1386 1367 err_disable_runtime_pm: 1387 1368 pm_runtime_dont_use_autosuspend(&pdev->dev); 1388 1369 pm_runtime_disable(&pdev->dev); 1370 + 1371 + if (rkvdec->sram_pool) 1372 + gen_pool_destroy(rkvdec->sram_pool); 1373 + 1389 1374 return ret; 1390 1375 } 1391 1376
+13
drivers/media/platform/rockchip/rkvdec/rkvdec.h
··· 19 19 #include <media/v4l2-ctrls.h> 20 20 #include <media/v4l2-device.h> 21 21 #include <media/v4l2-ioctl.h> 22 + #include <media/v4l2-mem2mem.h> 22 23 #include <media/videobuf2-core.h> 23 24 #include <media/videobuf2-dma-contig.h> 24 25 25 26 #define RKVDEC_QUIRK_DISABLE_QOS BIT(0) 26 27 27 28 struct rkvdec_ctx; 29 + struct rkvdec_rcb_config; 28 30 29 31 struct rkvdec_ctrl_desc { 30 32 struct v4l2_ctrl_config cfg; ··· 71 69 unsigned int num_regs; 72 70 const struct rkvdec_coded_fmt_desc *coded_fmts; 73 71 size_t num_coded_fmts; 72 + const struct rcb_size_info *rcb_sizes; 73 + size_t num_rcb_sizes; 74 74 unsigned int quirks; 75 75 }; 76 76 ··· 123 119 void __iomem *regs; 124 120 struct mutex vdev_lock; /* serializes ioctls */ 125 121 struct delayed_work watchdog_work; 122 + struct gen_pool *sram_pool; 123 + struct iommu_domain *iommu_domain; 126 124 struct iommu_domain *empty_domain; 127 125 const struct rkvdec_variant *variant; 128 126 }; ··· 137 131 struct v4l2_ctrl_handler ctrl_hdl; 138 132 struct rkvdec_dev *dev; 139 133 enum rkvdec_image_fmt image_fmt; 134 + struct rkvdec_rcb_config *rcb_config; 140 135 void *priv; 141 136 }; 142 137 ··· 146 139 return container_of(file_to_v4l2_fh(filp), struct rkvdec_ctx, fh); 147 140 } 148 141 142 + enum rkvdec_alloc_type { 143 + RKVDEC_ALLOC_DMA = 0, 144 + RKVDEC_ALLOC_SRAM = 1, 145 + }; 146 + 149 147 struct rkvdec_aux_buf { 150 148 void *cpu; 151 149 dma_addr_t dma; 152 150 size_t size; 151 + enum rkvdec_alloc_type type; 153 152 }; 154 153 155 154 void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);