diff options
Diffstat (limited to 'drivers/gpu/nvgpu/os/linux/channel.c')
-rw-r--r-- | drivers/gpu/nvgpu/os/linux/channel.c | 508 |
1 files changed, 0 insertions, 508 deletions
diff --git a/drivers/gpu/nvgpu/os/linux/channel.c b/drivers/gpu/nvgpu/os/linux/channel.c deleted file mode 100644 index fef44f2b..00000000 --- a/drivers/gpu/nvgpu/os/linux/channel.c +++ /dev/null | |||
@@ -1,508 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017-2018, 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 <nvgpu/enabled.h> | ||
18 | #include <nvgpu/debug.h> | ||
19 | #include <nvgpu/error_notifier.h> | ||
20 | #include <nvgpu/os_sched.h> | ||
21 | |||
22 | /* | ||
23 | * This is required for nvgpu_vm_find_buf() which is used in the tracing | ||
24 | * code. Once we can get and access userspace buffers without requiring | ||
25 | * direct dma_buf usage this can be removed. | ||
26 | */ | ||
27 | #include <nvgpu/linux/vm.h> | ||
28 | |||
29 | #include "gk20a/gk20a.h" | ||
30 | |||
31 | #include "channel.h" | ||
32 | #include "ioctl_channel.h" | ||
33 | #include "os_linux.h" | ||
34 | |||
35 | #include <nvgpu/hw/gk20a/hw_pbdma_gk20a.h> | ||
36 | |||
37 | #include <linux/uaccess.h> | ||
38 | #include <linux/dma-buf.h> | ||
39 | #include <trace/events/gk20a.h> | ||
40 | #include <uapi/linux/nvgpu.h> | ||
41 | |||
42 | #include "sync_sema_android.h" | ||
43 | |||
44 | u32 nvgpu_submit_gpfifo_user_flags_to_common_flags(u32 user_flags) | ||
45 | { | ||
46 | u32 flags = 0; | ||
47 | |||
48 | if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_FENCE_WAIT) | ||
49 | flags |= NVGPU_SUBMIT_FLAGS_FENCE_WAIT; | ||
50 | |||
51 | if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_FENCE_GET) | ||
52 | flags |= NVGPU_SUBMIT_FLAGS_FENCE_GET; | ||
53 | |||
54 | if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_HW_FORMAT) | ||
55 | flags |= NVGPU_SUBMIT_FLAGS_HW_FORMAT; | ||
56 | |||
57 | if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_SYNC_FENCE) | ||
58 | flags |= NVGPU_SUBMIT_FLAGS_SYNC_FENCE; | ||
59 | |||
60 | if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_SUPPRESS_WFI) | ||
61 | flags |= NVGPU_SUBMIT_FLAGS_SUPPRESS_WFI; | ||
62 | |||
63 | if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_SKIP_BUFFER_REFCOUNTING) | ||
64 | flags |= NVGPU_SUBMIT_FLAGS_SKIP_BUFFER_REFCOUNTING; | ||
65 | |||
66 | return flags; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * API to convert error_notifiers in common code and of the form | ||
71 | * NVGPU_ERR_NOTIFIER_* into Linux specific error_notifiers exposed to user | ||
72 | * space and of the form NVGPU_CHANNEL_* | ||
73 | */ | ||
74 | static u32 nvgpu_error_notifier_to_channel_notifier(u32 error_notifier) | ||
75 | { | ||
76 | switch (error_notifier) { | ||
77 | case NVGPU_ERR_NOTIFIER_FIFO_ERROR_IDLE_TIMEOUT: | ||
78 | return NVGPU_CHANNEL_FIFO_ERROR_IDLE_TIMEOUT; | ||
79 | case NVGPU_ERR_NOTIFIER_GR_ERROR_SW_METHOD: | ||
80 | return NVGPU_CHANNEL_GR_ERROR_SW_METHOD; | ||
81 | case NVGPU_ERR_NOTIFIER_GR_ERROR_SW_NOTIFY: | ||
82 | return NVGPU_CHANNEL_GR_ERROR_SW_NOTIFY; | ||
83 | case NVGPU_ERR_NOTIFIER_GR_EXCEPTION: | ||
84 | return NVGPU_CHANNEL_GR_EXCEPTION; | ||
85 | case NVGPU_ERR_NOTIFIER_GR_SEMAPHORE_TIMEOUT: | ||
86 | return NVGPU_CHANNEL_GR_SEMAPHORE_TIMEOUT; | ||
87 | case NVGPU_ERR_NOTIFIER_GR_ILLEGAL_NOTIFY: | ||
88 | return NVGPU_CHANNEL_GR_ILLEGAL_NOTIFY; | ||
89 | case NVGPU_ERR_NOTIFIER_FIFO_ERROR_MMU_ERR_FLT: | ||
90 | return NVGPU_CHANNEL_FIFO_ERROR_MMU_ERR_FLT; | ||
91 | case NVGPU_ERR_NOTIFIER_PBDMA_ERROR: | ||
92 | return NVGPU_CHANNEL_PBDMA_ERROR; | ||
93 | case NVGPU_ERR_NOTIFIER_FECS_ERR_UNIMP_FIRMWARE_METHOD: | ||
94 | return NVGPU_CHANNEL_FECS_ERR_UNIMP_FIRMWARE_METHOD; | ||
95 | case NVGPU_ERR_NOTIFIER_RESETCHANNEL_VERIF_ERROR: | ||
96 | return NVGPU_CHANNEL_RESETCHANNEL_VERIF_ERROR; | ||
97 | case NVGPU_ERR_NOTIFIER_PBDMA_PUSHBUFFER_CRC_MISMATCH: | ||
98 | return NVGPU_CHANNEL_PBDMA_PUSHBUFFER_CRC_MISMATCH; | ||
99 | } | ||
100 | |||
101 | pr_warn("%s: invalid error_notifier requested %u\n", __func__, error_notifier); | ||
102 | |||
103 | return error_notifier; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * nvgpu_set_error_notifier_locked() | ||
108 | * Should be called with ch->error_notifier_mutex held | ||
109 | * | ||
110 | * error should be of the form NVGPU_ERR_NOTIFIER_* | ||
111 | */ | ||
112 | void nvgpu_set_error_notifier_locked(struct channel_gk20a *ch, u32 error) | ||
113 | { | ||
114 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
115 | |||
116 | error = nvgpu_error_notifier_to_channel_notifier(error); | ||
117 | |||
118 | if (priv->error_notifier.dmabuf) { | ||
119 | struct nvgpu_notification *notification = | ||
120 | priv->error_notifier.notification; | ||
121 | struct timespec time_data; | ||
122 | u64 nsec; | ||
123 | |||
124 | getnstimeofday(&time_data); | ||
125 | nsec = ((u64)time_data.tv_sec) * 1000000000u + | ||
126 | (u64)time_data.tv_nsec; | ||
127 | notification->time_stamp.nanoseconds[0] = | ||
128 | (u32)nsec; | ||
129 | notification->time_stamp.nanoseconds[1] = | ||
130 | (u32)(nsec >> 32); | ||
131 | notification->info32 = error; | ||
132 | notification->status = 0xffff; | ||
133 | |||
134 | nvgpu_err(ch->g, | ||
135 | "error notifier set to %d for ch %d", error, ch->chid); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /* error should be of the form NVGPU_ERR_NOTIFIER_* */ | ||
140 | void nvgpu_set_error_notifier(struct channel_gk20a *ch, u32 error) | ||
141 | { | ||
142 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
143 | |||
144 | nvgpu_mutex_acquire(&priv->error_notifier.mutex); | ||
145 | nvgpu_set_error_notifier_locked(ch, error); | ||
146 | nvgpu_mutex_release(&priv->error_notifier.mutex); | ||
147 | } | ||
148 | |||
149 | void nvgpu_set_error_notifier_if_empty(struct channel_gk20a *ch, u32 error) | ||
150 | { | ||
151 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
152 | |||
153 | nvgpu_mutex_acquire(&priv->error_notifier.mutex); | ||
154 | if (priv->error_notifier.dmabuf) { | ||
155 | struct nvgpu_notification *notification = | ||
156 | priv->error_notifier.notification; | ||
157 | |||
158 | /* Don't overwrite error flag if it is already set */ | ||
159 | if (notification->status != 0xffff) | ||
160 | nvgpu_set_error_notifier_locked(ch, error); | ||
161 | } | ||
162 | nvgpu_mutex_release(&priv->error_notifier.mutex); | ||
163 | } | ||
164 | |||
165 | /* error_notifier should be of the form NVGPU_ERR_NOTIFIER_* */ | ||
166 | bool nvgpu_is_error_notifier_set(struct channel_gk20a *ch, u32 error_notifier) | ||
167 | { | ||
168 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
169 | bool notifier_set = false; | ||
170 | |||
171 | error_notifier = nvgpu_error_notifier_to_channel_notifier(error_notifier); | ||
172 | |||
173 | nvgpu_mutex_acquire(&priv->error_notifier.mutex); | ||
174 | if (priv->error_notifier.dmabuf) { | ||
175 | struct nvgpu_notification *notification = | ||
176 | priv->error_notifier.notification; | ||
177 | u32 err = notification->info32; | ||
178 | |||
179 | if (err == error_notifier) | ||
180 | notifier_set = true; | ||
181 | } | ||
182 | nvgpu_mutex_release(&priv->error_notifier.mutex); | ||
183 | |||
184 | return notifier_set; | ||
185 | } | ||
186 | |||
187 | static void gk20a_channel_update_runcb_fn(struct work_struct *work) | ||
188 | { | ||
189 | struct nvgpu_channel_completion_cb *completion_cb = | ||
190 | container_of(work, struct nvgpu_channel_completion_cb, work); | ||
191 | struct nvgpu_channel_linux *priv = | ||
192 | container_of(completion_cb, | ||
193 | struct nvgpu_channel_linux, completion_cb); | ||
194 | struct channel_gk20a *ch = priv->ch; | ||
195 | void (*fn)(struct channel_gk20a *, void *); | ||
196 | void *user_data; | ||
197 | |||
198 | nvgpu_spinlock_acquire(&completion_cb->lock); | ||
199 | fn = completion_cb->fn; | ||
200 | user_data = completion_cb->user_data; | ||
201 | nvgpu_spinlock_release(&completion_cb->lock); | ||
202 | |||
203 | if (fn) | ||
204 | fn(ch, user_data); | ||
205 | } | ||
206 | |||
207 | static void nvgpu_channel_work_completion_init(struct channel_gk20a *ch) | ||
208 | { | ||
209 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
210 | |||
211 | priv->completion_cb.fn = NULL; | ||
212 | priv->completion_cb.user_data = NULL; | ||
213 | nvgpu_spinlock_init(&priv->completion_cb.lock); | ||
214 | INIT_WORK(&priv->completion_cb.work, gk20a_channel_update_runcb_fn); | ||
215 | } | ||
216 | |||
217 | static void nvgpu_channel_work_completion_clear(struct channel_gk20a *ch) | ||
218 | { | ||
219 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
220 | |||
221 | nvgpu_spinlock_acquire(&priv->completion_cb.lock); | ||
222 | priv->completion_cb.fn = NULL; | ||
223 | priv->completion_cb.user_data = NULL; | ||
224 | nvgpu_spinlock_release(&priv->completion_cb.lock); | ||
225 | cancel_work_sync(&priv->completion_cb.work); | ||
226 | } | ||
227 | |||
228 | static void nvgpu_channel_work_completion_signal(struct channel_gk20a *ch) | ||
229 | { | ||
230 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
231 | |||
232 | if (priv->completion_cb.fn) | ||
233 | schedule_work(&priv->completion_cb.work); | ||
234 | } | ||
235 | |||
236 | static void nvgpu_channel_work_completion_cancel_sync(struct channel_gk20a *ch) | ||
237 | { | ||
238 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
239 | |||
240 | if (priv->completion_cb.fn) | ||
241 | cancel_work_sync(&priv->completion_cb.work); | ||
242 | } | ||
243 | |||
244 | struct channel_gk20a *gk20a_open_new_channel_with_cb(struct gk20a *g, | ||
245 | void (*update_fn)(struct channel_gk20a *, void *), | ||
246 | void *update_fn_data, | ||
247 | int runlist_id, | ||
248 | bool is_privileged_channel) | ||
249 | { | ||
250 | struct channel_gk20a *ch; | ||
251 | struct nvgpu_channel_linux *priv; | ||
252 | |||
253 | ch = gk20a_open_new_channel(g, runlist_id, is_privileged_channel, | ||
254 | nvgpu_current_pid(g), nvgpu_current_tid(g)); | ||
255 | |||
256 | if (ch) { | ||
257 | priv = ch->os_priv; | ||
258 | nvgpu_spinlock_acquire(&priv->completion_cb.lock); | ||
259 | priv->completion_cb.fn = update_fn; | ||
260 | priv->completion_cb.user_data = update_fn_data; | ||
261 | nvgpu_spinlock_release(&priv->completion_cb.lock); | ||
262 | } | ||
263 | |||
264 | return ch; | ||
265 | } | ||
266 | |||
267 | static void nvgpu_channel_open_linux(struct channel_gk20a *ch) | ||
268 | { | ||
269 | } | ||
270 | |||
271 | static void nvgpu_channel_close_linux(struct channel_gk20a *ch) | ||
272 | { | ||
273 | nvgpu_channel_work_completion_clear(ch); | ||
274 | |||
275 | #if defined(CONFIG_GK20A_CYCLE_STATS) | ||
276 | gk20a_channel_free_cycle_stats_buffer(ch); | ||
277 | gk20a_channel_free_cycle_stats_snapshot(ch); | ||
278 | #endif | ||
279 | } | ||
280 | |||
281 | static int nvgpu_channel_alloc_linux(struct gk20a *g, struct channel_gk20a *ch) | ||
282 | { | ||
283 | struct nvgpu_channel_linux *priv; | ||
284 | int err; | ||
285 | |||
286 | priv = nvgpu_kzalloc(g, sizeof(*priv)); | ||
287 | if (!priv) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | ch->os_priv = priv; | ||
291 | priv->ch = ch; | ||
292 | |||
293 | #ifdef CONFIG_SYNC | ||
294 | ch->has_os_fence_framework_support = true; | ||
295 | #endif | ||
296 | |||
297 | err = nvgpu_mutex_init(&priv->error_notifier.mutex); | ||
298 | if (err) { | ||
299 | nvgpu_kfree(g, priv); | ||
300 | return err; | ||
301 | } | ||
302 | |||
303 | nvgpu_channel_work_completion_init(ch); | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static void nvgpu_channel_free_linux(struct gk20a *g, struct channel_gk20a *ch) | ||
309 | { | ||
310 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
311 | |||
312 | nvgpu_mutex_destroy(&priv->error_notifier.mutex); | ||
313 | nvgpu_kfree(g, priv); | ||
314 | |||
315 | ch->os_priv = NULL; | ||
316 | |||
317 | #ifdef CONFIG_SYNC | ||
318 | ch->has_os_fence_framework_support = false; | ||
319 | #endif | ||
320 | } | ||
321 | |||
322 | static int nvgpu_channel_init_os_fence_framework(struct channel_gk20a *ch, | ||
323 | const char *fmt, ...) | ||
324 | { | ||
325 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
326 | struct nvgpu_os_fence_framework *fence_framework; | ||
327 | char name[30]; | ||
328 | va_list args; | ||
329 | |||
330 | fence_framework = &priv->fence_framework; | ||
331 | |||
332 | va_start(args, fmt); | ||
333 | vsnprintf(name, sizeof(name), fmt, args); | ||
334 | va_end(args); | ||
335 | |||
336 | fence_framework->timeline = gk20a_sync_timeline_create(name); | ||
337 | |||
338 | if (!fence_framework->timeline) | ||
339 | return -EINVAL; | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | static void nvgpu_channel_signal_os_fence_framework(struct channel_gk20a *ch) | ||
344 | { | ||
345 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
346 | struct nvgpu_os_fence_framework *fence_framework; | ||
347 | |||
348 | fence_framework = &priv->fence_framework; | ||
349 | |||
350 | gk20a_sync_timeline_signal(fence_framework->timeline); | ||
351 | } | ||
352 | |||
353 | static void nvgpu_channel_destroy_os_fence_framework(struct channel_gk20a *ch) | ||
354 | { | ||
355 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
356 | struct nvgpu_os_fence_framework *fence_framework; | ||
357 | |||
358 | fence_framework = &priv->fence_framework; | ||
359 | |||
360 | gk20a_sync_timeline_destroy(fence_framework->timeline); | ||
361 | fence_framework->timeline = NULL; | ||
362 | } | ||
363 | |||
364 | static bool nvgpu_channel_fence_framework_exists(struct channel_gk20a *ch) | ||
365 | { | ||
366 | struct nvgpu_channel_linux *priv = ch->os_priv; | ||
367 | struct nvgpu_os_fence_framework *fence_framework; | ||
368 | |||
369 | fence_framework = &priv->fence_framework; | ||
370 | |||
371 | return (fence_framework->timeline != NULL); | ||
372 | } | ||
373 | |||
374 | static int nvgpu_channel_copy_user_gpfifo(struct nvgpu_gpfifo_entry *dest, | ||
375 | struct nvgpu_gpfifo_userdata userdata, u32 start, u32 length) | ||
376 | { | ||
377 | struct nvgpu_gpfifo_entry __user *user_gpfifo = userdata.entries; | ||
378 | unsigned long n; | ||
379 | |||
380 | n = copy_from_user(dest, user_gpfifo + start, | ||
381 | length * sizeof(struct nvgpu_gpfifo_entry)); | ||
382 | |||
383 | return n == 0 ? 0 : -EFAULT; | ||
384 | } | ||
385 | |||
386 | int nvgpu_init_channel_support_linux(struct nvgpu_os_linux *l) | ||
387 | { | ||
388 | struct gk20a *g = &l->g; | ||
389 | struct fifo_gk20a *f = &g->fifo; | ||
390 | int chid; | ||
391 | int err; | ||
392 | |||
393 | for (chid = 0; chid < (int)f->num_channels; chid++) { | ||
394 | struct channel_gk20a *ch = &f->channel[chid]; | ||
395 | |||
396 | err = nvgpu_channel_alloc_linux(g, ch); | ||
397 | if (err) | ||
398 | goto err_clean; | ||
399 | } | ||
400 | |||
401 | g->os_channel.open = nvgpu_channel_open_linux; | ||
402 | g->os_channel.close = nvgpu_channel_close_linux; | ||
403 | g->os_channel.work_completion_signal = | ||
404 | nvgpu_channel_work_completion_signal; | ||
405 | g->os_channel.work_completion_cancel_sync = | ||
406 | nvgpu_channel_work_completion_cancel_sync; | ||
407 | |||
408 | g->os_channel.os_fence_framework_inst_exists = | ||
409 | nvgpu_channel_fence_framework_exists; | ||
410 | g->os_channel.init_os_fence_framework = | ||
411 | nvgpu_channel_init_os_fence_framework; | ||
412 | g->os_channel.signal_os_fence_framework = | ||
413 | nvgpu_channel_signal_os_fence_framework; | ||
414 | g->os_channel.destroy_os_fence_framework = | ||
415 | nvgpu_channel_destroy_os_fence_framework; | ||
416 | |||
417 | g->os_channel.copy_user_gpfifo = | ||
418 | nvgpu_channel_copy_user_gpfifo; | ||
419 | |||
420 | return 0; | ||
421 | |||
422 | err_clean: | ||
423 | for (; chid >= 0; chid--) { | ||
424 | struct channel_gk20a *ch = &f->channel[chid]; | ||
425 | |||
426 | nvgpu_channel_free_linux(g, ch); | ||
427 | } | ||
428 | return err; | ||
429 | } | ||
430 | |||
431 | void nvgpu_remove_channel_support_linux(struct nvgpu_os_linux *l) | ||
432 | { | ||
433 | struct gk20a *g = &l->g; | ||
434 | struct fifo_gk20a *f = &g->fifo; | ||
435 | unsigned int chid; | ||
436 | |||
437 | for (chid = 0; chid < f->num_channels; chid++) { | ||
438 | struct channel_gk20a *ch = &f->channel[chid]; | ||
439 | |||
440 | nvgpu_channel_free_linux(g, ch); | ||
441 | } | ||
442 | |||
443 | g->os_channel.os_fence_framework_inst_exists = NULL; | ||
444 | g->os_channel.init_os_fence_framework = NULL; | ||
445 | g->os_channel.signal_os_fence_framework = NULL; | ||
446 | g->os_channel.destroy_os_fence_framework = NULL; | ||
447 | } | ||
448 | |||
449 | u32 nvgpu_get_gpfifo_entry_size(void) | ||
450 | { | ||
451 | return sizeof(struct nvgpu_gpfifo_entry); | ||
452 | } | ||
453 | |||
454 | #ifdef CONFIG_DEBUG_FS | ||
455 | static void trace_write_pushbuffer(struct channel_gk20a *c, | ||
456 | struct nvgpu_gpfifo_entry *g) | ||
457 | { | ||
458 | void *mem = NULL; | ||
459 | unsigned int words; | ||
460 | u64 offset; | ||
461 | struct dma_buf *dmabuf = NULL; | ||
462 | |||
463 | if (gk20a_debug_trace_cmdbuf) { | ||
464 | u64 gpu_va = (u64)g->entry0 | | ||
465 | (u64)((u64)pbdma_gp_entry1_get_hi_v(g->entry1) << 32); | ||
466 | int err; | ||
467 | |||
468 | words = pbdma_gp_entry1_length_v(g->entry1); | ||
469 | err = nvgpu_vm_find_buf(c->vm, gpu_va, &dmabuf, &offset); | ||
470 | if (!err) | ||
471 | mem = dma_buf_vmap(dmabuf); | ||
472 | } | ||
473 | |||
474 | if (mem) { | ||
475 | u32 i; | ||
476 | /* | ||
477 | * Write in batches of 128 as there seems to be a limit | ||
478 | * of how much you can output to ftrace at once. | ||
479 | */ | ||
480 | for (i = 0; i < words; i += 128U) { | ||
481 | trace_gk20a_push_cmdbuf( | ||
482 | c->g->name, | ||
483 | 0, | ||
484 | min(words - i, 128U), | ||
485 | offset + i * sizeof(u32), | ||
486 | mem); | ||
487 | } | ||
488 | dma_buf_vunmap(dmabuf, mem); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | void trace_write_pushbuffers(struct channel_gk20a *c, u32 count) | ||
493 | { | ||
494 | struct nvgpu_gpfifo_entry *gp = c->gpfifo.mem.cpu_va; | ||
495 | u32 n = c->gpfifo.entry_num; | ||
496 | u32 start = c->gpfifo.put; | ||
497 | u32 i; | ||
498 | |||
499 | if (!gk20a_debug_trace_cmdbuf) | ||
500 | return; | ||
501 | |||
502 | if (!gp) | ||
503 | return; | ||
504 | |||
505 | for (i = 0; i < count; i++) | ||
506 | trace_write_pushbuffer(c, &gp[(start + i) % n]); | ||
507 | } | ||
508 | #endif | ||