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 } 180