1 /*
2  * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
3  *
4  * Copyright (C) 2010 Samsung Electronics
5  *
6  * Author: Pawel Osciak <pawel@osciak.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation.
11  */
12 
13 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/dma-mapping.h>
16 
17 #include <media/videobuf2-core.h>
18 #include <media/videobuf2-memops.h>
19 
20 struct vb2_dc_conf {
21 	struct device		*dev;
22 };
23 
24 struct vb2_dc_buf {
25 	struct vb2_dc_conf		*conf;
26 	void				*vaddr;
27 	dma_addr_t			dma_addr;
28 	unsigned long			size;
29 	struct vm_area_struct		*vma;
30 	atomic_t			refcount;
31 	struct vb2_vmarea_handler	handler;
32 };
33 
34 static void vb2_dma_contig_put(void *buf_priv);
35 
vb2_dma_contig_alloc(void * alloc_ctx,unsigned long size)36 static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
37 {
38 	struct vb2_dc_conf *conf = alloc_ctx;
39 	struct vb2_dc_buf *buf;
40 
41 	buf = kzalloc(sizeof *buf, GFP_KERNEL);
42 	if (!buf)
43 		return ERR_PTR(-ENOMEM);
44 
45 	buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
46 					GFP_KERNEL);
47 	if (!buf->vaddr) {
48 		dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
49 			size);
50 		kfree(buf);
51 		return ERR_PTR(-ENOMEM);
52 	}
53 
54 	buf->conf = conf;
55 	buf->size = size;
56 
57 	buf->handler.refcount = &buf->refcount;
58 	buf->handler.put = vb2_dma_contig_put;
59 	buf->handler.arg = buf;
60 
61 	atomic_inc(&buf->refcount);
62 
63 	return buf;
64 }
65 
vb2_dma_contig_put(void * buf_priv)66 static void vb2_dma_contig_put(void *buf_priv)
67 {
68 	struct vb2_dc_buf *buf = buf_priv;
69 
70 	if (atomic_dec_and_test(&buf->refcount)) {
71 		dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
72 				  buf->dma_addr);
73 		kfree(buf);
74 	}
75 }
76 
vb2_dma_contig_cookie(void * buf_priv)77 static void *vb2_dma_contig_cookie(void *buf_priv)
78 {
79 	struct vb2_dc_buf *buf = buf_priv;
80 
81 	return &buf->dma_addr;
82 }
83 
vb2_dma_contig_vaddr(void * buf_priv)84 static void *vb2_dma_contig_vaddr(void *buf_priv)
85 {
86 	struct vb2_dc_buf *buf = buf_priv;
87 	if (!buf)
88 		return 0;
89 
90 	return buf->vaddr;
91 }
92 
vb2_dma_contig_num_users(void * buf_priv)93 static unsigned int vb2_dma_contig_num_users(void *buf_priv)
94 {
95 	struct vb2_dc_buf *buf = buf_priv;
96 
97 	return atomic_read(&buf->refcount);
98 }
99 
vb2_dma_contig_mmap(void * buf_priv,struct vm_area_struct * vma)100 static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
101 {
102 	struct vb2_dc_buf *buf = buf_priv;
103 
104 	if (!buf) {
105 		printk(KERN_ERR "No buffer to map\n");
106 		return -EINVAL;
107 	}
108 
109 	return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
110 				  &vb2_common_vm_ops, &buf->handler);
111 }
112 
vb2_dma_contig_get_userptr(void * alloc_ctx,unsigned long vaddr,unsigned long size,int write)113 static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
114 					unsigned long size, int write)
115 {
116 	struct vb2_dc_buf *buf;
117 	struct vm_area_struct *vma;
118 	dma_addr_t dma_addr = 0;
119 	int ret;
120 
121 	buf = kzalloc(sizeof *buf, GFP_KERNEL);
122 	if (!buf)
123 		return ERR_PTR(-ENOMEM);
124 
125 	ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
126 	if (ret) {
127 		printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
128 				vaddr);
129 		kfree(buf);
130 		return ERR_PTR(ret);
131 	}
132 
133 	buf->size = size;
134 	buf->dma_addr = dma_addr;
135 	buf->vma = vma;
136 
137 	return buf;
138 }
139 
vb2_dma_contig_put_userptr(void * mem_priv)140 static void vb2_dma_contig_put_userptr(void *mem_priv)
141 {
142 	struct vb2_dc_buf *buf = mem_priv;
143 
144 	if (!buf)
145 		return;
146 
147 	vb2_put_vma(buf->vma);
148 	kfree(buf);
149 }
150 
151 const struct vb2_mem_ops vb2_dma_contig_memops = {
152 	.alloc		= vb2_dma_contig_alloc,
153 	.put		= vb2_dma_contig_put,
154 	.cookie		= vb2_dma_contig_cookie,
155 	.vaddr		= vb2_dma_contig_vaddr,
156 	.mmap		= vb2_dma_contig_mmap,
157 	.get_userptr	= vb2_dma_contig_get_userptr,
158 	.put_userptr	= vb2_dma_contig_put_userptr,
159 	.num_users	= vb2_dma_contig_num_users,
160 };
161 EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
162 
vb2_dma_contig_init_ctx(struct device * dev)163 void *vb2_dma_contig_init_ctx(struct device *dev)
164 {
165 	struct vb2_dc_conf *conf;
166 
167 	conf = kzalloc(sizeof *conf, GFP_KERNEL);
168 	if (!conf)
169 		return ERR_PTR(-ENOMEM);
170 
171 	conf->dev = dev;
172 
173 	return conf;
174 }
175 EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
176 
vb2_dma_contig_cleanup_ctx(void * alloc_ctx)177 void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
178 {
179 	kfree(alloc_ctx);
180 }
181 EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
182 
183 MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
184 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
185 MODULE_LICENSE("GPL");
186