media: tegra: Tegra videobuf2
[linux-2.6.git] / drivers / media / video / videobuf2-dma-nvmap.c
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <linux/module.h>
18 #include <linux/slab.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/nvmap.h>
21
22 #include <media/videobuf2-core.h>
23 #include <media/videobuf2-memops.h>
24
25 struct vb2_dc_conf {
26         struct device           *dev;
27         struct nvmap_client     *nvmap_client;
28 };
29
30 struct vb2_dc_buf {
31         struct vb2_dc_conf              *conf;
32         void                            *vaddr;
33         dma_addr_t                      paddr;
34         unsigned long                   size;
35         struct vm_area_struct           *vma;
36         atomic_t                        refcount;
37         struct vb2_vmarea_handler       handler;
38
39         struct nvmap_handle_ref         *nvmap_ref;
40 };
41
42 static void vb2_dma_nvmap_put(void *buf_priv);
43
44 static void *vb2_dma_nvmap_alloc(void *alloc_ctx, unsigned long size)
45 {
46         struct vb2_dc_conf *conf = alloc_ctx;
47         struct vb2_dc_buf *buf;
48         int ret;
49
50         buf = kzalloc(sizeof *buf, GFP_KERNEL);
51         if (!buf) {
52                 ret = -ENOMEM;
53                 goto exit;
54         }
55
56         buf->nvmap_ref = nvmap_alloc(conf->nvmap_client, size, 32,
57                                      NVMAP_HANDLE_CACHEABLE, NVMAP_HEAP_SYSMEM);
58         if (IS_ERR(buf->nvmap_ref)) {
59                 dev_err(conf->dev, "nvmap_alloc failed\n");
60                 ret = -ENOMEM;
61                 goto exit_free;
62         }
63
64         buf->paddr = nvmap_pin(conf->nvmap_client, buf->nvmap_ref);
65         if (IS_ERR_VALUE(buf->paddr)) {
66                 dev_err(conf->dev, "nvmap_pin failed\n");
67                 ret = -ENOMEM;
68                 goto exit_dealloc;
69         }
70
71         buf->vaddr = nvmap_mmap(buf->nvmap_ref);
72         if (!buf->vaddr) {
73                 dev_err(conf->dev, "nvmap_mmap failed\n");
74                 ret = -ENOMEM;
75                 goto exit_unpin;
76         }
77
78         buf->conf = conf;
79         buf->size = size;
80
81         buf->handler.refcount = &buf->refcount;
82         buf->handler.put = vb2_dma_nvmap_put;
83         buf->handler.arg = buf;
84
85         atomic_inc(&buf->refcount);
86
87         return buf;
88
89 exit_unpin:
90         nvmap_unpin(conf->nvmap_client, buf->nvmap_ref);
91 exit_dealloc:
92         nvmap_free(conf->nvmap_client, buf->nvmap_ref);
93 exit_free:
94         kfree(buf);
95 exit:
96         return ERR_PTR(ret);
97 }
98
99 static void vb2_dma_nvmap_put(void *buf_priv)
100 {
101         struct vb2_dc_buf *buf = buf_priv;
102
103         if (atomic_dec_and_test(&buf->refcount)) {
104                 nvmap_munmap(buf->nvmap_ref, buf->vaddr);
105                 nvmap_unpin(buf->conf->nvmap_client, buf->nvmap_ref);
106                 nvmap_free(buf->conf->nvmap_client, buf->nvmap_ref);
107                 kfree(buf);
108         }
109 }
110
111 static void *vb2_dma_nvmap_cookie(void *buf_priv)
112 {
113         struct vb2_dc_buf *buf = buf_priv;
114
115         return &buf->paddr;
116 }
117
118 static void *vb2_dma_nvmap_vaddr(void *buf_priv)
119 {
120         struct vb2_dc_buf *buf = buf_priv;
121         if (!buf)
122                 return 0;
123
124         return buf->vaddr;
125 }
126
127 static unsigned int vb2_dma_nvmap_num_users(void *buf_priv)
128 {
129         struct vb2_dc_buf *buf = buf_priv;
130
131         return atomic_read(&buf->refcount);
132 }
133
134 static int vb2_dma_nvmap_mmap(void *buf_priv, struct vm_area_struct *vma)
135 {
136         struct vb2_dc_buf *buf = buf_priv;
137
138         if (!buf) {
139                 printk(KERN_ERR "No buffer to map\n");
140                 return -EINVAL;
141         }
142
143         return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
144                                   &vb2_common_vm_ops, &buf->handler);
145 }
146
147 static void *vb2_dma_nvmap_get_userptr(void *alloc_ctx, unsigned long vaddr,
148                                         unsigned long size, int write)
149 {
150         struct vb2_dc_buf *buf;
151         struct vm_area_struct *vma;
152         dma_addr_t paddr = 0;
153         int ret;
154
155         buf = kzalloc(sizeof *buf, GFP_KERNEL);
156         if (!buf)
157                 return ERR_PTR(-ENOMEM);
158
159         ret = vb2_get_contig_userptr(vaddr, size, &vma, &paddr);
160         if (ret) {
161                 printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
162                                 vaddr);
163                 kfree(buf);
164                 return ERR_PTR(ret);
165         }
166
167         buf->size = size;
168         buf->paddr = paddr;
169         buf->vma = vma;
170
171         return buf;
172 }
173
174 static void vb2_dma_nvmap_put_userptr(void *mem_priv)
175 {
176         struct vb2_dc_buf *buf = mem_priv;
177
178         if (!buf)
179                 return;
180
181         vb2_put_vma(buf->vma);
182         kfree(buf);
183 }
184
185 const struct vb2_mem_ops vb2_dma_nvmap_memops = {
186         .alloc          = vb2_dma_nvmap_alloc,
187         .put            = vb2_dma_nvmap_put,
188         .cookie         = vb2_dma_nvmap_cookie,
189         .vaddr          = vb2_dma_nvmap_vaddr,
190         .mmap           = vb2_dma_nvmap_mmap,
191         .get_userptr    = vb2_dma_nvmap_get_userptr,
192         .put_userptr    = vb2_dma_nvmap_put_userptr,
193         .num_users      = vb2_dma_nvmap_num_users,
194 };
195 EXPORT_SYMBOL_GPL(vb2_dma_nvmap_memops);
196
197 void *vb2_dma_nvmap_init_ctx(struct device *dev)
198 {
199         struct vb2_dc_conf *conf;
200         int ret;
201
202         conf = kzalloc(sizeof *conf, GFP_KERNEL);
203         if (!conf) {
204                 ret = -ENOMEM;
205                 goto exit;
206         }
207
208         conf->dev = dev;
209
210         conf->nvmap_client = nvmap_create_client(nvmap_dev,
211                                                  "videobuf2-dma-nvmap");
212         if (!conf->nvmap_client) {
213                 ret = -ENOMEM;
214                 goto exit_free;
215         }
216
217         return conf;
218
219 exit_free:
220         kfree(conf);
221 exit:
222         return ERR_PTR(ret);
223 }
224 EXPORT_SYMBOL_GPL(vb2_dma_nvmap_init_ctx);
225
226 void vb2_dma_nvmap_cleanup_ctx(void *alloc_ctx)
227 {
228         struct vb2_dc_conf *conf = alloc_ctx;
229
230         nvmap_client_put(conf->nvmap_client);
231
232         kfree(alloc_ctx);
233 }
234 EXPORT_SYMBOL_GPL(vb2_dma_nvmap_cleanup_ctx);
235
236 MODULE_DESCRIPTION("DMA-nvmap memory handling routines for videobuf2");
237 MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
238 MODULE_LICENSE("GPL");