gpu: nvgpu: Keep host1x on when GPU on
[linux-3.10.git] / drivers / gpu / nvgpu / gk20a / channel_sync_gk20a.c
1 /*
2  * drivers/video/tegra/host/gk20a/channel_sync_gk20a.c
3  *
4  * GK20A Channel Synchronization Abstraction
5  *
6  * Copyright (c) 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/gk20a.h>
19
20 #include "channel_sync_gk20a.h"
21 #include "gk20a.h"
22
23 #ifdef CONFIG_SYNC
24 #include "../../../staging/android/sync.h"
25 #endif
26
27 #ifdef CONFIG_TEGRA_GK20A
28 #include <linux/nvhost.h>
29 #endif
30
31 #ifdef CONFIG_TEGRA_GK20A
32
33 struct gk20a_channel_syncpt {
34         struct gk20a_channel_sync ops;
35         struct channel_gk20a *c;
36         struct platform_device *host1x_pdev;
37         u32 id;
38 };
39
40 static void add_wait_cmd(u32 *ptr, u32 id, u32 thresh)
41 {
42         /* syncpoint_a */
43         ptr[0] = 0x2001001C;
44         /* payload */
45         ptr[1] = thresh;
46         /* syncpoint_b */
47         ptr[2] = 0x2001001D;
48         /* syncpt_id, switch_en, wait */
49         ptr[3] = (id << 8) | 0x10;
50 }
51
52 int gk20a_channel_syncpt_wait_cpu(struct gk20a_channel_sync *s,
53                                   struct gk20a_channel_fence *fence,
54                                   int timeout)
55 {
56         struct gk20a_channel_syncpt *sp =
57                 container_of(s, struct gk20a_channel_syncpt, ops);
58         if (!fence->valid)
59                 return 0;
60         return nvhost_syncpt_wait_timeout_ext(
61                         sp->host1x_pdev, sp->id, fence->thresh,
62                         timeout, NULL, NULL);
63 }
64
65 bool gk20a_channel_syncpt_is_expired(struct gk20a_channel_sync *s,
66                                      struct gk20a_channel_fence *fence)
67 {
68         struct gk20a_channel_syncpt *sp =
69                 container_of(s, struct gk20a_channel_syncpt, ops);
70         if (!fence->valid)
71                 return true;
72         return nvhost_syncpt_is_expired_ext(sp->host1x_pdev, sp->id,
73                         fence->thresh);
74 }
75
76 int gk20a_channel_syncpt_wait_syncpt(struct gk20a_channel_sync *s, u32 id,
77                 u32 thresh, struct priv_cmd_entry **entry)
78 {
79         struct gk20a_channel_syncpt *sp =
80                 container_of(s, struct gk20a_channel_syncpt, ops);
81         struct priv_cmd_entry *wait_cmd = NULL;
82
83         if (id >= nvhost_syncpt_nb_pts_ext(sp->host1x_pdev)) {
84                 dev_warn(dev_from_gk20a(sp->c->g),
85                                 "invalid wait id in gpfifo submit, elided");
86                 return 0;
87         }
88
89         if (nvhost_syncpt_is_expired_ext(sp->host1x_pdev, id, thresh))
90                 return 0;
91
92         gk20a_channel_alloc_priv_cmdbuf(sp->c, 4, &wait_cmd);
93         if (wait_cmd == NULL) {
94                 gk20a_err(dev_from_gk20a(sp->c->g),
95                                 "not enough priv cmd buffer space");
96                 return -EAGAIN;
97         }
98
99         add_wait_cmd(&wait_cmd->ptr[0], id, thresh);
100
101         *entry = wait_cmd;
102         return 0;
103 }
104
105 int gk20a_channel_syncpt_wait_fd(struct gk20a_channel_sync *s, int fd,
106                        struct priv_cmd_entry **entry)
107 {
108 #ifdef CONFIG_SYNC
109         int i;
110         int num_wait_cmds;
111         struct sync_pt *pt;
112         struct sync_fence *sync_fence;
113         struct priv_cmd_entry *wait_cmd = NULL;
114         struct gk20a_channel_syncpt *sp =
115                 container_of(s, struct gk20a_channel_syncpt, ops);
116         struct channel_gk20a *c = sp->c;
117
118         sync_fence = nvhost_sync_fdget(fd);
119         if (!sync_fence)
120                 return -EINVAL;
121
122         /* validate syncpt ids */
123         list_for_each_entry(pt, &sync_fence->pt_list_head, pt_list) {
124                 u32 wait_id = nvhost_sync_pt_id(pt);
125                 if (!wait_id ||
126                          wait_id >= nvhost_syncpt_nb_pts_ext(sp->host1x_pdev)) {
127                         sync_fence_put(sync_fence);
128                         return -EINVAL;
129                 }
130         }
131
132         num_wait_cmds = nvhost_sync_num_pts(sync_fence);
133         gk20a_channel_alloc_priv_cmdbuf(c, 4 * num_wait_cmds, &wait_cmd);
134         if (wait_cmd == NULL) {
135                 gk20a_err(dev_from_gk20a(c->g),
136                                 "not enough priv cmd buffer space");
137                 sync_fence_put(sync_fence);
138                 return -EAGAIN;
139         }
140
141         i = 0;
142         list_for_each_entry(pt, &sync_fence->pt_list_head, pt_list) {
143                 u32 wait_id = nvhost_sync_pt_id(pt);
144                 u32 wait_value = nvhost_sync_pt_thresh(pt);
145
146                 if (nvhost_syncpt_is_expired_ext(sp->host1x_pdev,
147                                 wait_id, wait_value)) {
148                         wait_cmd->ptr[i * 4 + 0] = 0;
149                         wait_cmd->ptr[i * 4 + 1] = 0;
150                         wait_cmd->ptr[i * 4 + 2] = 0;
151                         wait_cmd->ptr[i * 4 + 3] = 0;
152                 } else
153                         add_wait_cmd(&wait_cmd->ptr[i * 4], wait_id,
154                                         wait_value);
155                 i++;
156         }
157         WARN_ON(i != num_wait_cmds);
158         sync_fence_put(sync_fence);
159
160         *entry = wait_cmd;
161         return 0;
162 #else
163         return -ENODEV;
164 #endif
165 }
166
167 static void gk20a_channel_syncpt_update(void *priv, int nr_completed)
168 {
169         struct channel_gk20a *ch20a = priv;
170         gk20a_channel_update(ch20a, nr_completed);
171 }
172
173 static int __gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
174                                        bool gfx_class, bool wfi_cmd,
175                                        bool register_irq,
176                                        struct priv_cmd_entry **entry,
177                                        struct gk20a_channel_fence *fence)
178 {
179         u32 thresh;
180         int incr_cmd_size;
181         int j = 0;
182         int err;
183         struct priv_cmd_entry *incr_cmd = NULL;
184         struct gk20a_channel_syncpt *sp =
185                 container_of(s, struct gk20a_channel_syncpt, ops);
186         struct channel_gk20a *c = sp->c;
187
188         incr_cmd_size = 4;
189         if (wfi_cmd)
190                 incr_cmd_size += 2;
191
192         gk20a_channel_alloc_priv_cmdbuf(c, incr_cmd_size, &incr_cmd);
193         if (incr_cmd == NULL) {
194                 gk20a_err(dev_from_gk20a(c->g),
195                                 "not enough priv cmd buffer space");
196                 return -EAGAIN;
197         }
198
199         if (gfx_class) {
200                 WARN_ON(wfi_cmd); /* No sense to use gfx class + wfi. */
201                 /* setobject KEPLER_C */
202                 incr_cmd->ptr[j++] = 0x20010000;
203                 incr_cmd->ptr[j++] = KEPLER_C;
204                 /* syncpt incr */
205                 incr_cmd->ptr[j++] = 0x200100B2;
206                 incr_cmd->ptr[j++] = sp->id |
207                         (0x1 << 20) | (0x1 << 16);
208         } else {
209                 if (wfi_cmd) {
210                         /* wfi */
211                         incr_cmd->ptr[j++] = 0x2001001E;
212                         /* handle, ignored */
213                         incr_cmd->ptr[j++] = 0x00000000;
214                 }
215                 /* syncpoint_a */
216                 incr_cmd->ptr[j++] = 0x2001001C;
217                 /* payload, ignored */
218                 incr_cmd->ptr[j++] = 0;
219                 /* syncpoint_b */
220                 incr_cmd->ptr[j++] = 0x2001001D;
221                 /* syncpt_id, incr */
222                 incr_cmd->ptr[j++] = (sp->id << 8) | 0x1;
223         }
224         WARN_ON(j != incr_cmd_size);
225
226         thresh = nvhost_syncpt_incr_max_ext(sp->host1x_pdev, sp->id, 1);
227
228         if (register_irq) {
229                 /* nvhost action_gpfifo_submit_complete releases this ref. */
230                 err = gk20a_busy(c->g->dev);
231
232                 if (!err) {
233                         err = nvhost_intr_register_notifier(sp->host1x_pdev,
234                                         sp->id, thresh,
235                                         gk20a_channel_syncpt_update, c);
236                         if (err)
237                                 gk20a_idle(c->g->dev);
238                 }
239
240                 /* Adding interrupt action should never fail. A proper error
241                  * handling here would require us to decrement the syncpt max
242                  * back to its original value. */
243                 WARN(err, "failed to set submit complete interrupt");
244         }
245
246         fence->thresh = thresh;
247         fence->valid = true;
248         fence->wfi = wfi_cmd;
249         *entry = incr_cmd;
250         return 0;
251 }
252
253 int gk20a_channel_syncpt_incr_wfi(struct gk20a_channel_sync *s,
254                                   struct priv_cmd_entry **entry,
255                                   struct gk20a_channel_fence *fence)
256 {
257         return __gk20a_channel_syncpt_incr(s,
258                         false /* use host class */,
259                         true /* wfi */,
260                         false /* no irq handler */,
261                         entry, fence);
262 }
263
264 int gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
265                               struct priv_cmd_entry **entry,
266                               struct gk20a_channel_fence *fence)
267 {
268         struct gk20a_channel_syncpt *sp =
269                 container_of(s, struct gk20a_channel_syncpt, ops);
270         /* Don't put wfi cmd to this one since we're not returning
271          * a fence to user space. */
272         return __gk20a_channel_syncpt_incr(s,
273                         sp->c->obj_class == KEPLER_C /* may use gfx class */,
274                         false /* no wfi */,
275                         true /* register irq */,
276                         entry, fence);
277 }
278
279 int gk20a_channel_syncpt_incr_user_syncpt(struct gk20a_channel_sync *s,
280                                           struct priv_cmd_entry **entry,
281                                           struct gk20a_channel_fence *fence,
282                                           u32 *id, u32 *thresh)
283 {
284         struct gk20a_channel_syncpt *sp =
285                 container_of(s, struct gk20a_channel_syncpt, ops);
286         /* Need to do 'host incr + wfi' or 'gfx incr' since we return the fence
287          * to user space. */
288         int err = __gk20a_channel_syncpt_incr(s,
289                         sp->c->obj_class == KEPLER_C /* use gfx class? */,
290                         sp->c->obj_class != KEPLER_C /* wfi if host class */,
291                         true /* register irq */,
292                         entry, fence);
293         if (err)
294                 return err;
295         *id = sp->id;
296         *thresh = fence->thresh;
297         return 0;
298 }
299
300 int gk20a_channel_syncpt_incr_user_fd(struct gk20a_channel_sync *s,
301                                       struct priv_cmd_entry **entry,
302                                       struct gk20a_channel_fence *fence,
303                                       int *fd)
304 {
305 #ifdef CONFIG_SYNC
306         int err;
307         struct nvhost_ctrl_sync_fence_info pt;
308         struct gk20a_channel_syncpt *sp =
309                 container_of(s, struct gk20a_channel_syncpt, ops);
310         err = gk20a_channel_syncpt_incr_user_syncpt(s, entry, fence,
311                                                     &pt.id, &pt.thresh);
312         if (err)
313                 return err;
314         return nvhost_sync_create_fence_fd(sp->host1x_pdev, &pt, 1,
315                                            "fence", fd);
316 #else
317         return -ENODEV;
318 #endif
319 }
320
321 void gk20a_channel_syncpt_set_min_eq_max(struct gk20a_channel_sync *s)
322 {
323         struct gk20a_channel_syncpt *sp =
324                 container_of(s, struct gk20a_channel_syncpt, ops);
325         nvhost_syncpt_set_min_eq_max_ext(sp->host1x_pdev, sp->id);
326 }
327
328 static void gk20a_channel_syncpt_destroy(struct gk20a_channel_sync *s)
329 {
330         struct gk20a_channel_syncpt *sp =
331                 container_of(s, struct gk20a_channel_syncpt, ops);
332         nvhost_free_syncpt(sp->id);
333         kfree(sp);
334 }
335
336 static struct gk20a_channel_sync *
337 gk20a_channel_syncpt_create(struct channel_gk20a *c)
338 {
339         struct gk20a_channel_syncpt *sp;
340
341         sp = kzalloc(sizeof(*sp), GFP_KERNEL);
342         if (!sp)
343                 return NULL;
344
345         sp->c = c;
346         sp->host1x_pdev = c->g->host1x_dev;
347         sp->id = nvhost_get_syncpt_host_managed(sp->host1x_pdev, c->hw_chid);
348
349         sp->ops.wait_cpu                = gk20a_channel_syncpt_wait_cpu;
350         sp->ops.is_expired              = gk20a_channel_syncpt_is_expired;
351         sp->ops.wait_syncpt             = gk20a_channel_syncpt_wait_syncpt;
352         sp->ops.wait_fd                 = gk20a_channel_syncpt_wait_fd;
353         sp->ops.incr                    = gk20a_channel_syncpt_incr;
354         sp->ops.incr_wfi                = gk20a_channel_syncpt_incr_wfi;
355         sp->ops.incr_user_syncpt        = gk20a_channel_syncpt_incr_user_syncpt;
356         sp->ops.incr_user_fd            = gk20a_channel_syncpt_incr_user_fd;
357         sp->ops.set_min_eq_max          = gk20a_channel_syncpt_set_min_eq_max;
358         sp->ops.destroy                 = gk20a_channel_syncpt_destroy;
359
360         sp->ops.syncpt_aggressive_destroy = true;
361
362         return &sp->ops;
363 }
364 #endif /* CONFIG_TEGRA_GK20A */
365
366 struct gk20a_channel_sync *gk20a_channel_sync_create(struct channel_gk20a *c)
367 {
368 #ifdef CONFIG_TEGRA_GK20A
369         if (gk20a_platform_has_syncpoints(c->g->dev))
370                 return gk20a_channel_syncpt_create(c);
371 #endif
372         WARN_ON(1);
373         return NULL;
374 }