Commit 4711b248 authored by Russell King's avatar Russell King

[ARM] Add DMA mmap() support.

This adds DMA mmap() support for the ARM architecture, as discussed
around March 2003 on the linux-arch and linux-kernel mailing lists.

Subsystems such as ALSA (for sample ring buffers) and video drivers
(for framebuffers in system memory) require this infrastructure to
provide userspace with an architecture clean method to mmap these
memory areas.
Signed-off-by: default avatarRussell King <rmk@arm.linux.org.uk>
parent 467bea26
/*
* linux/arch/arm/mm/consistent.c
*
* Copyright (C) 2000-2002 Russell King
* Copyright (C) 2000-2004 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -65,6 +65,7 @@ struct vm_region {
struct list_head vm_list;
unsigned long vm_start;
unsigned long vm_end;
struct page *vm_pages;
};
static struct vm_region consistent_head = {
......@@ -206,6 +207,8 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, int gfp,
pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start);
struct page *end = page + (1 << order);
c->vm_pages = page;
/*
* Set the "dma handle"
*/
......@@ -215,6 +218,9 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, int gfp,
BUG_ON(!pte_none(*pte));
set_page_count(page, 1);
/*
* x86 does not mark the pages reserved...
*/
SetPageReserved(page);
set_pte(pte, mk_pte(page, prot));
page++;
......@@ -264,6 +270,50 @@ dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, int
}
EXPORT_SYMBOL(dma_alloc_writecombine);
static int dma_mmap(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t dma_addr, size_t size)
{
unsigned long flags, user_size, kern_size;
struct vm_region *c;
int ret = -ENXIO;
user_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
spin_lock_irqsave(&consistent_lock, flags);
c = vm_region_find(&consistent_head, (unsigned long)cpu_addr);
spin_unlock_irqrestore(&consistent_lock, flags);
if (c) {
kern_size = (c->vm_end - c->vm_start) >> PAGE_SHIFT;
if (vma->vm_pgoff < kern_size ||
user_size <= (kern_size - vma->vm_pgoff)) {
vma->vm_flags |= VM_RESERVED;
ret = remap_pfn_range(vma, vma->vm_start,
page_to_pfn(c->vm_pages),
user_size, vma->vm_page_prot);
}
}
return ret;
}
int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t dma_addr, size_t size)
{
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
return dma_mmap(dev, vma, cpu_addr, dma_addr, size);
}
EXPORT_SYMBOL(dma_mmap_coherent);
int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t dma_addr, size_t size)
{
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return dma_mmap(dev, vma, cpu_addr, dma_addr, size);
}
EXPORT_SYMBOL(dma_mmap_writecombine);
/*
* free a page as defined by the above mapping.
*/
......@@ -300,6 +350,10 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr
if (pfn_valid(pfn)) {
struct page *page = pfn_to_page(pfn);
/*
* x86 does not mark the pages reserved...
*/
ClearPageReserved(page);
__free_page(page);
......
......@@ -86,6 +86,22 @@ extern void
dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t handle);
/**
* dma_mmap_coherent - map a coherent DMA allocation into user space
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
* @vma: vm_area_struct describing requested user mapping
* @cpu_addr: kernel CPU-view address returned from dma_alloc_coherent
* @handle: device-view address returned from dma_alloc_coherent
* @size: size of memory originally requested in dma_alloc_coherent
*
* Map a coherent DMA buffer previously allocated by dma_alloc_coherent
* into user space. The coherent DMA buffer must not be freed by the
* driver until the user space mapping has been released.
*/
int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t handle, size_t size);
/**
* dma_alloc_writecombine - allocate writecombining memory for DMA
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
......@@ -103,6 +119,9 @@ dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, int
#define dma_free_writecombine(dev,size,cpu_addr,handle) \
dma_free_coherent(dev,size,cpu_addr,handle)
int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t handle, size_t size);
/**
* dma_map_single - map a single buffer for streaming DMA
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment