18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/device.h> 38c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 48c2ecf20Sopenharmony_ci#include <linux/err.h> 58c2ecf20Sopenharmony_ci#include <linux/highmem.h> 68c2ecf20Sopenharmony_ci#include <linux/idr.h> 78c2ecf20Sopenharmony_ci#include <linux/list.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 118c2ecf20Sopenharmony_ci#include <uapi/linux/dma-heap.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "heap-helpers.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_civoid init_heap_helper_buffer(struct heap_helper_buffer *buffer, 168c2ecf20Sopenharmony_ci void (*free)(struct heap_helper_buffer *)) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci buffer->priv_virt = NULL; 198c2ecf20Sopenharmony_ci mutex_init(&buffer->lock); 208c2ecf20Sopenharmony_ci buffer->vmap_cnt = 0; 218c2ecf20Sopenharmony_ci buffer->vaddr = NULL; 228c2ecf20Sopenharmony_ci buffer->pagecount = 0; 238c2ecf20Sopenharmony_ci buffer->pages = NULL; 248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&buffer->attachments); 258c2ecf20Sopenharmony_ci buffer->free = free; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer, 298c2ecf20Sopenharmony_ci int fd_flags) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci exp_info.exp_name = dma_heap_get_name(buffer->heap); 348c2ecf20Sopenharmony_ci exp_info.ops = &heap_helper_ops; 358c2ecf20Sopenharmony_ci exp_info.size = buffer->size; 368c2ecf20Sopenharmony_ci exp_info.flags = fd_flags; 378c2ecf20Sopenharmony_ci exp_info.priv = buffer; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return dma_buf_export(&exp_info); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void *dma_heap_map_kernel(struct heap_helper_buffer *buffer) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci void *vaddr; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL); 478c2ecf20Sopenharmony_ci if (!vaddr) 488c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return vaddr; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void dma_heap_buffer_destroy(struct heap_helper_buffer *buffer) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci if (buffer->vmap_cnt > 0) { 568c2ecf20Sopenharmony_ci WARN(1, "%s: buffer still mapped in the kernel\n", __func__); 578c2ecf20Sopenharmony_ci vunmap(buffer->vaddr); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci buffer->free(buffer); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void *dma_heap_buffer_vmap_get(struct heap_helper_buffer *buffer) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci void *vaddr; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (buffer->vmap_cnt) { 688c2ecf20Sopenharmony_ci buffer->vmap_cnt++; 698c2ecf20Sopenharmony_ci return buffer->vaddr; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci vaddr = dma_heap_map_kernel(buffer); 728c2ecf20Sopenharmony_ci if (IS_ERR(vaddr)) 738c2ecf20Sopenharmony_ci return vaddr; 748c2ecf20Sopenharmony_ci buffer->vaddr = vaddr; 758c2ecf20Sopenharmony_ci buffer->vmap_cnt++; 768c2ecf20Sopenharmony_ci return vaddr; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void dma_heap_buffer_vmap_put(struct heap_helper_buffer *buffer) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci if (!--buffer->vmap_cnt) { 828c2ecf20Sopenharmony_ci vunmap(buffer->vaddr); 838c2ecf20Sopenharmony_ci buffer->vaddr = NULL; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct dma_heaps_attachment { 888c2ecf20Sopenharmony_ci struct device *dev; 898c2ecf20Sopenharmony_ci struct sg_table table; 908c2ecf20Sopenharmony_ci struct list_head list; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int dma_heap_attach(struct dma_buf *dmabuf, 948c2ecf20Sopenharmony_ci struct dma_buf_attachment *attachment) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct dma_heaps_attachment *a; 978c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 988c2ecf20Sopenharmony_ci int ret; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci a = kzalloc(sizeof(*a), GFP_KERNEL); 1018c2ecf20Sopenharmony_ci if (!a) 1028c2ecf20Sopenharmony_ci return -ENOMEM; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = sg_alloc_table_from_pages(&a->table, buffer->pages, 1058c2ecf20Sopenharmony_ci buffer->pagecount, 0, 1068c2ecf20Sopenharmony_ci buffer->pagecount << PAGE_SHIFT, 1078c2ecf20Sopenharmony_ci GFP_KERNEL); 1088c2ecf20Sopenharmony_ci if (ret) { 1098c2ecf20Sopenharmony_ci kfree(a); 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci a->dev = attachment->dev; 1148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&a->list); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci attachment->priv = a; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci mutex_lock(&buffer->lock); 1198c2ecf20Sopenharmony_ci list_add(&a->list, &buffer->attachments); 1208c2ecf20Sopenharmony_ci mutex_unlock(&buffer->lock); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void dma_heap_detach(struct dma_buf *dmabuf, 1268c2ecf20Sopenharmony_ci struct dma_buf_attachment *attachment) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct dma_heaps_attachment *a = attachment->priv; 1298c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci mutex_lock(&buffer->lock); 1328c2ecf20Sopenharmony_ci list_del(&a->list); 1338c2ecf20Sopenharmony_ci mutex_unlock(&buffer->lock); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci sg_free_table(&a->table); 1368c2ecf20Sopenharmony_ci kfree(a); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic 1408c2ecf20Sopenharmony_cistruct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment, 1418c2ecf20Sopenharmony_ci enum dma_data_direction direction) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct dma_heaps_attachment *a = attachment->priv; 1448c2ecf20Sopenharmony_ci struct sg_table *table = &a->table; 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = dma_map_sgtable(attachment->dev, table, direction, 0); 1488c2ecf20Sopenharmony_ci if (ret) 1498c2ecf20Sopenharmony_ci table = ERR_PTR(ret); 1508c2ecf20Sopenharmony_ci return table; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, 1548c2ecf20Sopenharmony_ci struct sg_table *table, 1558c2ecf20Sopenharmony_ci enum dma_data_direction direction) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci dma_unmap_sgtable(attachment->dev, table, direction, 0); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic vm_fault_t dma_heap_vm_fault(struct vm_fault *vmf) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 1638c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = vma->vm_private_data; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (vmf->pgoff > buffer->pagecount) 1668c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci vmf->page = buffer->pages[vmf->pgoff]; 1698c2ecf20Sopenharmony_ci get_page(vmf->page); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct vm_operations_struct dma_heap_vm_ops = { 1758c2ecf20Sopenharmony_ci .fault = dma_heap_vm_fault, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci vma->vm_ops = &dma_heap_vm_ops; 1868c2ecf20Sopenharmony_ci vma->vm_private_data = buffer; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void dma_heap_dma_buf_release(struct dma_buf *dmabuf) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci dma_heap_buffer_destroy(buffer); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int dma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, 1998c2ecf20Sopenharmony_ci enum dma_data_direction direction) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 2028c2ecf20Sopenharmony_ci struct dma_heaps_attachment *a; 2038c2ecf20Sopenharmony_ci int ret = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci mutex_lock(&buffer->lock); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (buffer->vmap_cnt) 2088c2ecf20Sopenharmony_ci invalidate_kernel_vmap_range(buffer->vaddr, buffer->size); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci list_for_each_entry(a, &buffer->attachments, list) { 2118c2ecf20Sopenharmony_ci dma_sync_sg_for_cpu(a->dev, a->table.sgl, a->table.nents, 2128c2ecf20Sopenharmony_ci direction); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci mutex_unlock(&buffer->lock); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return ret; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int dma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf, 2208c2ecf20Sopenharmony_ci enum dma_data_direction direction) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 2238c2ecf20Sopenharmony_ci struct dma_heaps_attachment *a; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci mutex_lock(&buffer->lock); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (buffer->vmap_cnt) 2288c2ecf20Sopenharmony_ci flush_kernel_vmap_range(buffer->vaddr, buffer->size); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci list_for_each_entry(a, &buffer->attachments, list) { 2318c2ecf20Sopenharmony_ci dma_sync_sg_for_device(a->dev, a->table.sgl, a->table.nents, 2328c2ecf20Sopenharmony_ci direction); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci mutex_unlock(&buffer->lock); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void *dma_heap_dma_buf_vmap(struct dma_buf *dmabuf) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 2428c2ecf20Sopenharmony_ci void *vaddr; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci mutex_lock(&buffer->lock); 2458c2ecf20Sopenharmony_ci vaddr = dma_heap_buffer_vmap_get(buffer); 2468c2ecf20Sopenharmony_ci mutex_unlock(&buffer->lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return vaddr; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void dma_heap_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct heap_helper_buffer *buffer = dmabuf->priv; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mutex_lock(&buffer->lock); 2568c2ecf20Sopenharmony_ci dma_heap_buffer_vmap_put(buffer); 2578c2ecf20Sopenharmony_ci mutex_unlock(&buffer->lock); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciconst struct dma_buf_ops heap_helper_ops = { 2618c2ecf20Sopenharmony_ci .map_dma_buf = dma_heap_map_dma_buf, 2628c2ecf20Sopenharmony_ci .unmap_dma_buf = dma_heap_unmap_dma_buf, 2638c2ecf20Sopenharmony_ci .mmap = dma_heap_mmap, 2648c2ecf20Sopenharmony_ci .release = dma_heap_dma_buf_release, 2658c2ecf20Sopenharmony_ci .attach = dma_heap_attach, 2668c2ecf20Sopenharmony_ci .detach = dma_heap_detach, 2678c2ecf20Sopenharmony_ci .begin_cpu_access = dma_heap_dma_buf_begin_cpu_access, 2688c2ecf20Sopenharmony_ci .end_cpu_access = dma_heap_dma_buf_end_cpu_access, 2698c2ecf20Sopenharmony_ci .vmap = dma_heap_dma_buf_vmap, 2708c2ecf20Sopenharmony_ci .vunmap = dma_heap_dma_buf_vunmap, 2718c2ecf20Sopenharmony_ci}; 272