12cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm/*
22cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * helper functions for physically contiguous capture buffers
32cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm *
42cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * The functions support hardware lacking scatter gather support
52cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * (i.e. the buffers must be linear in physical memory)
62cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm *
72cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * Copyright (c) 2008 Magnus Damm
82cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm *
92cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * Based on videobuf-vmalloc.c,
102cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
112cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm *
122cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * This program is free software; you can redistribute it and/or modify
132cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * it under the terms of the GNU General Public License as published by
142cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm * the Free Software Foundation; either version 2
152cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm */
162cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
172cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm#include <linux/init.h>
182cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm#include <linux/module.h>
19f19ad390146e7745cbc529f3bef8469cf21f3a6bHans Verkuil#include <linux/mm.h>
20720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm#include <linux/pagemap.h>
212cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm#include <linux/dma-mapping.h>
22f39c1ab3c3878f1a50ca129e55d17ae63215fcbeGuennadi Liakhovetski#include <linux/sched.h>
235a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
242cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm#include <media/videobuf-dma-contig.h>
252cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
262cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammstruct videobuf_dma_contig_memory {
272cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	u32 magic;
282cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	void *vaddr;
292cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	dma_addr_t dma_handle;
302cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	unsigned long size;
312cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm};
322cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
332cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm#define MAGIC_DC_MEM 0x0733ac61
34c60f2b5c1defb6b1345968e1c65c2008c221d57dGuennadi Liakhovetski#define MAGIC_CHECK(is, should)						    \
35c60f2b5c1defb6b1345968e1c65c2008c221d57dGuennadi Liakhovetski	if (unlikely((is) != (should)))	{				    \
36c60f2b5c1defb6b1345968e1c65c2008c221d57dGuennadi Liakhovetski		pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
37c60f2b5c1defb6b1345968e1c65c2008c221d57dGuennadi Liakhovetski		BUG();							    \
382cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	}
392cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
402cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammstatic void
412cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammvideobuf_vm_open(struct vm_area_struct *vma)
422cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
432cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_mapping *map = vma->vm_private_data;
442cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
452cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
462cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		map, map->count, vma->vm_start, vma->vm_end);
472cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
482cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	map->count++;
492cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
502cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
512cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammstatic void videobuf_vm_close(struct vm_area_struct *vma)
522cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
532cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_mapping *map = vma->vm_private_data;
542cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_queue *q = map->q;
552cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	int i;
562cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
57f35f1bb8fc1e56646a3dab0ecd12e23bca6323c4Guennadi Liakhovetski	dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
582cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		map, map->count, vma->vm_start, vma->vm_end);
592cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
602cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	map->count--;
612cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	if (0 == map->count) {
622cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		struct videobuf_dma_contig_memory *mem;
632cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
64f35f1bb8fc1e56646a3dab0ecd12e23bca6323c4Guennadi Liakhovetski		dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
6597397687886aa8ecd4ec603fab9e70e970c11597Hans Verkuil		videobuf_queue_lock(q);
662cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
672cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		/* We need first to cancel streams, before unmapping */
682cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		if (q->streaming)
692cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			videobuf_queue_cancel(q);
702cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
712cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		for (i = 0; i < VIDEO_MAX_FRAME; i++) {
722cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			if (NULL == q->bufs[i])
732cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				continue;
742cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
752cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			if (q->bufs[i]->map != map)
762cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				continue;
772cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
782cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			mem = q->bufs[i]->priv;
792cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			if (mem) {
802cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				/* This callback is called only if kernel has
812cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				   allocated memory and this memory is mmapped.
822cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				   In this case, memory should be freed,
832cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				   in order to do memory unmap.
842cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				 */
852cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
862cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
872cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
882cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				/* vfree is not atomic - can't be
892cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				   called with IRQ's disabled
902cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				 */
91f35f1bb8fc1e56646a3dab0ecd12e23bca6323c4Guennadi Liakhovetski				dev_dbg(q->dev, "buf[%d] freeing %p\n",
922cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm					i, mem->vaddr);
932cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
942cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				dma_free_coherent(q->dev, mem->size,
952cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm						  mem->vaddr, mem->dma_handle);
962cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				mem->vaddr = NULL;
972cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			}
982cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
992cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			q->bufs[i]->map   = NULL;
1002cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			q->bufs[i]->baddr = 0;
1012cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		}
1022cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
1032cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		kfree(map);
1042cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
10597397687886aa8ecd4ec603fab9e70e970c11597Hans Verkuil		videobuf_queue_unlock(q);
1062cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	}
1072cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
1082cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
109f0f37e2f77731b3473fa6bd5ee53255d9a9cdb40Alexey Dobriyanstatic const struct vm_operations_struct videobuf_vm_ops = {
1102cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	.open     = videobuf_vm_open,
1112cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	.close    = videobuf_vm_close,
1122cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm};
1132cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
114720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm/**
115720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * videobuf_dma_contig_user_put() - reset pointer to user space buffer
116720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * @mem: per-buffer private videobuf-dma-contig data
117720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm *
118720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * This function resets the user space pointer
119720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm */
120720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Dammstatic void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
121720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm{
122720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	mem->dma_handle = 0;
123720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	mem->size = 0;
124720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm}
125720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
126720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm/**
127720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * videobuf_dma_contig_user_get() - setup user space memory pointer
128720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * @mem: per-buffer private videobuf-dma-contig data
129720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * @vb: video buffer to map
130720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm *
131720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * This function validates and sets up a pointer to user space memory.
132720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * Only physically contiguous pfn-mapped memory is accepted.
133720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm *
134720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm * Returns 0 if successful.
135720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm */
136720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Dammstatic int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
137720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm					struct videobuf_buffer *vb)
138720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm{
139720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	struct mm_struct *mm = current->mm;
140720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	struct vm_area_struct *vma;
141720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	unsigned long prev_pfn, this_pfn;
142720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	unsigned long pages_done, user_address;
14331bedfa5068936b15a388842be1d03cdd1bdfb07Muralidharan Karicheri	unsigned int offset;
144720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	int ret;
145720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
14631bedfa5068936b15a388842be1d03cdd1bdfb07Muralidharan Karicheri	offset = vb->baddr & ~PAGE_MASK;
14731bedfa5068936b15a388842be1d03cdd1bdfb07Muralidharan Karicheri	mem->size = PAGE_ALIGN(vb->size + offset);
148720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	ret = -EINVAL;
149720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
150720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	down_read(&mm->mmap_sem);
151720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
152720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	vma = find_vma(mm, vb->baddr);
153720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	if (!vma)
154720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		goto out_up;
155720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
156720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	if ((vb->baddr + mem->size) > vma->vm_end)
157720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		goto out_up;
158720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
159720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	pages_done = 0;
160720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	prev_pfn = 0; /* kill warning */
161720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	user_address = vb->baddr;
162720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
163720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	while (pages_done < (mem->size >> PAGE_SHIFT)) {
164720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		ret = follow_pfn(vma, user_address, &this_pfn);
165720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		if (ret)
166720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm			break;
167720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
168720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		if (pages_done == 0)
16931bedfa5068936b15a388842be1d03cdd1bdfb07Muralidharan Karicheri			mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
170720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		else if (this_pfn != (prev_pfn + 1))
171720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm			ret = -EFAULT;
172720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
173720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		if (ret)
174720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm			break;
175720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
176720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		prev_pfn = this_pfn;
177720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		user_address += PAGE_SIZE;
178720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		pages_done++;
179720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	}
180720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
181720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm out_up:
182720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	up_read(&current->mm->mmap_sem);
183720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
184720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	return ret;
185720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm}
186720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
18733c38283f03d8ea0358229fc03c1beebe67aed0ePawel Osciakstatic struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
1882cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
1892cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_dma_contig_memory *mem;
1902cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_buffer *vb;
1912cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
1922cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
1932cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	if (vb) {
1942cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		mem = vb->priv = ((char *)vb) + size;
1952cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		mem->magic = MAGIC_DC_MEM;
1962cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	}
1972cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
1982cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	return vb;
1992cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
2002cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
201037c75eb14cd6adb837f81f0c2b2a52c31c91e69Hans Verkuilstatic void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
2022cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
2032cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_dma_contig_memory *mem = buf->priv;
2042cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2052cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	BUG_ON(!mem);
2062cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2072cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2082cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	return mem->vaddr;
2092cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
2102cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2112cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammstatic int __videobuf_iolock(struct videobuf_queue *q,
2122cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			     struct videobuf_buffer *vb,
2132cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			     struct v4l2_framebuffer *fbuf)
2142cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
2152cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_dma_contig_memory *mem = vb->priv;
2162cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2172cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	BUG_ON(!mem);
2182cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2192cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2202cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	switch (vb->memory) {
2212cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	case V4L2_MEMORY_MMAP:
2222cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
2232cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2242cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		/* All handling should be done by __videobuf_mmap_mapper() */
2252cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		if (!mem->vaddr) {
2262cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			dev_err(q->dev, "memory is not alloced/mmapped.\n");
2272cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			return -EINVAL;
2282cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		}
2292cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		break;
2302cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	case V4L2_MEMORY_USERPTR:
2312cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
2322cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
233720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		/* handle pointer from user space */
2342cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		if (vb->baddr)
235720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm			return videobuf_dma_contig_user_get(mem, vb);
2362cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
237720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		/* allocate memory for the read() method */
2382cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		mem->size = PAGE_ALIGN(vb->size);
2392cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
2402cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm						&mem->dma_handle, GFP_KERNEL);
2412cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		if (!mem->vaddr) {
2422cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
2432cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm					 mem->size);
2442cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			return -ENOMEM;
2452cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		}
2462cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2472cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
2482cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			mem->vaddr, mem->size);
2492cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		break;
2502cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	case V4L2_MEMORY_OVERLAY:
2512cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	default:
2522cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
2532cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			__func__);
2542cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		return -EINVAL;
2552cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	}
2562cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2572cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	return 0;
2582cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
2592cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2602cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammstatic int __videobuf_mmap_mapper(struct videobuf_queue *q,
2610b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil				  struct videobuf_buffer *buf,
2622cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				  struct vm_area_struct *vma)
2632cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
2642cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_dma_contig_memory *mem;
2652cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_mapping *map;
2662cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	int retval;
2670b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil	unsigned long size;
2682cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2692cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	dev_dbg(q->dev, "%s\n", __func__);
2702cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2712cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	/* create mapping + update buffer list */
2722cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
2732cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	if (!map)
2742cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		return -ENOMEM;
2752cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2760b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil	buf->map = map;
2772cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	map->q = q;
2782cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2790b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil	buf->baddr = vma->vm_start;
2802cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2810b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil	mem = buf->priv;
2822cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	BUG_ON(!mem);
2832cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
2842cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2850b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil	mem->size = PAGE_ALIGN(buf->bsize);
2862cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
2872cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm					&mem->dma_handle, GFP_KERNEL);
2882cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	if (!mem->vaddr) {
2892cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
2902cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			mem->size);
2912cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		goto error;
2922cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	}
2932cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
2942cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		mem->vaddr, mem->size);
2952cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2962cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	/* Try to remap memory */
2972cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
2982cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	size = vma->vm_end - vma->vm_start;
2992cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	size = (size < mem->size) ? size : mem->size;
3002cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3012cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
3022cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	retval = remap_pfn_range(vma, vma->vm_start,
30371460af58f8565110160283849db4d6bf7e1efa1Linus Torvalds				 mem->dma_handle >> PAGE_SHIFT,
3042cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				 size, vma->vm_page_prot);
3052cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	if (retval) {
3062cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
3072cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		dma_free_coherent(q->dev, mem->size,
3082cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				  mem->vaddr, mem->dma_handle);
3092cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		goto error;
3102cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	}
3112cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3122cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	vma->vm_ops          = &videobuf_vm_ops;
3132cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	vma->vm_flags       |= VM_DONTEXPAND;
3142cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	vma->vm_private_data = map;
3152cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3162cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
3172cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		map, q, vma->vm_start, vma->vm_end,
3180b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil		(long int)buf->bsize,
3190b62b73778554cd47480ea465f0b255cc63b4336Hans Verkuil		vma->vm_pgoff, buf->i);
3202cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3212cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	videobuf_vm_open(vma);
3222cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3232cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	return 0;
3242cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3252cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammerror:
3262cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	kfree(map);
3272cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	return -ENOMEM;
3282cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
3292cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3302cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammstatic struct videobuf_qtype_ops qops = {
3312cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	.magic        = MAGIC_QTYPE_OPS,
3322cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
33333c38283f03d8ea0358229fc03c1beebe67aed0ePawel Osciak	.alloc_vb     = __videobuf_alloc_vb,
3342cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	.iolock       = __videobuf_iolock,
3352cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	.mmap_mapper  = __videobuf_mmap_mapper,
336037c75eb14cd6adb837f81f0c2b2a52c31c91e69Hans Verkuil	.vaddr        = __videobuf_to_vaddr,
3372cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm};
3382cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3392cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammvoid videobuf_queue_dma_contig_init(struct videobuf_queue *q,
34038a54f35a0a90c0b62b111dd4de24248b22616b9Jonathan Corbet				    const struct videobuf_queue_ops *ops,
3412cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				    struct device *dev,
3422cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				    spinlock_t *irqlock,
3432cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				    enum v4l2_buf_type type,
3442cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				    enum v4l2_field field,
3452cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm				    unsigned int msize,
34608bff03ed697a583612b62a6ac566bd5bce98012Hans Verkuil				    void *priv,
34708bff03ed697a583612b62a6ac566bd5bce98012Hans Verkuil				    struct mutex *ext_lock)
3482cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
3492cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
35008bff03ed697a583612b62a6ac566bd5bce98012Hans Verkuil				 priv, &qops, ext_lock);
3512cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
3522cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus DammEXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
3532cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3542cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammdma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
3552cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
3562cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_dma_contig_memory *mem = buf->priv;
3572cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3582cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	BUG_ON(!mem);
3592cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3602cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3612cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	return mem->dma_handle;
3622cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
3632cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus DammEXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
3642cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3652cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Dammvoid videobuf_dma_contig_free(struct videobuf_queue *q,
3662cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm			      struct videobuf_buffer *buf)
3672cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm{
3682cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	struct videobuf_dma_contig_memory *mem = buf->priv;
3692cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3702cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	/* mmapped memory can't be freed here, otherwise mmapped region
3712cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	   would be released, while still needed. In this case, the memory
3722cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	   release should happen inside videobuf_vm_close().
3732cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	   So, it should free memory only if the memory were allocated for
3742cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	   read() operation.
3752cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	 */
376720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	if (buf->memory != V4L2_MEMORY_USERPTR)
3772cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		return;
3782cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3792cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	if (!mem)
3802cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm		return;
3812cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3822cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
3832cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
384720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	/* handle user space pointer case */
385720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	if (buf->baddr) {
386720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		videobuf_dma_contig_user_put(mem);
387720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm		return;
388720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	}
389720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm
390720b17e759a50635c429ccaa2ec3d01edb4f92d6Magnus Damm	/* read() method */
391b2b476f53a9d24b00a313adf7f6ca92515a2af54Pawel Osciak	if (mem->vaddr) {
392b2b476f53a9d24b00a313adf7f6ca92515a2af54Pawel Osciak		dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
393b2b476f53a9d24b00a313adf7f6ca92515a2af54Pawel Osciak		mem->vaddr = NULL;
394b2b476f53a9d24b00a313adf7f6ca92515a2af54Pawel Osciak	}
3952cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm}
3962cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus DammEXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
3972cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus Damm
3982cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus DammMODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
3992cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus DammMODULE_AUTHOR("Magnus Damm");
4002cc45cf25a2c14da6d18b7570a23ec09fb8f439aMagnus DammMODULE_LICENSE("GPL");
401