1*e5640dbbSDetlev Casanova // SPDX-License-Identifier: GPL-2.0 2*e5640dbbSDetlev Casanova /* 3*e5640dbbSDetlev Casanova * Rockchip video decoder Rows and Cols Buffers manager 4*e5640dbbSDetlev Casanova * 5*e5640dbbSDetlev Casanova * Copyright (C) 2025 Collabora, Ltd. 6*e5640dbbSDetlev Casanova * Detlev Casanova <detlev.casanova@collabora.com> 7*e5640dbbSDetlev Casanova */ 8*e5640dbbSDetlev Casanova 9*e5640dbbSDetlev Casanova #include "rkvdec.h" 10*e5640dbbSDetlev Casanova #include "rkvdec-rcb.h" 11*e5640dbbSDetlev Casanova 12*e5640dbbSDetlev Casanova #include <linux/iommu.h> 13*e5640dbbSDetlev Casanova #include <linux/genalloc.h> 14*e5640dbbSDetlev Casanova #include <linux/sizes.h> 15*e5640dbbSDetlev Casanova #include <linux/types.h> 16*e5640dbbSDetlev Casanova 17*e5640dbbSDetlev Casanova struct rkvdec_rcb_config { 18*e5640dbbSDetlev Casanova struct rkvdec_aux_buf *rcb_bufs; 19*e5640dbbSDetlev Casanova size_t rcb_count; 20*e5640dbbSDetlev Casanova }; 21*e5640dbbSDetlev Casanova 22*e5640dbbSDetlev Casanova static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info, 23*e5640dbbSDetlev Casanova unsigned int width, unsigned int height) 24*e5640dbbSDetlev Casanova { 25*e5640dbbSDetlev Casanova return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height : width); 26*e5640dbbSDetlev Casanova } 27*e5640dbbSDetlev Casanova 28*e5640dbbSDetlev Casanova dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id) 29*e5640dbbSDetlev Casanova { 30*e5640dbbSDetlev Casanova return ctx->rcb_config->rcb_bufs[id].dma; 31*e5640dbbSDetlev Casanova } 32*e5640dbbSDetlev Casanova 33*e5640dbbSDetlev Casanova size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id) 34*e5640dbbSDetlev Casanova { 35*e5640dbbSDetlev Casanova return ctx->rcb_config->rcb_bufs[id].size; 36*e5640dbbSDetlev Casanova } 37*e5640dbbSDetlev Casanova 38*e5640dbbSDetlev Casanova int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx) 39*e5640dbbSDetlev Casanova { 40*e5640dbbSDetlev Casanova return ctx->rcb_config->rcb_count; 41*e5640dbbSDetlev Casanova } 42*e5640dbbSDetlev Casanova 43*e5640dbbSDetlev Casanova void rkvdec_free_rcb(struct rkvdec_ctx *ctx) 44*e5640dbbSDetlev Casanova { 45*e5640dbbSDetlev Casanova struct rkvdec_dev *dev = ctx->dev; 46*e5640dbbSDetlev Casanova struct rkvdec_rcb_config *cfg = ctx->rcb_config; 47*e5640dbbSDetlev Casanova unsigned long virt_addr; 48*e5640dbbSDetlev Casanova int i; 49*e5640dbbSDetlev Casanova 50*e5640dbbSDetlev Casanova if (!cfg) 51*e5640dbbSDetlev Casanova return; 52*e5640dbbSDetlev Casanova 53*e5640dbbSDetlev Casanova for (i = 0; i < cfg->rcb_count; i++) { 54*e5640dbbSDetlev Casanova size_t rcb_size = cfg->rcb_bufs[i].size; 55*e5640dbbSDetlev Casanova 56*e5640dbbSDetlev Casanova if (!cfg->rcb_bufs[i].cpu) 57*e5640dbbSDetlev Casanova continue; 58*e5640dbbSDetlev Casanova 59*e5640dbbSDetlev Casanova switch (cfg->rcb_bufs[i].type) { 60*e5640dbbSDetlev Casanova case RKVDEC_ALLOC_SRAM: 61*e5640dbbSDetlev Casanova virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu; 62*e5640dbbSDetlev Casanova 63*e5640dbbSDetlev Casanova if (dev->iommu_domain) 64*e5640dbbSDetlev Casanova iommu_unmap(dev->iommu_domain, virt_addr, rcb_size); 65*e5640dbbSDetlev Casanova gen_pool_free(dev->sram_pool, virt_addr, rcb_size); 66*e5640dbbSDetlev Casanova break; 67*e5640dbbSDetlev Casanova case RKVDEC_ALLOC_DMA: 68*e5640dbbSDetlev Casanova dma_free_coherent(dev->dev, 69*e5640dbbSDetlev Casanova rcb_size, 70*e5640dbbSDetlev Casanova cfg->rcb_bufs[i].cpu, 71*e5640dbbSDetlev Casanova cfg->rcb_bufs[i].dma); 72*e5640dbbSDetlev Casanova break; 73*e5640dbbSDetlev Casanova } 74*e5640dbbSDetlev Casanova } 75*e5640dbbSDetlev Casanova 76*e5640dbbSDetlev Casanova if (cfg->rcb_bufs) 77*e5640dbbSDetlev Casanova devm_kfree(dev->dev, cfg->rcb_bufs); 78*e5640dbbSDetlev Casanova 79*e5640dbbSDetlev Casanova devm_kfree(dev->dev, cfg); 80*e5640dbbSDetlev Casanova } 81*e5640dbbSDetlev Casanova 82*e5640dbbSDetlev Casanova int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, 83*e5640dbbSDetlev Casanova const struct rcb_size_info *size_info, 84*e5640dbbSDetlev Casanova size_t rcb_count) 85*e5640dbbSDetlev Casanova { 86*e5640dbbSDetlev Casanova int ret, i; 87*e5640dbbSDetlev Casanova u32 width, height; 88*e5640dbbSDetlev Casanova struct rkvdec_dev *rkvdec = ctx->dev; 89*e5640dbbSDetlev Casanova struct rkvdec_rcb_config *cfg; 90*e5640dbbSDetlev Casanova 91*e5640dbbSDetlev Casanova if (!size_info || !rcb_count) { 92*e5640dbbSDetlev Casanova ctx->rcb_config = NULL; 93*e5640dbbSDetlev Casanova return 0; 94*e5640dbbSDetlev Casanova } 95*e5640dbbSDetlev Casanova 96*e5640dbbSDetlev Casanova ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL); 97*e5640dbbSDetlev Casanova if (!ctx->rcb_config) 98*e5640dbbSDetlev Casanova return -ENOMEM; 99*e5640dbbSDetlev Casanova 100*e5640dbbSDetlev Casanova cfg = ctx->rcb_config; 101*e5640dbbSDetlev Casanova 102*e5640dbbSDetlev Casanova cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL); 103*e5640dbbSDetlev Casanova if (!cfg->rcb_bufs) { 104*e5640dbbSDetlev Casanova ret = -ENOMEM; 105*e5640dbbSDetlev Casanova goto err_alloc; 106*e5640dbbSDetlev Casanova } 107*e5640dbbSDetlev Casanova 108*e5640dbbSDetlev Casanova width = ctx->decoded_fmt.fmt.pix_mp.width; 109*e5640dbbSDetlev Casanova height = ctx->decoded_fmt.fmt.pix_mp.height; 110*e5640dbbSDetlev Casanova 111*e5640dbbSDetlev Casanova for (i = 0; i < rcb_count; i++) { 112*e5640dbbSDetlev Casanova void *cpu = NULL; 113*e5640dbbSDetlev Casanova dma_addr_t dma; 114*e5640dbbSDetlev Casanova size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height); 115*e5640dbbSDetlev Casanova enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM; 116*e5640dbbSDetlev Casanova 117*e5640dbbSDetlev Casanova /* Try allocating an SRAM buffer */ 118*e5640dbbSDetlev Casanova if (ctx->dev->sram_pool) { 119*e5640dbbSDetlev Casanova if (rkvdec->iommu_domain) 120*e5640dbbSDetlev Casanova rcb_size = ALIGN(rcb_size, SZ_4K); 121*e5640dbbSDetlev Casanova 122*e5640dbbSDetlev Casanova cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool, 123*e5640dbbSDetlev Casanova rcb_size, 124*e5640dbbSDetlev Casanova &dma, 125*e5640dbbSDetlev Casanova SZ_4K); 126*e5640dbbSDetlev Casanova } 127*e5640dbbSDetlev Casanova 128*e5640dbbSDetlev Casanova /* If an IOMMU is used, map the SRAM address through it */ 129*e5640dbbSDetlev Casanova if (cpu && rkvdec->iommu_domain) { 130*e5640dbbSDetlev Casanova unsigned long virt_addr = (unsigned long)cpu; 131*e5640dbbSDetlev Casanova phys_addr_t phys_addr = dma; 132*e5640dbbSDetlev Casanova 133*e5640dbbSDetlev Casanova ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr, 134*e5640dbbSDetlev Casanova rcb_size, IOMMU_READ | IOMMU_WRITE, 0); 135*e5640dbbSDetlev Casanova if (ret) { 136*e5640dbbSDetlev Casanova gen_pool_free(ctx->dev->sram_pool, 137*e5640dbbSDetlev Casanova (unsigned long)cpu, 138*e5640dbbSDetlev Casanova rcb_size); 139*e5640dbbSDetlev Casanova cpu = NULL; 140*e5640dbbSDetlev Casanova goto ram_fallback; 141*e5640dbbSDetlev Casanova } 142*e5640dbbSDetlev Casanova 143*e5640dbbSDetlev Casanova /* 144*e5640dbbSDetlev Casanova * The registers will be configured with the virtual 145*e5640dbbSDetlev Casanova * address so that it goes through the IOMMU 146*e5640dbbSDetlev Casanova */ 147*e5640dbbSDetlev Casanova dma = virt_addr; 148*e5640dbbSDetlev Casanova } 149*e5640dbbSDetlev Casanova 150*e5640dbbSDetlev Casanova ram_fallback: 151*e5640dbbSDetlev Casanova /* Fallback to RAM */ 152*e5640dbbSDetlev Casanova if (!cpu) { 153*e5640dbbSDetlev Casanova cpu = dma_alloc_coherent(ctx->dev->dev, 154*e5640dbbSDetlev Casanova rcb_size, 155*e5640dbbSDetlev Casanova &dma, 156*e5640dbbSDetlev Casanova GFP_KERNEL); 157*e5640dbbSDetlev Casanova alloc_type = RKVDEC_ALLOC_DMA; 158*e5640dbbSDetlev Casanova } 159*e5640dbbSDetlev Casanova 160*e5640dbbSDetlev Casanova if (!cpu) { 161*e5640dbbSDetlev Casanova ret = -ENOMEM; 162*e5640dbbSDetlev Casanova goto err_alloc; 163*e5640dbbSDetlev Casanova } 164*e5640dbbSDetlev Casanova 165*e5640dbbSDetlev Casanova cfg->rcb_bufs[i].cpu = cpu; 166*e5640dbbSDetlev Casanova cfg->rcb_bufs[i].dma = dma; 167*e5640dbbSDetlev Casanova cfg->rcb_bufs[i].size = rcb_size; 168*e5640dbbSDetlev Casanova cfg->rcb_bufs[i].type = alloc_type; 169*e5640dbbSDetlev Casanova 170*e5640dbbSDetlev Casanova cfg->rcb_count += 1; 171*e5640dbbSDetlev Casanova } 172*e5640dbbSDetlev Casanova 173*e5640dbbSDetlev Casanova return 0; 174*e5640dbbSDetlev Casanova 175*e5640dbbSDetlev Casanova err_alloc: 176*e5640dbbSDetlev Casanova rkvdec_free_rcb(ctx); 177*e5640dbbSDetlev Casanova 178*e5640dbbSDetlev Casanova return ret; 179*e5640dbbSDetlev Casanova } 180