xref: /linux/drivers/accel/amdxdna/amdxdna_cbuf.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1*3cc5d7a5SMax Zhen // SPDX-License-Identifier: GPL-2.0
2*3cc5d7a5SMax Zhen /*
3*3cc5d7a5SMax Zhen  * Copyright (C) 2026, Advanced Micro Devices, Inc.
4*3cc5d7a5SMax Zhen  */
5*3cc5d7a5SMax Zhen 
6*3cc5d7a5SMax Zhen #include <drm/drm_mm.h>
7*3cc5d7a5SMax Zhen #include <drm/drm_prime.h>
8*3cc5d7a5SMax Zhen 
9*3cc5d7a5SMax Zhen #include "amdxdna_cbuf.h"
10*3cc5d7a5SMax Zhen #include "amdxdna_pci_drv.h"
11*3cc5d7a5SMax Zhen 
12*3cc5d7a5SMax Zhen /*
13*3cc5d7a5SMax Zhen  * Carveout memory is a chunk of memory which is physically contiguous and
14*3cc5d7a5SMax Zhen  * is reserved during early boot time. There is only one chunk of such memory
15*3cc5d7a5SMax Zhen  * per device. Once available, all BOs accessible from device should be
16*3cc5d7a5SMax Zhen  * allocated from this memory. This is a platform debug/bringup feature.
17*3cc5d7a5SMax Zhen  */
18*3cc5d7a5SMax Zhen struct amdxdna_carveout {
19*3cc5d7a5SMax Zhen 	u64		addr;
20*3cc5d7a5SMax Zhen 	u64		size;
21*3cc5d7a5SMax Zhen 	struct drm_mm	mm;
22*3cc5d7a5SMax Zhen 	struct mutex	lock; /* protect mm */
23*3cc5d7a5SMax Zhen };
24*3cc5d7a5SMax Zhen 
25*3cc5d7a5SMax Zhen bool amdxdna_use_carveout(struct amdxdna_dev *xdna)
26*3cc5d7a5SMax Zhen {
27*3cc5d7a5SMax Zhen 	return !!xdna->carveout;
28*3cc5d7a5SMax Zhen }
29*3cc5d7a5SMax Zhen 
30*3cc5d7a5SMax Zhen void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size)
31*3cc5d7a5SMax Zhen {
32*3cc5d7a5SMax Zhen 	if (amdxdna_use_carveout(xdna)) {
33*3cc5d7a5SMax Zhen 		*addr = xdna->carveout->addr;
34*3cc5d7a5SMax Zhen 		*size = xdna->carveout->size;
35*3cc5d7a5SMax Zhen 	} else {
36*3cc5d7a5SMax Zhen 		*addr = 0;
37*3cc5d7a5SMax Zhen 		*size = 0;
38*3cc5d7a5SMax Zhen 	}
39*3cc5d7a5SMax Zhen }
40*3cc5d7a5SMax Zhen 
41*3cc5d7a5SMax Zhen int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size)
42*3cc5d7a5SMax Zhen {
43*3cc5d7a5SMax Zhen 	struct amdxdna_carveout *carveout;
44*3cc5d7a5SMax Zhen 
45*3cc5d7a5SMax Zhen 	/* Only allow carveout memory to be set up once. */
46*3cc5d7a5SMax Zhen 	if (amdxdna_use_carveout(xdna)) {
47*3cc5d7a5SMax Zhen 		XDNA_ERR(xdna, "Carveout memory has already been set up.");
48*3cc5d7a5SMax Zhen 		return -EBUSY;
49*3cc5d7a5SMax Zhen 	}
50*3cc5d7a5SMax Zhen 
51*3cc5d7a5SMax Zhen 	carveout = kzalloc_obj(*carveout);
52*3cc5d7a5SMax Zhen 	if (!carveout)
53*3cc5d7a5SMax Zhen 		return -ENOMEM;
54*3cc5d7a5SMax Zhen 
55*3cc5d7a5SMax Zhen 	carveout->addr = carveout_addr;
56*3cc5d7a5SMax Zhen 	carveout->size = carveout_size;
57*3cc5d7a5SMax Zhen 	mutex_init(&carveout->lock);
58*3cc5d7a5SMax Zhen 	drm_mm_init(&carveout->mm, carveout->addr, carveout->size);
59*3cc5d7a5SMax Zhen 
60*3cc5d7a5SMax Zhen 	xdna->carveout = carveout;
61*3cc5d7a5SMax Zhen 	XDNA_INFO(xdna, "Use carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr);
62*3cc5d7a5SMax Zhen 	return 0;
63*3cc5d7a5SMax Zhen }
64*3cc5d7a5SMax Zhen 
65*3cc5d7a5SMax Zhen void amdxdna_carveout_fini(struct amdxdna_dev *xdna)
66*3cc5d7a5SMax Zhen {
67*3cc5d7a5SMax Zhen 	struct amdxdna_carveout *carveout = xdna->carveout;
68*3cc5d7a5SMax Zhen 
69*3cc5d7a5SMax Zhen 	if (!amdxdna_use_carveout(xdna))
70*3cc5d7a5SMax Zhen 		return;
71*3cc5d7a5SMax Zhen 
72*3cc5d7a5SMax Zhen 	XDNA_INFO(xdna, "Cleanup carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr);
73*3cc5d7a5SMax Zhen 	drm_mm_takedown(&carveout->mm);
74*3cc5d7a5SMax Zhen 	mutex_destroy(&carveout->lock);
75*3cc5d7a5SMax Zhen 	kfree(carveout);
76*3cc5d7a5SMax Zhen 	xdna->carveout = NULL;
77*3cc5d7a5SMax Zhen }
78*3cc5d7a5SMax Zhen 
79*3cc5d7a5SMax Zhen struct amdxdna_cbuf_priv {
80*3cc5d7a5SMax Zhen 	struct amdxdna_dev *xdna;
81*3cc5d7a5SMax Zhen 	struct drm_mm_node node;
82*3cc5d7a5SMax Zhen };
83*3cc5d7a5SMax Zhen 
84*3cc5d7a5SMax Zhen static struct sg_table *amdxdna_cbuf_map(struct dma_buf_attachment *attach,
85*3cc5d7a5SMax Zhen 					 enum dma_data_direction direction)
86*3cc5d7a5SMax Zhen {
87*3cc5d7a5SMax Zhen 	struct amdxdna_cbuf_priv *cbuf = attach->dmabuf->priv;
88*3cc5d7a5SMax Zhen 	struct device *dev = attach->dev;
89*3cc5d7a5SMax Zhen 	struct scatterlist *sgl, *sg;
90*3cc5d7a5SMax Zhen 	int ret, n_entries, i;
91*3cc5d7a5SMax Zhen 	struct sg_table *sgt;
92*3cc5d7a5SMax Zhen 	dma_addr_t dma_addr;
93*3cc5d7a5SMax Zhen 	size_t dma_size;
94*3cc5d7a5SMax Zhen 	size_t max_seg;
95*3cc5d7a5SMax Zhen 
96*3cc5d7a5SMax Zhen 	sgt = kzalloc_obj(*sgt);
97*3cc5d7a5SMax Zhen 	if (!sgt)
98*3cc5d7a5SMax Zhen 		return ERR_PTR(-ENOMEM);
99*3cc5d7a5SMax Zhen 
100*3cc5d7a5SMax Zhen 	max_seg = min_t(size_t, UINT_MAX, dma_max_mapping_size(dev));
101*3cc5d7a5SMax Zhen 	n_entries = (cbuf->node.size + max_seg - 1) / max_seg;
102*3cc5d7a5SMax Zhen 	sgl = kzalloc_objs(*sg, n_entries);
103*3cc5d7a5SMax Zhen 	if (!sgl) {
104*3cc5d7a5SMax Zhen 		ret = -ENOMEM;
105*3cc5d7a5SMax Zhen 		goto free_sgt;
106*3cc5d7a5SMax Zhen 	}
107*3cc5d7a5SMax Zhen 	sg_init_table(sgl, n_entries);
108*3cc5d7a5SMax Zhen 	sgt->orig_nents = n_entries;
109*3cc5d7a5SMax Zhen 	sgt->nents = n_entries;
110*3cc5d7a5SMax Zhen 	sgt->sgl = sgl;
111*3cc5d7a5SMax Zhen 
112*3cc5d7a5SMax Zhen 	dma_size = cbuf->node.size;
113*3cc5d7a5SMax Zhen 	dma_addr = dma_map_resource(dev, cbuf->node.start, dma_size,
114*3cc5d7a5SMax Zhen 				    direction, DMA_ATTR_SKIP_CPU_SYNC);
115*3cc5d7a5SMax Zhen 	ret = dma_mapping_error(dev, dma_addr);
116*3cc5d7a5SMax Zhen 	if (ret) {
117*3cc5d7a5SMax Zhen 		pr_err("Failed to dma_map_resource carveout dma buf, ret %d\n", ret);
118*3cc5d7a5SMax Zhen 		goto free_sgl;
119*3cc5d7a5SMax Zhen 	}
120*3cc5d7a5SMax Zhen 
121*3cc5d7a5SMax Zhen 	for_each_sgtable_dma_sg(sgt, sg, i) {
122*3cc5d7a5SMax Zhen 		size_t len = min_t(size_t, max_seg, dma_size);
123*3cc5d7a5SMax Zhen 
124*3cc5d7a5SMax Zhen 		sg_dma_address(sg) = dma_addr;
125*3cc5d7a5SMax Zhen 		sg_dma_len(sg) = len;
126*3cc5d7a5SMax Zhen 		dma_addr += len;
127*3cc5d7a5SMax Zhen 		dma_size -= len;
128*3cc5d7a5SMax Zhen 	}
129*3cc5d7a5SMax Zhen 
130*3cc5d7a5SMax Zhen 	return sgt;
131*3cc5d7a5SMax Zhen 
132*3cc5d7a5SMax Zhen free_sgl:
133*3cc5d7a5SMax Zhen 	kfree(sgl);
134*3cc5d7a5SMax Zhen free_sgt:
135*3cc5d7a5SMax Zhen 	kfree(sgt);
136*3cc5d7a5SMax Zhen 	return ERR_PTR(ret);
137*3cc5d7a5SMax Zhen }
138*3cc5d7a5SMax Zhen 
139*3cc5d7a5SMax Zhen static void amdxdna_cbuf_unmap(struct dma_buf_attachment *attach,
140*3cc5d7a5SMax Zhen 			       struct sg_table *sgt,
141*3cc5d7a5SMax Zhen 			       enum dma_data_direction direction)
142*3cc5d7a5SMax Zhen {
143*3cc5d7a5SMax Zhen 	dma_unmap_resource(attach->dev, sg_dma_address(sgt->sgl),
144*3cc5d7a5SMax Zhen 			   drm_prime_get_contiguous_size(sgt), direction,
145*3cc5d7a5SMax Zhen 			   DMA_ATTR_SKIP_CPU_SYNC);
146*3cc5d7a5SMax Zhen 	sg_free_table(sgt);
147*3cc5d7a5SMax Zhen 	kfree(sgt);
148*3cc5d7a5SMax Zhen }
149*3cc5d7a5SMax Zhen 
150*3cc5d7a5SMax Zhen static void amdxdna_cbuf_release(struct dma_buf *dbuf)
151*3cc5d7a5SMax Zhen {
152*3cc5d7a5SMax Zhen 	struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
153*3cc5d7a5SMax Zhen 	struct amdxdna_carveout *carveout;
154*3cc5d7a5SMax Zhen 
155*3cc5d7a5SMax Zhen 	carveout = cbuf->xdna->carveout;
156*3cc5d7a5SMax Zhen 	mutex_lock(&carveout->lock);
157*3cc5d7a5SMax Zhen 	drm_mm_remove_node(&cbuf->node);
158*3cc5d7a5SMax Zhen 	mutex_unlock(&carveout->lock);
159*3cc5d7a5SMax Zhen 
160*3cc5d7a5SMax Zhen 	kfree(cbuf);
161*3cc5d7a5SMax Zhen }
162*3cc5d7a5SMax Zhen 
163*3cc5d7a5SMax Zhen static vm_fault_t amdxdna_cbuf_vm_fault(struct vm_fault *vmf)
164*3cc5d7a5SMax Zhen {
165*3cc5d7a5SMax Zhen 	struct vm_area_struct *vma = vmf->vma;
166*3cc5d7a5SMax Zhen 	struct amdxdna_cbuf_priv *cbuf;
167*3cc5d7a5SMax Zhen 	unsigned long pfn;
168*3cc5d7a5SMax Zhen 	pgoff_t pgoff;
169*3cc5d7a5SMax Zhen 
170*3cc5d7a5SMax Zhen 	cbuf = vma->vm_private_data;
171*3cc5d7a5SMax Zhen 	pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
172*3cc5d7a5SMax Zhen 	pfn = (cbuf->node.start >> PAGE_SHIFT) + pgoff;
173*3cc5d7a5SMax Zhen 
174*3cc5d7a5SMax Zhen 	return vmf_insert_pfn(vma, vmf->address, pfn);
175*3cc5d7a5SMax Zhen }
176*3cc5d7a5SMax Zhen 
177*3cc5d7a5SMax Zhen static const struct vm_operations_struct amdxdna_cbuf_vm_ops = {
178*3cc5d7a5SMax Zhen 	.fault = amdxdna_cbuf_vm_fault,
179*3cc5d7a5SMax Zhen };
180*3cc5d7a5SMax Zhen 
181*3cc5d7a5SMax Zhen static int amdxdna_cbuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
182*3cc5d7a5SMax Zhen {
183*3cc5d7a5SMax Zhen 	struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
184*3cc5d7a5SMax Zhen 
185*3cc5d7a5SMax Zhen 	vma->vm_ops = &amdxdna_cbuf_vm_ops;
186*3cc5d7a5SMax Zhen 	vma->vm_private_data = cbuf;
187*3cc5d7a5SMax Zhen 	vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
188*3cc5d7a5SMax Zhen 
189*3cc5d7a5SMax Zhen 	return 0;
190*3cc5d7a5SMax Zhen }
191*3cc5d7a5SMax Zhen 
192*3cc5d7a5SMax Zhen static int amdxdna_cbuf_vmap(struct dma_buf *dbuf, struct iosys_map *map)
193*3cc5d7a5SMax Zhen {
194*3cc5d7a5SMax Zhen 	struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
195*3cc5d7a5SMax Zhen 	void *kva;
196*3cc5d7a5SMax Zhen 
197*3cc5d7a5SMax Zhen 	kva = memremap(cbuf->node.start, cbuf->node.size, MEMREMAP_WB);
198*3cc5d7a5SMax Zhen 	if (!kva) {
199*3cc5d7a5SMax Zhen 		pr_err("Failed to vmap carveout dma buf\n");
200*3cc5d7a5SMax Zhen 		return -ENOMEM;
201*3cc5d7a5SMax Zhen 	}
202*3cc5d7a5SMax Zhen 
203*3cc5d7a5SMax Zhen 	iosys_map_set_vaddr(map, kva);
204*3cc5d7a5SMax Zhen 	return 0;
205*3cc5d7a5SMax Zhen }
206*3cc5d7a5SMax Zhen 
207*3cc5d7a5SMax Zhen static void amdxdna_cbuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map)
208*3cc5d7a5SMax Zhen {
209*3cc5d7a5SMax Zhen 	memunmap(map->vaddr);
210*3cc5d7a5SMax Zhen }
211*3cc5d7a5SMax Zhen 
212*3cc5d7a5SMax Zhen static const struct dma_buf_ops amdxdna_cbuf_dmabuf_ops = {
213*3cc5d7a5SMax Zhen 	.map_dma_buf = amdxdna_cbuf_map,
214*3cc5d7a5SMax Zhen 	.unmap_dma_buf = amdxdna_cbuf_unmap,
215*3cc5d7a5SMax Zhen 	.release = amdxdna_cbuf_release,
216*3cc5d7a5SMax Zhen 	.mmap = amdxdna_cbuf_mmap,
217*3cc5d7a5SMax Zhen 	.vmap = amdxdna_cbuf_vmap,
218*3cc5d7a5SMax Zhen 	.vunmap = amdxdna_cbuf_vunmap,
219*3cc5d7a5SMax Zhen };
220*3cc5d7a5SMax Zhen 
221*3cc5d7a5SMax Zhen static int amdxdna_cbuf_clear(struct dma_buf *dbuf)
222*3cc5d7a5SMax Zhen {
223*3cc5d7a5SMax Zhen 	struct iosys_map vmap = IOSYS_MAP_INIT_VADDR(NULL);
224*3cc5d7a5SMax Zhen 
225*3cc5d7a5SMax Zhen 	dma_buf_vmap(dbuf, &vmap);
226*3cc5d7a5SMax Zhen 	if (!vmap.vaddr)
227*3cc5d7a5SMax Zhen 		return -EFAULT;
228*3cc5d7a5SMax Zhen 
229*3cc5d7a5SMax Zhen 	memset(vmap.vaddr, 0, dbuf->size);
230*3cc5d7a5SMax Zhen 	dma_buf_vunmap(dbuf, &vmap);
231*3cc5d7a5SMax Zhen 
232*3cc5d7a5SMax Zhen 	return 0;
233*3cc5d7a5SMax Zhen }
234*3cc5d7a5SMax Zhen 
235*3cc5d7a5SMax Zhen struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment)
236*3cc5d7a5SMax Zhen {
237*3cc5d7a5SMax Zhen 	struct amdxdna_dev *xdna = to_xdna_dev(dev);
238*3cc5d7a5SMax Zhen 	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
239*3cc5d7a5SMax Zhen 	struct amdxdna_carveout *carveout;
240*3cc5d7a5SMax Zhen 	struct amdxdna_cbuf_priv *cbuf;
241*3cc5d7a5SMax Zhen 	struct dma_buf *dbuf;
242*3cc5d7a5SMax Zhen 	int ret;
243*3cc5d7a5SMax Zhen 
244*3cc5d7a5SMax Zhen 	cbuf = kzalloc_obj(*cbuf);
245*3cc5d7a5SMax Zhen 	if (!cbuf)
246*3cc5d7a5SMax Zhen 		return ERR_PTR(-ENOMEM);
247*3cc5d7a5SMax Zhen 	cbuf->xdna = xdna;
248*3cc5d7a5SMax Zhen 
249*3cc5d7a5SMax Zhen 	carveout = xdna->carveout;
250*3cc5d7a5SMax Zhen 	mutex_lock(&carveout->lock);
251*3cc5d7a5SMax Zhen 	ret = drm_mm_insert_node_generic(&carveout->mm, &cbuf->node, size,
252*3cc5d7a5SMax Zhen 					 alignment, 0, DRM_MM_INSERT_BEST);
253*3cc5d7a5SMax Zhen 	mutex_unlock(&carveout->lock);
254*3cc5d7a5SMax Zhen 	if (ret)
255*3cc5d7a5SMax Zhen 		goto free_cbuf;
256*3cc5d7a5SMax Zhen 
257*3cc5d7a5SMax Zhen 	exp_info.size = size;
258*3cc5d7a5SMax Zhen 	exp_info.ops = &amdxdna_cbuf_dmabuf_ops;
259*3cc5d7a5SMax Zhen 	exp_info.priv = cbuf;
260*3cc5d7a5SMax Zhen 	exp_info.flags = O_RDWR;
261*3cc5d7a5SMax Zhen 	dbuf = dma_buf_export(&exp_info);
262*3cc5d7a5SMax Zhen 	if (IS_ERR(dbuf)) {
263*3cc5d7a5SMax Zhen 		ret = PTR_ERR(dbuf);
264*3cc5d7a5SMax Zhen 		goto remove_node;
265*3cc5d7a5SMax Zhen 	}
266*3cc5d7a5SMax Zhen 
267*3cc5d7a5SMax Zhen 	ret = amdxdna_cbuf_clear(dbuf);
268*3cc5d7a5SMax Zhen 	if (ret) {
269*3cc5d7a5SMax Zhen 		dma_buf_put(dbuf);
270*3cc5d7a5SMax Zhen 		goto out;
271*3cc5d7a5SMax Zhen 	}
272*3cc5d7a5SMax Zhen 	return dbuf;
273*3cc5d7a5SMax Zhen 
274*3cc5d7a5SMax Zhen remove_node:
275*3cc5d7a5SMax Zhen 	drm_mm_remove_node(&cbuf->node);
276*3cc5d7a5SMax Zhen free_cbuf:
277*3cc5d7a5SMax Zhen 	kfree(cbuf);
278*3cc5d7a5SMax Zhen out:
279*3cc5d7a5SMax Zhen 	return ERR_PTR(ret);
280*3cc5d7a5SMax Zhen }
281