18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DMABUF CMA heap exporter 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012, 2019 Linaro Ltd. 68c2ecf20Sopenharmony_ci * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/cma.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-heap.h> 138c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/highmem.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 208c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "heap-helpers.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct cma_heap { 258c2ecf20Sopenharmony_ci struct dma_heap *heap; 268c2ecf20Sopenharmony_ci struct cma *cma; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void cma_heap_free(struct heap_helper_buffer *buffer) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct cma_heap *cma_heap = dma_heap_get_drvdata(buffer->heap); 328c2ecf20Sopenharmony_ci unsigned long nr_pages = buffer->pagecount; 338c2ecf20Sopenharmony_ci struct page *cma_pages = buffer->priv_virt; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* free page list */ 368c2ecf20Sopenharmony_ci kfree(buffer->pages); 378c2ecf20Sopenharmony_ci /* release memory */ 388c2ecf20Sopenharmony_ci cma_release(cma_heap->cma, cma_pages, nr_pages); 398c2ecf20Sopenharmony_ci kfree(buffer); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* dmabuf heap CMA operations functions */ 438c2ecf20Sopenharmony_cistatic int cma_heap_allocate(struct dma_heap *heap, 448c2ecf20Sopenharmony_ci unsigned long len, 458c2ecf20Sopenharmony_ci unsigned long fd_flags, 468c2ecf20Sopenharmony_ci unsigned long heap_flags) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct cma_heap *cma_heap = dma_heap_get_drvdata(heap); 498c2ecf20Sopenharmony_ci struct heap_helper_buffer *helper_buffer; 508c2ecf20Sopenharmony_ci struct page *cma_pages; 518c2ecf20Sopenharmony_ci size_t size = PAGE_ALIGN(len); 528c2ecf20Sopenharmony_ci unsigned long nr_pages = size >> PAGE_SHIFT; 538c2ecf20Sopenharmony_ci unsigned long align = get_order(size); 548c2ecf20Sopenharmony_ci struct dma_buf *dmabuf; 558c2ecf20Sopenharmony_ci int ret = -ENOMEM; 568c2ecf20Sopenharmony_ci pgoff_t pg; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (align > CONFIG_CMA_ALIGNMENT) 598c2ecf20Sopenharmony_ci align = CONFIG_CMA_ALIGNMENT; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL); 628c2ecf20Sopenharmony_ci if (!helper_buffer) 638c2ecf20Sopenharmony_ci return -ENOMEM; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci init_heap_helper_buffer(helper_buffer, cma_heap_free); 668c2ecf20Sopenharmony_ci helper_buffer->heap = heap; 678c2ecf20Sopenharmony_ci helper_buffer->size = len; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci cma_pages = cma_alloc(cma_heap->cma, nr_pages, align, false); 708c2ecf20Sopenharmony_ci if (!cma_pages) 718c2ecf20Sopenharmony_ci goto free_buf; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (PageHighMem(cma_pages)) { 748c2ecf20Sopenharmony_ci unsigned long nr_clear_pages = nr_pages; 758c2ecf20Sopenharmony_ci struct page *page = cma_pages; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci while (nr_clear_pages > 0) { 788c2ecf20Sopenharmony_ci void *vaddr = kmap_atomic(page); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci memset(vaddr, 0, PAGE_SIZE); 818c2ecf20Sopenharmony_ci kunmap_atomic(vaddr); 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * Avoid wasting time zeroing memory if the process 848c2ecf20Sopenharmony_ci * has been killed by by SIGKILL 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci if (fatal_signal_pending(current)) 878c2ecf20Sopenharmony_ci goto free_cma; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci page++; 908c2ecf20Sopenharmony_ci nr_clear_pages--; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } else { 938c2ecf20Sopenharmony_ci memset(page_address(cma_pages), 0, size); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci helper_buffer->pagecount = nr_pages; 978c2ecf20Sopenharmony_ci helper_buffer->pages = kmalloc_array(helper_buffer->pagecount, 988c2ecf20Sopenharmony_ci sizeof(*helper_buffer->pages), 998c2ecf20Sopenharmony_ci GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!helper_buffer->pages) { 1018c2ecf20Sopenharmony_ci ret = -ENOMEM; 1028c2ecf20Sopenharmony_ci goto free_cma; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (pg = 0; pg < helper_buffer->pagecount; pg++) 1068c2ecf20Sopenharmony_ci helper_buffer->pages[pg] = &cma_pages[pg]; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* create the dmabuf */ 1098c2ecf20Sopenharmony_ci dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags); 1108c2ecf20Sopenharmony_ci if (IS_ERR(dmabuf)) { 1118c2ecf20Sopenharmony_ci ret = PTR_ERR(dmabuf); 1128c2ecf20Sopenharmony_ci goto free_pages; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci helper_buffer->dmabuf = dmabuf; 1168c2ecf20Sopenharmony_ci helper_buffer->priv_virt = cma_pages; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = dma_buf_fd(dmabuf, fd_flags); 1198c2ecf20Sopenharmony_ci if (ret < 0) { 1208c2ecf20Sopenharmony_ci dma_buf_put(dmabuf); 1218c2ecf20Sopenharmony_ci /* just return, as put will call release and that will free */ 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cifree_pages: 1288c2ecf20Sopenharmony_ci kfree(helper_buffer->pages); 1298c2ecf20Sopenharmony_cifree_cma: 1308c2ecf20Sopenharmony_ci cma_release(cma_heap->cma, cma_pages, nr_pages); 1318c2ecf20Sopenharmony_cifree_buf: 1328c2ecf20Sopenharmony_ci kfree(helper_buffer); 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct dma_heap_ops cma_heap_ops = { 1378c2ecf20Sopenharmony_ci .allocate = cma_heap_allocate, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int __add_cma_heap(struct cma *cma, void *data) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct cma_heap *cma_heap; 1438c2ecf20Sopenharmony_ci struct dma_heap_export_info exp_info; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!cma_heap) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci cma_heap->cma = cma; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci exp_info.name = cma_get_name(cma); 1518c2ecf20Sopenharmony_ci exp_info.ops = &cma_heap_ops; 1528c2ecf20Sopenharmony_ci exp_info.priv = cma_heap; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci cma_heap->heap = dma_heap_add(&exp_info); 1558c2ecf20Sopenharmony_ci if (IS_ERR(cma_heap->heap)) { 1568c2ecf20Sopenharmony_ci int ret = PTR_ERR(cma_heap->heap); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci kfree(cma_heap); 1598c2ecf20Sopenharmony_ci return ret; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int add_default_cma_heap(void) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct cma *default_cma = dev_get_cma_area(NULL); 1688c2ecf20Sopenharmony_ci int ret = 0; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (default_cma) 1718c2ecf20Sopenharmony_ci ret = __add_cma_heap(default_cma, NULL); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_cimodule_init(add_default_cma_heap); 1768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DMA-BUF CMA Heap"); 1778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 178