[media] media: vb2: support userptr for PFN mappings

Some video devices need to use contiguous memory which is
not backed by pages as it happens with vmalloc. This patch
provides USERPTR handling for those devices.

Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Javier Martin 2012-02-16 12:19:08 -03:00 committed by Mauro Carvalho Chehab
parent a1212162f4
commit 570d2a48b7
1 changed files with 46 additions and 22 deletions

View File

@ -10,6 +10,7 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
#include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/sched.h> #include <linux/sched.h>
@ -22,6 +23,7 @@
struct vb2_vmalloc_buf { struct vb2_vmalloc_buf {
void *vaddr; void *vaddr;
struct page **pages; struct page **pages;
struct vm_area_struct *vma;
int write; int write;
unsigned long size; unsigned long size;
unsigned int n_pages; unsigned int n_pages;
@ -71,6 +73,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
struct vb2_vmalloc_buf *buf; struct vb2_vmalloc_buf *buf;
unsigned long first, last; unsigned long first, last;
int n_pages, offset; int n_pages, offset;
struct vm_area_struct *vma;
dma_addr_t physp;
buf = kzalloc(sizeof(*buf), GFP_KERNEL); buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf) if (!buf)
@ -80,23 +84,37 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
offset = vaddr & ~PAGE_MASK; offset = vaddr & ~PAGE_MASK;
buf->size = size; buf->size = size;
first = vaddr >> PAGE_SHIFT;
last = (vaddr + size - 1) >> PAGE_SHIFT;
buf->n_pages = last - first + 1;
buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), GFP_KERNEL);
if (!buf->pages)
goto fail_pages_array_alloc;
/* current->mm->mmap_sem is taken by videobuf2 core */ vma = find_vma(current->mm, vaddr);
n_pages = get_user_pages(current, current->mm, vaddr & PAGE_MASK, if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
buf->n_pages, write, 1, /* force */ if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
buf->pages, NULL); goto fail_pages_array_alloc;
if (n_pages != buf->n_pages) buf->vma = vma;
goto fail_get_user_pages; buf->vaddr = ioremap_nocache(physp, size);
if (!buf->vaddr)
goto fail_pages_array_alloc;
} else {
first = vaddr >> PAGE_SHIFT;
last = (vaddr + size - 1) >> PAGE_SHIFT;
buf->n_pages = last - first + 1;
buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
GFP_KERNEL);
if (!buf->pages)
goto fail_pages_array_alloc;
buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL); /* current->mm->mmap_sem is taken by videobuf2 core */
if (!buf->vaddr) n_pages = get_user_pages(current, current->mm,
goto fail_get_user_pages; vaddr & PAGE_MASK, buf->n_pages,
write, 1, /* force */
buf->pages, NULL);
if (n_pages != buf->n_pages)
goto fail_get_user_pages;
buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
PAGE_KERNEL);
if (!buf->vaddr)
goto fail_get_user_pages;
}
buf->vaddr += offset; buf->vaddr += offset;
return buf; return buf;
@ -120,14 +138,20 @@ static void vb2_vmalloc_put_userptr(void *buf_priv)
unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
unsigned int i; unsigned int i;
if (vaddr) if (buf->pages) {
vm_unmap_ram((void *)vaddr, buf->n_pages); if (vaddr)
for (i = 0; i < buf->n_pages; ++i) { vm_unmap_ram((void *)vaddr, buf->n_pages);
if (buf->write) for (i = 0; i < buf->n_pages; ++i) {
set_page_dirty_lock(buf->pages[i]); if (buf->write)
put_page(buf->pages[i]); set_page_dirty_lock(buf->pages[i]);
put_page(buf->pages[i]);
}
kfree(buf->pages);
} else {
if (buf->vma)
vb2_put_vma(buf->vma);
iounmap(buf->vaddr);
} }
kfree(buf->pages);
kfree(buf); kfree(buf);
} }