Commit 570d2a48 authored by Javier Martin's avatar Javier Martin Committed by Mauro Carvalho Chehab

[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: default avatarJavier Martin <javier.martin@vista-silicon.com>
Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent a1212162
...@@ -10,6 +10,7 @@ ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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)
buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL); goto fail_pages_array_alloc;
if (!buf->vaddr) } else {
goto fail_get_user_pages; 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 */
n_pages = get_user_pages(current, current->mm,
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) ...@@ -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);
} }
......
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