ion_cma_heap.c revision 2a3fcc51639cb4ad1a841bc169a1ddb993455076
1/* 2 * drivers/staging/android/ion/ion_cma_heap.c 3 * 4 * Copyright (C) Linaro 2012 5 * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson. 6 * 7 * This software is licensed under the terms of the GNU General Public 8 * License version 2, as published by the Free Software Foundation, and 9 * may be copied, distributed, and modified under those terms. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 */ 17 18#include <linux/device.h> 19#include <linux/slab.h> 20#include <linux/errno.h> 21#include <linux/err.h> 22#include <linux/dma-mapping.h> 23 24#include "ion.h" 25#include "ion_priv.h" 26 27#define ION_CMA_ALLOCATE_FAILED -1 28 29struct ion_cma_heap { 30 struct ion_heap heap; 31 struct device *dev; 32}; 33 34#define to_cma_heap(x) container_of(x, struct ion_cma_heap, heap) 35 36struct ion_cma_buffer_info { 37 void *cpu_addr; 38 dma_addr_t handle; 39 struct sg_table *table; 40}; 41 42/* 43 * Create scatter-list for the already allocated DMA buffer. 44 * This function could be replaced by dma_common_get_sgtable 45 * as soon as it will avalaible. 46 */ 47static int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt, 48 void *cpu_addr, dma_addr_t handle, size_t size) 49{ 50 struct page *page = virt_to_page(cpu_addr); 51 int ret; 52 53 ret = sg_alloc_table(sgt, 1, GFP_KERNEL); 54 if (unlikely(ret)) 55 return ret; 56 57 sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); 58 return 0; 59} 60 61/* ION CMA heap operations functions */ 62static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer, 63 unsigned long len, unsigned long align, 64 unsigned long flags) 65{ 66 struct ion_cma_heap *cma_heap = to_cma_heap(heap); 67 struct device *dev = cma_heap->dev; 68 struct ion_cma_buffer_info *info; 69 70 dev_dbg(dev, "Request buffer allocation len %ld\n", len); 71 72 if (buffer->flags & ION_FLAG_CACHED) 73 return -EINVAL; 74 75 if (align > PAGE_SIZE) 76 return -EINVAL; 77 78 info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL); 79 if (!info) { 80 dev_err(dev, "Can't allocate buffer info\n"); 81 return ION_CMA_ALLOCATE_FAILED; 82 } 83 84 info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 85 GFP_HIGHUSER | __GFP_ZERO); 86 87 if (!info->cpu_addr) { 88 dev_err(dev, "Fail to allocate buffer\n"); 89 goto err; 90 } 91 92 info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL); 93 if (!info->table) { 94 dev_err(dev, "Fail to allocate sg table\n"); 95 goto free_mem; 96 } 97 98 if (ion_cma_get_sgtable 99 (dev, info->table, info->cpu_addr, info->handle, len)) 100 goto free_table; 101 /* keep this for memory release */ 102 buffer->priv_virt = info; 103 dev_dbg(dev, "Allocate buffer %p\n", buffer); 104 return 0; 105 106free_table: 107 kfree(info->table); 108free_mem: 109 dma_free_coherent(dev, len, info->cpu_addr, info->handle); 110err: 111 kfree(info); 112 return ION_CMA_ALLOCATE_FAILED; 113} 114 115static void ion_cma_free(struct ion_buffer *buffer) 116{ 117 struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); 118 struct device *dev = cma_heap->dev; 119 struct ion_cma_buffer_info *info = buffer->priv_virt; 120 121 dev_dbg(dev, "Release buffer %p\n", buffer); 122 /* release memory */ 123 dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle); 124 /* release sg table */ 125 sg_free_table(info->table); 126 kfree(info->table); 127 kfree(info); 128} 129 130/* return physical address in addr */ 131static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer, 132 ion_phys_addr_t *addr, size_t *len) 133{ 134 struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); 135 struct device *dev = cma_heap->dev; 136 struct ion_cma_buffer_info *info = buffer->priv_virt; 137 138 dev_dbg(dev, "Return buffer %p physical address %pa\n", buffer, 139 &info->handle); 140 141 *addr = info->handle; 142 *len = buffer->size; 143 144 return 0; 145} 146 147static struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap, 148 struct ion_buffer *buffer) 149{ 150 struct ion_cma_buffer_info *info = buffer->priv_virt; 151 152 return info->table; 153} 154 155static void ion_cma_heap_unmap_dma(struct ion_heap *heap, 156 struct ion_buffer *buffer) 157{ 158 return; 159} 160 161static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer, 162 struct vm_area_struct *vma) 163{ 164 struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap); 165 struct device *dev = cma_heap->dev; 166 struct ion_cma_buffer_info *info = buffer->priv_virt; 167 168 return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle, 169 buffer->size); 170} 171 172static void *ion_cma_map_kernel(struct ion_heap *heap, 173 struct ion_buffer *buffer) 174{ 175 struct ion_cma_buffer_info *info = buffer->priv_virt; 176 /* kernel memory mapping has been done at allocation time */ 177 return info->cpu_addr; 178} 179 180static void ion_cma_unmap_kernel(struct ion_heap *heap, 181 struct ion_buffer *buffer) 182{ 183} 184 185static struct ion_heap_ops ion_cma_ops = { 186 .allocate = ion_cma_allocate, 187 .free = ion_cma_free, 188 .map_dma = ion_cma_heap_map_dma, 189 .unmap_dma = ion_cma_heap_unmap_dma, 190 .phys = ion_cma_phys, 191 .map_user = ion_cma_mmap, 192 .map_kernel = ion_cma_map_kernel, 193 .unmap_kernel = ion_cma_unmap_kernel, 194}; 195 196struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data) 197{ 198 struct ion_cma_heap *cma_heap; 199 200 cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL); 201 202 if (!cma_heap) 203 return ERR_PTR(-ENOMEM); 204 205 cma_heap->heap.ops = &ion_cma_ops; 206 /* get device from private heaps data, later it will be 207 * used to make the link with reserved CMA memory */ 208 cma_heap->dev = data->priv; 209 cma_heap->heap.type = ION_HEAP_TYPE_DMA; 210 return &cma_heap->heap; 211} 212 213void ion_cma_heap_destroy(struct ion_heap *heap) 214{ 215 struct ion_cma_heap *cma_heap = to_cma_heap(heap); 216 217 kfree(cma_heap); 218} 219