123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- /*
- * drivers/gpu/ion/ion_cma_heap.c
- *
- * Copyright (C) Linaro 2012
- * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/device.h>
- #include <linux/ion.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/err.h>
- #include <linux/dma-mapping.h>
- #include <linux/msm_ion.h>
- #include <linux/highmem.h>
- #include <mach/iommu_domains.h>
- #include <asm/cacheflush.h>
- /* for ion_heap_ops structure */
- #include "ion_priv.h"
- #define ION_CMA_ALLOCATE_FAILED -1
- struct ion_cma_buffer_info {
- void *cpu_addr;
- dma_addr_t handle;
- struct sg_table *table;
- bool is_cached;
- };
- static int cma_heap_has_outer_cache;
- /*
- * Create scatter-list for the already allocated DMA buffer.
- * This function could be replace by dma_common_get_sgtable
- * as soon as it will avalaible.
- */
- int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt,
- void *cpu_addr, dma_addr_t handle, size_t size)
- {
- struct page *page = phys_to_page(handle);
- int ret;
- ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
- if (unlikely(ret))
- return ret;
- sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
- return 0;
- }
- /* ION CMA heap operations functions */
- static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
- unsigned long len, unsigned long align,
- unsigned long flags)
- {
- struct device *dev = heap->priv;
- struct ion_cma_buffer_info *info;
- dev_dbg(dev, "Request buffer allocation len %ld\n", len);
- info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL);
- if (!info) {
- dev_err(dev, "Can't allocate buffer info\n");
- return ION_CMA_ALLOCATE_FAILED;
- }
- if (!ION_IS_CACHED(flags))
- info->cpu_addr = dma_alloc_writecombine(dev, len,
- &(info->handle), GFP_KERNEL);
- else
- info->cpu_addr = dma_alloc_nonconsistent(dev, len,
- &(info->handle), GFP_KERNEL);
- if (!info->cpu_addr) {
- dev_err(dev, "Fail to allocate buffer\n");
- goto err;
- }
- info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
- if (!info->table) {
- dev_err(dev, "Fail to allocate sg table\n");
- goto err;
- }
- info->is_cached = ION_IS_CACHED(flags);
- ion_cma_get_sgtable(dev,
- info->table, info->cpu_addr, info->handle, len);
- /* keep this for memory release */
- buffer->priv_virt = info;
- dev_dbg(dev, "Allocate buffer %p\n", buffer);
- if (heap->id == ION_QSECOM_HEAP_ID ) {
- // printk("[ION_alloc id==27|QSEECOM] 0x%p/0x%x => kmap_flush_unused\n", (void*)info->handle, (unsigned int)len);
- kmap_flush_unused();
- }
- return 0;
- err:
- kfree(info);
- return ION_CMA_ALLOCATE_FAILED;
- }
- static void ion_cma_free(struct ion_buffer *buffer)
- {
- struct device *dev = buffer->heap->priv;
- struct ion_cma_buffer_info *info = buffer->priv_virt;
- dev_dbg(dev, "Release buffer %p\n", buffer);
- /* release memory */
- dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
- sg_free_table(info->table);
- /* release sg table */
- kfree(info->table);
- kfree(info);
- }
- /* return physical address in addr */
- static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
- ion_phys_addr_t *addr, size_t *len)
- {
- struct device *dev = heap->priv;
- struct ion_cma_buffer_info *info = buffer->priv_virt;
- dev_dbg(dev, "Return buffer %p physical address 0x%pa\n", buffer,
- &info->handle);
- *addr = info->handle;
- *len = buffer->size;
- return 0;
- }
- struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
- struct ion_buffer *buffer)
- {
- struct ion_cma_buffer_info *info = buffer->priv_virt;
- return info->table;
- }
- void ion_cma_heap_unmap_dma(struct ion_heap *heap,
- struct ion_buffer *buffer)
- {
- return;
- }
- static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
- struct vm_area_struct *vma)
- {
- struct device *dev = buffer->heap->priv;
- struct ion_cma_buffer_info *info = buffer->priv_virt;
- #ifdef CONFIG_TIMA_RKP
- if (buffer->size) {
- /* iommu optimization- needs to be turned ON from
- * the tz side.
- */
- cpu_v7_tima_iommu_opt(vma->vm_start, vma->vm_end, (unsigned long)vma->vm_mm->pgd);
- __asm__ __volatile__ (
- "mcr p15, 0, r0, c8, c3, 0\n"
- "dsb\n"
- "isb\n");
- }
- #endif
- if (info->is_cached)
- return dma_mmap_nonconsistent(dev, vma, info->cpu_addr,
- info->handle, buffer->size);
- else
- return dma_mmap_writecombine(dev, vma, info->cpu_addr,
- info->handle, buffer->size);
- }
- static void *ion_cma_map_kernel(struct ion_heap *heap,
- struct ion_buffer *buffer)
- {
- struct ion_cma_buffer_info *info = buffer->priv_virt;
- return info->cpu_addr;
- }
- static void ion_cma_unmap_kernel(struct ion_heap *heap,
- struct ion_buffer *buffer)
- {
- return;
- }
- static int ion_cma_print_debug(struct ion_heap *heap, struct seq_file *s,
- const struct list_head *mem_map)
- {
- if (mem_map) {
- struct mem_map_data *data;
- seq_printf(s, "\nMemory Map\n");
- seq_printf(s, "%16.s %14.s %14.s %14.s\n",
- "client", "start address", "end address",
- "size (hex)");
- list_for_each_entry(data, mem_map, node) {
- const char *client_name = "(null)";
- if (data->client_name)
- client_name = data->client_name;
- seq_printf(s, "%16.s %14pa %14pa %14lu (%lx)\n",
- client_name, &data->addr,
- &data->addr_end,
- data->size, data->size);
- }
- }
- return 0;
- }
- static struct ion_heap_ops ion_cma_ops = {
- .allocate = ion_cma_allocate,
- .free = ion_cma_free,
- .map_dma = ion_cma_heap_map_dma,
- .unmap_dma = ion_cma_heap_unmap_dma,
- .phys = ion_cma_phys,
- .map_user = ion_cma_mmap,
- .map_kernel = ion_cma_map_kernel,
- .unmap_kernel = ion_cma_unmap_kernel,
- .print_debug = ion_cma_print_debug,
- };
- struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)
- {
- struct ion_heap *heap;
- heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
- if (!heap)
- return ERR_PTR(-ENOMEM);
- heap->ops = &ion_cma_ops;
- /* set device as private heaps data, later it will be
- * used to make the link with reserved CMA memory */
- heap->priv = data->priv;
- heap->type = ION_HEAP_TYPE_DMA;
- cma_heap_has_outer_cache = data->has_outer_cache;
- return heap;
- }
- void ion_cma_heap_destroy(struct ion_heap *heap)
- {
- kfree(heap);
- }
|