xref: /linux/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c (revision f4b369c6fe0ceaba2da2daff8c9eb415f85926dd)
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