1/* 2 * Copyright (C) 2013 Google, Inc. 3 * 4 * This software is licensed under the terms of the GNU General Public 5 * License version 2, as published by the Free Software Foundation, and 6 * may be copied, distributed, and modified under those terms. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14 15#include <linux/dma-buf.h> 16#include <linux/highmem.h> 17#include <linux/memblock.h> 18#include <linux/slab.h> 19 20struct adf_memblock_pdata { 21 phys_addr_t base; 22}; 23 24static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach, 25 enum dma_data_direction direction) 26{ 27 struct adf_memblock_pdata *pdata = attach->dmabuf->priv; 28 unsigned long pfn = PFN_DOWN(pdata->base); 29 struct page *page = pfn_to_page(pfn); 30 struct sg_table *table; 31 int nents, ret; 32 33 table = kzalloc(sizeof(*table), GFP_KERNEL); 34 if (!table) 35 return ERR_PTR(-ENOMEM); 36 37 ret = sg_alloc_table(table, 1, GFP_KERNEL); 38 if (ret < 0) 39 goto err_alloc; 40 41 sg_set_page(table->sgl, page, attach->dmabuf->size, 0); 42 43 nents = dma_map_sg(attach->dev, table->sgl, 1, direction); 44 if (!nents) { 45 ret = -EINVAL; 46 goto err_map; 47 } 48 49 return table; 50 51err_map: 52 sg_free_table(table); 53err_alloc: 54 kfree(table); 55 return ERR_PTR(ret); 56} 57 58static void adf_memblock_unmap(struct dma_buf_attachment *attach, 59 struct sg_table *table, enum dma_data_direction direction) 60{ 61 dma_unmap_sg(attach->dev, table->sgl, 1, direction); 62 sg_free_table(table); 63} 64 65static void __init_memblock adf_memblock_release(struct dma_buf *buf) 66{ 67 struct adf_memblock_pdata *pdata = buf->priv; 68 int err = memblock_free(pdata->base, buf->size); 69 70 if (err < 0) 71 pr_warn("%s: freeing memblock failed: %d\n", __func__, err); 72 kfree(pdata); 73} 74 75static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset, 76 bool atomic) 77{ 78 struct adf_memblock_pdata *pdata = buf->priv; 79 unsigned long pfn = PFN_DOWN(pdata->base) + pgoffset; 80 struct page *page = pfn_to_page(pfn); 81 82 if (atomic) 83 return kmap_atomic(page); 84 else 85 return kmap(page); 86} 87 88static void *adf_memblock_kmap_atomic(struct dma_buf *buf, 89 unsigned long pgoffset) 90{ 91 return adf_memblock_do_kmap(buf, pgoffset, true); 92} 93 94static void adf_memblock_kunmap_atomic(struct dma_buf *buf, 95 unsigned long pgoffset, void *vaddr) 96{ 97 kunmap_atomic(vaddr); 98} 99 100static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset) 101{ 102 return adf_memblock_do_kmap(buf, pgoffset, false); 103} 104 105static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset, 106 void *vaddr) 107{ 108 kunmap(vaddr); 109} 110 111static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma) 112{ 113 struct adf_memblock_pdata *pdata = buf->priv; 114 115 return remap_pfn_range(vma, vma->vm_start, PFN_DOWN(pdata->base), 116 vma->vm_end - vma->vm_start, vma->vm_page_prot); 117} 118 119struct dma_buf_ops adf_memblock_ops = { 120 .map_dma_buf = adf_memblock_map, 121 .unmap_dma_buf = adf_memblock_unmap, 122 .release = adf_memblock_release, 123 .kmap_atomic = adf_memblock_kmap_atomic, 124 .kunmap_atomic = adf_memblock_kunmap_atomic, 125 .kmap = adf_memblock_kmap, 126 .kunmap = adf_memblock_kunmap, 127 .mmap = adf_memblock_mmap, 128}; 129 130/** 131 * adf_memblock_export - export a memblock reserved area as a dma-buf 132 * 133 * @base: base physical address 134 * @size: memblock size 135 * @flags: mode flags for the dma-buf's file 136 * 137 * @base and @size must be page-aligned. 138 * 139 * Returns a dma-buf on success or ERR_PTR(-errno) on failure. 140 */ 141struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags) 142{ 143 struct adf_memblock_pdata *pdata; 144 struct dma_buf *buf; 145 146 if (PAGE_ALIGN(base) != base || PAGE_ALIGN(size) != size) 147 return ERR_PTR(-EINVAL); 148 149 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 150 if (!pdata) 151 return ERR_PTR(-ENOMEM); 152 153 pdata->base = base; 154 buf = dma_buf_export(pdata, &adf_memblock_ops, size, flags, NULL); 155 if (IS_ERR(buf)) 156 kfree(pdata); 157 158 return buf; 159} 160EXPORT_SYMBOL(adf_memblock_export); 161