diff options
-rw-r--r-- | drivers/media/video/videobuf-dma-contig.c | 199 | ||||
-rw-r--r-- | include/media/videobuf-dma-contig.h | 10 |
2 files changed, 159 insertions, 50 deletions
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c9691115f2d2..b6b5cc1a43cb 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c | |||
@@ -27,6 +27,7 @@ struct videobuf_dma_contig_memory { | |||
27 | u32 magic; | 27 | u32 magic; |
28 | void *vaddr; | 28 | void *vaddr; |
29 | dma_addr_t dma_handle; | 29 | dma_addr_t dma_handle; |
30 | bool cached; | ||
30 | unsigned long size; | 31 | unsigned long size; |
31 | }; | 32 | }; |
32 | 33 | ||
@@ -37,8 +38,58 @@ struct videobuf_dma_contig_memory { | |||
37 | BUG(); \ | 38 | BUG(); \ |
38 | } | 39 | } |
39 | 40 | ||
40 | static void | 41 | static int __videobuf_dc_alloc(struct device *dev, |
41 | videobuf_vm_open(struct vm_area_struct *vma) | 42 | struct videobuf_dma_contig_memory *mem, |
43 | unsigned long size, unsigned long flags) | ||
44 | { | ||
45 | mem->size = size; | ||
46 | if (mem->cached) { | ||
47 | mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); | ||
48 | if (mem->vaddr) { | ||
49 | int err; | ||
50 | |||
51 | mem->dma_handle = dma_map_single(dev, mem->vaddr, | ||
52 | mem->size, | ||
53 | DMA_FROM_DEVICE); | ||
54 | err = dma_mapping_error(dev, mem->dma_handle); | ||
55 | if (err) { | ||
56 | dev_err(dev, "dma_map_single failed\n"); | ||
57 | |||
58 | free_pages_exact(mem->vaddr, mem->size); | ||
59 | mem->vaddr = 0; | ||
60 | return err; | ||
61 | } | ||
62 | } | ||
63 | } else | ||
64 | mem->vaddr = dma_alloc_coherent(dev, mem->size, | ||
65 | &mem->dma_handle, flags); | ||
66 | |||
67 | if (!mem->vaddr) { | ||
68 | dev_err(dev, "memory alloc size %ld failed\n", mem->size); | ||
69 | return -ENOMEM; | ||
70 | } | ||
71 | |||
72 | dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static void __videobuf_dc_free(struct device *dev, | ||
78 | struct videobuf_dma_contig_memory *mem) | ||
79 | { | ||
80 | if (mem->cached) { | ||
81 | if (!mem->vaddr) | ||
82 | return; | ||
83 | dma_unmap_single(dev, mem->dma_handle, mem->size, | ||
84 | DMA_FROM_DEVICE); | ||
85 | free_pages_exact(mem->vaddr, mem->size); | ||
86 | } else | ||
87 | dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); | ||
88 | |||
89 | mem->vaddr = NULL; | ||
90 | } | ||
91 | |||
92 | static void videobuf_vm_open(struct vm_area_struct *vma) | ||
42 | { | 93 | { |
43 | struct videobuf_mapping *map = vma->vm_private_data; | 94 | struct videobuf_mapping *map = vma->vm_private_data; |
44 | 95 | ||
@@ -91,12 +142,11 @@ static void videobuf_vm_close(struct vm_area_struct *vma) | |||
91 | dev_dbg(q->dev, "buf[%d] freeing %p\n", | 142 | dev_dbg(q->dev, "buf[%d] freeing %p\n", |
92 | i, mem->vaddr); | 143 | i, mem->vaddr); |
93 | 144 | ||
94 | dma_free_coherent(q->dev, mem->size, | 145 | __videobuf_dc_free(q->dev, mem); |
95 | mem->vaddr, mem->dma_handle); | ||
96 | mem->vaddr = NULL; | 146 | mem->vaddr = NULL; |
97 | } | 147 | } |
98 | 148 | ||
99 | q->bufs[i]->map = NULL; | 149 | q->bufs[i]->map = NULL; |
100 | q->bufs[i]->baddr = 0; | 150 | q->bufs[i]->baddr = 0; |
101 | } | 151 | } |
102 | 152 | ||
@@ -107,8 +157,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma) | |||
107 | } | 157 | } |
108 | 158 | ||
109 | static const struct vm_operations_struct videobuf_vm_ops = { | 159 | static const struct vm_operations_struct videobuf_vm_ops = { |
110 | .open = videobuf_vm_open, | 160 | .open = videobuf_vm_open, |
111 | .close = videobuf_vm_close, | 161 | .close = videobuf_vm_close, |
112 | }; | 162 | }; |
113 | 163 | ||
114 | /** | 164 | /** |
@@ -178,26 +228,38 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, | |||
178 | pages_done++; | 228 | pages_done++; |
179 | } | 229 | } |
180 | 230 | ||
181 | out_up: | 231 | out_up: |
182 | up_read(¤t->mm->mmap_sem); | 232 | up_read(¤t->mm->mmap_sem); |
183 | 233 | ||
184 | return ret; | 234 | return ret; |
185 | } | 235 | } |
186 | 236 | ||
187 | static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) | 237 | static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) |
188 | { | 238 | { |
189 | struct videobuf_dma_contig_memory *mem; | 239 | struct videobuf_dma_contig_memory *mem; |
190 | struct videobuf_buffer *vb; | 240 | struct videobuf_buffer *vb; |
191 | 241 | ||
192 | vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); | 242 | vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); |
193 | if (vb) { | 243 | if (vb) { |
194 | mem = vb->priv = ((char *)vb) + size; | 244 | vb->priv = ((char *)vb) + size; |
245 | mem = vb->priv; | ||
195 | mem->magic = MAGIC_DC_MEM; | 246 | mem->magic = MAGIC_DC_MEM; |
247 | mem->cached = cached; | ||
196 | } | 248 | } |
197 | 249 | ||
198 | return vb; | 250 | return vb; |
199 | } | 251 | } |
200 | 252 | ||
253 | static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) | ||
254 | { | ||
255 | return __videobuf_alloc_vb(size, false); | ||
256 | } | ||
257 | |||
258 | static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) | ||
259 | { | ||
260 | return __videobuf_alloc_vb(size, true); | ||
261 | } | ||
262 | |||
201 | static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) | 263 | static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) |
202 | { | 264 | { |
203 | struct videobuf_dma_contig_memory *mem = buf->priv; | 265 | struct videobuf_dma_contig_memory *mem = buf->priv; |
@@ -235,28 +297,32 @@ static int __videobuf_iolock(struct videobuf_queue *q, | |||
235 | return videobuf_dma_contig_user_get(mem, vb); | 297 | return videobuf_dma_contig_user_get(mem, vb); |
236 | 298 | ||
237 | /* allocate memory for the read() method */ | 299 | /* allocate memory for the read() method */ |
238 | mem->size = PAGE_ALIGN(vb->size); | 300 | if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size), |
239 | mem->vaddr = dma_alloc_coherent(q->dev, mem->size, | 301 | GFP_KERNEL)) |
240 | &mem->dma_handle, GFP_KERNEL); | ||
241 | if (!mem->vaddr) { | ||
242 | dev_err(q->dev, "dma_alloc_coherent %ld failed\n", | ||
243 | mem->size); | ||
244 | return -ENOMEM; | 302 | return -ENOMEM; |
245 | } | ||
246 | |||
247 | dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n", | ||
248 | mem->vaddr, mem->size); | ||
249 | break; | 303 | break; |
250 | case V4L2_MEMORY_OVERLAY: | 304 | case V4L2_MEMORY_OVERLAY: |
251 | default: | 305 | default: |
252 | dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", | 306 | dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__); |
253 | __func__); | ||
254 | return -EINVAL; | 307 | return -EINVAL; |
255 | } | 308 | } |
256 | 309 | ||
257 | return 0; | 310 | return 0; |
258 | } | 311 | } |
259 | 312 | ||
313 | static int __videobuf_sync(struct videobuf_queue *q, | ||
314 | struct videobuf_buffer *buf) | ||
315 | { | ||
316 | struct videobuf_dma_contig_memory *mem = buf->priv; | ||
317 | BUG_ON(!mem); | ||
318 | MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); | ||
319 | |||
320 | dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, | ||
321 | DMA_FROM_DEVICE); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
260 | static int __videobuf_mmap_mapper(struct videobuf_queue *q, | 326 | static int __videobuf_mmap_mapper(struct videobuf_queue *q, |
261 | struct videobuf_buffer *buf, | 327 | struct videobuf_buffer *buf, |
262 | struct vm_area_struct *vma) | 328 | struct vm_area_struct *vma) |
@@ -265,6 +331,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, | |||
265 | struct videobuf_mapping *map; | 331 | struct videobuf_mapping *map; |
266 | int retval; | 332 | int retval; |
267 | unsigned long size; | 333 | unsigned long size; |
334 | unsigned long pos, start = vma->vm_start; | ||
335 | struct page *page; | ||
268 | 336 | ||
269 | dev_dbg(q->dev, "%s\n", __func__); | 337 | dev_dbg(q->dev, "%s\n", __func__); |
270 | 338 | ||
@@ -282,41 +350,50 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, | |||
282 | BUG_ON(!mem); | 350 | BUG_ON(!mem); |
283 | MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); | 351 | MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); |
284 | 352 | ||
285 | mem->size = PAGE_ALIGN(buf->bsize); | 353 | if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize), |
286 | mem->vaddr = dma_alloc_coherent(q->dev, mem->size, | 354 | GFP_KERNEL | __GFP_COMP)) |
287 | &mem->dma_handle, GFP_KERNEL); | ||
288 | if (!mem->vaddr) { | ||
289 | dev_err(q->dev, "dma_alloc_coherent size %ld failed\n", | ||
290 | mem->size); | ||
291 | goto error; | 355 | goto error; |
292 | } | ||
293 | dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n", | ||
294 | mem->vaddr, mem->size); | ||
295 | 356 | ||
296 | /* Try to remap memory */ | 357 | /* Try to remap memory */ |
297 | 358 | ||
298 | size = vma->vm_end - vma->vm_start; | 359 | size = vma->vm_end - vma->vm_start; |
299 | size = (size < mem->size) ? size : mem->size; | 360 | size = (size < mem->size) ? size : mem->size; |
300 | 361 | ||
301 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | 362 | if (!mem->cached) |
302 | retval = remap_pfn_range(vma, vma->vm_start, | 363 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
303 | mem->dma_handle >> PAGE_SHIFT, | 364 | |
304 | size, vma->vm_page_prot); | 365 | pos = (unsigned long)mem->vaddr; |
305 | if (retval) { | 366 | |
306 | dev_err(q->dev, "mmap: remap failed with error %d. ", retval); | 367 | while (size > 0) { |
307 | dma_free_coherent(q->dev, mem->size, | 368 | page = virt_to_page((void *)pos); |
308 | mem->vaddr, mem->dma_handle); | 369 | if (NULL == page) { |
309 | goto error; | 370 | dev_err(q->dev, "mmap: virt_to_page failed\n"); |
371 | __videobuf_dc_free(q->dev, mem); | ||
372 | goto error; | ||
373 | } | ||
374 | retval = vm_insert_page(vma, start, page); | ||
375 | if (retval) { | ||
376 | dev_err(q->dev, "mmap: insert failed with error %d\n", | ||
377 | retval); | ||
378 | __videobuf_dc_free(q->dev, mem); | ||
379 | goto error; | ||
380 | } | ||
381 | start += PAGE_SIZE; | ||
382 | pos += PAGE_SIZE; | ||
383 | |||
384 | if (size > PAGE_SIZE) | ||
385 | size -= PAGE_SIZE; | ||
386 | else | ||
387 | size = 0; | ||
310 | } | 388 | } |
311 | 389 | ||
312 | vma->vm_ops = &videobuf_vm_ops; | 390 | vma->vm_ops = &videobuf_vm_ops; |
313 | vma->vm_flags |= VM_DONTEXPAND; | 391 | vma->vm_flags |= VM_DONTEXPAND; |
314 | vma->vm_private_data = map; | 392 | vma->vm_private_data = map; |
315 | 393 | ||
316 | dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", | 394 | dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", |
317 | map, q, vma->vm_start, vma->vm_end, | 395 | map, q, vma->vm_start, vma->vm_end, |
318 | (long int)buf->bsize, | 396 | (long int)buf->bsize, vma->vm_pgoff, buf->i); |
319 | vma->vm_pgoff, buf->i); | ||
320 | 397 | ||
321 | videobuf_vm_open(vma); | 398 | videobuf_vm_open(vma); |
322 | 399 | ||
@@ -328,12 +405,20 @@ error: | |||
328 | } | 405 | } |
329 | 406 | ||
330 | static struct videobuf_qtype_ops qops = { | 407 | static struct videobuf_qtype_ops qops = { |
331 | .magic = MAGIC_QTYPE_OPS, | 408 | .magic = MAGIC_QTYPE_OPS, |
409 | .alloc_vb = __videobuf_alloc_uncached, | ||
410 | .iolock = __videobuf_iolock, | ||
411 | .mmap_mapper = __videobuf_mmap_mapper, | ||
412 | .vaddr = __videobuf_to_vaddr, | ||
413 | }; | ||
332 | 414 | ||
333 | .alloc_vb = __videobuf_alloc_vb, | 415 | static struct videobuf_qtype_ops qops_cached = { |
334 | .iolock = __videobuf_iolock, | 416 | .magic = MAGIC_QTYPE_OPS, |
335 | .mmap_mapper = __videobuf_mmap_mapper, | 417 | .alloc_vb = __videobuf_alloc_cached, |
336 | .vaddr = __videobuf_to_vaddr, | 418 | .iolock = __videobuf_iolock, |
419 | .sync = __videobuf_sync, | ||
420 | .mmap_mapper = __videobuf_mmap_mapper, | ||
421 | .vaddr = __videobuf_to_vaddr, | ||
337 | }; | 422 | }; |
338 | 423 | ||
339 | void videobuf_queue_dma_contig_init(struct videobuf_queue *q, | 424 | void videobuf_queue_dma_contig_init(struct videobuf_queue *q, |
@@ -351,6 +436,20 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, | |||
351 | } | 436 | } |
352 | EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); | 437 | EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); |
353 | 438 | ||
439 | void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, | ||
440 | const struct videobuf_queue_ops *ops, | ||
441 | struct device *dev, | ||
442 | spinlock_t *irqlock, | ||
443 | enum v4l2_buf_type type, | ||
444 | enum v4l2_field field, | ||
445 | unsigned int msize, | ||
446 | void *priv, struct mutex *ext_lock) | ||
447 | { | ||
448 | videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, | ||
449 | priv, &qops_cached, ext_lock); | ||
450 | } | ||
451 | EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); | ||
452 | |||
354 | dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) | 453 | dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) |
355 | { | 454 | { |
356 | struct videobuf_dma_contig_memory *mem = buf->priv; | 455 | struct videobuf_dma_contig_memory *mem = buf->priv; |
@@ -389,7 +488,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, | |||
389 | 488 | ||
390 | /* read() method */ | 489 | /* read() method */ |
391 | if (mem->vaddr) { | 490 | if (mem->vaddr) { |
392 | dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); | 491 | __videobuf_dc_free(q->dev, mem); |
393 | mem->vaddr = NULL; | 492 | mem->vaddr = NULL; |
394 | } | 493 | } |
395 | } | 494 | } |
diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h index f0ed82543d9f..f473aeb86d3f 100644 --- a/include/media/videobuf-dma-contig.h +++ b/include/media/videobuf-dma-contig.h | |||
@@ -26,6 +26,16 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, | |||
26 | void *priv, | 26 | void *priv, |
27 | struct mutex *ext_lock); | 27 | struct mutex *ext_lock); |
28 | 28 | ||
29 | void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, | ||
30 | const struct videobuf_queue_ops *ops, | ||
31 | struct device *dev, | ||
32 | spinlock_t *irqlock, | ||
33 | enum v4l2_buf_type type, | ||
34 | enum v4l2_field field, | ||
35 | unsigned int msize, | ||
36 | void *priv, | ||
37 | struct mutex *ext_lock); | ||
38 | |||
29 | dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); | 39 | dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); |
30 | void videobuf_dma_contig_free(struct videobuf_queue *q, | 40 | void videobuf_dma_contig_free(struct videobuf_queue *q, |
31 | struct videobuf_buffer *buf); | 41 | struct videobuf_buffer *buf); |