aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Harper <james.harper@ejbdigital.com.au>2014-06-12 05:53:38 -0400
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-07-26 10:33:15 -0400
commit7b4eeed174b71c325705ff8c53f333bc79d0ee7a (patch)
tree3f23ad213911ab98dd30694109c6c0bcfe17afd7
parentb601fe5688ae285693c64e833003c14acb38378a (diff)
[media] vmalloc_sg: make sure all pages in vmalloc area are really DMA-ready
Patch originally written by Konrad. Rebased on current linux media tree. Under Xen, vmalloc_32() isn't guaranteed to return pages which are really under 4G in machine physical addresses (only in virtual pseudo-physical addresses). To work around this, implement a vmalloc variant which allocates each page with dma_alloc_coherent() to guarantee that each page is suitable for the device in question. Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Signed-off-by: James Harper <james.harper@ejbdigital.com.au> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c62
-rw-r--r--include/media/videobuf-dma-sg.h3
2 files changed, 61 insertions, 4 deletions
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 828e7c10bd70..3c8cc023a5a5 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
211int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, 211int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
212 int nr_pages) 212 int nr_pages)
213{ 213{
214 int i;
215
214 dprintk(1, "init kernel [%d pages]\n", nr_pages); 216 dprintk(1, "init kernel [%d pages]\n", nr_pages);
215 217
216 dma->direction = direction; 218 dma->direction = direction;
217 dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); 219 dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages),
220 GFP_KERNEL);
221 if (!dma->vaddr_pages)
222 return -ENOMEM;
223
224 dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL);
225 if (!dma->dma_addr) {
226 kfree(dma->vaddr_pages);
227 return -ENOMEM;
228 }
229 for (i = 0; i < nr_pages; i++) {
230 void *addr;
231
232 addr = dma_alloc_coherent(dma->dev, PAGE_SIZE,
233 &(dma->dma_addr[i]), GFP_KERNEL);
234 if (addr == NULL)
235 goto out_free_pages;
236
237 dma->vaddr_pages[i] = virt_to_page(addr);
238 }
239 dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP,
240 PAGE_KERNEL);
218 if (NULL == dma->vaddr) { 241 if (NULL == dma->vaddr) {
219 dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); 242 dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
220 return -ENOMEM; 243 goto out_free_pages;
221 } 244 }
222 245
223 dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", 246 dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
@@ -228,6 +251,19 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
228 dma->nr_pages = nr_pages; 251 dma->nr_pages = nr_pages;
229 252
230 return 0; 253 return 0;
254out_free_pages:
255 while (i > 0) {
256 void *addr = page_address(dma->vaddr_pages[i]);
257 dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
258 i--;
259 }
260 kfree(dma->dma_addr);
261 dma->dma_addr = NULL;
262 kfree(dma->vaddr_pages);
263 dma->vaddr_pages = NULL;
264
265 return -ENOMEM;
266
231} 267}
232EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); 268EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
233 269
@@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
322 dma->pages = NULL; 358 dma->pages = NULL;
323 } 359 }
324 360
325 vfree(dma->vaddr); 361 if (dma->dma_addr) {
326 dma->vaddr = NULL; 362 for (i = 0; i < dma->nr_pages; i++) {
363 void *addr;
364
365 addr = page_address(dma->vaddr_pages[i]);
366 dma_free_coherent(dma->dev, PAGE_SIZE, addr,
367 dma->dma_addr[i]);
368 }
369 kfree(dma->dma_addr);
370 dma->dma_addr = NULL;
371 kfree(dma->vaddr_pages);
372 dma->vaddr_pages = NULL;
373 vunmap(dma->vaddr);
374 dma->vaddr = NULL;
375 }
327 376
328 if (dma->bus_addr) 377 if (dma->bus_addr)
329 dma->bus_addr = 0; 378 dma->bus_addr = 0;
@@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q,
461 510
462 MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); 511 MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
463 512
513 if (!mem->dma.dev)
514 mem->dma.dev = q->dev;
515 else
516 WARN_ON(mem->dma.dev != q->dev);
517
464 switch (vb->memory) { 518 switch (vb->memory) {
465 case V4L2_MEMORY_MMAP: 519 case V4L2_MEMORY_MMAP:
466 case V4L2_MEMORY_USERPTR: 520 case V4L2_MEMORY_USERPTR:
diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h
index d8fb6012c10d..fb6fd4d8f4ed 100644
--- a/include/media/videobuf-dma-sg.h
+++ b/include/media/videobuf-dma-sg.h
@@ -53,6 +53,9 @@ struct videobuf_dmabuf {
53 53
54 /* for kernel buffers */ 54 /* for kernel buffers */
55 void *vaddr; 55 void *vaddr;
56 struct page **vaddr_pages;
57 dma_addr_t *dma_addr;
58 struct device *dev;
56 59
57 /* for overlay buffers (pci-pci dma) */ 60 /* for overlay buffers (pci-pci dma) */
58 dma_addr_t bus_addr; 61 dma_addr_t bus_addr;