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