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