7510cf1dbd08e78d224d11da0b9d719fb5839b0d
[linux-3.10.git] / drivers / video / tegra / nvmap / nvmap_dmabuf.c
1 /*
2  * dma_buf exporter for nvmap
3  *
4  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include <linux/slab.h>
21 #include <linux/uaccess.h>
22 #include <linux/export.h>
23 #include <linux/nvmap.h>
24 #include <linux/dma-buf.h>
25
26 #include "nvmap_priv.h"
27 #include "nvmap_ioctl.h"
28
29 struct nvmap_handle_info {
30         struct nvmap_client *client;
31         ulong id;
32         struct nvmap_handle_ref *ref;
33         struct nvmap_handle *handle;
34 };
35
36 static int nvmap_dmabuf_attach(struct dma_buf *dmabuf, struct device *dev,
37                                struct dma_buf_attachment *attach)
38 {
39         struct nvmap_handle_info *info = dmabuf->priv;
40         struct nvmap_handle_ref *ref;
41
42         ref = nvmap_duplicate_handle_id(info->client, info->id, 0);
43         if (IS_ERR(ref))
44                 return PTR_ERR(ref);
45
46         info->ref = ref;
47         attach->priv = info;
48
49         dev_dbg(dev, "%s(%08lx)\n", __func__, info->id);
50         return 0;
51 }
52
53 static void nvmap_dmabuf_detach(struct dma_buf *dmabuf,
54                                 struct dma_buf_attachment *attach)
55 {
56         struct nvmap_handle_info *info = dmabuf->priv;
57
58         nvmap_free(info->client, info->ref);
59
60         dev_dbg(attach->dev, "%s(%08lx)\n", __func__, info->id);
61 }
62
63 static struct sg_table *nvmap_dmabuf_map_dma_buf(
64         struct dma_buf_attachment *attach, enum dma_data_direction dir)
65 {
66         struct nvmap_handle_info *info = attach->dmabuf->priv;
67         int err;
68         struct sg_table *sgt;
69         dma_addr_t addr;
70
71         sgt = nvmap_sg_table(info->client, info->ref);
72         if (IS_ERR(sgt))
73                 return sgt;
74
75         err = nvmap_pin(info->client, info->ref, &addr);
76         if (err)
77                 goto err_pin;
78
79         sg_dma_address(sgt->sgl) = addr;
80
81         dev_dbg(attach->dev, "%s(%08lx)\n", __func__, info->id);
82         return sgt;
83
84 err_pin:
85         nvmap_free_sg_table(info->client, info->ref, sgt);
86         return ERR_PTR(err);
87 }
88
89 static void nvmap_dmabuf_unmap_dma_buf(struct dma_buf_attachment *attach,
90                                        struct sg_table *sgt,
91                                        enum dma_data_direction dir)
92 {
93         struct nvmap_handle_info *info = attach->dmabuf->priv;
94
95         nvmap_unpin(info->client, info->ref);
96         nvmap_free_sg_table(info->client, info->ref, sgt);
97
98         dev_dbg(attach->dev, "%s(%08lx)\n", __func__, info->id);
99 }
100
101 static void nvmap_dmabuf_release(struct dma_buf *dmabuf)
102 {
103         struct nvmap_handle_info *info = dmabuf->priv;
104
105         pr_debug("%s(%08lx)\n", __func__, info->id);
106
107         nvmap_handle_put(info->handle);
108         nvmap_client_put(info->client);
109         kfree(info);
110 }
111
112 static void *nvmap_dmabuf_kmap(struct dma_buf *dmabuf, unsigned long page_num)
113 {
114         struct nvmap_handle_info *info = dmabuf->priv;
115
116         pr_debug("%s(%08lx)\n", __func__, info->id);
117         return nvmap_kmap(info->ref, page_num);
118 }
119
120 static void nvmap_dmabuf_kunmap(struct dma_buf *dmabuf,
121                 unsigned long page_num, void *addr)
122 {
123         struct nvmap_handle_info *info = dmabuf->priv;
124
125         pr_debug("%s(%08lx)\n", __func__, info->id);
126         nvmap_kunmap(info->ref, page_num, addr);
127 }
128
129 static void *nvmap_dmabuf_kmap_atomic(struct dma_buf *dmabuf,
130                                       unsigned long page_num)
131 {
132         WARN(1, "%s() can't be called from atomic\n", __func__);
133         return NULL;
134 }
135
136 static int nvmap_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
137 {
138         WARN(1, "%s() not implemented yet\n", __func__);
139         return -1;
140 }
141
142 static void *nvmap_dmabuf_vmap(struct dma_buf *dmabuf)
143 {
144         struct nvmap_handle_info *info = dmabuf->priv;
145
146         pr_debug("%s(%08lx)\n", __func__, info->id);
147         return nvmap_mmap(info->ref);
148 }
149
150 static void nvmap_dmabuf_vunmap(struct dma_buf *dmabuf, void *vaddr)
151 {
152         struct nvmap_handle_info *info = dmabuf->priv;
153
154         pr_debug("%s(%08lx)\n", __func__, info->id);
155         nvmap_munmap(info->ref, vaddr);
156 }
157
158 static struct dma_buf_ops nvmap_dma_buf_ops = {
159         .attach         = nvmap_dmabuf_attach,
160         .detach         = nvmap_dmabuf_detach,
161         .map_dma_buf    = nvmap_dmabuf_map_dma_buf,
162         .unmap_dma_buf  = nvmap_dmabuf_unmap_dma_buf,
163         .release        = nvmap_dmabuf_release,
164         .kmap_atomic    = nvmap_dmabuf_kmap_atomic,
165         .kmap           = nvmap_dmabuf_kmap,
166         .kunmap         = nvmap_dmabuf_kunmap,
167         .mmap           = nvmap_dmabuf_mmap,
168         .vmap           = nvmap_dmabuf_vmap,
169         .vunmap         = nvmap_dmabuf_vunmap,
170 };
171
172 struct dma_buf *nvmap_share_dmabuf(struct nvmap_client *client, ulong id)
173 {
174         struct dma_buf *dmabuf;
175         struct nvmap_handle_info *info;
176         int err;
177         struct nvmap_handle *handle;
178
179         if (!nvmap_client_get(client))
180                 return ERR_PTR(-EINVAL);
181
182         handle = nvmap_validate_get(client, id, 0);
183         if (!handle) {
184                 err = -EINVAL;
185                 goto err_nvmap_validate_get;
186         }
187
188         info = kzalloc(sizeof(*info), GFP_KERNEL);
189         if (!info) {
190                 err = -ENOMEM;
191                 goto err_nomem;
192         }
193         info->id = id;
194         info->handle = handle;
195         info->client = client;
196
197         dmabuf = dma_buf_export(info, &nvmap_dma_buf_ops, handle->size,
198                                 O_RDWR);
199         if (IS_ERR(dmabuf)) {
200                 err = PTR_ERR(dmabuf);
201                 goto err_export;
202         }
203         pr_debug("%s(%08lx) %p\n", __func__, info->id, dmabuf);
204         return dmabuf;
205
206 err_export:
207         kfree(info);
208 err_nomem:
209         nvmap_handle_put(handle);
210 err_nvmap_validate_get:
211         nvmap_client_put(client);
212         return ERR_PTR(err);
213 }
214 EXPORT_SYMBOL_GPL(nvmap_share_dmabuf);
215
216 int nvmap_ioctl_share_dmabuf(struct file *filp, void __user *arg)
217 {
218         int err;
219         struct nvmap_create_handle op;
220         struct nvmap_client *client = filp->private_data;
221         struct dma_buf *dmabuf;
222         ulong handle;
223
224         BUG_ON(!client);
225
226         if (copy_from_user(&op, (void __user *)arg, sizeof(op)))
227                 return -EFAULT;
228
229         handle = unmarshal_user_id(op.id);
230         if (!handle)
231                 return -EINVAL;
232
233         dmabuf = nvmap_share_dmabuf(client, handle);
234         if (IS_ERR(dmabuf))
235                 return -EINVAL;
236
237         op.fd = dma_buf_fd(dmabuf, O_CLOEXEC);
238         if (op.fd < 0) {
239                 err = op.fd;
240                 goto err_out;
241         }
242
243         if (copy_to_user((void __user *)arg, &op, sizeof(op))) {
244                 err = -EFAULT;
245                 goto err_out;
246         }
247         return 0;
248
249 err_out:
250         nvmap_dmabuf_release(dmabuf);
251         return err;
252 }