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