summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c
diff options
context:
space:
mode:
authorArto Merilainen <amerilainen@nvidia.com>2014-03-19 03:38:25 -0400
committerDan Willemsen <dwillemsen@nvidia.com>2015-03-18 15:08:53 -0400
commita9785995d5f22aaeb659285f8aeb64d8b56982e0 (patch)
treecc75f75bcf43db316a002a7a240b81f299bf6d7f /drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c
parent61efaf843c22b85424036ec98015121c08f5f16c (diff)
gpu: nvgpu: Add NVIDIA GPU Driver
This patch moves the NVIDIA GPU driver to a new location. Bug 1482562 Change-Id: I24293810b9d0f1504fd9be00135e21dad656ccb6 Signed-off-by: Arto Merilainen <amerilainen@nvidia.com> Reviewed-on: http://git-master/r/383722 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c')
-rw-r--r--drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c
new file mode 100644
index 00000000..9f9c3ba7
--- /dev/null
+++ b/drivers/gpu/nvgpu/gk20a/channel_sync_gk20a.c
@@ -0,0 +1,356 @@
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
33struct 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
40static 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
52int 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
65bool 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
76int 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
105int 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 num_wait_cmds = nvhost_sync_num_pts(sync_fence);
123 gk20a_channel_alloc_priv_cmdbuf(c, 4 * num_wait_cmds, &wait_cmd);
124 if (wait_cmd == NULL) {
125 gk20a_err(dev_from_gk20a(c->g),
126 "not enough priv cmd buffer space");
127 sync_fence_put(sync_fence);
128 return -EAGAIN;
129 }
130
131 i = 0;
132 list_for_each_entry(pt, &sync_fence->pt_list_head, pt_list) {
133 u32 wait_id = nvhost_sync_pt_id(pt);
134 u32 wait_value = nvhost_sync_pt_thresh(pt);
135
136 if (nvhost_syncpt_is_expired_ext(sp->host1x_pdev,
137 wait_id, wait_value)) {
138 wait_cmd->ptr[i * 4 + 0] = 0;
139 wait_cmd->ptr[i * 4 + 1] = 0;
140 wait_cmd->ptr[i * 4 + 2] = 0;
141 wait_cmd->ptr[i * 4 + 3] = 0;
142 } else
143 add_wait_cmd(&wait_cmd->ptr[i * 4], wait_id,
144 wait_value);
145 i++;
146 }
147 WARN_ON(i != num_wait_cmds);
148 sync_fence_put(sync_fence);
149
150 *entry = wait_cmd;
151 return 0;
152#else
153 return -ENODEV;
154#endif
155}
156
157static void gk20a_channel_syncpt_update(void *priv, int nr_completed)
158{
159 struct channel_gk20a *ch20a = priv;
160 gk20a_channel_update(ch20a, nr_completed);
161}
162
163static int __gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
164 bool gfx_class, bool wfi_cmd,
165 struct priv_cmd_entry **entry,
166 struct gk20a_channel_fence *fence)
167{
168 u32 thresh;
169 int incr_cmd_size;
170 int j = 0;
171 int err;
172 struct priv_cmd_entry *incr_cmd = NULL;
173 struct gk20a_channel_syncpt *sp =
174 container_of(s, struct gk20a_channel_syncpt, ops);
175 struct channel_gk20a *c = sp->c;
176
177 /* nvhost action_gpfifo_submit_complete releases this ref. */
178 err = gk20a_channel_busy(c->g->dev);
179 if (err)
180 return err;
181
182 incr_cmd_size = 4;
183 if (wfi_cmd)
184 incr_cmd_size += 2;
185
186 gk20a_channel_alloc_priv_cmdbuf(c, incr_cmd_size, &incr_cmd);
187 if (incr_cmd == NULL) {
188 gk20a_channel_idle(c->g->dev);
189 gk20a_err(dev_from_gk20a(c->g),
190 "not enough priv cmd buffer space");
191 return -EAGAIN;
192 }
193
194 if (gfx_class) {
195 WARN_ON(wfi_cmd); /* No sense to use gfx class + wfi. */
196 /* setobject KEPLER_C */
197 incr_cmd->ptr[j++] = 0x20010000;
198 incr_cmd->ptr[j++] = KEPLER_C;
199 /* syncpt incr */
200 incr_cmd->ptr[j++] = 0x200100B2;
201 incr_cmd->ptr[j++] = sp->id |
202 (0x1 << 20) | (0x1 << 16);
203 } else {
204 if (wfi_cmd) {
205 /* wfi */
206 incr_cmd->ptr[j++] = 0x2001001E;
207 /* handle, ignored */
208 incr_cmd->ptr[j++] = 0x00000000;
209 }
210 /* syncpoint_a */
211 incr_cmd->ptr[j++] = 0x2001001C;
212 /* payload, ignored */
213 incr_cmd->ptr[j++] = 0;
214 /* syncpoint_b */
215 incr_cmd->ptr[j++] = 0x2001001D;
216 /* syncpt_id, incr */
217 incr_cmd->ptr[j++] = (sp->id << 8) | 0x1;
218 }
219 WARN_ON(j != incr_cmd_size);
220
221 thresh = nvhost_syncpt_incr_max_ext(sp->host1x_pdev, sp->id, 1);
222
223 err = nvhost_intr_register_notifier(sp->host1x_pdev, sp->id, thresh,
224 gk20a_channel_syncpt_update, c);
225
226 /* Adding interrupt action should never fail. A proper error handling
227 * here would require us to decrement the syncpt max back to its
228 * original value. */
229 if (WARN(err, "failed to set submit complete interrupt")) {
230 gk20a_channel_idle(c->g->dev);
231 err = 0; /* Ignore this error. */
232 }
233
234 fence->thresh = thresh;
235 fence->valid = true;
236 fence->wfi = wfi_cmd;
237 *entry = incr_cmd;
238 return 0;
239}
240
241int gk20a_channel_syncpt_incr_wfi(struct gk20a_channel_sync *s,
242 struct priv_cmd_entry **entry,
243 struct gk20a_channel_fence *fence)
244{
245 return __gk20a_channel_syncpt_incr(s,
246 false /* use host class */,
247 true /* wfi */,
248 entry, fence);
249}
250
251int gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
252 struct priv_cmd_entry **entry,
253 struct gk20a_channel_fence *fence)
254{
255 struct gk20a_channel_syncpt *sp =
256 container_of(s, struct gk20a_channel_syncpt, ops);
257 /* Don't put wfi cmd to this one since we're not returning
258 * a fence to user space. */
259 return __gk20a_channel_syncpt_incr(s,
260 sp->c->obj_class == KEPLER_C /* may use gfx class */,
261 false /* no wfi */,
262 entry, fence);
263}
264
265int gk20a_channel_syncpt_incr_user_syncpt(struct gk20a_channel_sync *s,
266 struct priv_cmd_entry **entry,
267 struct gk20a_channel_fence *fence,
268 u32 *id, u32 *thresh)
269{
270 struct gk20a_channel_syncpt *sp =
271 container_of(s, struct gk20a_channel_syncpt, ops);
272 /* Need to do 'host incr + wfi' or 'gfx incr' since we return the fence
273 * to user space. */
274 int err = __gk20a_channel_syncpt_incr(s,
275 sp->c->obj_class == KEPLER_C /* use gfx class? */,
276 sp->c->obj_class != KEPLER_C /* wfi if host class */,
277 entry, fence);
278 if (err)
279 return err;
280 *id = sp->id;
281 *thresh = fence->thresh;
282 return 0;
283}
284
285int gk20a_channel_syncpt_incr_user_fd(struct gk20a_channel_sync *s,
286 struct priv_cmd_entry **entry,
287 struct gk20a_channel_fence *fence,
288 int *fd)
289{
290#ifdef CONFIG_SYNC
291 int err;
292 struct nvhost_ctrl_sync_fence_info pt;
293 struct gk20a_channel_syncpt *sp =
294 container_of(s, struct gk20a_channel_syncpt, ops);
295 err = gk20a_channel_syncpt_incr_user_syncpt(s, entry, fence,
296 &pt.id, &pt.thresh);
297 if (err)
298 return err;
299 return nvhost_sync_create_fence_fd(sp->host1x_pdev, &pt, 1,
300 "fence", fd);
301#else
302 return -ENODEV;
303#endif
304}
305
306void gk20a_channel_syncpt_set_min_eq_max(struct gk20a_channel_sync *s)
307{
308 struct gk20a_channel_syncpt *sp =
309 container_of(s, struct gk20a_channel_syncpt, ops);
310 nvhost_syncpt_set_min_eq_max_ext(sp->host1x_pdev, sp->id);
311}
312
313static void gk20a_channel_syncpt_destroy(struct gk20a_channel_sync *s)
314{
315 struct gk20a_channel_syncpt *sp =
316 container_of(s, struct gk20a_channel_syncpt, ops);
317 nvhost_free_syncpt(sp->id);
318 kfree(sp);
319}
320
321static struct gk20a_channel_sync *
322gk20a_channel_syncpt_create(struct channel_gk20a *c)
323{
324 struct gk20a_channel_syncpt *sp;
325
326 sp = kzalloc(sizeof(*sp), GFP_KERNEL);
327 if (!sp)
328 return NULL;
329
330 sp->c = c;
331 sp->host1x_pdev = to_platform_device(c->g->dev->dev.parent);
332 sp->id = nvhost_get_syncpt_host_managed(sp->host1x_pdev, c->hw_chid);
333
334 sp->ops.wait_cpu = gk20a_channel_syncpt_wait_cpu;
335 sp->ops.is_expired = gk20a_channel_syncpt_is_expired;
336 sp->ops.wait_syncpt = gk20a_channel_syncpt_wait_syncpt;
337 sp->ops.wait_fd = gk20a_channel_syncpt_wait_fd;
338 sp->ops.incr = gk20a_channel_syncpt_incr;
339 sp->ops.incr_wfi = gk20a_channel_syncpt_incr_wfi;
340 sp->ops.incr_user_syncpt = gk20a_channel_syncpt_incr_user_syncpt;
341 sp->ops.incr_user_fd = gk20a_channel_syncpt_incr_user_fd;
342 sp->ops.set_min_eq_max = gk20a_channel_syncpt_set_min_eq_max;
343 sp->ops.destroy = gk20a_channel_syncpt_destroy;
344 return &sp->ops;
345}
346#endif /* CONFIG_TEGRA_GK20A */
347
348struct gk20a_channel_sync *gk20a_channel_sync_create(struct channel_gk20a *c)
349{
350#ifdef CONFIG_TEGRA_GK20A
351 if (gk20a_platform_has_syncpoints(c->g->dev))
352 return gk20a_channel_syncpt_create(c);
353#endif
354 WARN_ON(1);
355 return NULL;
356}