aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMagnus Damm <damm@igel.co.jp>2009-06-16 18:32:36 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-16 22:47:40 -0400
commit720b17e759a50635c429ccaa2ec3d01edb4f92d6 (patch)
tree8f6d86a05b328a54bd312b62e1fbc844c1dc30ba
parent3b6748e2dd69906af3835db4dc9d1c8a3ee4c68c (diff)
videobuf-dma-contig: zero copy USERPTR support
Since videobuf-dma-contig is designed to handle physically contiguous memory, this patch modifies the videobuf-dma-contig code to only accept a user space pointer to physically contiguous memory. For now only VM_PFNMAP vmas are supported, so forget hotplug. On SuperH Mobile we use this with our sh_mobile_ceu_camera driver together with various multimedia accelerator blocks that are exported to user space using UIO. The UIO kernel code exports physically contiguous memory to user space and lets the user space application mmap() this memory and pass a pointer using the USERPTR interface for V4L2 zero copy operation. With this approach we support zero copy capture, hardware scaling and various forms of hardware encoding and decoding. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Magnus Damm <damm@igel.co.jp> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Paul Mundt <lethal@linux-sh.org> Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org> Cc: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/media/video/videobuf-dma-contig.c94
1 files changed, 89 insertions, 5 deletions
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index 6109fb5f34e2..0c29a019bc89 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -17,6 +17,7 @@
17#include <linux/init.h> 17#include <linux/init.h>
18#include <linux/module.h> 18#include <linux/module.h>
19#include <linux/mm.h> 19#include <linux/mm.h>
20#include <linux/pagemap.h>
20#include <linux/dma-mapping.h> 21#include <linux/dma-mapping.h>
21#include <media/videobuf-dma-contig.h> 22#include <media/videobuf-dma-contig.h>
22 23
@@ -25,6 +26,7 @@ struct videobuf_dma_contig_memory {
25 void *vaddr; 26 void *vaddr;
26 dma_addr_t dma_handle; 27 dma_addr_t dma_handle;
27 unsigned long size; 28 unsigned long size;
29 int is_userptr;
28}; 30};
29 31
30#define MAGIC_DC_MEM 0x0733ac61 32#define MAGIC_DC_MEM 0x0733ac61
@@ -108,6 +110,82 @@ static struct vm_operations_struct videobuf_vm_ops = {
108 .close = videobuf_vm_close, 110 .close = videobuf_vm_close,
109}; 111};
110 112
113/**
114 * videobuf_dma_contig_user_put() - reset pointer to user space buffer
115 * @mem: per-buffer private videobuf-dma-contig data
116 *
117 * This function resets the user space pointer
118 */
119static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
120{
121 mem->is_userptr = 0;
122 mem->dma_handle = 0;
123 mem->size = 0;
124}
125
126/**
127 * videobuf_dma_contig_user_get() - setup user space memory pointer
128 * @mem: per-buffer private videobuf-dma-contig data
129 * @vb: video buffer to map
130 *
131 * This function validates and sets up a pointer to user space memory.
132 * Only physically contiguous pfn-mapped memory is accepted.
133 *
134 * Returns 0 if successful.
135 */
136static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
137 struct videobuf_buffer *vb)
138{
139 struct mm_struct *mm = current->mm;
140 struct vm_area_struct *vma;
141 unsigned long prev_pfn, this_pfn;
142 unsigned long pages_done, user_address;
143 int ret;
144
145 mem->size = PAGE_ALIGN(vb->size);
146 mem->is_userptr = 0;
147 ret = -EINVAL;
148
149 down_read(&mm->mmap_sem);
150
151 vma = find_vma(mm, vb->baddr);
152 if (!vma)
153 goto out_up;
154
155 if ((vb->baddr + mem->size) > vma->vm_end)
156 goto out_up;
157
158 pages_done = 0;
159 prev_pfn = 0; /* kill warning */
160 user_address = vb->baddr;
161
162 while (pages_done < (mem->size >> PAGE_SHIFT)) {
163 ret = follow_pfn(vma, user_address, &this_pfn);
164 if (ret)
165 break;
166
167 if (pages_done == 0)
168 mem->dma_handle = this_pfn << PAGE_SHIFT;
169 else if (this_pfn != (prev_pfn + 1))
170 ret = -EFAULT;
171
172 if (ret)
173 break;
174
175 prev_pfn = this_pfn;
176 user_address += PAGE_SIZE;
177 pages_done++;
178 }
179
180 if (!ret)
181 mem->is_userptr = 1;
182
183 out_up:
184 up_read(&current->mm->mmap_sem);
185
186 return ret;
187}
188
111static void *__videobuf_alloc(size_t size) 189static void *__videobuf_alloc(size_t size)
112{ 190{
113 struct videobuf_dma_contig_memory *mem; 191 struct videobuf_dma_contig_memory *mem;
@@ -154,12 +232,11 @@ static int __videobuf_iolock(struct videobuf_queue *q,
154 case V4L2_MEMORY_USERPTR: 232 case V4L2_MEMORY_USERPTR:
155 dev_dbg(q->dev, "%s memory method USERPTR\n", __func__); 233 dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
156 234
157 /* The only USERPTR currently supported is the one needed for 235 /* handle pointer from user space */
158 read() method.
159 */
160 if (vb->baddr) 236 if (vb->baddr)
161 return -EINVAL; 237 return videobuf_dma_contig_user_get(mem, vb);
162 238
239 /* allocate memory for the read() method */
163 mem->size = PAGE_ALIGN(vb->size); 240 mem->size = PAGE_ALIGN(vb->size);
164 mem->vaddr = dma_alloc_coherent(q->dev, mem->size, 241 mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
165 &mem->dma_handle, GFP_KERNEL); 242 &mem->dma_handle, GFP_KERNEL);
@@ -400,7 +477,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
400 So, it should free memory only if the memory were allocated for 477 So, it should free memory only if the memory were allocated for
401 read() operation. 478 read() operation.
402 */ 479 */
403 if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) 480 if (buf->memory != V4L2_MEMORY_USERPTR)
404 return; 481 return;
405 482
406 if (!mem) 483 if (!mem)
@@ -408,6 +485,13 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
408 485
409 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 486 MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
410 487
488 /* handle user space pointer case */
489 if (buf->baddr) {
490 videobuf_dma_contig_user_put(mem);
491 return;
492 }
493
494 /* read() method */
411 dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); 495 dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
412 mem->vaddr = NULL; 496 mem->vaddr = NULL;
413} 497}