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;
695bc3cb74SMauro Carvalho Chehab 
70cca36e2eSHans Verkuil 	dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
715bc3cb74SMauro Carvalho Chehab 		map, map->count, vma->vm_start, vma->vm_end);
725bc3cb74SMauro Carvalho Chehab 
735bc3cb74SMauro Carvalho Chehab 	map->count++;
745bc3cb74SMauro Carvalho Chehab }
755bc3cb74SMauro Carvalho Chehab 
765bc3cb74SMauro Carvalho Chehab static void videobuf_vm_close(struct vm_area_struct *vma)
775bc3cb74SMauro Carvalho Chehab {
785bc3cb74SMauro Carvalho Chehab 	struct videobuf_mapping *map = vma->vm_private_data;
795bc3cb74SMauro Carvalho Chehab 	struct videobuf_queue *q = map->q;
805bc3cb74SMauro Carvalho Chehab 	int i;
815bc3cb74SMauro Carvalho Chehab 
825bc3cb74SMauro Carvalho Chehab 	dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
835bc3cb74SMauro Carvalho Chehab 		map, map->count, vma->vm_start, vma->vm_end);
845bc3cb74SMauro Carvalho Chehab 
85cca36e2eSHans Verkuil 	map->count--;
86cca36e2eSHans Verkuil 	if (0 == map->count) {
875bc3cb74SMauro Carvalho Chehab 		struct videobuf_dma_contig_memory *mem;
885bc3cb74SMauro Carvalho Chehab 
895bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
90cca36e2eSHans Verkuil 		videobuf_queue_lock(q);
915bc3cb74SMauro Carvalho Chehab 
925bc3cb74SMauro Carvalho Chehab 		/* We need first to cancel streams, before unmapping */
935bc3cb74SMauro Carvalho Chehab 		if (q->streaming)
945bc3cb74SMauro Carvalho Chehab 			videobuf_queue_cancel(q);
955bc3cb74SMauro Carvalho Chehab 
965bc3cb74SMauro Carvalho Chehab 		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
975bc3cb74SMauro Carvalho Chehab 			if (NULL == q->bufs[i])
985bc3cb74SMauro Carvalho Chehab 				continue;
995bc3cb74SMauro Carvalho Chehab 
1005bc3cb74SMauro Carvalho Chehab 			if (q->bufs[i]->map != map)
1015bc3cb74SMauro Carvalho Chehab 				continue;
1025bc3cb74SMauro Carvalho Chehab 
1035bc3cb74SMauro Carvalho Chehab 			mem = q->bufs[i]->priv;
1045bc3cb74SMauro Carvalho Chehab 			if (mem) {
1055bc3cb74SMauro Carvalho Chehab 				/* This callback is called only if kernel has
1065bc3cb74SMauro Carvalho Chehab 				   allocated memory and this memory is mmapped.
1075bc3cb74SMauro Carvalho Chehab 				   In this case, memory should be freed,
1085bc3cb74SMauro Carvalho Chehab 				   in order to do memory unmap.
1095bc3cb74SMauro Carvalho Chehab 				 */
1105bc3cb74SMauro Carvalho Chehab 
1115bc3cb74SMauro Carvalho Chehab 				MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
1125bc3cb74SMauro Carvalho Chehab 
1135bc3cb74SMauro Carvalho Chehab 				/* vfree is not atomic - can't be
1145bc3cb74SMauro Carvalho Chehab 				   called with IRQ's disabled
1155bc3cb74SMauro Carvalho Chehab 				 */
1165bc3cb74SMauro Carvalho Chehab 				dev_dbg(q->dev, "buf[%d] freeing %p\n",
1175bc3cb74SMauro Carvalho Chehab 					i, mem->vaddr);
1185bc3cb74SMauro Carvalho Chehab 
1195bc3cb74SMauro Carvalho Chehab 				__videobuf_dc_free(q->dev, mem);
1205bc3cb74SMauro Carvalho Chehab 				mem->vaddr = NULL;
1215bc3cb74SMauro Carvalho Chehab 			}
1225bc3cb74SMauro Carvalho Chehab 
1235bc3cb74SMauro Carvalho Chehab 			q->bufs[i]->map = NULL;
1245bc3cb74SMauro Carvalho Chehab 			q->bufs[i]->baddr = 0;
1255bc3cb74SMauro Carvalho Chehab 		}
1265bc3cb74SMauro Carvalho Chehab 
1275bc3cb74SMauro Carvalho Chehab 		kfree(map);
1285bc3cb74SMauro Carvalho Chehab 
129a242f426SAl Viro 		videobuf_queue_unlock(q);
1305bc3cb74SMauro Carvalho Chehab 	}
131cca36e2eSHans Verkuil }
1325bc3cb74SMauro Carvalho Chehab 
1335bc3cb74SMauro Carvalho Chehab static const struct vm_operations_struct videobuf_vm_ops = {
1345bc3cb74SMauro Carvalho Chehab 	.open	= videobuf_vm_open,
1355bc3cb74SMauro Carvalho Chehab 	.close	= videobuf_vm_close,
1365bc3cb74SMauro Carvalho Chehab };
1375bc3cb74SMauro Carvalho Chehab 
1385bc3cb74SMauro Carvalho Chehab /**
1395bc3cb74SMauro Carvalho Chehab  * videobuf_dma_contig_user_put() - reset pointer to user space buffer
1405bc3cb74SMauro Carvalho Chehab  * @mem: per-buffer private videobuf-dma-contig data
1415bc3cb74SMauro Carvalho Chehab  *
1425bc3cb74SMauro Carvalho Chehab  * This function resets the user space pointer
1435bc3cb74SMauro Carvalho Chehab  */
1445bc3cb74SMauro Carvalho Chehab static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
1455bc3cb74SMauro Carvalho Chehab {
1465bc3cb74SMauro Carvalho Chehab 	mem->dma_handle = 0;
1475bc3cb74SMauro Carvalho Chehab 	mem->size = 0;
1485bc3cb74SMauro Carvalho Chehab }
1495bc3cb74SMauro Carvalho Chehab 
1505bc3cb74SMauro Carvalho Chehab /**
1515bc3cb74SMauro Carvalho Chehab  * videobuf_dma_contig_user_get() - setup user space memory pointer
1525bc3cb74SMauro Carvalho Chehab  * @mem: per-buffer private videobuf-dma-contig data
1535bc3cb74SMauro Carvalho Chehab  * @vb: video buffer to map
1545bc3cb74SMauro Carvalho Chehab  *
1555bc3cb74SMauro Carvalho Chehab  * This function validates and sets up a pointer to user space memory.
1565bc3cb74SMauro Carvalho Chehab  * Only physically contiguous pfn-mapped memory is accepted.
1575bc3cb74SMauro Carvalho Chehab  *
1585bc3cb74SMauro Carvalho Chehab  * Returns 0 if successful.
1595bc3cb74SMauro Carvalho Chehab  */
1605bc3cb74SMauro Carvalho Chehab static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
1615bc3cb74SMauro Carvalho Chehab 					struct videobuf_buffer *vb)
1625bc3cb74SMauro Carvalho Chehab {
1635bc3cb74SMauro Carvalho Chehab 	struct mm_struct *mm = current->mm;
1645bc3cb74SMauro Carvalho Chehab 	struct vm_area_struct *vma;
1655bc3cb74SMauro Carvalho Chehab 	unsigned long prev_pfn, this_pfn;
1665bc3cb74SMauro Carvalho Chehab 	unsigned long pages_done, user_address;
1675bc3cb74SMauro Carvalho Chehab 	unsigned int offset;
1685bc3cb74SMauro Carvalho Chehab 	int ret;
1695bc3cb74SMauro Carvalho Chehab 
1705bc3cb74SMauro Carvalho Chehab 	offset = vb->baddr & ~PAGE_MASK;
1715bc3cb74SMauro Carvalho Chehab 	mem->size = PAGE_ALIGN(vb->size + offset);
1725bc3cb74SMauro Carvalho Chehab 	ret = -EINVAL;
1735bc3cb74SMauro Carvalho Chehab 
1745bc3cb74SMauro Carvalho Chehab 	down_read(&mm->mmap_sem);
1755bc3cb74SMauro Carvalho Chehab 
1765bc3cb74SMauro Carvalho Chehab 	vma = find_vma(mm, vb->baddr);
1775bc3cb74SMauro Carvalho Chehab 	if (!vma)
1785bc3cb74SMauro Carvalho Chehab 		goto out_up;
1795bc3cb74SMauro Carvalho Chehab 
1805bc3cb74SMauro Carvalho Chehab 	if ((vb->baddr + mem->size) > vma->vm_end)
1815bc3cb74SMauro Carvalho Chehab 		goto out_up;
1825bc3cb74SMauro Carvalho Chehab 
1835bc3cb74SMauro Carvalho Chehab 	pages_done = 0;
1845bc3cb74SMauro Carvalho Chehab 	prev_pfn = 0; /* kill warning */
1855bc3cb74SMauro Carvalho Chehab 	user_address = vb->baddr;
1865bc3cb74SMauro Carvalho Chehab 
1875bc3cb74SMauro Carvalho Chehab 	while (pages_done < (mem->size >> PAGE_SHIFT)) {
1885bc3cb74SMauro Carvalho Chehab 		ret = follow_pfn(vma, user_address, &this_pfn);
1895bc3cb74SMauro Carvalho Chehab 		if (ret)
1905bc3cb74SMauro Carvalho Chehab 			break;
1915bc3cb74SMauro Carvalho Chehab 
1925bc3cb74SMauro Carvalho Chehab 		if (pages_done == 0)
1935bc3cb74SMauro Carvalho Chehab 			mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
1945bc3cb74SMauro Carvalho Chehab 		else if (this_pfn != (prev_pfn + 1))
1955bc3cb74SMauro Carvalho Chehab 			ret = -EFAULT;
1965bc3cb74SMauro Carvalho Chehab 
1975bc3cb74SMauro Carvalho Chehab 		if (ret)
1985bc3cb74SMauro Carvalho Chehab 			break;
1995bc3cb74SMauro Carvalho Chehab 
2005bc3cb74SMauro Carvalho Chehab 		prev_pfn = this_pfn;
2015bc3cb74SMauro Carvalho Chehab 		user_address += PAGE_SIZE;
2025bc3cb74SMauro Carvalho Chehab 		pages_done++;
2035bc3cb74SMauro Carvalho Chehab 	}
2045bc3cb74SMauro Carvalho Chehab 
2055bc3cb74SMauro Carvalho Chehab out_up:
2065bc3cb74SMauro Carvalho Chehab 	up_read(&current->mm->mmap_sem);
2075bc3cb74SMauro Carvalho Chehab 
2085bc3cb74SMauro Carvalho Chehab 	return ret;
2095bc3cb74SMauro Carvalho Chehab }
2105bc3cb74SMauro Carvalho Chehab 
211cb132cd5SMauro Carvalho Chehab static struct videobuf_buffer *__videobuf_alloc(size_t size)
2125bc3cb74SMauro Carvalho Chehab {
2135bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem;
2145bc3cb74SMauro Carvalho Chehab 	struct videobuf_buffer *vb;
2155bc3cb74SMauro Carvalho Chehab 
2165bc3cb74SMauro Carvalho Chehab 	vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
2175bc3cb74SMauro Carvalho Chehab 	if (vb) {
2185bc3cb74SMauro Carvalho Chehab 		vb->priv = ((char *)vb) + size;
2195bc3cb74SMauro Carvalho Chehab 		mem = vb->priv;
2205bc3cb74SMauro Carvalho Chehab 		mem->magic = MAGIC_DC_MEM;
2215bc3cb74SMauro Carvalho Chehab 	}
2225bc3cb74SMauro Carvalho Chehab 
2235bc3cb74SMauro Carvalho Chehab 	return vb;
2245bc3cb74SMauro Carvalho Chehab }
2255bc3cb74SMauro Carvalho Chehab 
2265bc3cb74SMauro Carvalho Chehab static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
2275bc3cb74SMauro Carvalho Chehab {
2285bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = buf->priv;
2295bc3cb74SMauro Carvalho Chehab 
2305bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
2315bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2325bc3cb74SMauro Carvalho Chehab 
2335bc3cb74SMauro Carvalho Chehab 	return mem->vaddr;
2345bc3cb74SMauro Carvalho Chehab }
2355bc3cb74SMauro Carvalho Chehab 
2365bc3cb74SMauro Carvalho Chehab static int __videobuf_iolock(struct videobuf_queue *q,
2375bc3cb74SMauro Carvalho Chehab 			     struct videobuf_buffer *vb,
2385bc3cb74SMauro Carvalho Chehab 			     struct v4l2_framebuffer *fbuf)
2395bc3cb74SMauro Carvalho Chehab {
2405bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = vb->priv;
2415bc3cb74SMauro Carvalho Chehab 
2425bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
2435bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2445bc3cb74SMauro Carvalho Chehab 
2455bc3cb74SMauro Carvalho Chehab 	switch (vb->memory) {
2465bc3cb74SMauro Carvalho Chehab 	case V4L2_MEMORY_MMAP:
2475bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
2485bc3cb74SMauro Carvalho Chehab 
2495bc3cb74SMauro Carvalho Chehab 		/* All handling should be done by __videobuf_mmap_mapper() */
2505bc3cb74SMauro Carvalho Chehab 		if (!mem->vaddr) {
2515bc3cb74SMauro Carvalho Chehab 			dev_err(q->dev, "memory is not alloced/mmapped.\n");
2525bc3cb74SMauro Carvalho Chehab 			return -EINVAL;
2535bc3cb74SMauro Carvalho Chehab 		}
2545bc3cb74SMauro Carvalho Chehab 		break;
2555bc3cb74SMauro Carvalho Chehab 	case V4L2_MEMORY_USERPTR:
2565bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
2575bc3cb74SMauro Carvalho Chehab 
2585bc3cb74SMauro Carvalho Chehab 		/* handle pointer from user space */
2595bc3cb74SMauro Carvalho Chehab 		if (vb->baddr)
2605bc3cb74SMauro Carvalho Chehab 			return videobuf_dma_contig_user_get(mem, vb);
2615bc3cb74SMauro Carvalho Chehab 
2625bc3cb74SMauro Carvalho Chehab 		/* allocate memory for the read() method */
2635bc3cb74SMauro Carvalho Chehab 		if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
2645bc3cb74SMauro Carvalho Chehab 					GFP_KERNEL))
2655bc3cb74SMauro Carvalho Chehab 			return -ENOMEM;
2665bc3cb74SMauro Carvalho Chehab 		break;
2675bc3cb74SMauro Carvalho Chehab 	case V4L2_MEMORY_OVERLAY:
2685bc3cb74SMauro Carvalho Chehab 	default:
2695bc3cb74SMauro Carvalho Chehab 		dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
2705bc3cb74SMauro Carvalho Chehab 		return -EINVAL;
2715bc3cb74SMauro Carvalho Chehab 	}
2725bc3cb74SMauro Carvalho Chehab 
2735bc3cb74SMauro Carvalho Chehab 	return 0;
2745bc3cb74SMauro Carvalho Chehab }
2755bc3cb74SMauro Carvalho Chehab 
2765bc3cb74SMauro Carvalho Chehab static int __videobuf_mmap_mapper(struct videobuf_queue *q,
2775bc3cb74SMauro Carvalho Chehab 				  struct videobuf_buffer *buf,
2785bc3cb74SMauro Carvalho Chehab 				  struct vm_area_struct *vma)
2795bc3cb74SMauro Carvalho Chehab {
2805bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem;
2815bc3cb74SMauro Carvalho Chehab 	struct videobuf_mapping *map;
2825bc3cb74SMauro Carvalho Chehab 	int retval;
2835bc3cb74SMauro Carvalho Chehab 	unsigned long size;
2845bc3cb74SMauro Carvalho Chehab 
2855bc3cb74SMauro Carvalho Chehab 	dev_dbg(q->dev, "%s\n", __func__);
2865bc3cb74SMauro Carvalho Chehab 
2875bc3cb74SMauro Carvalho Chehab 	/* create mapping + update buffer list */
2885bc3cb74SMauro Carvalho Chehab 	map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
2895bc3cb74SMauro Carvalho Chehab 	if (!map)
2905bc3cb74SMauro Carvalho Chehab 		return -ENOMEM;
2915bc3cb74SMauro Carvalho Chehab 
2925bc3cb74SMauro Carvalho Chehab 	buf->map = map;
2935bc3cb74SMauro Carvalho Chehab 	map->q = q;
2945bc3cb74SMauro Carvalho Chehab 
2955bc3cb74SMauro Carvalho Chehab 	buf->baddr = vma->vm_start;
2965bc3cb74SMauro Carvalho Chehab 
2975bc3cb74SMauro Carvalho Chehab 	mem = buf->priv;
2985bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
2995bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3005bc3cb74SMauro Carvalho Chehab 
3015bc3cb74SMauro Carvalho Chehab 	if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
3025bc3cb74SMauro Carvalho Chehab 				GFP_KERNEL | __GFP_COMP))
3035bc3cb74SMauro Carvalho Chehab 		goto error;
3045bc3cb74SMauro Carvalho Chehab 
3055bc3cb74SMauro Carvalho Chehab 	/* Try to remap memory */
3065bc3cb74SMauro Carvalho Chehab 	size = vma->vm_end - vma->vm_start;
3075bc3cb74SMauro Carvalho Chehab 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
3088a6a547fSFancy Fang 
3098a6a547fSFancy Fang 	/* the "vm_pgoff" is just used in v4l2 to find the
3108a6a547fSFancy Fang 	 * corresponding buffer data structure which is allocated
3118a6a547fSFancy Fang 	 * earlier and it does not mean the offset from the physical
3128a6a547fSFancy Fang 	 * buffer start address as usual. So set it to 0 to pass
3138a6a547fSFancy Fang 	 * the sanity check in vm_iomap_memory().
3148a6a547fSFancy Fang 	 */
3158a6a547fSFancy Fang 	vma->vm_pgoff = 0;
3168a6a547fSFancy Fang 
31729f1cdb0SMa Haijun 	retval = vm_iomap_memory(vma, mem->dma_handle, size);
3185bc3cb74SMauro Carvalho Chehab 	if (retval) {
3195bc3cb74SMauro Carvalho Chehab 		dev_err(q->dev, "mmap: remap failed with error %d. ",
3205bc3cb74SMauro Carvalho Chehab 			retval);
3215bc3cb74SMauro Carvalho Chehab 		dma_free_coherent(q->dev, mem->size,
3225bc3cb74SMauro Carvalho Chehab 				  mem->vaddr, mem->dma_handle);
3235bc3cb74SMauro Carvalho Chehab 		goto error;
3245bc3cb74SMauro Carvalho Chehab 	}
3255bc3cb74SMauro Carvalho Chehab 
3265bc3cb74SMauro Carvalho Chehab 	vma->vm_ops = &videobuf_vm_ops;
3275bc3cb74SMauro Carvalho Chehab 	vma->vm_flags |= VM_DONTEXPAND;
3285bc3cb74SMauro Carvalho Chehab 	vma->vm_private_data = map;
3295bc3cb74SMauro Carvalho Chehab 
3305bc3cb74SMauro Carvalho Chehab 	dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
3315bc3cb74SMauro Carvalho Chehab 		map, q, vma->vm_start, vma->vm_end,
3325bc3cb74SMauro Carvalho Chehab 		(long int)buf->bsize, vma->vm_pgoff, buf->i);
3335bc3cb74SMauro Carvalho Chehab 
3345bc3cb74SMauro Carvalho Chehab 	videobuf_vm_open(vma);
3355bc3cb74SMauro Carvalho Chehab 
3365bc3cb74SMauro Carvalho Chehab 	return 0;
3375bc3cb74SMauro Carvalho Chehab 
3385bc3cb74SMauro Carvalho Chehab error:
3395bc3cb74SMauro Carvalho Chehab 	kfree(map);
3405bc3cb74SMauro Carvalho Chehab 	return -ENOMEM;
3415bc3cb74SMauro Carvalho Chehab }
3425bc3cb74SMauro Carvalho Chehab 
3435bc3cb74SMauro Carvalho Chehab static struct videobuf_qtype_ops qops = {
3445bc3cb74SMauro Carvalho Chehab 	.magic		= MAGIC_QTYPE_OPS,
345cb132cd5SMauro Carvalho Chehab 	.alloc_vb	= __videobuf_alloc,
3465bc3cb74SMauro Carvalho Chehab 	.iolock		= __videobuf_iolock,
3475bc3cb74SMauro Carvalho Chehab 	.mmap_mapper	= __videobuf_mmap_mapper,
3485bc3cb74SMauro Carvalho Chehab 	.vaddr		= __videobuf_to_vaddr,
3495bc3cb74SMauro Carvalho Chehab };
3505bc3cb74SMauro Carvalho Chehab 
3515bc3cb74SMauro Carvalho Chehab void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
3525bc3cb74SMauro Carvalho Chehab 				    const struct videobuf_queue_ops *ops,
3535bc3cb74SMauro Carvalho Chehab 				    struct device *dev,
3545bc3cb74SMauro Carvalho Chehab 				    spinlock_t *irqlock,
3555bc3cb74SMauro Carvalho Chehab 				    enum v4l2_buf_type type,
3565bc3cb74SMauro Carvalho Chehab 				    enum v4l2_field field,
3575bc3cb74SMauro Carvalho Chehab 				    unsigned int msize,
3585bc3cb74SMauro Carvalho Chehab 				    void *priv,
3595bc3cb74SMauro Carvalho Chehab 				    struct mutex *ext_lock)
3605bc3cb74SMauro Carvalho Chehab {
3615bc3cb74SMauro Carvalho Chehab 	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
3625bc3cb74SMauro Carvalho Chehab 				 priv, &qops, ext_lock);
3635bc3cb74SMauro Carvalho Chehab }
3645bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
3655bc3cb74SMauro Carvalho Chehab 
3665bc3cb74SMauro Carvalho Chehab dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
3675bc3cb74SMauro Carvalho Chehab {
3685bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = buf->priv;
3695bc3cb74SMauro Carvalho Chehab 
3705bc3cb74SMauro Carvalho Chehab 	BUG_ON(!mem);
3715bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3725bc3cb74SMauro Carvalho Chehab 
3735bc3cb74SMauro Carvalho Chehab 	return mem->dma_handle;
3745bc3cb74SMauro Carvalho Chehab }
3755bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
3765bc3cb74SMauro Carvalho Chehab 
3775bc3cb74SMauro Carvalho Chehab void videobuf_dma_contig_free(struct videobuf_queue *q,
3785bc3cb74SMauro Carvalho Chehab 			      struct videobuf_buffer *buf)
3795bc3cb74SMauro Carvalho Chehab {
3805bc3cb74SMauro Carvalho Chehab 	struct videobuf_dma_contig_memory *mem = buf->priv;
3815bc3cb74SMauro Carvalho Chehab 
3825bc3cb74SMauro Carvalho Chehab 	/* mmapped memory can't be freed here, otherwise mmapped region
3835bc3cb74SMauro Carvalho Chehab 	   would be released, while still needed. In this case, the memory
3845bc3cb74SMauro Carvalho Chehab 	   release should happen inside videobuf_vm_close().
3855bc3cb74SMauro Carvalho Chehab 	   So, it should free memory only if the memory were allocated for
3865bc3cb74SMauro Carvalho Chehab 	   read() operation.
3875bc3cb74SMauro Carvalho Chehab 	 */
3885bc3cb74SMauro Carvalho Chehab 	if (buf->memory != V4L2_MEMORY_USERPTR)
3895bc3cb74SMauro Carvalho Chehab 		return;
3905bc3cb74SMauro Carvalho Chehab 
3915bc3cb74SMauro Carvalho Chehab 	if (!mem)
3925bc3cb74SMauro Carvalho Chehab 		return;
3935bc3cb74SMauro Carvalho Chehab 
3945bc3cb74SMauro Carvalho Chehab 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3955bc3cb74SMauro Carvalho Chehab 
3965bc3cb74SMauro Carvalho Chehab 	/* handle user space pointer case */
3975bc3cb74SMauro Carvalho Chehab 	if (buf->baddr) {
3985bc3cb74SMauro Carvalho Chehab 		videobuf_dma_contig_user_put(mem);
3995bc3cb74SMauro Carvalho Chehab 		return;
4005bc3cb74SMauro Carvalho Chehab 	}
4015bc3cb74SMauro Carvalho Chehab 
4025bc3cb74SMauro Carvalho Chehab 	/* read() method */
4035bc3cb74SMauro Carvalho Chehab 	if (mem->vaddr) {
4045bc3cb74SMauro Carvalho Chehab 		__videobuf_dc_free(q->dev, mem);
4055bc3cb74SMauro Carvalho Chehab 		mem->vaddr = NULL;
4065bc3cb74SMauro Carvalho Chehab 	}
4075bc3cb74SMauro Carvalho Chehab }
4085bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
4095bc3cb74SMauro Carvalho Chehab 
4105bc3cb74SMauro Carvalho Chehab MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
4115bc3cb74SMauro Carvalho Chehab MODULE_AUTHOR("Magnus Damm");
4125bc3cb74SMauro Carvalho Chehab MODULE_LICENSE("GPL");
413