diff options
Diffstat (limited to 'drivers/base/dma-mapping.c')
| -rw-r--r-- | drivers/base/dma-mapping.c | 49 | 
1 files changed, 49 insertions, 0 deletions
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 6f3676f1559..3fbedc75e7c 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -10,6 +10,7 @@  #include <linux/dma-mapping.h>  #include <linux/export.h>  #include <linux/gfp.h> +#include <asm-generic/dma-coherent.h>  /*   * Managed DMA API @@ -217,4 +218,52 @@ void dmam_release_declared_memory(struct device *dev)  }  EXPORT_SYMBOL(dmam_release_declared_memory); +/* + * Create scatter-list for the already allocated DMA buffer. + */ +int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, +		 void *cpu_addr, dma_addr_t handle, size_t size) +{ +	struct page *page = virt_to_page(cpu_addr); +	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; +} +EXPORT_SYMBOL(dma_common_get_sgtable); +  #endif + +/* + * Create userspace mapping for the DMA-coherent memory. + */ +int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, +		    void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ +	int ret = -ENXIO; +#ifdef CONFIG_MMU +	unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; +	unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; +	unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr)); +	unsigned long off = vma->vm_pgoff; + +	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + +	if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) +		return ret; + +	if (off < count && user_count <= (count - off)) { +		ret = remap_pfn_range(vma, vma->vm_start, +				      pfn + off, +				      user_count << PAGE_SHIFT, +				      vma->vm_page_prot); +	} +#endif	/* CONFIG_MMU */ + +	return ret; +} +EXPORT_SYMBOL(dma_common_mmap);  |