diff options
Diffstat (limited to 'drivers/gpu/ion/ion_heap.c')
| -rw-r--r-- | drivers/gpu/ion/ion_heap.c | 75 | 
1 files changed, 75 insertions, 0 deletions
diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 8ce3c1907ba..b000eb39294 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -16,8 +16,83 @@  #include <linux/err.h>  #include <linux/ion.h> +#include <linux/mm.h> +#include <linux/scatterlist.h> +#include <linux/vmalloc.h>  #include "ion_priv.h" +void *ion_heap_map_kernel(struct ion_heap *heap, +			  struct ion_buffer *buffer) +{ +	struct scatterlist *sg; +	int i, j; +	void *vaddr; +	pgprot_t pgprot; +	struct sg_table *table = buffer->sg_table; +	int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; +	struct page **pages = vmalloc(sizeof(struct page *) * npages); +	struct page **tmp = pages; + +	if (!pages) +		return 0; + +	if (buffer->flags & ION_FLAG_CACHED) +		pgprot = PAGE_KERNEL; +	else +		pgprot = pgprot_writecombine(PAGE_KERNEL); + +	for_each_sg(table->sgl, sg, table->nents, i) { +		int npages_this_entry = PAGE_ALIGN(sg_dma_len(sg)) / PAGE_SIZE; +		struct page *page = sg_page(sg); +		BUG_ON(i >= npages); +		for (j = 0; j < npages_this_entry; j++) { +			*(tmp++) = page++; +		} +	} +	vaddr = vmap(pages, npages, VM_MAP, pgprot); +	vfree(pages); + +	return vaddr; +} + +void ion_heap_unmap_kernel(struct ion_heap *heap, +			   struct ion_buffer *buffer) +{ +	vunmap(buffer->vaddr); +} + +int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, +		      struct vm_area_struct *vma) +{ +	struct sg_table *table = buffer->sg_table; +	unsigned long addr = vma->vm_start; +	unsigned long offset = vma->vm_pgoff * PAGE_SIZE; +	struct scatterlist *sg; +	int i; + +	for_each_sg(table->sgl, sg, table->nents, i) { +		struct page *page = sg_page(sg); +		unsigned long remainder = vma->vm_end - addr; +		unsigned long len = sg_dma_len(sg); + +		if (offset >= sg_dma_len(sg)) { +			offset -= sg_dma_len(sg); +			continue; +		} else if (offset) { +			page += offset / PAGE_SIZE; +			len = sg_dma_len(sg) - offset; +			offset = 0; +		} +		len = min(len, remainder); +		remap_pfn_range(vma, addr, page_to_pfn(page), len, +				vma->vm_page_prot); +		addr += len; +		if (addr >= vma->vm_end) +			return 0; +	} +	return 0; +} +  struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)  {  	struct ion_heap *heap = NULL;  |