/* * drivers/gpu/ion/ion_system_heap.c * * Copyright (C) 2011 Google, Inc. * * 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 #include #include #include #include #include #include "ion_priv.h" static int ion_system_heap_allocate(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long size, unsigned long align, unsigned long flags) { buffer->priv_virt = vmalloc_user(size); if (!buffer->priv_virt) return -ENOMEM; return 0; } void ion_system_heap_free(struct ion_buffer *buffer) { vfree(buffer->priv_virt); } struct scatterlist *ion_system_heap_map_dma(struct ion_heap *heap, struct ion_buffer *buffer) { struct scatterlist *sglist; struct page *page; int i; int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; void *vaddr = buffer->priv_virt; sglist = vmalloc(npages * sizeof(struct scatterlist)); if (!sglist) return ERR_PTR(-ENOMEM); memset(sglist, 0, npages * sizeof(struct scatterlist)); sg_init_table(sglist, npages); for (i = 0; i < npages; i++) { page = vmalloc_to_page(vaddr); if (!page) goto end; sg_set_page(&sglist[i], page, PAGE_SIZE, 0); vaddr += PAGE_SIZE; } /* XXX do cache maintenance for dma? */ return sglist; end: vfree(sglist); return NULL; } void ion_system_heap_unmap_dma(struct ion_heap *heap, struct ion_buffer *buffer) { /* XXX undo cache maintenance for dma? */ if (buffer->sglist) vfree(buffer->sglist); } void *ion_system_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) { return buffer->priv_virt; } void ion_system_heap_unmap_kernel(struct ion_heap *heap, struct ion_buffer *buffer) { } int ion_system_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, struct vm_area_struct *vma) { return remap_vmalloc_range(vma, buffer->priv_virt, vma->vm_pgoff); } static struct ion_heap_ops vmalloc_ops = { .allocate = ion_system_heap_allocate, .free = ion_system_heap_free, .map_dma = ion_system_heap_map_dma, .unmap_dma = ion_system_heap_unmap_dma, .map_kernel = ion_system_heap_map_kernel, .unmap_kernel = ion_system_heap_unmap_kernel, .map_user = ion_system_heap_map_user, }; struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused) { struct ion_heap *heap; heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); if (!heap) return ERR_PTR(-ENOMEM); heap->ops = &vmalloc_ops; heap->type = ION_HEAP_TYPE_SYSTEM; return heap; } void ion_system_heap_destroy(struct ion_heap *heap) { kfree(heap); } static int ion_system_contig_heap_allocate(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long len, unsigned long align, unsigned long flags) { buffer->priv_virt = kzalloc(len, GFP_KERNEL); if (!buffer->priv_virt) return -ENOMEM; return 0; } void ion_system_contig_heap_free(struct ion_buffer *buffer) { kfree(buffer->priv_virt); } static int ion_system_contig_heap_phys(struct ion_heap *heap, struct ion_buffer *buffer, ion_phys_addr_t *addr, size_t *len) { *addr = virt_to_phys(buffer->priv_virt); *len = buffer->size; return 0; } struct scatterlist *ion_system_contig_heap_map_dma(struct ion_heap *heap, struct ion_buffer *buffer) { struct scatterlist *sglist; sglist = vmalloc(sizeof(struct scatterlist)); if (!sglist) return ERR_PTR(-ENOMEM); sg_init_table(sglist, 1); sg_set_page(sglist, virt_to_page(buffer->priv_virt), buffer->size, 0); return sglist; } int ion_system_contig_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, struct vm_area_struct *vma) { unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt)); return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot); } static struct ion_heap_ops kmalloc_ops = { .allocate = ion_system_contig_heap_allocate, .free = ion_system_contig_heap_free, .phys = ion_system_contig_heap_phys, .map_dma = ion_system_contig_heap_map_dma, .unmap_dma = ion_system_heap_unmap_dma, .map_kernel = ion_system_heap_map_kernel, .unmap_kernel = ion_system_heap_unmap_kernel, .map_user = ion_system_contig_heap_map_user, }; struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused) { struct ion_heap *heap; heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); if (!heap) return ERR_PTR(-ENOMEM); heap->ops = &kmalloc_ops; heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG; return heap; } void ion_system_contig_heap_destroy(struct ion_heap *heap) { kfree(heap); }