Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b4eeed1 authored by James Harper's avatar James Harper Committed by Mauro Carvalho Chehab
Browse files

[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: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: default avatarJames Harper <james.harper@ejbdigital.com.au>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent b601fe56
Loading
Loading
Loading
Loading
+58 −4
Original line number Diff line number Diff line
@@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
			     int nr_pages)
{
	int i;

	dprintk(1, "init kernel [%d pages]\n", nr_pages);

	dma->direction = direction;
	dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
	dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages),
				   GFP_KERNEL);
	if (!dma->vaddr_pages)
		return -ENOMEM;

	dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL);
	if (!dma->dma_addr) {
		kfree(dma->vaddr_pages);
		return -ENOMEM;
	}
	for (i = 0; i < nr_pages; i++) {
		void *addr;

		addr = dma_alloc_coherent(dma->dev, PAGE_SIZE,
					  &(dma->dma_addr[i]), GFP_KERNEL);
		if (addr == NULL)
			goto out_free_pages;

		dma->vaddr_pages[i] = virt_to_page(addr);
	}
	dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP,
			  PAGE_KERNEL);
	if (NULL == dma->vaddr) {
		dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
		return -ENOMEM;
		goto out_free_pages;
	}

	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,
	dma->nr_pages = nr_pages;

	return 0;
out_free_pages:
	while (i > 0) {
		void *addr = page_address(dma->vaddr_pages[i]);
		dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
		i--;
	}
	kfree(dma->dma_addr);
	dma->dma_addr = NULL;
	kfree(dma->vaddr_pages);
	dma->vaddr_pages = NULL;

	return -ENOMEM;

}
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);

@@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
		dma->pages = NULL;
	}

	vfree(dma->vaddr);
	if (dma->dma_addr) {
		for (i = 0; i < dma->nr_pages; i++) {
			void *addr;

			addr = page_address(dma->vaddr_pages[i]);
			dma_free_coherent(dma->dev, PAGE_SIZE, addr,
					  dma->dma_addr[i]);
		}
		kfree(dma->dma_addr);
		dma->dma_addr = NULL;
		kfree(dma->vaddr_pages);
		dma->vaddr_pages = NULL;
		vunmap(dma->vaddr);
		dma->vaddr = NULL;
	}

	if (dma->bus_addr)
		dma->bus_addr = 0;
@@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q,

	MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);

	if (!mem->dma.dev)
		mem->dma.dev = q->dev;
	else
		WARN_ON(mem->dma.dev != q->dev);

	switch (vb->memory) {
	case V4L2_MEMORY_MMAP:
	case V4L2_MEMORY_USERPTR:
+3 −0
Original line number Diff line number Diff line
@@ -53,6 +53,9 @@ struct videobuf_dmabuf {

	/* for kernel buffers */
	void                *vaddr;
	struct page         **vaddr_pages;
	dma_addr_t          *dma_addr;
	struct device       *dev;

	/* for overlay buffers (pci-pci dma) */
	dma_addr_t          bus_addr;