xref: /openbmc/linux/drivers/media/common/videobuf2/videobuf2-memops.c (revision 1a931707ad4a46e79d4ecfee56d8f6e8cc8d4f28)
17952be9bSHans Verkuil /*
27952be9bSHans Verkuil  * videobuf2-memops.c - generic memory handling routines for videobuf2
37952be9bSHans Verkuil  *
47952be9bSHans Verkuil  * Copyright (C) 2010 Samsung Electronics
57952be9bSHans Verkuil  *
67952be9bSHans Verkuil  * Author: Pawel Osciak <pawel@osciak.com>
77952be9bSHans Verkuil  *	   Marek Szyprowski <m.szyprowski@samsung.com>
87952be9bSHans Verkuil  *
97952be9bSHans Verkuil  * This program is free software; you can redistribute it and/or modify
107952be9bSHans Verkuil  * it under the terms of the GNU General Public License as published by
117952be9bSHans Verkuil  * the Free Software Foundation.
127952be9bSHans Verkuil  */
137952be9bSHans Verkuil 
147952be9bSHans Verkuil #include <linux/slab.h>
157952be9bSHans Verkuil #include <linux/module.h>
167952be9bSHans Verkuil #include <linux/dma-mapping.h>
177952be9bSHans Verkuil #include <linux/vmalloc.h>
187952be9bSHans Verkuil #include <linux/mm.h>
197952be9bSHans Verkuil #include <linux/sched.h>
207952be9bSHans Verkuil #include <linux/file.h>
217952be9bSHans Verkuil 
227952be9bSHans Verkuil #include <media/videobuf2-v4l2.h>
237952be9bSHans Verkuil #include <media/videobuf2-memops.h>
247952be9bSHans Verkuil 
257952be9bSHans Verkuil /**
267952be9bSHans Verkuil  * vb2_create_framevec() - map virtual addresses to pfns
277952be9bSHans Verkuil  * @start:	Virtual user address where we start mapping
287952be9bSHans Verkuil  * @length:	Length of a range to map
29*e2fc6eddSHans Verkuil  * @write:	Should we map for writing into the area
307952be9bSHans Verkuil  *
317952be9bSHans Verkuil  * This function allocates and fills in a vector with pfns corresponding to
327952be9bSHans Verkuil  * virtual address range passed in arguments. If pfns have corresponding pages,
337952be9bSHans Verkuil  * page references are also grabbed to pin pages in memory. The function
347952be9bSHans Verkuil  * returns pointer to the vector on success and error pointer in case of
357952be9bSHans Verkuil  * failure. Returned vector needs to be freed via vb2_destroy_pfnvec().
367952be9bSHans Verkuil  */
vb2_create_framevec(unsigned long start,unsigned long length,bool write)377952be9bSHans Verkuil struct frame_vector *vb2_create_framevec(unsigned long start,
38*e2fc6eddSHans Verkuil 					 unsigned long length,
39*e2fc6eddSHans Verkuil 					 bool write)
407952be9bSHans Verkuil {
417952be9bSHans Verkuil 	int ret;
427952be9bSHans Verkuil 	unsigned long first, last;
437952be9bSHans Verkuil 	unsigned long nr;
447952be9bSHans Verkuil 	struct frame_vector *vec;
457952be9bSHans Verkuil 
467952be9bSHans Verkuil 	first = start >> PAGE_SHIFT;
477952be9bSHans Verkuil 	last = (start + length - 1) >> PAGE_SHIFT;
487952be9bSHans Verkuil 	nr = last - first + 1;
497952be9bSHans Verkuil 	vec = frame_vector_create(nr);
507952be9bSHans Verkuil 	if (!vec)
517952be9bSHans Verkuil 		return ERR_PTR(-ENOMEM);
52*e2fc6eddSHans Verkuil 	ret = get_vaddr_frames(start & PAGE_MASK, nr, write, vec);
537952be9bSHans Verkuil 	if (ret < 0)
547952be9bSHans Verkuil 		goto out_destroy;
557952be9bSHans Verkuil 	/* We accept only complete set of PFNs */
567952be9bSHans Verkuil 	if (ret != nr) {
577952be9bSHans Verkuil 		ret = -EFAULT;
587952be9bSHans Verkuil 		goto out_release;
597952be9bSHans Verkuil 	}
607952be9bSHans Verkuil 	return vec;
617952be9bSHans Verkuil out_release:
627952be9bSHans Verkuil 	put_vaddr_frames(vec);
637952be9bSHans Verkuil out_destroy:
647952be9bSHans Verkuil 	frame_vector_destroy(vec);
657952be9bSHans Verkuil 	return ERR_PTR(ret);
667952be9bSHans Verkuil }
677952be9bSHans Verkuil EXPORT_SYMBOL(vb2_create_framevec);
687952be9bSHans Verkuil 
697952be9bSHans Verkuil /**
707952be9bSHans Verkuil  * vb2_destroy_framevec() - release vector of mapped pfns
717952be9bSHans Verkuil  * @vec:	vector of pfns / pages to release
727952be9bSHans Verkuil  *
737952be9bSHans Verkuil  * This releases references to all pages in the vector @vec (if corresponding
747952be9bSHans Verkuil  * pfns are backed by pages) and frees the passed vector.
757952be9bSHans Verkuil  */
vb2_destroy_framevec(struct frame_vector * vec)767952be9bSHans Verkuil void vb2_destroy_framevec(struct frame_vector *vec)
777952be9bSHans Verkuil {
787952be9bSHans Verkuil 	put_vaddr_frames(vec);
797952be9bSHans Verkuil 	frame_vector_destroy(vec);
807952be9bSHans Verkuil }
817952be9bSHans Verkuil EXPORT_SYMBOL(vb2_destroy_framevec);
827952be9bSHans Verkuil 
837952be9bSHans Verkuil /**
847952be9bSHans Verkuil  * vb2_common_vm_open() - increase refcount of the vma
857952be9bSHans Verkuil  * @vma:	virtual memory region for the mapping
867952be9bSHans Verkuil  *
877952be9bSHans Verkuil  * This function adds another user to the provided vma. It expects
887952be9bSHans Verkuil  * struct vb2_vmarea_handler pointer in vma->vm_private_data.
897952be9bSHans Verkuil  */
vb2_common_vm_open(struct vm_area_struct * vma)907952be9bSHans Verkuil static void vb2_common_vm_open(struct vm_area_struct *vma)
917952be9bSHans Verkuil {
927952be9bSHans Verkuil 	struct vb2_vmarea_handler *h = vma->vm_private_data;
937952be9bSHans Verkuil 
947952be9bSHans Verkuil 	pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
957952be9bSHans Verkuil 	       __func__, h, refcount_read(h->refcount), vma->vm_start,
967952be9bSHans Verkuil 	       vma->vm_end);
977952be9bSHans Verkuil 
987952be9bSHans Verkuil 	refcount_inc(h->refcount);
997952be9bSHans Verkuil }
1007952be9bSHans Verkuil 
1017952be9bSHans Verkuil /**
1027952be9bSHans Verkuil  * vb2_common_vm_close() - decrease refcount of the vma
1037952be9bSHans Verkuil  * @vma:	virtual memory region for the mapping
1047952be9bSHans Verkuil  *
1057952be9bSHans Verkuil  * This function releases the user from the provided vma. It expects
1067952be9bSHans Verkuil  * struct vb2_vmarea_handler pointer in vma->vm_private_data.
1077952be9bSHans Verkuil  */
vb2_common_vm_close(struct vm_area_struct * vma)1087952be9bSHans Verkuil static void vb2_common_vm_close(struct vm_area_struct *vma)
1097952be9bSHans Verkuil {
1107952be9bSHans Verkuil 	struct vb2_vmarea_handler *h = vma->vm_private_data;
1117952be9bSHans Verkuil 
1127952be9bSHans Verkuil 	pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
1137952be9bSHans Verkuil 	       __func__, h, refcount_read(h->refcount), vma->vm_start,
1147952be9bSHans Verkuil 	       vma->vm_end);
1157952be9bSHans Verkuil 
1167952be9bSHans Verkuil 	h->put(h->arg);
1177952be9bSHans Verkuil }
1187952be9bSHans Verkuil 
1197952be9bSHans Verkuil /*
1204b129dc9SMauro Carvalho Chehab  * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmapped
1217952be9bSHans Verkuil  * video buffers
1227952be9bSHans Verkuil  */
1237952be9bSHans Verkuil const struct vm_operations_struct vb2_common_vm_ops = {
1247952be9bSHans Verkuil 	.open = vb2_common_vm_open,
1257952be9bSHans Verkuil 	.close = vb2_common_vm_close,
1267952be9bSHans Verkuil };
1277952be9bSHans Verkuil EXPORT_SYMBOL_GPL(vb2_common_vm_ops);
1287952be9bSHans Verkuil 
1297952be9bSHans Verkuil MODULE_DESCRIPTION("common memory handling routines for videobuf2");
1307952be9bSHans Verkuil MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
1317952be9bSHans Verkuil MODULE_LICENSE("GPL");
132