]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/gpu/nvgpu/gk20a/as_gk20a.c
gpu: nvgpu: implement mapping for sparse allocation
[sojka/nv-tegra/linux-3.10.git] / drivers / gpu / nvgpu / gk20a / as_gk20a.c
1 /*
2  * drivers/video/tegra/host/gk20a/as_gk20a.c
3  *
4  * GK20A Address Spaces
5  *
6  * Copyright (c) 2011-2014, NVIDIA CORPORATION.  All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  */
17
18 #include <linux/slab.h>
19 #include <linux/fs.h>
20 #include <linux/cdev.h>
21 #include <linux/uaccess.h>
22
23 #include <trace/events/gk20a.h>
24
25 #include "gk20a.h"
26
27 /* dumb allocator... */
28 static int generate_as_share_id(struct gk20a_as *as)
29 {
30         gk20a_dbg_fn("");
31         return ++as->last_share_id;
32 }
33 /* still dumb */
34 static void release_as_share_id(struct gk20a_as *as, int id)
35 {
36         gk20a_dbg_fn("");
37         return;
38 }
39
40 static int gk20a_as_alloc_share(struct gk20a_as *as,
41                                 struct gk20a_as_share **out)
42 {
43         struct gk20a_as_share *as_share;
44         int err = 0;
45
46         gk20a_dbg_fn("");
47
48         *out = 0;
49         as_share = kzalloc(sizeof(*as_share), GFP_KERNEL);
50         if (!as_share)
51                 return -ENOMEM;
52
53         as_share->as = as;
54         as_share->id = generate_as_share_id(as_share->as);
55         as_share->ref_cnt.counter = 1;
56
57         /* this will set as_share->vm. */
58         err = gk20a_vm_alloc_share(as_share);
59         if (err)
60                 goto failed;
61
62         *out = as_share;
63         return 0;
64
65  failed:
66         kfree(as_share);
67         return err;
68 }
69
70 /*
71  * channels and the device nodes call this to release.
72  * once the ref_cnt hits zero the share is deleted.
73  */
74 int gk20a_as_release_share(struct gk20a_as_share *as_share)
75 {
76         int err;
77
78         gk20a_dbg_fn("");
79
80         if (atomic_dec_return(&as_share->ref_cnt) > 0)
81                 return 0;
82
83         err = gk20a_vm_release_share(as_share);
84         release_as_share_id(as_share->as, as_share->id);
85         kfree(as_share);
86         return err;
87 }
88
89 static int gk20a_as_ioctl_bind_channel(
90                 struct gk20a_as_share *as_share,
91                 struct nvhost_as_bind_channel_args *args)
92 {
93         int err = 0;
94         struct channel_gk20a *ch;
95
96         gk20a_dbg_fn("");
97
98         ch = gk20a_get_channel_from_file(args->channel_fd);
99         if (!ch || gk20a_channel_as_bound(ch))
100                 return -EINVAL;
101
102         atomic_inc(&as_share->ref_cnt);
103
104         /* this will set channel_gk20a->vm */
105         err = gk20a_vm_bind_channel(as_share, ch);
106         if (err) {
107                 atomic_dec(&as_share->ref_cnt);
108                 return err;
109         }
110
111         return err;
112 }
113
114 static int gk20a_as_ioctl_alloc_space(
115                 struct gk20a_as_share *as_share,
116                 struct nvhost_as_alloc_space_args *args)
117 {
118         gk20a_dbg_fn("");
119         return gk20a_vm_alloc_space(as_share, args);
120 }
121
122 static int gk20a_as_ioctl_free_space(
123                 struct gk20a_as_share *as_share,
124                 struct nvhost_as_free_space_args *args)
125 {
126         gk20a_dbg_fn("");
127         return gk20a_vm_free_space(as_share, args);
128 }
129
130 static int gk20a_as_ioctl_map_buffer_ex(
131                 struct gk20a_as_share *as_share,
132                 struct nvhost_as_map_buffer_ex_args *args)
133 {
134         gk20a_dbg_fn("");
135
136         return gk20a_vm_map_buffer(as_share, args->dmabuf_fd,
137                                    &args->as_offset, args->flags,
138                                    args->kind,
139                                    args->buffer_offset,
140                                    args->mapping_size
141                                    );
142 }
143
144 static int gk20a_as_ioctl_map_buffer(
145                 struct gk20a_as_share *as_share,
146                 struct nvhost_as_map_buffer_args *args)
147 {
148         gk20a_dbg_fn("");
149         return gk20a_vm_map_buffer(as_share, args->nvmap_handle,
150                                    &args->o_a.offset,
151                                    args->flags, NV_KIND_DEFAULT,
152                                    0, 0);
153         /* args->o_a.offset will be set if !err */
154 }
155
156 static int gk20a_as_ioctl_unmap_buffer(
157                 struct gk20a_as_share *as_share,
158                 struct nvhost_as_unmap_buffer_args *args)
159 {
160         gk20a_dbg_fn("");
161         return gk20a_vm_unmap_buffer(as_share, args->offset);
162 }
163
164 int gk20a_as_dev_open(struct inode *inode, struct file *filp)
165 {
166         struct gk20a_as_share *as_share;
167         struct gk20a *g;
168         int err;
169
170         gk20a_dbg_fn("");
171
172         g = container_of(inode->i_cdev, struct gk20a, as.cdev);
173
174         err = gk20a_get_client(g);
175         if (err) {
176                 gk20a_dbg_fn("fail to get channel!");
177                 return err;
178         }
179
180         err = gk20a_as_alloc_share(&g->as, &as_share);
181         if (err) {
182                 gk20a_dbg_fn("failed to alloc share");
183                 gk20a_put_client(g);
184                 return err;
185         }
186
187         filp->private_data = as_share;
188         return 0;
189 }
190
191 int gk20a_as_dev_release(struct inode *inode, struct file *filp)
192 {
193         struct gk20a_as_share *as_share = filp->private_data;
194         int ret;
195         struct gk20a *g = gk20a_from_as(as_share->as);
196
197         gk20a_dbg_fn("");
198
199         ret = gk20a_as_release_share(as_share);
200
201         gk20a_put_client(g);
202
203         return ret;
204 }
205
206 long gk20a_as_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
207 {
208         int err = 0;
209         struct gk20a_as_share *as_share = filp->private_data;
210         struct gk20a *g = gk20a_from_as(as_share->as);
211
212         u8 buf[NVHOST_AS_IOCTL_MAX_ARG_SIZE];
213
214         if ((_IOC_TYPE(cmd) != NVHOST_AS_IOCTL_MAGIC) ||
215                 (_IOC_NR(cmd) == 0) ||
216                 (_IOC_NR(cmd) > NVHOST_AS_IOCTL_LAST))
217                 return -EFAULT;
218
219         BUG_ON(_IOC_SIZE(cmd) > NVHOST_AS_IOCTL_MAX_ARG_SIZE);
220
221         if (_IOC_DIR(cmd) & _IOC_WRITE) {
222                 if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd)))
223                         return -EFAULT;
224         }
225
226         err = gk20a_busy(g->dev);
227         if (err)
228                 return err;
229
230         switch (cmd) {
231         case NVHOST_AS_IOCTL_BIND_CHANNEL:
232                 trace_gk20a_as_ioctl_bind_channel(dev_name(dev_from_gk20a(g)));
233                 err = gk20a_as_ioctl_bind_channel(as_share,
234                                (struct nvhost_as_bind_channel_args *)buf);
235
236                 break;
237         case NVHOST32_AS_IOCTL_ALLOC_SPACE:
238         {
239                 struct nvhost32_as_alloc_space_args *args32 =
240                         (struct nvhost32_as_alloc_space_args *)buf;
241                 struct nvhost_as_alloc_space_args args;
242
243                 args.pages = args32->pages;
244                 args.page_size = args32->page_size;
245                 args.flags = args32->flags;
246                 args.o_a.offset = args32->o_a.offset;
247                 trace_gk20a_as_ioctl_alloc_space(dev_name(dev_from_gk20a(g)));
248                 err = gk20a_as_ioctl_alloc_space(as_share, &args);
249                 args32->o_a.offset = args.o_a.offset;
250                 break;
251         }
252         case NVHOST_AS_IOCTL_ALLOC_SPACE:
253                 trace_gk20a_as_ioctl_alloc_space(dev_name(dev_from_gk20a(g)));
254                 err = gk20a_as_ioctl_alloc_space(as_share,
255                                 (struct nvhost_as_alloc_space_args *)buf);
256                 break;
257         case NVHOST_AS_IOCTL_FREE_SPACE:
258                 trace_gk20a_as_ioctl_free_space(dev_name(dev_from_gk20a(g)));
259                 err = gk20a_as_ioctl_free_space(as_share,
260                                 (struct nvhost_as_free_space_args *)buf);
261                 break;
262         case NVHOST_AS_IOCTL_MAP_BUFFER:
263                 trace_gk20a_as_ioctl_map_buffer(dev_name(dev_from_gk20a(g)));
264                 err = gk20a_as_ioctl_map_buffer(as_share,
265                                 (struct nvhost_as_map_buffer_args *)buf);
266                 break;
267         case NVHOST_AS_IOCTL_MAP_BUFFER_EX:
268                 trace_gk20a_as_ioctl_map_buffer(dev_name(dev_from_gk20a(g)));
269                 err = gk20a_as_ioctl_map_buffer_ex(as_share,
270                                 (struct nvhost_as_map_buffer_ex_args *)buf);
271                 break;
272         case NVHOST_AS_IOCTL_UNMAP_BUFFER:
273                 trace_gk20a_as_ioctl_unmap_buffer(dev_name(dev_from_gk20a(g)));
274                 err = gk20a_as_ioctl_unmap_buffer(as_share,
275                                 (struct nvhost_as_unmap_buffer_args *)buf);
276                 break;
277         default:
278                 dev_err(dev_from_gk20a(g), "unrecognized as ioctl: 0x%x", cmd);
279                 err = -ENOTTY;
280                 break;
281         }
282
283         gk20a_idle(g->dev);
284
285         if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ))
286                 err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd));
287
288         return err;
289 }