15bc3cb74SMauro Carvalho Chehab /*
25bc3cb74SMauro Carvalho Chehab  * helper functions for physically contiguous capture buffers
35bc3cb74SMauro Carvalho Chehab  *
45bc3cb74SMauro Carvalho Chehab  * The functions support hardware lacking scatter gather support
55bc3cb74SMauro Carvalho Chehab  * (i.e. the buffers must be linear in physical memory)
65bc3cb74SMauro Carvalho Chehab  *
75bc3cb74SMauro Carvalho Chehab  * Copyright (c) 2008 Magnus Damm
85bc3cb74SMauro Carvalho Chehab  *
95bc3cb74SMauro Carvalho Chehab  * Based on videobuf-vmalloc.c,
105bc3cb74SMauro Carvalho Chehab  * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
115bc3cb74SMauro Carvalho Chehab  *
125bc3cb74SMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or modify
135bc3cb74SMauro Carvalho Chehab  * it under the terms of the GNU General Public License as published by
145bc3cb74SMauro Carvalho Chehab  * the Free Software Foundation; either version 2
155bc3cb74SMauro Carvalho Chehab  */
165bc3cb74SMauro Carvalho Chehab 
175bc3cb74SMauro Carvalho Chehab #include <linux/init.h>
185bc3cb74SMauro Carvalho Chehab #include <linux/module.h>
195bc3cb74SMauro Carvalho Chehab #include <linux/mm.h>
205bc3cb74SMauro Carvalho Chehab #include <linux/pagemap.h>
215bc3cb74SMauro Carvalho Chehab #include <linux/dma-mapping.h>
225bc3cb74SMauro Carvalho Chehab #include <linux/sched.h>
235bc3cb74SMauro Carvalho Chehab #include <linux/slab.h>
245bc3cb74SMauro Carvalho Chehab #include <media/videobuf-dma-contig.h>
255bc3cb74SMauro Carvalho Chehab 
265bc3cb74SMauro Carvalho Chehab struct videobuf_dma_contig_memory {
275bc3cb74SMauro Carvalho Chehab 	u32 magic;
285bc3cb74SMauro Carvalho Chehab 	void *vaddr;
295bc3cb74SMauro Carvalho Chehab 	dma_addr_t dma_handle;
305bc3cb74SMauro Carvalho Chehab 	unsigned long size;
315bc3cb74SMauro Carvalho Chehab };
325bc3cb74SMauro Carvalho Chehab 
335bc3cb74SMauro Carvalho Chehab #define MAGIC_DC_MEM 0x0733ac61
345bc3cb74SMauro Carvalho Chehab #define MAGIC_CHECK(is, should)						    \
355bc3cb74SMauro Carvalho Chehab 	if (unlikely((is) != (should)))	{				    \
365bc3cb74SMauro Carvalho Chehab 		pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
375bc3cb74SMauro Carvalho Chehab 		BUG();							    \
385bc3cb74SMauro Carvalho Chehab 	}
395bc3cb74SMauro Carvalho Chehab 
405bc3cb74SMauro Carvalho Chehab static int __videobuf_dc_alloc(struct device *dev,
415bc3cb74SMauro Carvalho Chehab 			       struct videobuf_dma_contig_memory *mem,
425bc3cb74SMauro Carvalho Chehab 			       unsigned long size, gfp_t flags)
435bc3cb74SMauro Carvalho Chehab {
445bc3cb74SMauro Carvalho Chehab 	mem->size = size;
455bc3cb74SMauro Carvalho Chehab 	mem->vaddr = dma_alloc_coherent(dev, mem->size,
465bc3cb74SMauro Carvalho Chehab 					&mem->dma_handle, flags);
475bc3cb74SMauro Carvalho Chehab 
485bc3cb74SMauro Carvalho Chehab 	if (!mem->vaddr) {
495bc3cb74SMauro Carvalho Chehab 		dev_err(dev, "memory alloc size %ld failed\n", mem->size);
505bc3cb74SMauro Carvalho Chehab 		return -ENOMEM;
515bc3cb74SMauro Carvalho Chehab 	}
525bc3cb74SMauro Carvalho Chehab 
535bc3cb74SMauro Carvalho Chehab 	dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
545bc3cb74SMauro Carvalho Chehab 
555bc3cb74SMauro Carvalho Chehab 	return 0;
565bc3cb74SMauro Carvalho Chehab }
575bc3cb74SMauro Carvalho Chehab 
585bc3cb74SMauro Carvalho Chehab static void __videobuf_dc_free(struct device *dev,
595bc3cb74SMauro Carvalho Chehab 			       struct videobuf_dma_contig_memory *mem)
605bc3cb74SMauro Carvalho Chehab {
615bc3cb74SMauro Carvalho Chehab 	dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
625bc3cb74SMauro Carvalho Chehab 
635bc3cb74SMauro Carvalho Chehab 	mem->vaddr = NULL;
645bc3cb74SMauro Carvalho Chehab }
655bc3cb74SMauro Carvalho Chehab 
665bc3cb74SMauro Carvalho Chehab static void videobuf_vm_open(struct vm_area_struct *vma)
675bc3cb74SMauro Carvalho Chehab {
685bc3cb74SMauro Carvalho Chehab 	struct videobuf_mapping *map = vma->vm_private_data;
69a242f426SAl Viro 	struct videobuf_queue *q = map->q;
705bc3cb74SMauro Carvalho Chehab 
71a242f426SAl Viro 	dev_dbg(q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
725bc3cb74SMauro Carvalho Chehab 		map, map->count, vma->vm_start, vma->vm_end);
735bc3cb74SMauro Carvalho Chehab 
74a242f426SAl Viro 	videobuf_queue_lock(q);
755bc3cb74SMauro Carvalho Chehab 	map->count++;
76a242f426SAl Viro 	videobuf_queue_unlock(q);
775bc3cb74SMauro Carvalho Chehab }
785bc3cb74SMauro Carvalho Chehab 
795bc3cb74SMauro Carvalho Chehab static void videobuf_vm_close(struct vm_area_struct *vma)
805bc3cb74SMauro Carvalho Chehab {
815bc3cb74SMauro Carvalho Chehab 	struct videobuf_mapping *map = vma->vm_private_data;
825bc3cb74SMauro Carvalho Chehab 	struct videobuf_queue *q = map->q;
835bc3cb74SMauro Carvalho Chehab 	int i;
845bc3cb74SMauro Carvalho Chehab 
855bc3cb74SMauro Carvalho Chehab 	dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
865bc3cb74SMauro Carvalho Chehab 		map, map->count, vma->vm_start, vma->vm_end);
875bc3cb74SMauro Carvalho Chehab 
88a242f426SAl Viro 	videobuf_queue_lock(q);
89a242f426SAl Viro 	if (!--map->count) {
905bc3cb74SMauro Carvalho Chehab 		struct videobuf_dma_contig_memory *mem;
915bc3cb74SMauro Carvalho Chehab 
925bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
935bc3cb74SMauro Carvalho Chehab 
945bc3cb74SMauro Carvalho Chehab 		/* We need first to cancel streams, before unmapping */
955bc3cb74SMauro Carvalho Chehab 		if (q->streaming)
965bc3cb74SMauro Carvalho Chehab 			videobuf_queue_cancel(q);
975bc3cb74SMauro Carvalho Chehab 
985bc3cb74SMauro Carvalho Chehab 		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
995bc3cb74SMauro Carvalho Chehab 			if (NULL == q->bufs[i])
1005bc3cb74SMauro Carvalho Chehab 				continue;
1015bc3cb74SMauro Carvalho Chehab 
1025bc3cb74SMauro Carvalho Chehab 			if (q->bufs[i]->map != map)
1035bc3cb74SMauro Carvalho Chehab 				continue;
1045bc3cb74SMauro Carvalho Chehab 
1055bc3cb74SMauro Carvalho Chehab 			mem = q->bufs[i]->priv;
1065bc3cb74SMauro Carvalho Chehab 			if (mem) {
1075bc3cb74SMauro Carvalho Chehab 				/* This callback is called only if kernel has
1085bc3cb74SMauro Carvalho Chehab 				   allocated memory and this memory is mmapped.
1095bc3cb74SMauro Carvalho Chehab 				   In this case, memory should be freed,
1105bc3cb74SMauro Carvalho Chehab 				   in order to do memory unmap.
1115bc3cb74SMauro Carvalho Chehab 				 */
1125bc3cb74SMauro Carvalho Chehab 
1135bc3cb74SMauro Carvalho Chehab 				MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
1145bc3cb74SMauro Carvalho Chehab 
1155bc3cb74SMauro Carvalho Chehab 				/* vfree is not atomic - can't be
1165bc3cb74SMauro Carvalho Chehab 				   called with IRQ's disabled
1175bc3cb74SMauro Carvalho Chehab 				 */
1185bc3cb74SMauro Carvalho Chehab 				dev_dbg(q->dev, "buf[%d] freeing %p\n",
1195bc3cb74SMauro Carvalho Chehab 					i, mem->vaddr);
1205bc3cb74SMauro Carvalho Chehab 
1215bc3cb74SMauro Carvalho Chehab 				__videobuf_dc_free(q->dev, mem);
1225bc3cb74SMauro Carvalho Chehab 				mem->vaddr = NULL;
1235bc3cb74SMauro Carvalho Chehab 			}
1245bc3cb74SMauro Carvalho Chehab 
1255bc3cb74SMauro Carvalho Chehab 			q->bufs[i]->map = NULL;
1265bc3cb74SMauro Carvalho Chehab 			q->bufs[i]->baddr = 0;
1275bc3cb74SMauro Carvalho Chehab 		}
1285bc3cb74SMauro Carvalho Chehab 
1295bc3cb74SMauro Carvalho Chehab 		kfree(map);
1305bc3cb74SMauro Carvalho Chehab 
1315bc3cb74SMauro Carvalho Chehab 	}
132a242f426SAl Viro 	videobuf_queue_unlock(q);
1335bc3cb74SMauro Carvalho Chehab }
1345bc3cb74SMauro Carvalho Chehab 
1355bc3cb74SMauro Carvalho Chehab static const struct vm_operations_struct videobuf_vm_ops = {
1365bc3cb74SMauro Carvalho Chehab 	.open	= videobuf_vm_open,
1375bc3cb74SMauro Carvalho Chehab 	.close	= videobuf_vm_close,
1385bc3cb74SMauro Carvalho Chehab };
1395bc3cb74SMauro Carvalho Chehab 
1405bc3cb74SMauro Carvalho Chehab /**
1415bc3cb74SMauro Carvalho Chehab  * videobuf_dma_contig_user_put() - reset pointer to user space buffer
1425bc3cb74SMauro Carvalho Chehab  * @mem: per-buffer private videobuf-dma-contig data
1435bc3cb74SMauro Carvalho Chehab  *
1445bc3cb74SMauro Carvalho Chehab  * This function resets the user space pointer
1455bc3cb74SMauro Carvalho Chehab  */
1465bc3cb74SMauro Carvalho Chehab static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
1475bc3cb74SMauro Carvalho Chehab {
1485bc3cb74SMauro Carvalho Chehab 	mem->dma_handle = 0;
1495bc3cb74SMauro Carvalho Chehab 	mem->size = 0;
1505bc3cb74SMauro Carvalho Chehab }
1515bc3cb74SMauro Carvalho Chehab 
1525bc3cb74SMauro Carvalho Chehab /**
1535bc3cb74SMauro Carvalho Chehab  * videobuf_dma_contig_user_get() - setup user space memory pointer
1545bc3cb74SMauro Carvalho Chehab  * @mem: per-buffer private videobuf-dma-contig data
1555bc3cb74SMauro Carvalho Chehab  * @vb: video buffer to map
1565bc3cb74SMauro Carvalho Chehab  *
1575bc3cb74SMauro Carvalho Chehab  * This function validates and sets up a pointer to user space memory.
1585bc3cb74SMauro Carvalho Chehab  * Only physically contiguous pfn-mapped memory is accepted.
1595bc3cb74SMauro Carvalho Chehab  *
1605bc3cb74SMauro Carvalho Chehab  * Returns 0 if successful.
1615bc3cb74SMauro Carvalho Chehab  */
1625bc3cb74SMauro Carvalho Chehab static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
1635bc3cb74SMauro Carvalho Chehab 					struct videobuf_buffer *vb)
1645bc3cb74SMauro Carvalho Chehab {
1655bc3cb74SMauro Carvalho Chehab 	struct mm_struct *mm = current->mm;
1665bc3cb74SMauro Carvalho Chehab 	struct vm_area_struct *vma;
1675bc3cb74SMauro Carvalho Chehab 	unsigned long prev_pfn, this_pfn;
1685bc3cb74SMauro Carvalho Chehab 	unsigned long pages_done, user_address;
1695bc3cb74SMauro Carvalho Chehab 	unsigned int offset;
1705bc3cb74SMauro Carvalho Chehab 	int ret;
1715bc3cb74SMauro Carvalho Chehab 
1725bc3cb74SMauro Carvalho Chehab 	offset = vb->baddr & ~PAGE_MASK;
1735bc3cb74SMauro Carvalho Chehab 	mem->size = PAGE_ALIGN(vb->size + offset);
1745bc3cb74SMauro Carvalho Chehab 	ret = -EINVAL;
1755bc3cb74SMauro Carvalho Chehab 
1765bc3cb74SMauro Carvalho Chehab 	down_read(&mm->mmap_sem);
1775bc3cb74SMauro Carvalho Chehab 
1785bc3cb74SMauro Carvalho Chehab 	vma = find_vma(mm, vb->baddr);
1795bc3cb74SMauro Carvalho Chehab 	if (!vma)
1805bc3cb74SMauro Carvalho Chehab 		goto out_up;
1815bc3cb74SMauro Carvalho Chehab 
1825bc3cb74SMauro Carvalho Chehab 	if ((vb->baddr + mem->size) > vma->vm_end)
1835bc3cb74SMauro Carvalho Chehab 		goto out_up;
1845bc3cb74SMauro Carvalho Chehab 
1855bc3cb74SMauro Carvalho Chehab 	pages_done = 0;
1865bc3cb74SMauro Carvalho Chehab 	prev_pfn = 0; /* kill warning */
1875bc3cb74SMauro Carvalho Chehab 	user_address = vb->baddr;
1885bc3cb74SMauro Carvalho Chehab 
1895bc3cb74SMauro Carvalho Chehab 	while (pages_done < (mem->size >> PAGE_SHIFT)) {
1905bc3cb74SMauro Carvalho Chehab 		ret = follow_pfn(vma, user_address, &this_pfn);
1915bc3cb74SMauro Carvalho Chehab 		if (ret)
1925bc3cb74SMauro Carvalho Chehab 			break;
1935bc3cb74SMauro Carvalho Chehab 
1945bc3cb74SMauro Carvalho Chehab 		if (pages_done == 0)
1955bc3cb74SMauro Carvalho Chehab 			mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
1965bc3cb74SMauro Carvalho Chehab 		else if (this_pfn != (prev_pfn + 1))
1975bc3cb74SMauro Carvalho Chehab 			ret = -EFAULT;
1985bc3cb74SMauro Carvalho Chehab 
1995bc3cb74SMauro Carvalho Chehab 		if (ret)
2005bc3cb74SMauro Carvalho Chehab 			break;
2015bc3cb74SMauro Carvalho Chehab 
2025bc3cb74SMauro Carvalho Chehab 		prev_pfn = this_pfn;
2035bc3cb74SMauro Carvalho Chehab 		user_address += PAGE_SIZE;
2045bc3cb74SMauro Carvalho Chehab 		pages_done++;
2055bc3cb74SMauro Carvalho Chehab 	}
2065bc3cb74SMauro Carvalho Chehab 
2075bc3cb74SMauro Carvalho Chehab out_up:
2085bc3cb74SMauro Carvalho Chehab 	up_read(&current->mm->mmap_sem);
2095bc3cb74SMauro Carvalho Chehab 
2105bc3cb74SMauro Carvalho Chehab 	return ret;
2115bc3cb74SMauro Carvalho Chehab }
2125bc3cb74SMauro Carvalho Chehab 
213cb132cd5SMauro Carvalho Chehab static struct videobuf_buffer *__videobuf_alloc(size_t size)
2145bc3cb74SMauro Carvalho Chehab {
2155bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem;
2165bc3cb74SMauro Carvalho Chehab 	struct videobuf_buffer *vb;
2175bc3cb74SMauro Carvalho Chehab 
2185bc3cb74SMauro Carvalho Chehab 	vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
2195bc3cb74SMauro Carvalho Chehab 	if (vb) {
2205bc3cb74SMauro Carvalho Chehab 		vb->priv = ((char *)vb) + size;
2215bc3cb74SMauro Carvalho Chehab 		mem = vb->priv;
2225bc3cb74SMauro Carvalho Chehab 		mem->magic = MAGIC_DC_MEM;
2235bc3cb74SMauro Carvalho Chehab 	}
2245bc3cb74SMauro Carvalho Chehab 
2255bc3cb74SMauro Carvalho Chehab 	return vb;
2265bc3cb74SMauro Carvalho Chehab }
2275bc3cb74SMauro Carvalho Chehab 
2285bc3cb74SMauro Carvalho Chehab static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
2295bc3cb74SMauro Carvalho Chehab {
2305bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = buf->priv;
2315bc3cb74SMauro Carvalho Chehab 
2325bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
2335bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2345bc3cb74SMauro Carvalho Chehab 
2355bc3cb74SMauro Carvalho Chehab 	return mem->vaddr;
2365bc3cb74SMauro Carvalho Chehab }
2375bc3cb74SMauro Carvalho Chehab 
2385bc3cb74SMauro Carvalho Chehab static int __videobuf_iolock(struct videobuf_queue *q,
2395bc3cb74SMauro Carvalho Chehab 			     struct videobuf_buffer *vb,
2405bc3cb74SMauro Carvalho Chehab 			     struct v4l2_framebuffer *fbuf)
2415bc3cb74SMauro Carvalho Chehab {
2425bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = vb->priv;
2435bc3cb74SMauro Carvalho Chehab 
2445bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
2455bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2465bc3cb74SMauro Carvalho Chehab 
2475bc3cb74SMauro Carvalho Chehab 	switch (vb->memory) {
2485bc3cb74SMauro Carvalho Chehab 	case V4L2_MEMORY_MMAP:
2495bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
2505bc3cb74SMauro Carvalho Chehab 
2515bc3cb74SMauro Carvalho Chehab 		/* All handling should be done by __videobuf_mmap_mapper() */
2525bc3cb74SMauro Carvalho Chehab 		if (!mem->vaddr) {
2535bc3cb74SMauro Carvalho Chehab 			dev_err(q->dev, "memory is not alloced/mmapped.\n");
2545bc3cb74SMauro Carvalho Chehab 			return -EINVAL;
2555bc3cb74SMauro Carvalho Chehab 		}
2565bc3cb74SMauro Carvalho Chehab 		break;
2575bc3cb74SMauro Carvalho Chehab 	case V4L2_MEMORY_USERPTR:
2585bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
2595bc3cb74SMauro Carvalho Chehab 
2605bc3cb74SMauro Carvalho Chehab 		/* handle pointer from user space */
2615bc3cb74SMauro Carvalho Chehab 		if (vb->baddr)
2625bc3cb74SMauro Carvalho Chehab 			return videobuf_dma_contig_user_get(mem, vb);
2635bc3cb74SMauro Carvalho Chehab 
2645bc3cb74SMauro Carvalho Chehab 		/* allocate memory for the read() method */
2655bc3cb74SMauro Carvalho Chehab 		if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
2665bc3cb74SMauro Carvalho Chehab 					GFP_KERNEL))
2675bc3cb74SMauro Carvalho Chehab 			return -ENOMEM;
2685bc3cb74SMauro Carvalho Chehab 		break;
2695bc3cb74SMauro Carvalho Chehab 	case V4L2_MEMORY_OVERLAY:
2705bc3cb74SMauro Carvalho Chehab 	default:
2715bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
2725bc3cb74SMauro Carvalho Chehab 		return -EINVAL;
2735bc3cb74SMauro Carvalho Chehab 	}
2745bc3cb74SMauro Carvalho Chehab 
2755bc3cb74SMauro Carvalho Chehab 	return 0;
2765bc3cb74SMauro Carvalho Chehab }
2775bc3cb74SMauro Carvalho Chehab 
2785bc3cb74SMauro Carvalho Chehab static int __videobuf_mmap_mapper(struct videobuf_queue *q,
2795bc3cb74SMauro Carvalho Chehab 				  struct videobuf_buffer *buf,
2805bc3cb74SMauro Carvalho Chehab 				  struct vm_area_struct *vma)
2815bc3cb74SMauro Carvalho Chehab {
2825bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem;
2835bc3cb74SMauro Carvalho Chehab 	struct videobuf_mapping *map;
2845bc3cb74SMauro Carvalho Chehab 	int retval;
2855bc3cb74SMauro Carvalho Chehab 	unsigned long size;
2865bc3cb74SMauro Carvalho Chehab 
2875bc3cb74SMauro Carvalho Chehab 	dev_dbg(q->dev, "%s\n", __func__);
2885bc3cb74SMauro Carvalho Chehab 
2895bc3cb74SMauro Carvalho Chehab 	/* create mapping + update buffer list */
2905bc3cb74SMauro Carvalho Chehab 	map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
2915bc3cb74SMauro Carvalho Chehab 	if (!map)
2925bc3cb74SMauro Carvalho Chehab 		return -ENOMEM;
2935bc3cb74SMauro Carvalho Chehab 
2945bc3cb74SMauro Carvalho Chehab 	buf->map = map;
2955bc3cb74SMauro Carvalho Chehab 	map->q = q;
2965bc3cb74SMauro Carvalho Chehab 
2975bc3cb74SMauro Carvalho Chehab 	buf->baddr = vma->vm_start;
2985bc3cb74SMauro Carvalho Chehab 
2995bc3cb74SMauro Carvalho Chehab 	mem = buf->priv;
3005bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
3015bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3025bc3cb74SMauro Carvalho Chehab 
3035bc3cb74SMauro Carvalho Chehab 	if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
3045bc3cb74SMauro Carvalho Chehab 				GFP_KERNEL | __GFP_COMP))
3055bc3cb74SMauro Carvalho Chehab 		goto error;
3065bc3cb74SMauro Carvalho Chehab 
3075bc3cb74SMauro Carvalho Chehab 	/* Try to remap memory */
3085bc3cb74SMauro Carvalho Chehab 
3095bc3cb74SMauro Carvalho Chehab 	size = vma->vm_end - vma->vm_start;
3105bc3cb74SMauro Carvalho Chehab 	size = (size < mem->size) ? size : mem->size;
3115bc3cb74SMauro Carvalho Chehab 
3125bc3cb74SMauro Carvalho Chehab 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
3135bc3cb74SMauro Carvalho Chehab 	retval = remap_pfn_range(vma, vma->vm_start,
3145bc3cb74SMauro Carvalho Chehab 				 mem->dma_handle >> PAGE_SHIFT,
3155bc3cb74SMauro Carvalho Chehab 				 size, vma->vm_page_prot);
3165bc3cb74SMauro Carvalho Chehab 	if (retval) {
3175bc3cb74SMauro Carvalho Chehab 		dev_err(q->dev, "mmap: remap failed with error %d. ",
3185bc3cb74SMauro Carvalho Chehab 			retval);
3195bc3cb74SMauro Carvalho Chehab 		dma_free_coherent(q->dev, mem->size,
3205bc3cb74SMauro Carvalho Chehab 				  mem->vaddr, mem->dma_handle);
3215bc3cb74SMauro Carvalho Chehab 		goto error;
3225bc3cb74SMauro Carvalho Chehab 	}
3235bc3cb74SMauro Carvalho Chehab 
3245bc3cb74SMauro Carvalho Chehab 	vma->vm_ops = &videobuf_vm_ops;
3255bc3cb74SMauro Carvalho Chehab 	vma->vm_flags |= VM_DONTEXPAND;
3265bc3cb74SMauro Carvalho Chehab 	vma->vm_private_data = map;
3275bc3cb74SMauro Carvalho Chehab 
3285bc3cb74SMauro Carvalho Chehab 	dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
3295bc3cb74SMauro Carvalho Chehab 		map, q, vma->vm_start, vma->vm_end,
3305bc3cb74SMauro Carvalho Chehab 		(long int)buf->bsize, vma->vm_pgoff, buf->i);
3315bc3cb74SMauro Carvalho Chehab 
3325bc3cb74SMauro Carvalho Chehab 	videobuf_vm_open(vma);
3335bc3cb74SMauro Carvalho Chehab 
3345bc3cb74SMauro Carvalho Chehab 	return 0;
3355bc3cb74SMauro Carvalho Chehab 
3365bc3cb74SMauro Carvalho Chehab error:
3375bc3cb74SMauro Carvalho Chehab 	kfree(map);
3385bc3cb74SMauro Carvalho Chehab 	return -ENOMEM;
3395bc3cb74SMauro Carvalho Chehab }
3405bc3cb74SMauro Carvalho Chehab 
3415bc3cb74SMauro Carvalho Chehab static struct videobuf_qtype_ops qops = {
3425bc3cb74SMauro Carvalho Chehab 	.magic		= MAGIC_QTYPE_OPS,
343cb132cd5SMauro Carvalho Chehab 	.alloc_vb	= __videobuf_alloc,
3445bc3cb74SMauro Carvalho Chehab 	.iolock		= __videobuf_iolock,
3455bc3cb74SMauro Carvalho Chehab 	.mmap_mapper	= __videobuf_mmap_mapper,
3465bc3cb74SMauro Carvalho Chehab 	.vaddr		= __videobuf_to_vaddr,
3475bc3cb74SMauro Carvalho Chehab };
3485bc3cb74SMauro Carvalho Chehab 
3495bc3cb74SMauro Carvalho Chehab void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
3505bc3cb74SMauro Carvalho Chehab 				    const struct videobuf_queue_ops *ops,
3515bc3cb74SMauro Carvalho Chehab 				    struct device *dev,
3525bc3cb74SMauro Carvalho Chehab 				    spinlock_t *irqlock,
3535bc3cb74SMauro Carvalho Chehab 				    enum v4l2_buf_type type,
3545bc3cb74SMauro Carvalho Chehab 				    enum v4l2_field field,
3555bc3cb74SMauro Carvalho Chehab 				    unsigned int msize,
3565bc3cb74SMauro Carvalho Chehab 				    void *priv,
3575bc3cb74SMauro Carvalho Chehab 				    struct mutex *ext_lock)
3585bc3cb74SMauro Carvalho Chehab {
3595bc3cb74SMauro Carvalho Chehab 	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
3605bc3cb74SMauro Carvalho Chehab 				 priv, &qops, ext_lock);
3615bc3cb74SMauro Carvalho Chehab }
3625bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
3635bc3cb74SMauro Carvalho Chehab 
3645bc3cb74SMauro Carvalho Chehab dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
3655bc3cb74SMauro Carvalho Chehab {
3665bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = buf->priv;
3675bc3cb74SMauro Carvalho Chehab 
3685bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
3695bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3705bc3cb74SMauro Carvalho Chehab 
3715bc3cb74SMauro Carvalho Chehab 	return mem->dma_handle;
3725bc3cb74SMauro Carvalho Chehab }
3735bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
3745bc3cb74SMauro Carvalho Chehab 
3755bc3cb74SMauro Carvalho Chehab void videobuf_dma_contig_free(struct videobuf_queue *q,
3765bc3cb74SMauro Carvalho Chehab 			      struct videobuf_buffer *buf)
3775bc3cb74SMauro Carvalho Chehab {
3785bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = buf->priv;
3795bc3cb74SMauro Carvalho Chehab 
3805bc3cb74SMauro Carvalho Chehab 	/* mmapped memory can't be freed here, otherwise mmapped region
3815bc3cb74SMauro Carvalho Chehab 	   would be released, while still needed. In this case, the memory
3825bc3cb74SMauro Carvalho Chehab 	   release should happen inside videobuf_vm_close().
3835bc3cb74SMauro Carvalho Chehab 	   So, it should free memory only if the memory were allocated for
3845bc3cb74SMauro Carvalho Chehab 	   read() operation.
3855bc3cb74SMauro Carvalho Chehab 	 */
3865bc3cb74SMauro Carvalho Chehab 	if (buf->memory != V4L2_MEMORY_USERPTR)
3875bc3cb74SMauro Carvalho Chehab 		return;
3885bc3cb74SMauro Carvalho Chehab 
3895bc3cb74SMauro Carvalho Chehab 	if (!mem)
3905bc3cb74SMauro Carvalho Chehab 		return;
3915bc3cb74SMauro Carvalho Chehab 
3925bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3935bc3cb74SMauro Carvalho Chehab 
3945bc3cb74SMauro Carvalho Chehab 	/* handle user space pointer case */
3955bc3cb74SMauro Carvalho Chehab 	if (buf->baddr) {
3965bc3cb74SMauro Carvalho Chehab 		videobuf_dma_contig_user_put(mem);
3975bc3cb74SMauro Carvalho Chehab 		return;
3985bc3cb74SMauro Carvalho Chehab 	}
3995bc3cb74SMauro Carvalho Chehab 
4005bc3cb74SMauro Carvalho Chehab 	/* read() method */
4015bc3cb74SMauro Carvalho Chehab 	if (mem->vaddr) {
4025bc3cb74SMauro Carvalho Chehab 		__videobuf_dc_free(q->dev, mem);
4035bc3cb74SMauro Carvalho Chehab 		mem->vaddr = NULL;
4045bc3cb74SMauro Carvalho Chehab 	}
4055bc3cb74SMauro Carvalho Chehab }
4065bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
4075bc3cb74SMauro Carvalho Chehab 
4085bc3cb74SMauro Carvalho Chehab MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
4095bc3cb74SMauro Carvalho Chehab MODULE_AUTHOR("Magnus Damm");
4105bc3cb74SMauro Carvalho Chehab MODULE_LICENSE("GPL");
411