aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2013-03-22 10:34:03 -0400
committerThierry Reding <thierry.reding@avionic-design.de>2013-04-22 06:32:43 -0400
commit6579324a41cc414009a601738b70a53d6376325c (patch)
tree61b3491351217d07ff75851c6d989bf02b213383
parent7ede0b0bf3e2595d40d6195b6fe4c4dcef438830 (diff)
gpu: host1x: Add channel support
Add support for host1x client modules, and host1x channels to submit work to the clients. Signed-off-by: Arto Merilainen <amerilainen@nvidia.com> Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Thierry Reding <thierry.reding@avionic-design.de> Tested-by: Erik Faye-Lund <kusmabite@gmail.com> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
-rw-r--r--drivers/gpu/host1x/Kconfig12
-rw-r--r--drivers/gpu/host1x/Makefile3
-rw-r--r--drivers/gpu/host1x/cdma.c487
-rw-r--r--drivers/gpu/host1x/cdma.h100
-rw-r--r--drivers/gpu/host1x/channel.c126
-rw-r--r--drivers/gpu/host1x/channel.h52
-rw-r--r--drivers/gpu/host1x/dev.c17
-rw-r--r--drivers/gpu/host1x/dev.h113
-rw-r--r--drivers/gpu/host1x/host1x.h28
-rw-r--r--drivers/gpu/host1x/host1x_bo.h87
-rw-r--r--drivers/gpu/host1x/hw/cdma_hw.c324
-rw-r--r--drivers/gpu/host1x/hw/channel_hw.c143
-rw-r--r--drivers/gpu/host1x/hw/host1x01.c5
-rw-r--r--drivers/gpu/host1x/hw/host1x01_hardware.h116
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x01_channel.h102
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x01_sync.h12
-rw-r--r--drivers/gpu/host1x/hw/hw_host1x01_uclass.h168
-rw-r--r--drivers/gpu/host1x/hw/syncpt_hw.c11
-rw-r--r--drivers/gpu/host1x/intr.c28
-rw-r--r--drivers/gpu/host1x/intr.h6
-rw-r--r--drivers/gpu/host1x/job.c603
-rw-r--r--drivers/gpu/host1x/job.h162
-rw-r--r--drivers/gpu/host1x/syncpt.c11
-rw-r--r--drivers/gpu/host1x/syncpt.h6
-rw-r--r--include/trace/events/host1x.h192
25 files changed, 2913 insertions, 1 deletions
diff --git a/drivers/gpu/host1x/Kconfig b/drivers/gpu/host1x/Kconfig
index 567ae8b7c7c9..7d6bed222542 100644
--- a/drivers/gpu/host1x/Kconfig
+++ b/drivers/gpu/host1x/Kconfig
@@ -8,3 +8,15 @@ config TEGRA_HOST1X
8 Tegra's graphics- and multimedia-related modules. The modules served 8 Tegra's graphics- and multimedia-related modules. The modules served
9 by host1x are referred to as clients. host1x includes some other 9 by host1x are referred to as clients. host1x includes some other
10 functionality, such as synchronization. 10 functionality, such as synchronization.
11
12if TEGRA_HOST1X
13
14config TEGRA_HOST1X_FIREWALL
15 bool "Enable HOST1X security firewall"
16 default y
17 help
18 Say yes if kernel should protect command streams from tampering.
19
20 If unsure, choose Y.
21
22endif
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index 5ef47ffce674..06a995b60902 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -4,6 +4,9 @@ host1x-y = \
4 syncpt.o \ 4 syncpt.o \
5 dev.o \ 5 dev.o \
6 intr.o \ 6 intr.o \
7 cdma.o \
8 channel.o \
9 job.o \
7 hw/host1x01.o 10 hw/host1x01.o
8 11
9obj-$(CONFIG_TEGRA_HOST1X) += host1x.o 12obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c
new file mode 100644
index 000000000000..33935de91bb1
--- /dev/null
+++ b/drivers/gpu/host1x/cdma.c
@@ -0,0 +1,487 @@
1/*
2 * Tegra host1x Command DMA
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20#include <asm/cacheflush.h>
21#include <linux/device.h>
22#include <linux/dma-mapping.h>
23#include <linux/interrupt.h>
24#include <linux/kernel.h>
25#include <linux/kfifo.h>
26#include <linux/slab.h>
27#include <trace/events/host1x.h>
28
29#include "cdma.h"
30#include "channel.h"
31#include "dev.h"
32#include "debug.h"
33#include "host1x_bo.h"
34#include "job.h"
35
36/*
37 * push_buffer
38 *
39 * The push buffer is a circular array of words to be fetched by command DMA.
40 * Note that it works slightly differently to the sync queue; fence == pos
41 * means that the push buffer is full, not empty.
42 */
43
44#define HOST1X_PUSHBUFFER_SLOTS 512
45
46/*
47 * Clean up push buffer resources
48 */
49static void host1x_pushbuffer_destroy(struct push_buffer *pb)
50{
51 struct host1x_cdma *cdma = pb_to_cdma(pb);
52 struct host1x *host1x = cdma_to_host1x(cdma);
53
54 if (pb->phys != 0)
55 dma_free_writecombine(host1x->dev, pb->size_bytes + 4,
56 pb->mapped, pb->phys);
57
58 pb->mapped = NULL;
59 pb->phys = 0;
60}
61
62/*
63 * Init push buffer resources
64 */
65static int host1x_pushbuffer_init(struct push_buffer *pb)
66{
67 struct host1x_cdma *cdma = pb_to_cdma(pb);
68 struct host1x *host1x = cdma_to_host1x(cdma);
69
70 pb->mapped = NULL;
71 pb->phys = 0;
72 pb->size_bytes = HOST1X_PUSHBUFFER_SLOTS * 8;
73
74 /* initialize buffer pointers */
75 pb->fence = pb->size_bytes - 8;
76 pb->pos = 0;
77
78 /* allocate and map pushbuffer memory */
79 pb->mapped = dma_alloc_writecombine(host1x->dev, pb->size_bytes + 4,
80 &pb->phys, GFP_KERNEL);
81 if (!pb->mapped)
82 goto fail;
83
84 host1x_hw_pushbuffer_init(host1x, pb);
85
86 return 0;
87
88fail:
89 host1x_pushbuffer_destroy(pb);
90 return -ENOMEM;
91}
92
93/*
94 * Push two words to the push buffer
95 * Caller must ensure push buffer is not full
96 */
97static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
98{
99 u32 pos = pb->pos;
100 u32 *p = (u32 *)((u32)pb->mapped + pos);
101 WARN_ON(pos == pb->fence);
102 *(p++) = op1;
103 *(p++) = op2;
104 pb->pos = (pos + 8) & (pb->size_bytes - 1);
105}
106
107/*
108 * Pop a number of two word slots from the push buffer
109 * Caller must ensure push buffer is not empty
110 */
111static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
112{
113 /* Advance the next write position */
114 pb->fence = (pb->fence + slots * 8) & (pb->size_bytes - 1);
115}
116
117/*
118 * Return the number of two word slots free in the push buffer
119 */
120static u32 host1x_pushbuffer_space(struct push_buffer *pb)
121{
122 return ((pb->fence - pb->pos) & (pb->size_bytes - 1)) / 8;
123}
124
125/*
126 * Sleep (if necessary) until the requested event happens
127 * - CDMA_EVENT_SYNC_QUEUE_EMPTY : sync queue is completely empty.
128 * - Returns 1
129 * - CDMA_EVENT_PUSH_BUFFER_SPACE : there is space in the push buffer
130 * - Return the amount of space (> 0)
131 * Must be called with the cdma lock held.
132 */
133unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
134 enum cdma_event event)
135{
136 for (;;) {
137 unsigned int space;
138
139 if (event == CDMA_EVENT_SYNC_QUEUE_EMPTY)
140 space = list_empty(&cdma->sync_queue) ? 1 : 0;
141 else if (event == CDMA_EVENT_PUSH_BUFFER_SPACE) {
142 struct push_buffer *pb = &cdma->push_buffer;
143 space = host1x_pushbuffer_space(pb);
144 } else {
145 WARN_ON(1);
146 return -EINVAL;
147 }
148
149 if (space)
150 return space;
151
152 trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev),
153 event);
154
155 /* If somebody has managed to already start waiting, yield */
156 if (cdma->event != CDMA_EVENT_NONE) {
157 mutex_unlock(&cdma->lock);
158 schedule();
159 mutex_lock(&cdma->lock);
160 continue;
161 }
162 cdma->event = event;
163
164 mutex_unlock(&cdma->lock);
165 down(&cdma->sem);
166 mutex_lock(&cdma->lock);
167 }
168 return 0;
169}
170
171/*
172 * Start timer that tracks the time spent by the job.
173 * Must be called with the cdma lock held.
174 */
175static void cdma_start_timer_locked(struct host1x_cdma *cdma,
176 struct host1x_job *job)
177{
178 struct host1x *host = cdma_to_host1x(cdma);
179
180 if (cdma->timeout.client) {
181 /* timer already started */
182 return;
183 }
184
185 cdma->timeout.client = job->client;
186 cdma->timeout.syncpt = host1x_syncpt_get(host, job->syncpt_id);
187 cdma->timeout.syncpt_val = job->syncpt_end;
188 cdma->timeout.start_ktime = ktime_get();
189
190 schedule_delayed_work(&cdma->timeout.wq,
191 msecs_to_jiffies(job->timeout));
192}
193
194/*
195 * Stop timer when a buffer submission completes.
196 * Must be called with the cdma lock held.
197 */
198static void stop_cdma_timer_locked(struct host1x_cdma *cdma)
199{
200 cancel_delayed_work(&cdma->timeout.wq);
201 cdma->timeout.client = 0;
202}
203
204/*
205 * For all sync queue entries that have already finished according to the
206 * current sync point registers:
207 * - unpin & unref their mems
208 * - pop their push buffer slots
209 * - remove them from the sync queue
210 * This is normally called from the host code's worker thread, but can be
211 * called manually if necessary.
212 * Must be called with the cdma lock held.
213 */
214static void update_cdma_locked(struct host1x_cdma *cdma)
215{
216 bool signal = false;
217 struct host1x *host1x = cdma_to_host1x(cdma);
218 struct host1x_job *job, *n;
219
220 /* If CDMA is stopped, queue is cleared and we can return */
221 if (!cdma->running)
222 return;
223
224 /*
225 * Walk the sync queue, reading the sync point registers as necessary,
226 * to consume as many sync queue entries as possible without blocking
227 */
228 list_for_each_entry_safe(job, n, &cdma->sync_queue, list) {
229 struct host1x_syncpt *sp =
230 host1x_syncpt_get(host1x, job->syncpt_id);
231
232 /* Check whether this syncpt has completed, and bail if not */
233 if (!host1x_syncpt_is_expired(sp, job->syncpt_end)) {
234 /* Start timer on next pending syncpt */
235 if (job->timeout)
236 cdma_start_timer_locked(cdma, job);
237 break;
238 }
239
240 /* Cancel timeout, when a buffer completes */
241 if (cdma->timeout.client)
242 stop_cdma_timer_locked(cdma);
243
244 /* Unpin the memory */
245 host1x_job_unpin(job);
246
247 /* Pop push buffer slots */
248 if (job->num_slots) {
249 struct push_buffer *pb = &cdma->push_buffer;
250 host1x_pushbuffer_pop(pb, job->num_slots);
251 if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE)
252 signal = true;
253 }
254
255 list_del(&job->list);
256 host1x_job_put(job);
257 }
258
259 if (cdma->event == CDMA_EVENT_SYNC_QUEUE_EMPTY &&
260 list_empty(&cdma->sync_queue))
261 signal = true;
262
263 if (signal) {
264 cdma->event = CDMA_EVENT_NONE;
265 up(&cdma->sem);
266 }
267}
268
269void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
270 struct device *dev)
271{
272 u32 restart_addr;
273 u32 syncpt_incrs;
274 struct host1x_job *job = NULL;
275 u32 syncpt_val;
276 struct host1x *host1x = cdma_to_host1x(cdma);
277
278 syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
279
280 dev_dbg(dev, "%s: starting cleanup (thresh %d)\n",
281 __func__, syncpt_val);
282
283 /*
284 * Move the sync_queue read pointer to the first entry that hasn't
285 * completed based on the current HW syncpt value. It's likely there
286 * won't be any (i.e. we're still at the head), but covers the case
287 * where a syncpt incr happens just prior/during the teardown.
288 */
289
290 dev_dbg(dev, "%s: skip completed buffers still in sync_queue\n",
291 __func__);
292
293 list_for_each_entry(job, &cdma->sync_queue, list) {
294 if (syncpt_val < job->syncpt_end)
295 break;
296
297 host1x_job_dump(dev, job);
298 }
299
300 /*
301 * Walk the sync_queue, first incrementing with the CPU syncpts that
302 * are partially executed (the first buffer) or fully skipped while
303 * still in the current context (slots are also NOP-ed).
304 *
305 * At the point contexts are interleaved, syncpt increments must be
306 * done inline with the pushbuffer from a GATHER buffer to maintain
307 * the order (slots are modified to be a GATHER of syncpt incrs).
308 *
309 * Note: save in restart_addr the location where the timed out buffer
310 * started in the PB, so we can start the refetch from there (with the
311 * modified NOP-ed PB slots). This lets things appear to have completed
312 * properly for this buffer and resources are freed.
313 */
314
315 dev_dbg(dev, "%s: perform CPU incr on pending same ctx buffers\n",
316 __func__);
317
318 if (!list_empty(&cdma->sync_queue))
319 restart_addr = job->first_get;
320 else
321 restart_addr = cdma->last_pos;
322
323 /* do CPU increments as long as this context continues */
324 list_for_each_entry_from(job, &cdma->sync_queue, list) {
325 /* different context, gets us out of this loop */
326 if (job->client != cdma->timeout.client)
327 break;
328
329 /* won't need a timeout when replayed */
330 job->timeout = 0;
331
332 syncpt_incrs = job->syncpt_end - syncpt_val;
333 dev_dbg(dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs);
334
335 host1x_job_dump(dev, job);
336
337 /* safe to use CPU to incr syncpts */
338 host1x_hw_cdma_timeout_cpu_incr(host1x, cdma, job->first_get,
339 syncpt_incrs, job->syncpt_end,
340 job->num_slots);
341
342 syncpt_val += syncpt_incrs;
343 }
344
345 /* The following sumbits from the same client may be dependent on the
346 * failed submit and therefore they may fail. Force a small timeout
347 * to make the queue cleanup faster */
348
349 list_for_each_entry_from(job, &cdma->sync_queue, list)
350 if (job->client == cdma->timeout.client)
351 job->timeout = min_t(unsigned int, job->timeout, 500);
352
353 dev_dbg(dev, "%s: finished sync_queue modification\n", __func__);
354
355 /* roll back DMAGET and start up channel again */
356 host1x_hw_cdma_resume(host1x, cdma, restart_addr);
357}
358
359/*
360 * Create a cdma
361 */
362int host1x_cdma_init(struct host1x_cdma *cdma)
363{
364 int err;
365
366 mutex_init(&cdma->lock);
367 sema_init(&cdma->sem, 0);
368
369 INIT_LIST_HEAD(&cdma->sync_queue);
370
371 cdma->event = CDMA_EVENT_NONE;
372 cdma->running = false;
373 cdma->torndown = false;
374
375 err = host1x_pushbuffer_init(&cdma->push_buffer);
376 if (err)
377 return err;
378 return 0;
379}
380
381/*
382 * Destroy a cdma
383 */
384int host1x_cdma_deinit(struct host1x_cdma *cdma)
385{
386 struct push_buffer *pb = &cdma->push_buffer;
387 struct host1x *host1x = cdma_to_host1x(cdma);
388
389 if (cdma->running) {
390 pr_warn("%s: CDMA still running\n", __func__);
391 return -EBUSY;
392 }
393
394 host1x_pushbuffer_destroy(pb);
395 host1x_hw_cdma_timeout_destroy(host1x, cdma);
396
397 return 0;
398}
399
400/*
401 * Begin a cdma submit
402 */
403int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
404{
405 struct host1x *host1x = cdma_to_host1x(cdma);
406
407 mutex_lock(&cdma->lock);
408
409 if (job->timeout) {
410 /* init state on first submit with timeout value */
411 if (!cdma->timeout.initialized) {
412 int err;
413 err = host1x_hw_cdma_timeout_init(host1x, cdma,
414 job->syncpt_id);
415 if (err) {
416 mutex_unlock(&cdma->lock);
417 return err;
418 }
419 }
420 }
421 if (!cdma->running)
422 host1x_hw_cdma_start(host1x, cdma);
423
424 cdma->slots_free = 0;
425 cdma->slots_used = 0;
426 cdma->first_get = cdma->push_buffer.pos;
427
428 trace_host1x_cdma_begin(dev_name(job->channel->dev));
429 return 0;
430}
431
432/*
433 * Push two words into a push buffer slot
434 * Blocks as necessary if the push buffer is full.
435 */
436void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2)
437{
438 struct host1x *host1x = cdma_to_host1x(cdma);
439 struct push_buffer *pb = &cdma->push_buffer;
440 u32 slots_free = cdma->slots_free;
441
442 if (slots_free == 0) {
443 host1x_hw_cdma_flush(host1x, cdma);
444 slots_free = host1x_cdma_wait_locked(cdma,
445 CDMA_EVENT_PUSH_BUFFER_SPACE);
446 }
447 cdma->slots_free = slots_free - 1;
448 cdma->slots_used++;
449 host1x_pushbuffer_push(pb, op1, op2);
450}
451
452/*
453 * End a cdma submit
454 * Kick off DMA, add job to the sync queue, and a number of slots to be freed
455 * from the pushbuffer. The handles for a submit must all be pinned at the same
456 * time, but they can be unpinned in smaller chunks.
457 */
458void host1x_cdma_end(struct host1x_cdma *cdma,
459 struct host1x_job *job)
460{
461 struct host1x *host1x = cdma_to_host1x(cdma);
462 bool idle = list_empty(&cdma->sync_queue);
463
464 host1x_hw_cdma_flush(host1x, cdma);
465
466 job->first_get = cdma->first_get;
467 job->num_slots = cdma->slots_used;
468 host1x_job_get(job);
469 list_add_tail(&job->list, &cdma->sync_queue);
470
471 /* start timer on idle -> active transitions */
472 if (job->timeout && idle)
473 cdma_start_timer_locked(cdma, job);
474
475 trace_host1x_cdma_end(dev_name(job->channel->dev));
476 mutex_unlock(&cdma->lock);
477}
478
479/*
480 * Update cdma state according to current sync point values
481 */
482void host1x_cdma_update(struct host1x_cdma *cdma)
483{
484 mutex_lock(&cdma->lock);
485 update_cdma_locked(cdma);
486 mutex_unlock(&cdma->lock);
487}
diff --git a/drivers/gpu/host1x/cdma.h b/drivers/gpu/host1x/cdma.h
new file mode 100644
index 000000000000..313c4b784348
--- /dev/null
+++ b/drivers/gpu/host1x/cdma.h
@@ -0,0 +1,100 @@
1/*
2 * Tegra host1x Command DMA
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __HOST1X_CDMA_H
20#define __HOST1X_CDMA_H
21
22#include <linux/sched.h>
23#include <linux/semaphore.h>
24#include <linux/list.h>
25
26struct host1x_syncpt;
27struct host1x_userctx_timeout;
28struct host1x_job;
29
30/*
31 * cdma
32 *
33 * This is in charge of a host command DMA channel.
34 * Sends ops to a push buffer, and takes responsibility for unpinning
35 * (& possibly freeing) of memory after those ops have completed.
36 * Producer:
37 * begin
38 * push - send ops to the push buffer
39 * end - start command DMA and enqueue handles to be unpinned
40 * Consumer:
41 * update - call to update sync queue and push buffer, unpin memory
42 */
43
44struct push_buffer {
45 u32 *mapped; /* mapped pushbuffer memory */
46 dma_addr_t phys; /* physical address of pushbuffer */
47 u32 fence; /* index we've written */
48 u32 pos; /* index to write to */
49 u32 size_bytes;
50};
51
52struct buffer_timeout {
53 struct delayed_work wq; /* work queue */
54 bool initialized; /* timer one-time setup flag */
55 struct host1x_syncpt *syncpt; /* buffer completion syncpt */
56 u32 syncpt_val; /* syncpt value when completed */
57 ktime_t start_ktime; /* starting time */
58 /* context timeout information */
59 int client;
60};
61
62enum cdma_event {
63 CDMA_EVENT_NONE, /* not waiting for any event */
64 CDMA_EVENT_SYNC_QUEUE_EMPTY, /* wait for empty sync queue */
65 CDMA_EVENT_PUSH_BUFFER_SPACE /* wait for space in push buffer */
66};
67
68struct host1x_cdma {
69 struct mutex lock; /* controls access to shared state */
70 struct semaphore sem; /* signalled when event occurs */
71 enum cdma_event event; /* event that sem is waiting for */
72 unsigned int slots_used; /* pb slots used in current submit */
73 unsigned int slots_free; /* pb slots free in current submit */
74 unsigned int first_get; /* DMAGET value, where submit begins */
75 unsigned int last_pos; /* last value written to DMAPUT */
76 struct push_buffer push_buffer; /* channel's push buffer */
77 struct list_head sync_queue; /* job queue */
78 struct buffer_timeout timeout; /* channel's timeout state/wq */
79 bool running;
80 bool torndown;
81};
82
83#define cdma_to_channel(cdma) container_of(cdma, struct host1x_channel, cdma)
84#define cdma_to_host1x(cdma) dev_get_drvdata(cdma_to_channel(cdma)->dev->parent)
85#define pb_to_cdma(pb) container_of(pb, struct host1x_cdma, push_buffer)
86
87int host1x_cdma_init(struct host1x_cdma *cdma);
88int host1x_cdma_deinit(struct host1x_cdma *cdma);
89void host1x_cdma_stop(struct host1x_cdma *cdma);
90int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job);
91void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2);
92void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job);
93void host1x_cdma_update(struct host1x_cdma *cdma);
94void host1x_cdma_peek(struct host1x_cdma *cdma, u32 dmaget, int slot,
95 u32 *out);
96unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
97 enum cdma_event event);
98void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
99 struct device *dev);
100#endif
diff --git a/drivers/gpu/host1x/channel.c b/drivers/gpu/host1x/channel.c
new file mode 100644
index 000000000000..83ea51b9f0fc
--- /dev/null
+++ b/drivers/gpu/host1x/channel.c
@@ -0,0 +1,126 @@
1/*
2 * Tegra host1x Channel
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/slab.h>
20#include <linux/module.h>
21
22#include "channel.h"
23#include "dev.h"
24#include "job.h"
25
26/* Constructor for the host1x device list */
27int host1x_channel_list_init(struct host1x *host)
28{
29 INIT_LIST_HEAD(&host->chlist.list);
30 mutex_init(&host->chlist_mutex);
31
32 if (host->info->nb_channels > BITS_PER_LONG) {
33 WARN(1, "host1x hardware has more channels than supported by the driver\n");
34 return -ENOSYS;
35 }
36
37 return 0;
38}
39
40int host1x_job_submit(struct host1x_job *job)
41{
42 struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
43
44 return host1x_hw_channel_submit(host, job);
45}
46
47struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
48{
49 int err = 0;
50
51 mutex_lock(&channel->reflock);
52
53 if (channel->refcount == 0)
54 err = host1x_cdma_init(&channel->cdma);
55
56 if (!err)
57 channel->refcount++;
58
59 mutex_unlock(&channel->reflock);
60
61 return err ? NULL : channel;
62}
63
64void host1x_channel_put(struct host1x_channel *channel)
65{
66 mutex_lock(&channel->reflock);
67
68 if (channel->refcount == 1) {
69 struct host1x *host = dev_get_drvdata(channel->dev->parent);
70
71 host1x_hw_cdma_stop(host, &channel->cdma);
72 host1x_cdma_deinit(&channel->cdma);
73 }
74
75 channel->refcount--;
76
77 mutex_unlock(&channel->reflock);
78}
79
80struct host1x_channel *host1x_channel_request(struct device *dev)
81{
82 struct host1x *host = dev_get_drvdata(dev->parent);
83 int max_channels = host->info->nb_channels;
84 struct host1x_channel *channel = NULL;
85 int index, err;
86
87 mutex_lock(&host->chlist_mutex);
88
89 index = find_first_zero_bit(&host->allocated_channels, max_channels);
90 if (index >= max_channels)
91 goto fail;
92
93 channel = kzalloc(sizeof(*channel), GFP_KERNEL);
94 if (!channel)
95 goto fail;
96
97 err = host1x_hw_channel_init(host, channel, index);
98 if (err < 0)
99 goto fail;
100
101 /* Link device to host1x_channel */
102 channel->dev = dev;
103
104 /* Add to channel list */
105 list_add_tail(&channel->list, &host->chlist.list);
106
107 host->allocated_channels |= BIT(index);
108
109 mutex_unlock(&host->chlist_mutex);
110 return channel;
111
112fail:
113 dev_err(dev, "failed to init channel\n");
114 kfree(channel);
115 mutex_unlock(&host->chlist_mutex);
116 return NULL;
117}
118
119void host1x_channel_free(struct host1x_channel *channel)
120{
121 struct host1x *host = dev_get_drvdata(channel->dev->parent);
122
123 host->allocated_channels &= ~BIT(channel->id);
124 list_del(&channel->list);
125 kfree(channel);
126}
diff --git a/drivers/gpu/host1x/channel.h b/drivers/gpu/host1x/channel.h
new file mode 100644
index 000000000000..48723b8eea42
--- /dev/null
+++ b/drivers/gpu/host1x/channel.h
@@ -0,0 +1,52 @@
1/*
2 * Tegra host1x Channel
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __HOST1X_CHANNEL_H
20#define __HOST1X_CHANNEL_H
21
22#include <linux/io.h>
23
24#include "cdma.h"
25
26struct host1x;
27
28struct host1x_channel {
29 struct list_head list;
30
31 unsigned int refcount;
32 unsigned int id;
33 struct mutex reflock;
34 struct mutex submitlock;
35 void __iomem *regs;
36 struct device *dev;
37 struct host1x_cdma cdma;
38};
39
40/* channel list operations */
41int host1x_channel_list_init(struct host1x *host);
42
43struct host1x_channel *host1x_channel_request(struct device *dev);
44void host1x_channel_free(struct host1x_channel *channel);
45struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
46void host1x_channel_put(struct host1x_channel *channel);
47int host1x_job_submit(struct host1x_job *job);
48
49#define host1x_for_each_channel(host, channel) \
50 list_for_each_entry(channel, &host->chlist.list, list)
51
52#endif
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index b967f6e8df55..4e522c532bc8 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -29,6 +29,7 @@
29 29
30#include "dev.h" 30#include "dev.h"
31#include "intr.h" 31#include "intr.h"
32#include "channel.h"
32#include "hw/host1x01.h" 33#include "hw/host1x01.h"
33 34
34void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) 35void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
@@ -45,6 +46,16 @@ u32 host1x_sync_readl(struct host1x *host1x, u32 r)
45 return readl(sync_regs + r); 46 return readl(sync_regs + r);
46} 47}
47 48
49void host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r)
50{
51 writel(v, ch->regs + r);
52}
53
54u32 host1x_ch_readl(struct host1x_channel *ch, u32 r)
55{
56 return readl(ch->regs + r);
57}
58
48static const struct host1x_info host1x01_info = { 59static const struct host1x_info host1x01_info = {
49 .nb_channels = 8, 60 .nb_channels = 8,
50 .nb_pts = 32, 61 .nb_pts = 32,
@@ -112,6 +123,12 @@ static int host1x_probe(struct platform_device *pdev)
112 return err; 123 return err;
113 } 124 }
114 125
126 err = host1x_channel_list_init(host);
127 if (err) {
128 dev_err(&pdev->dev, "failed to initialize channel list\n");
129 return err;
130 }
131
115 err = clk_prepare_enable(host->clk); 132 err = clk_prepare_enable(host->clk);
116 if (err < 0) { 133 if (err < 0) {
117 dev_err(&pdev->dev, "failed to enable clock\n"); 134 dev_err(&pdev->dev, "failed to enable clock\n");
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index caf9cc62eb17..1a9b4383dc3b 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -20,10 +20,39 @@
20#include <linux/platform_device.h> 20#include <linux/platform_device.h>
21#include <linux/device.h> 21#include <linux/device.h>
22 22
23#include "channel.h"
23#include "syncpt.h" 24#include "syncpt.h"
24#include "intr.h" 25#include "intr.h"
26#include "cdma.h"
27#include "job.h"
25 28
26struct host1x_syncpt; 29struct host1x_syncpt;
30struct host1x_channel;
31struct host1x_cdma;
32struct host1x_job;
33struct push_buffer;
34
35struct host1x_channel_ops {
36 int (*init)(struct host1x_channel *channel, struct host1x *host,
37 unsigned int id);
38 int (*submit)(struct host1x_job *job);
39};
40
41struct host1x_cdma_ops {
42 void (*start)(struct host1x_cdma *cdma);
43 void (*stop)(struct host1x_cdma *cdma);
44 void (*flush)(struct host1x_cdma *cdma);
45 int (*timeout_init)(struct host1x_cdma *cdma, u32 syncpt_id);
46 void (*timeout_destroy)(struct host1x_cdma *cdma);
47 void (*freeze)(struct host1x_cdma *cdma);
48 void (*resume)(struct host1x_cdma *cdma, u32 getptr);
49 void (*timeout_cpu_incr)(struct host1x_cdma *cdma, u32 getptr,
50 u32 syncpt_incrs, u32 syncval, u32 nr_slots);
51};
52
53struct host1x_pushbuffer_ops {
54 void (*init)(struct push_buffer *pb);
55};
27 56
28struct host1x_syncpt_ops { 57struct host1x_syncpt_ops {
29 void (*restore)(struct host1x_syncpt *syncpt); 58 void (*restore)(struct host1x_syncpt *syncpt);
@@ -68,11 +97,22 @@ struct host1x {
68 97
69 const struct host1x_syncpt_ops *syncpt_op; 98 const struct host1x_syncpt_ops *syncpt_op;
70 const struct host1x_intr_ops *intr_op; 99 const struct host1x_intr_ops *intr_op;
100 const struct host1x_channel_ops *channel_op;
101 const struct host1x_cdma_ops *cdma_op;
102 const struct host1x_pushbuffer_ops *cdma_pb_op;
71 103
104 struct host1x_syncpt *nop_sp;
105
106 struct mutex chlist_mutex;
107 struct host1x_channel chlist;
108 unsigned long allocated_channels;
109 unsigned int num_allocated_channels;
72}; 110};
73 111
74void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v); 112void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
75u32 host1x_sync_readl(struct host1x *host1x, u32 r); 113u32 host1x_sync_readl(struct host1x *host1x, u32 r);
114void host1x_ch_writel(struct host1x_channel *ch, u32 r, u32 v);
115u32 host1x_ch_readl(struct host1x_channel *ch, u32 r);
76 116
77static inline void host1x_hw_syncpt_restore(struct host1x *host, 117static inline void host1x_hw_syncpt_restore(struct host1x *host,
78 struct host1x_syncpt *sp) 118 struct host1x_syncpt *sp)
@@ -144,4 +184,77 @@ static inline int host1x_hw_intr_free_syncpt_irq(struct host1x *host)
144{ 184{
145 return host->intr_op->free_syncpt_irq(host); 185 return host->intr_op->free_syncpt_irq(host);
146} 186}
187
188static inline int host1x_hw_channel_init(struct host1x *host,
189 struct host1x_channel *channel,
190 int chid)
191{
192 return host->channel_op->init(channel, host, chid);
193}
194
195static inline int host1x_hw_channel_submit(struct host1x *host,
196 struct host1x_job *job)
197{
198 return host->channel_op->submit(job);
199}
200
201static inline void host1x_hw_cdma_start(struct host1x *host,
202 struct host1x_cdma *cdma)
203{
204 host->cdma_op->start(cdma);
205}
206
207static inline void host1x_hw_cdma_stop(struct host1x *host,
208 struct host1x_cdma *cdma)
209{
210 host->cdma_op->stop(cdma);
211}
212
213static inline void host1x_hw_cdma_flush(struct host1x *host,
214 struct host1x_cdma *cdma)
215{
216 host->cdma_op->flush(cdma);
217}
218
219static inline int host1x_hw_cdma_timeout_init(struct host1x *host,
220 struct host1x_cdma *cdma,
221 u32 syncpt_id)
222{
223 return host->cdma_op->timeout_init(cdma, syncpt_id);
224}
225
226static inline void host1x_hw_cdma_timeout_destroy(struct host1x *host,
227 struct host1x_cdma *cdma)
228{
229 host->cdma_op->timeout_destroy(cdma);
230}
231
232static inline void host1x_hw_cdma_freeze(struct host1x *host,
233 struct host1x_cdma *cdma)
234{
235 host->cdma_op->freeze(cdma);
236}
237
238static inline void host1x_hw_cdma_resume(struct host1x *host,
239 struct host1x_cdma *cdma, u32 getptr)
240{
241 host->cdma_op->resume(cdma, getptr);
242}
243
244static inline void host1x_hw_cdma_timeout_cpu_incr(struct host1x *host,
245 struct host1x_cdma *cdma,
246 u32 getptr,
247 u32 syncpt_incrs,
248 u32 syncval, u32 nr_slots)
249{
250 host->cdma_op->timeout_cpu_incr(cdma, getptr, syncpt_incrs, syncval,
251 nr_slots);
252}
253
254static inline void host1x_hw_pushbuffer_init(struct host1x *host,
255 struct push_buffer *pb)
256{
257 host->cdma_pb_op->init(pb);
258}
259
147#endif 260#endif
diff --git a/drivers/gpu/host1x/host1x.h b/drivers/gpu/host1x/host1x.h
new file mode 100644
index 000000000000..bca6563f6766
--- /dev/null
+++ b/drivers/gpu/host1x/host1x.h
@@ -0,0 +1,28 @@
1/*
2 * Tegra host1x driver
3 *
4 * Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#ifndef __LINUX_HOST1X_H
22#define __LINUX_HOST1X_H
23
24enum host1x_class {
25 HOST1X_CLASS_HOST1X = 0x1
26};
27
28#endif
diff --git a/drivers/gpu/host1x/host1x_bo.h b/drivers/gpu/host1x/host1x_bo.h
new file mode 100644
index 000000000000..4c1f10bd773d
--- /dev/null
+++ b/drivers/gpu/host1x/host1x_bo.h
@@ -0,0 +1,87 @@
1/*
2 * Tegra host1x Memory Management Abstraction header
3 *
4 * Copyright (c) 2012-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef _HOST1X_BO_H
20#define _HOST1X_BO_H
21
22struct host1x_bo;
23
24struct host1x_bo_ops {
25 struct host1x_bo *(*get)(struct host1x_bo *bo);
26 void (*put)(struct host1x_bo *bo);
27 dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
28 void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
29 void *(*mmap)(struct host1x_bo *bo);
30 void (*munmap)(struct host1x_bo *bo, void *addr);
31 void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
32 void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
33};
34
35struct host1x_bo {
36 const struct host1x_bo_ops *ops;
37};
38
39static inline void host1x_bo_init(struct host1x_bo *bo,
40 const struct host1x_bo_ops *ops)
41{
42 bo->ops = ops;
43}
44
45static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
46{
47 return bo->ops->get(bo);
48}
49
50static inline void host1x_bo_put(struct host1x_bo *bo)
51{
52 bo->ops->put(bo);
53}
54
55static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
56 struct sg_table **sgt)
57{
58 return bo->ops->pin(bo, sgt);
59}
60
61static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
62{
63 bo->ops->unpin(bo, sgt);
64}
65
66static inline void *host1x_bo_mmap(struct host1x_bo *bo)
67{
68 return bo->ops->mmap(bo);
69}
70
71static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
72{
73 bo->ops->munmap(bo, addr);
74}
75
76static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
77{
78 return bo->ops->kmap(bo, pagenum);
79}
80
81static inline void host1x_bo_kunmap(struct host1x_bo *bo,
82 unsigned int pagenum, void *addr)
83{
84 bo->ops->kunmap(bo, pagenum, addr);
85}
86
87#endif
diff --git a/drivers/gpu/host1x/hw/cdma_hw.c b/drivers/gpu/host1x/hw/cdma_hw.c
new file mode 100644
index 000000000000..4eb22ef29776
--- /dev/null
+++ b/drivers/gpu/host1x/hw/cdma_hw.c
@@ -0,0 +1,324 @@
1/*
2 * Tegra host1x Command DMA
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/slab.h>
20#include <linux/scatterlist.h>
21#include <linux/dma-mapping.h>
22
23#include "cdma.h"
24#include "channel.h"
25#include "dev.h"
26#include "debug.h"
27
28/*
29 * Put the restart at the end of pushbuffer memor
30 */
31static void push_buffer_init(struct push_buffer *pb)
32{
33 *(pb->mapped + (pb->size_bytes >> 2)) = host1x_opcode_restart(0);
34}
35
36/*
37 * Increment timedout buffer's syncpt via CPU.
38 */
39static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
40 u32 syncpt_incrs, u32 syncval, u32 nr_slots)
41{
42 struct host1x *host1x = cdma_to_host1x(cdma);
43 struct push_buffer *pb = &cdma->push_buffer;
44 u32 i;
45
46 for (i = 0; i < syncpt_incrs; i++)
47 host1x_syncpt_cpu_incr(cdma->timeout.syncpt);
48
49 /* after CPU incr, ensure shadow is up to date */
50 host1x_syncpt_load(cdma->timeout.syncpt);
51
52 /* NOP all the PB slots */
53 while (nr_slots--) {
54 u32 *p = (u32 *)((u32)pb->mapped + getptr);
55 *(p++) = HOST1X_OPCODE_NOP;
56 *(p++) = HOST1X_OPCODE_NOP;
57 dev_dbg(host1x->dev, "%s: NOP at 0x%x\n", __func__,
58 pb->phys + getptr);
59 getptr = (getptr + 8) & (pb->size_bytes - 1);
60 }
61 wmb();
62}
63
64/*
65 * Start channel DMA
66 */
67static void cdma_start(struct host1x_cdma *cdma)
68{
69 struct host1x_channel *ch = cdma_to_channel(cdma);
70
71 if (cdma->running)
72 return;
73
74 cdma->last_pos = cdma->push_buffer.pos;
75
76 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
77 HOST1X_CHANNEL_DMACTRL);
78
79 /* set base, put and end pointer */
80 host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART);
81 host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT);
82 host1x_ch_writel(ch, cdma->push_buffer.phys +
83 cdma->push_buffer.size_bytes + 4,
84 HOST1X_CHANNEL_DMAEND);
85
86 /* reset GET */
87 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP |
88 HOST1X_CHANNEL_DMACTRL_DMAGETRST |
89 HOST1X_CHANNEL_DMACTRL_DMAINITGET,
90 HOST1X_CHANNEL_DMACTRL);
91
92 /* start the command DMA */
93 host1x_ch_writel(ch, 0, HOST1X_CHANNEL_DMACTRL);
94
95 cdma->running = true;
96}
97
98/*
99 * Similar to cdma_start(), but rather than starting from an idle
100 * state (where DMA GET is set to DMA PUT), on a timeout we restore
101 * DMA GET from an explicit value (so DMA may again be pending).
102 */
103static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr)
104{
105 struct host1x *host1x = cdma_to_host1x(cdma);
106 struct host1x_channel *ch = cdma_to_channel(cdma);
107
108 if (cdma->running)
109 return;
110
111 cdma->last_pos = cdma->push_buffer.pos;
112
113 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
114 HOST1X_CHANNEL_DMACTRL);
115
116 /* set base, end pointer (all of memory) */
117 host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART);
118 host1x_ch_writel(ch, cdma->push_buffer.phys +
119 cdma->push_buffer.size_bytes,
120 HOST1X_CHANNEL_DMAEND);
121
122 /* set GET, by loading the value in PUT (then reset GET) */
123 host1x_ch_writel(ch, getptr, HOST1X_CHANNEL_DMAPUT);
124 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP |
125 HOST1X_CHANNEL_DMACTRL_DMAGETRST |
126 HOST1X_CHANNEL_DMACTRL_DMAINITGET,
127 HOST1X_CHANNEL_DMACTRL);
128
129 dev_dbg(host1x->dev,
130 "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", __func__,
131 host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET),
132 host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT),
133 cdma->last_pos);
134
135 /* deassert GET reset and set PUT */
136 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
137 HOST1X_CHANNEL_DMACTRL);
138 host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT);
139
140 /* start the command DMA */
141 host1x_ch_writel(ch, 0, HOST1X_CHANNEL_DMACTRL);
142
143 cdma->running = true;
144}
145
146/*
147 * Kick channel DMA into action by writing its PUT offset (if it has changed)
148 */
149static void cdma_flush(struct host1x_cdma *cdma)
150{
151 struct host1x_channel *ch = cdma_to_channel(cdma);
152
153 if (cdma->push_buffer.pos != cdma->last_pos) {
154 host1x_ch_writel(ch, cdma->push_buffer.pos,
155 HOST1X_CHANNEL_DMAPUT);
156 cdma->last_pos = cdma->push_buffer.pos;
157 }
158}
159
160static void cdma_stop(struct host1x_cdma *cdma)
161{
162 struct host1x_channel *ch = cdma_to_channel(cdma);
163
164 mutex_lock(&cdma->lock);
165 if (cdma->running) {
166 host1x_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY);
167 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
168 HOST1X_CHANNEL_DMACTRL);
169 cdma->running = false;
170 }
171 mutex_unlock(&cdma->lock);
172}
173
174/*
175 * Stops both channel's command processor and CDMA immediately.
176 * Also, tears down the channel and resets corresponding module.
177 */
178static void cdma_freeze(struct host1x_cdma *cdma)
179{
180 struct host1x *host = cdma_to_host1x(cdma);
181 struct host1x_channel *ch = cdma_to_channel(cdma);
182 u32 cmdproc_stop;
183
184 if (cdma->torndown && !cdma->running) {
185 dev_warn(host->dev, "Already torn down\n");
186 return;
187 }
188
189 dev_dbg(host->dev, "freezing channel (id %d)\n", ch->id);
190
191 cmdproc_stop = host1x_sync_readl(host, HOST1X_SYNC_CMDPROC_STOP);
192 cmdproc_stop |= BIT(ch->id);
193 host1x_sync_writel(host, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
194
195 dev_dbg(host->dev, "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n",
196 __func__, host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET),
197 host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT),
198 cdma->last_pos);
199
200 host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
201 HOST1X_CHANNEL_DMACTRL);
202
203 host1x_sync_writel(host, BIT(ch->id), HOST1X_SYNC_CH_TEARDOWN);
204
205 cdma->running = false;
206 cdma->torndown = true;
207}
208
209static void cdma_resume(struct host1x_cdma *cdma, u32 getptr)
210{
211 struct host1x *host1x = cdma_to_host1x(cdma);
212 struct host1x_channel *ch = cdma_to_channel(cdma);
213 u32 cmdproc_stop;
214
215 dev_dbg(host1x->dev,
216 "resuming channel (id %d, DMAGET restart = 0x%x)\n",
217 ch->id, getptr);
218
219 cmdproc_stop = host1x_sync_readl(host1x, HOST1X_SYNC_CMDPROC_STOP);
220 cmdproc_stop &= ~(BIT(ch->id));
221 host1x_sync_writel(host1x, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
222
223 cdma->torndown = false;
224 cdma_timeout_restart(cdma, getptr);
225}
226
227/*
228 * If this timeout fires, it indicates the current sync_queue entry has
229 * exceeded its TTL and the userctx should be timed out and remaining
230 * submits already issued cleaned up (future submits return an error).
231 */
232static void cdma_timeout_handler(struct work_struct *work)
233{
234 struct host1x_cdma *cdma;
235 struct host1x *host1x;
236 struct host1x_channel *ch;
237
238 u32 syncpt_val;
239
240 u32 prev_cmdproc, cmdproc_stop;
241
242 cdma = container_of(to_delayed_work(work), struct host1x_cdma,
243 timeout.wq);
244 host1x = cdma_to_host1x(cdma);
245 ch = cdma_to_channel(cdma);
246
247 mutex_lock(&cdma->lock);
248
249 if (!cdma->timeout.client) {
250 dev_dbg(host1x->dev,
251 "cdma_timeout: expired, but has no clientid\n");
252 mutex_unlock(&cdma->lock);
253 return;
254 }
255
256 /* stop processing to get a clean snapshot */
257 prev_cmdproc = host1x_sync_readl(host1x, HOST1X_SYNC_CMDPROC_STOP);
258 cmdproc_stop = prev_cmdproc | BIT(ch->id);
259 host1x_sync_writel(host1x, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
260
261 dev_dbg(host1x->dev, "cdma_timeout: cmdproc was 0x%x is 0x%x\n",
262 prev_cmdproc, cmdproc_stop);
263
264 syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
265
266 /* has buffer actually completed? */
267 if ((s32)(syncpt_val - cdma->timeout.syncpt_val) >= 0) {
268 dev_dbg(host1x->dev,
269 "cdma_timeout: expired, but buffer had completed\n");
270 /* restore */
271 cmdproc_stop = prev_cmdproc & ~(BIT(ch->id));
272 host1x_sync_writel(host1x, cmdproc_stop,
273 HOST1X_SYNC_CMDPROC_STOP);
274 mutex_unlock(&cdma->lock);
275 return;
276 }
277
278 dev_warn(host1x->dev, "%s: timeout: %d (%s), HW thresh %d, done %d\n",
279 __func__, cdma->timeout.syncpt->id, cdma->timeout.syncpt->name,
280 syncpt_val, cdma->timeout.syncpt_val);
281
282 /* stop HW, resetting channel/module */
283 host1x_hw_cdma_freeze(host1x, cdma);
284
285 host1x_cdma_update_sync_queue(cdma, ch->dev);
286 mutex_unlock(&cdma->lock);
287}
288
289/*
290 * Init timeout resources
291 */
292static int cdma_timeout_init(struct host1x_cdma *cdma, u32 syncpt_id)
293{
294 INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler);
295 cdma->timeout.initialized = true;
296
297 return 0;
298}
299
300/*
301 * Clean up timeout resources
302 */
303static void cdma_timeout_destroy(struct host1x_cdma *cdma)
304{
305 if (cdma->timeout.initialized)
306 cancel_delayed_work(&cdma->timeout.wq);
307 cdma->timeout.initialized = false;
308}
309
310static const struct host1x_cdma_ops host1x_cdma_ops = {
311 .start = cdma_start,
312 .stop = cdma_stop,
313 .flush = cdma_flush,
314
315 .timeout_init = cdma_timeout_init,
316 .timeout_destroy = cdma_timeout_destroy,
317 .freeze = cdma_freeze,
318 .resume = cdma_resume,
319 .timeout_cpu_incr = cdma_timeout_cpu_incr,
320};
321
322static const struct host1x_pushbuffer_ops host1x_pushbuffer_ops = {
323 .init = push_buffer_init,
324};
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
new file mode 100644
index 000000000000..5137a5604215
--- /dev/null
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -0,0 +1,143 @@
1/*
2 * Tegra host1x Channel
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/slab.h>
20#include <trace/events/host1x.h>
21
22#include "host1x.h"
23#include "host1x_bo.h"
24#include "channel.h"
25#include "dev.h"
26#include "intr.h"
27#include "job.h"
28
29#define HOST1X_CHANNEL_SIZE 16384
30#define TRACE_MAX_LENGTH 128U
31
32static void submit_gathers(struct host1x_job *job)
33{
34 struct host1x_cdma *cdma = &job->channel->cdma;
35 unsigned int i;
36
37 for (i = 0; i < job->num_gathers; i++) {
38 struct host1x_job_gather *g = &job->gathers[i];
39 u32 op1 = host1x_opcode_gather(g->words);
40 u32 op2 = g->base + g->offset;
41 host1x_cdma_push(cdma, op1, op2);
42 }
43}
44
45static int channel_submit(struct host1x_job *job)
46{
47 struct host1x_channel *ch = job->channel;
48 struct host1x_syncpt *sp;
49 u32 user_syncpt_incrs = job->syncpt_incrs;
50 u32 prev_max = 0;
51 u32 syncval;
52 int err;
53 struct host1x_waitlist *completed_waiter = NULL;
54 struct host1x *host = dev_get_drvdata(ch->dev->parent);
55
56 sp = host->syncpt + job->syncpt_id;
57 trace_host1x_channel_submit(dev_name(ch->dev),
58 job->num_gathers, job->num_relocs,
59 job->num_waitchk, job->syncpt_id,
60 job->syncpt_incrs);
61
62 /* before error checks, return current max */
63 prev_max = job->syncpt_end = host1x_syncpt_read_max(sp);
64
65 /* get submit lock */
66 err = mutex_lock_interruptible(&ch->submitlock);
67 if (err)
68 goto error;
69
70 completed_waiter = kzalloc(sizeof(*completed_waiter), GFP_KERNEL);
71 if (!completed_waiter) {
72 mutex_unlock(&ch->submitlock);
73 err = -ENOMEM;
74 goto error;
75 }
76
77 /* begin a CDMA submit */
78 err = host1x_cdma_begin(&ch->cdma, job);
79 if (err) {
80 mutex_unlock(&ch->submitlock);
81 goto error;
82 }
83
84 if (job->serialize) {
85 /*
86 * Force serialization by inserting a host wait for the
87 * previous job to finish before this one can commence.
88 */
89 host1x_cdma_push(&ch->cdma,
90 host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
91 host1x_uclass_wait_syncpt_r(), 1),
92 host1x_class_host_wait_syncpt(job->syncpt_id,
93 host1x_syncpt_read_max(sp)));
94 }
95
96 syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs);
97
98 job->syncpt_end = syncval;
99
100 /* add a setclass for modules that require it */
101 if (job->class)
102 host1x_cdma_push(&ch->cdma,
103 host1x_opcode_setclass(job->class, 0, 0),
104 HOST1X_OPCODE_NOP);
105
106 submit_gathers(job);
107
108 /* end CDMA submit & stash pinned hMems into sync queue */
109 host1x_cdma_end(&ch->cdma, job);
110
111 trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval);
112
113 /* schedule a submit complete interrupt */
114 err = host1x_intr_add_action(host, job->syncpt_id, syncval,
115 HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch,
116 completed_waiter, NULL);
117 completed_waiter = NULL;
118 WARN(err, "Failed to set submit complete interrupt");
119
120 mutex_unlock(&ch->submitlock);
121
122 return 0;
123
124error:
125 kfree(completed_waiter);
126 return err;
127}
128
129static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
130 unsigned int index)
131{
132 ch->id = index;
133 mutex_init(&ch->reflock);
134 mutex_init(&ch->submitlock);
135
136 ch->regs = dev->regs + index * HOST1X_CHANNEL_SIZE;
137 return 0;
138}
139
140static const struct host1x_channel_ops host1x_channel_ops = {
141 .init = host1x_channel_init,
142 .submit = channel_submit,
143};
diff --git a/drivers/gpu/host1x/hw/host1x01.c b/drivers/gpu/host1x/hw/host1x01.c
index f5c35fa66d05..013ff381d825 100644
--- a/drivers/gpu/host1x/hw/host1x01.c
+++ b/drivers/gpu/host1x/hw/host1x01.c
@@ -21,6 +21,8 @@
21#include "hw/host1x01_hardware.h" 21#include "hw/host1x01_hardware.h"
22 22
23/* include code */ 23/* include code */
24#include "hw/cdma_hw.c"
25#include "hw/channel_hw.c"
24#include "hw/intr_hw.c" 26#include "hw/intr_hw.c"
25#include "hw/syncpt_hw.c" 27#include "hw/syncpt_hw.c"
26 28
@@ -28,6 +30,9 @@
28 30
29int host1x01_init(struct host1x *host) 31int host1x01_init(struct host1x *host)
30{ 32{
33 host->channel_op = &host1x_channel_ops;
34 host->cdma_op = &host1x_cdma_ops;
35 host->cdma_pb_op = &host1x_pushbuffer_ops;
31 host->syncpt_op = &host1x_syncpt_ops; 36 host->syncpt_op = &host1x_syncpt_ops;
32 host->intr_op = &host1x_intr_ops; 37 host->intr_op = &host1x_intr_ops;
33 38
diff --git a/drivers/gpu/host1x/hw/host1x01_hardware.h b/drivers/gpu/host1x/hw/host1x01_hardware.h
index 8cecbee7a270..5f0fb866efa8 100644
--- a/drivers/gpu/host1x/hw/host1x01_hardware.h
+++ b/drivers/gpu/host1x/hw/host1x01_hardware.h
@@ -22,6 +22,122 @@
22#include <linux/types.h> 22#include <linux/types.h>
23#include <linux/bitops.h> 23#include <linux/bitops.h>
24 24
25#include "hw_host1x01_channel.h"
25#include "hw_host1x01_sync.h" 26#include "hw_host1x01_sync.h"
27#include "hw_host1x01_uclass.h"
28
29static inline u32 host1x_class_host_wait_syncpt(
30 unsigned indx, unsigned threshold)
31{
32 return host1x_uclass_wait_syncpt_indx_f(indx)
33 | host1x_uclass_wait_syncpt_thresh_f(threshold);
34}
35
36static inline u32 host1x_class_host_load_syncpt_base(
37 unsigned indx, unsigned threshold)
38{
39 return host1x_uclass_load_syncpt_base_base_indx_f(indx)
40 | host1x_uclass_load_syncpt_base_value_f(threshold);
41}
42
43static inline u32 host1x_class_host_wait_syncpt_base(
44 unsigned indx, unsigned base_indx, unsigned offset)
45{
46 return host1x_uclass_wait_syncpt_base_indx_f(indx)
47 | host1x_uclass_wait_syncpt_base_base_indx_f(base_indx)
48 | host1x_uclass_wait_syncpt_base_offset_f(offset);
49}
50
51static inline u32 host1x_class_host_incr_syncpt_base(
52 unsigned base_indx, unsigned offset)
53{
54 return host1x_uclass_incr_syncpt_base_base_indx_f(base_indx)
55 | host1x_uclass_incr_syncpt_base_offset_f(offset);
56}
57
58static inline u32 host1x_class_host_incr_syncpt(
59 unsigned cond, unsigned indx)
60{
61 return host1x_uclass_incr_syncpt_cond_f(cond)
62 | host1x_uclass_incr_syncpt_indx_f(indx);
63}
64
65static inline u32 host1x_class_host_indoff_reg_write(
66 unsigned mod_id, unsigned offset, bool auto_inc)
67{
68 u32 v = host1x_uclass_indoff_indbe_f(0xf)
69 | host1x_uclass_indoff_indmodid_f(mod_id)
70 | host1x_uclass_indoff_indroffset_f(offset);
71 if (auto_inc)
72 v |= host1x_uclass_indoff_autoinc_f(1);
73 return v;
74}
75
76static inline u32 host1x_class_host_indoff_reg_read(
77 unsigned mod_id, unsigned offset, bool auto_inc)
78{
79 u32 v = host1x_uclass_indoff_indmodid_f(mod_id)
80 | host1x_uclass_indoff_indroffset_f(offset)
81 | host1x_uclass_indoff_rwn_read_v();
82 if (auto_inc)
83 v |= host1x_uclass_indoff_autoinc_f(1);
84 return v;
85}
86
87
88/* cdma opcodes */
89static inline u32 host1x_opcode_setclass(
90 unsigned class_id, unsigned offset, unsigned mask)
91{
92 return (0 << 28) | (offset << 16) | (class_id << 6) | mask;
93}
94
95static inline u32 host1x_opcode_incr(unsigned offset, unsigned count)
96{
97 return (1 << 28) | (offset << 16) | count;
98}
99
100static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count)
101{
102 return (2 << 28) | (offset << 16) | count;
103}
104
105static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask)
106{
107 return (3 << 28) | (offset << 16) | mask;
108}
109
110static inline u32 host1x_opcode_imm(unsigned offset, unsigned value)
111{
112 return (4 << 28) | (offset << 16) | value;
113}
114
115static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx)
116{
117 return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(),
118 host1x_class_host_incr_syncpt(cond, indx));
119}
120
121static inline u32 host1x_opcode_restart(unsigned address)
122{
123 return (5 << 28) | (address >> 4);
124}
125
126static inline u32 host1x_opcode_gather(unsigned count)
127{
128 return (6 << 28) | count;
129}
130
131static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count)
132{
133 return (6 << 28) | (offset << 16) | BIT(15) | count;
134}
135
136static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
137{
138 return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
139}
140
141#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
26 142
27#endif 143#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x01_channel.h b/drivers/gpu/host1x/hw/hw_host1x01_channel.h
new file mode 100644
index 000000000000..9ba133205668
--- /dev/null
+++ b/drivers/gpu/host1x/hw/hw_host1x01_channel.h
@@ -0,0 +1,102 @@
1/*
2 * Copyright (c) 2012-2013, NVIDIA Corporation.
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
18 /*
19 * Function naming determines intended use:
20 *
21 * <x>_r(void) : Returns the offset for register <x>.
22 *
23 * <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
24 *
25 * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
26 *
27 * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
28 * and masked to place it at field <y> of register <x>. This value
29 * can be |'d with others to produce a full register value for
30 * register <x>.
31 *
32 * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This
33 * value can be ~'d and then &'d to clear the value of field <y> for
34 * register <x>.
35 *
36 * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
37 * to place it at field <y> of register <x>. This value can be |'d
38 * with others to produce a full register value for <x>.
39 *
40 * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
41 * <x> value 'r' after being shifted to place its LSB at bit 0.
42 * This value is suitable for direct comparison with other unshifted
43 * values appropriate for use in field <y> of register <x>.
44 *
45 * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
46 * field <y> of register <x>. This value is suitable for direct
47 * comparison with unshifted values appropriate for use in field <y>
48 * of register <x>.
49 */
50
51#ifndef __hw_host1x_channel_host1x_h__
52#define __hw_host1x_channel_host1x_h__
53
54static inline u32 host1x_channel_dmastart_r(void)
55{
56 return 0x14;
57}
58#define HOST1X_CHANNEL_DMASTART \
59 host1x_channel_dmastart_r()
60static inline u32 host1x_channel_dmaput_r(void)
61{
62 return 0x18;
63}
64#define HOST1X_CHANNEL_DMAPUT \
65 host1x_channel_dmaput_r()
66static inline u32 host1x_channel_dmaget_r(void)
67{
68 return 0x1c;
69}
70#define HOST1X_CHANNEL_DMAGET \
71 host1x_channel_dmaget_r()
72static inline u32 host1x_channel_dmaend_r(void)
73{
74 return 0x20;
75}
76#define HOST1X_CHANNEL_DMAEND \
77 host1x_channel_dmaend_r()
78static inline u32 host1x_channel_dmactrl_r(void)
79{
80 return 0x24;
81}
82#define HOST1X_CHANNEL_DMACTRL \
83 host1x_channel_dmactrl_r()
84static inline u32 host1x_channel_dmactrl_dmastop(void)
85{
86 return 1 << 0;
87}
88#define HOST1X_CHANNEL_DMACTRL_DMASTOP \
89 host1x_channel_dmactrl_dmastop()
90static inline u32 host1x_channel_dmactrl_dmagetrst(void)
91{
92 return 1 << 1;
93}
94#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \
95 host1x_channel_dmactrl_dmagetrst()
96static inline u32 host1x_channel_dmactrl_dmainitget(void)
97{
98 return 1 << 2;
99}
100#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \
101 host1x_channel_dmactrl_dmainitget()
102#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x01_sync.h b/drivers/gpu/host1x/hw/hw_host1x01_sync.h
index eea0bb06052a..8f2a246c5426 100644
--- a/drivers/gpu/host1x/hw/hw_host1x01_sync.h
+++ b/drivers/gpu/host1x/hw/hw_host1x01_sync.h
@@ -77,6 +77,18 @@ static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id)
77} 77}
78#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \ 78#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \
79 host1x_sync_syncpt_thresh_int_enable_cpu0_r(id) 79 host1x_sync_syncpt_thresh_int_enable_cpu0_r(id)
80static inline u32 host1x_sync_cmdproc_stop_r(void)
81{
82 return 0xac;
83}
84#define HOST1X_SYNC_CMDPROC_STOP \
85 host1x_sync_cmdproc_stop_r()
86static inline u32 host1x_sync_ch_teardown_r(void)
87{
88 return 0xb0;
89}
90#define HOST1X_SYNC_CH_TEARDOWN \
91 host1x_sync_ch_teardown_r()
80static inline u32 host1x_sync_usec_clk_r(void) 92static inline u32 host1x_sync_usec_clk_r(void)
81{ 93{
82 return 0x1a4; 94 return 0x1a4;
diff --git a/drivers/gpu/host1x/hw/hw_host1x01_uclass.h b/drivers/gpu/host1x/hw/hw_host1x01_uclass.h
new file mode 100644
index 000000000000..7af660966ad6
--- /dev/null
+++ b/drivers/gpu/host1x/hw/hw_host1x01_uclass.h
@@ -0,0 +1,168 @@
1/*
2 * Copyright (c) 2012-2013, NVIDIA Corporation.
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
18 /*
19 * Function naming determines intended use:
20 *
21 * <x>_r(void) : Returns the offset for register <x>.
22 *
23 * <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
24 *
25 * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
26 *
27 * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
28 * and masked to place it at field <y> of register <x>. This value
29 * can be |'d with others to produce a full register value for
30 * register <x>.
31 *
32 * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This
33 * value can be ~'d and then &'d to clear the value of field <y> for
34 * register <x>.
35 *
36 * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
37 * to place it at field <y> of register <x>. This value can be |'d
38 * with others to produce a full register value for <x>.
39 *
40 * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
41 * <x> value 'r' after being shifted to place its LSB at bit 0.
42 * This value is suitable for direct comparison with other unshifted
43 * values appropriate for use in field <y> of register <x>.
44 *
45 * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
46 * field <y> of register <x>. This value is suitable for direct
47 * comparison with unshifted values appropriate for use in field <y>
48 * of register <x>.
49 */
50
51#ifndef __hw_host1x_uclass_host1x_h__
52#define __hw_host1x_uclass_host1x_h__
53
54static inline u32 host1x_uclass_incr_syncpt_r(void)
55{
56 return 0x0;
57}
58#define HOST1X_UCLASS_INCR_SYNCPT \
59 host1x_uclass_incr_syncpt_r()
60static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
61{
62 return (v & 0xff) << 8;
63}
64#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
65 host1x_uclass_incr_syncpt_cond_f(v)
66static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
67{
68 return (v & 0xff) << 0;
69}
70#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
71 host1x_uclass_incr_syncpt_indx_f(v)
72static inline u32 host1x_uclass_wait_syncpt_r(void)
73{
74 return 0x8;
75}
76#define HOST1X_UCLASS_WAIT_SYNCPT \
77 host1x_uclass_wait_syncpt_r()
78static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
79{
80 return (v & 0xff) << 24;
81}
82#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
83 host1x_uclass_wait_syncpt_indx_f(v)
84static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
85{
86 return (v & 0xffffff) << 0;
87}
88#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
89 host1x_uclass_wait_syncpt_thresh_f(v)
90static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
91{
92 return (v & 0xff) << 24;
93}
94#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
95 host1x_uclass_wait_syncpt_base_indx_f(v)
96static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
97{
98 return (v & 0xff) << 16;
99}
100#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
101 host1x_uclass_wait_syncpt_base_base_indx_f(v)
102static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
103{
104 return (v & 0xffff) << 0;
105}
106#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
107 host1x_uclass_wait_syncpt_base_offset_f(v)
108static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
109{
110 return (v & 0xff) << 24;
111}
112#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
113 host1x_uclass_load_syncpt_base_base_indx_f(v)
114static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
115{
116 return (v & 0xffffff) << 0;
117}
118#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
119 host1x_uclass_load_syncpt_base_value_f(v)
120static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
121{
122 return (v & 0xff) << 24;
123}
124#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
125 host1x_uclass_incr_syncpt_base_base_indx_f(v)
126static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
127{
128 return (v & 0xffffff) << 0;
129}
130#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
131 host1x_uclass_incr_syncpt_base_offset_f(v)
132static inline u32 host1x_uclass_indoff_r(void)
133{
134 return 0x2d;
135}
136#define HOST1X_UCLASS_INDOFF \
137 host1x_uclass_indoff_r()
138static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
139{
140 return (v & 0xf) << 28;
141}
142#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
143 host1x_uclass_indoff_indbe_f(v)
144static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
145{
146 return (v & 0x1) << 27;
147}
148#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
149 host1x_uclass_indoff_autoinc_f(v)
150static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
151{
152 return (v & 0xff) << 18;
153}
154#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
155 host1x_uclass_indoff_indmodid_f(v)
156static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
157{
158 return (v & 0xffff) << 2;
159}
160#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
161 host1x_uclass_indoff_indroffset_f(v)
162static inline u32 host1x_uclass_indoff_rwn_read_v(void)
163{
164 return 1;
165}
166#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
167 host1x_uclass_indoff_indroffset_f(v)
168#endif
diff --git a/drivers/gpu/host1x/hw/syncpt_hw.c b/drivers/gpu/host1x/hw/syncpt_hw.c
index 885b2578dc86..2c1f4af1094c 100644
--- a/drivers/gpu/host1x/hw/syncpt_hw.c
+++ b/drivers/gpu/host1x/hw/syncpt_hw.c
@@ -93,10 +93,21 @@ static void syncpt_cpu_incr(struct host1x_syncpt *sp)
93 wmb(); 93 wmb();
94} 94}
95 95
96/* remove a wait pointed to by patch_addr */
97static int syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr)
98{
99 u32 override = host1x_class_host_wait_syncpt(
100 HOST1X_SYNCPT_RESERVED, 0);
101
102 *((u32 *)patch_addr) = override;
103 return 0;
104}
105
96static const struct host1x_syncpt_ops host1x_syncpt_ops = { 106static const struct host1x_syncpt_ops host1x_syncpt_ops = {
97 .restore = syncpt_restore, 107 .restore = syncpt_restore,
98 .restore_wait_base = syncpt_restore_wait_base, 108 .restore_wait_base = syncpt_restore_wait_base,
99 .load_wait_base = syncpt_read_wait_base, 109 .load_wait_base = syncpt_read_wait_base,
100 .load = syncpt_load, 110 .load = syncpt_load,
101 .cpu_incr = syncpt_cpu_incr, 111 .cpu_incr = syncpt_cpu_incr,
112 .patch_wait = syncpt_patch_wait,
102}; 113};
diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c
index b1b5a80e3125..2491bf82e30c 100644
--- a/drivers/gpu/host1x/intr.c
+++ b/drivers/gpu/host1x/intr.c
@@ -21,6 +21,8 @@
21#include <linux/slab.h> 21#include <linux/slab.h>
22#include <linux/irq.h> 22#include <linux/irq.h>
23 23
24#include <trace/events/host1x.h>
25#include "channel.h"
24#include "dev.h" 26#include "dev.h"
25#include "intr.h" 27#include "intr.h"
26 28
@@ -66,7 +68,7 @@ static void remove_completed_waiters(struct list_head *head, u32 sync,
66 struct list_head completed[HOST1X_INTR_ACTION_COUNT]) 68 struct list_head completed[HOST1X_INTR_ACTION_COUNT])
67{ 69{
68 struct list_head *dest; 70 struct list_head *dest;
69 struct host1x_waitlist *waiter, *next; 71 struct host1x_waitlist *waiter, *next, *prev;
70 72
71 list_for_each_entry_safe(waiter, next, head, list) { 73 list_for_each_entry_safe(waiter, next, head, list) {
72 if ((s32)(waiter->thresh - sync) > 0) 74 if ((s32)(waiter->thresh - sync) > 0)
@@ -74,6 +76,17 @@ static void remove_completed_waiters(struct list_head *head, u32 sync,
74 76
75 dest = completed + waiter->action; 77 dest = completed + waiter->action;
76 78
79 /* consolidate submit cleanups */
80 if (waiter->action == HOST1X_INTR_ACTION_SUBMIT_COMPLETE &&
81 !list_empty(dest)) {
82 prev = list_entry(dest->prev,
83 struct host1x_waitlist, list);
84 if (prev->data == waiter->data) {
85 prev->count++;
86 dest = NULL;
87 }
88 }
89
77 /* PENDING->REMOVED or CANCELLED->HANDLED */ 90 /* PENDING->REMOVED or CANCELLED->HANDLED */
78 if (atomic_inc_return(&waiter->state) == WLS_HANDLED || !dest) { 91 if (atomic_inc_return(&waiter->state) == WLS_HANDLED || !dest) {
79 list_del(&waiter->list); 92 list_del(&waiter->list);
@@ -94,6 +107,18 @@ static void reset_threshold_interrupt(struct host1x *host,
94 host1x_hw_intr_enable_syncpt_intr(host, id); 107 host1x_hw_intr_enable_syncpt_intr(host, id);
95} 108}
96 109
110static void action_submit_complete(struct host1x_waitlist *waiter)
111{
112 struct host1x_channel *channel = waiter->data;
113
114 host1x_cdma_update(&channel->cdma);
115
116 /* Add nr_completed to trace */
117 trace_host1x_channel_submit_complete(dev_name(channel->dev),
118 waiter->count, waiter->thresh);
119
120}
121
97static void action_wakeup(struct host1x_waitlist *waiter) 122static void action_wakeup(struct host1x_waitlist *waiter)
98{ 123{
99 wait_queue_head_t *wq = waiter->data; 124 wait_queue_head_t *wq = waiter->data;
@@ -109,6 +134,7 @@ static void action_wakeup_interruptible(struct host1x_waitlist *waiter)
109typedef void (*action_handler)(struct host1x_waitlist *waiter); 134typedef void (*action_handler)(struct host1x_waitlist *waiter);
110 135
111static action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = { 136static action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = {
137 action_submit_complete,
112 action_wakeup, 138 action_wakeup,
113 action_wakeup_interruptible, 139 action_wakeup_interruptible,
114}; 140};
diff --git a/drivers/gpu/host1x/intr.h b/drivers/gpu/host1x/intr.h
index a3f06abe94bb..2b8adf016a05 100644
--- a/drivers/gpu/host1x/intr.h
+++ b/drivers/gpu/host1x/intr.h
@@ -26,6 +26,12 @@ struct host1x;
26 26
27enum host1x_intr_action { 27enum host1x_intr_action {
28 /* 28 /*
29 * Perform cleanup after a submit has completed.
30 * 'data' points to a channel
31 */
32 HOST1X_INTR_ACTION_SUBMIT_COMPLETE = 0,
33
34 /*
29 * Wake up a task. 35 * Wake up a task.
30 * 'data' points to a wait_queue_head_t 36 * 'data' points to a wait_queue_head_t
31 */ 37 */
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
new file mode 100644
index 000000000000..f665d679031c
--- /dev/null
+++ b/drivers/gpu/host1x/job.c
@@ -0,0 +1,603 @@
1/*
2 * Tegra host1x Job
3 *
4 * Copyright (c) 2010-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/dma-mapping.h>
20#include <linux/err.h>
21#include <linux/kref.h>
22#include <linux/module.h>
23#include <linux/scatterlist.h>
24#include <linux/slab.h>
25#include <linux/vmalloc.h>
26#include <trace/events/host1x.h>
27
28#include "channel.h"
29#include "dev.h"
30#include "host1x_bo.h"
31#include "job.h"
32#include "syncpt.h"
33
34struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
35 u32 num_cmdbufs, u32 num_relocs,
36 u32 num_waitchks)
37{
38 struct host1x_job *job = NULL;
39 unsigned int num_unpins = num_cmdbufs + num_relocs;
40 u64 total;
41 void *mem;
42
43 /* Check that we're not going to overflow */
44 total = sizeof(struct host1x_job) +
45 num_relocs * sizeof(struct host1x_reloc) +
46 num_unpins * sizeof(struct host1x_job_unpin_data) +
47 num_waitchks * sizeof(struct host1x_waitchk) +
48 num_cmdbufs * sizeof(struct host1x_job_gather) +
49 num_unpins * sizeof(dma_addr_t) +
50 num_unpins * sizeof(u32 *);
51 if (total > ULONG_MAX)
52 return NULL;
53
54 mem = job = kzalloc(total, GFP_KERNEL);
55 if (!job)
56 return NULL;
57
58 kref_init(&job->ref);
59 job->channel = ch;
60
61 /* Redistribute memory to the structs */
62 mem += sizeof(struct host1x_job);
63 job->relocarray = num_relocs ? mem : NULL;
64 mem += num_relocs * sizeof(struct host1x_reloc);
65 job->unpins = num_unpins ? mem : NULL;
66 mem += num_unpins * sizeof(struct host1x_job_unpin_data);
67 job->waitchk = num_waitchks ? mem : NULL;
68 mem += num_waitchks * sizeof(struct host1x_waitchk);
69 job->gathers = num_cmdbufs ? mem : NULL;
70 mem += num_cmdbufs * sizeof(struct host1x_job_gather);
71 job->addr_phys = num_unpins ? mem : NULL;
72
73 job->reloc_addr_phys = job->addr_phys;
74 job->gather_addr_phys = &job->addr_phys[num_relocs];
75
76 return job;
77}
78
79struct host1x_job *host1x_job_get(struct host1x_job *job)
80{
81 kref_get(&job->ref);
82 return job;
83}
84
85static void job_free(struct kref *ref)
86{
87 struct host1x_job *job = container_of(ref, struct host1x_job, ref);
88
89 kfree(job);
90}
91
92void host1x_job_put(struct host1x_job *job)
93{
94 kref_put(&job->ref, job_free);
95}
96
97void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo,
98 u32 words, u32 offset)
99{
100 struct host1x_job_gather *cur_gather = &job->gathers[job->num_gathers];
101
102 cur_gather->words = words;
103 cur_gather->bo = bo;
104 cur_gather->offset = offset;
105 job->num_gathers++;
106}
107
108/*
109 * NULL an already satisfied WAIT_SYNCPT host method, by patching its
110 * args in the command stream. The method data is changed to reference
111 * a reserved (never given out or incr) HOST1X_SYNCPT_RESERVED syncpt
112 * with a matching threshold value of 0, so is guaranteed to be popped
113 * by the host HW.
114 */
115static void host1x_syncpt_patch_offset(struct host1x_syncpt *sp,
116 struct host1x_bo *h, u32 offset)
117{
118 void *patch_addr = NULL;
119
120 /* patch the wait */
121 patch_addr = host1x_bo_kmap(h, offset >> PAGE_SHIFT);
122 if (patch_addr) {
123 host1x_syncpt_patch_wait(sp,
124 patch_addr + (offset & ~PAGE_MASK));
125 host1x_bo_kunmap(h, offset >> PAGE_SHIFT, patch_addr);
126 } else
127 pr_err("Could not map cmdbuf for wait check\n");
128}
129
130/*
131 * Check driver supplied waitchk structs for syncpt thresholds
132 * that have already been satisfied and NULL the comparison (to
133 * avoid a wrap condition in the HW).
134 */
135static int do_waitchks(struct host1x_job *job, struct host1x *host,
136 struct host1x_bo *patch)
137{
138 int i;
139
140 /* compare syncpt vs wait threshold */
141 for (i = 0; i < job->num_waitchk; i++) {
142 struct host1x_waitchk *wait = &job->waitchk[i];
143 struct host1x_syncpt *sp =
144 host1x_syncpt_get(host, wait->syncpt_id);
145
146 /* validate syncpt id */
147 if (wait->syncpt_id > host1x_syncpt_nb_pts(host))
148 continue;
149
150 /* skip all other gathers */
151 if (patch != wait->bo)
152 continue;
153
154 trace_host1x_syncpt_wait_check(wait->bo, wait->offset,
155 wait->syncpt_id, wait->thresh,
156 host1x_syncpt_read_min(sp));
157
158 if (host1x_syncpt_is_expired(sp, wait->thresh)) {
159 dev_dbg(host->dev,
160 "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n",
161 wait->syncpt_id, sp->name, wait->thresh,
162 host1x_syncpt_read_min(sp));
163
164 host1x_syncpt_patch_offset(sp, patch, wait->offset);
165 }
166
167 wait->bo = NULL;
168 }
169
170 return 0;
171}
172
173static unsigned int pin_job(struct host1x_job *job)
174{
175 unsigned int i;
176
177 job->num_unpins = 0;
178
179 for (i = 0; i < job->num_relocs; i++) {
180 struct host1x_reloc *reloc = &job->relocarray[i];
181 struct sg_table *sgt;
182 dma_addr_t phys_addr;
183
184 reloc->target = host1x_bo_get(reloc->target);
185 if (!reloc->target)
186 goto unpin;
187
188 phys_addr = host1x_bo_pin(reloc->target, &sgt);
189 if (!phys_addr)
190 goto unpin;
191
192 job->addr_phys[job->num_unpins] = phys_addr;
193 job->unpins[job->num_unpins].bo = reloc->target;
194 job->unpins[job->num_unpins].sgt = sgt;
195 job->num_unpins++;
196 }
197
198 for (i = 0; i < job->num_gathers; i++) {
199 struct host1x_job_gather *g = &job->gathers[i];
200 struct sg_table *sgt;
201 dma_addr_t phys_addr;
202
203 g->bo = host1x_bo_get(g->bo);
204 if (!g->bo)
205 goto unpin;
206
207 phys_addr = host1x_bo_pin(g->bo, &sgt);
208 if (!phys_addr)
209 goto unpin;
210
211 job->addr_phys[job->num_unpins] = phys_addr;
212 job->unpins[job->num_unpins].bo = g->bo;
213 job->unpins[job->num_unpins].sgt = sgt;
214 job->num_unpins++;
215 }
216
217 return job->num_unpins;
218
219unpin:
220 host1x_job_unpin(job);
221 return 0;
222}
223
224static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
225{
226 int i = 0;
227 u32 last_page = ~0;
228 void *cmdbuf_page_addr = NULL;
229
230 /* pin & patch the relocs for one gather */
231 while (i < job->num_relocs) {
232 struct host1x_reloc *reloc = &job->relocarray[i];
233 u32 reloc_addr = (job->reloc_addr_phys[i] +
234 reloc->target_offset) >> reloc->shift;
235 u32 *target;
236
237 /* skip all other gathers */
238 if (!(reloc->cmdbuf && cmdbuf == reloc->cmdbuf)) {
239 i++;
240 continue;
241 }
242
243 if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) {
244 if (cmdbuf_page_addr)
245 host1x_bo_kunmap(cmdbuf, last_page,
246 cmdbuf_page_addr);
247
248 cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
249 reloc->cmdbuf_offset >> PAGE_SHIFT);
250 last_page = reloc->cmdbuf_offset >> PAGE_SHIFT;
251
252 if (unlikely(!cmdbuf_page_addr)) {
253 pr_err("Could not map cmdbuf for relocation\n");
254 return -ENOMEM;
255 }
256 }
257
258 target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK);
259 *target = reloc_addr;
260
261 /* mark this gather as handled */
262 reloc->cmdbuf = 0;
263 }
264
265 if (cmdbuf_page_addr)
266 host1x_bo_kunmap(cmdbuf, last_page, cmdbuf_page_addr);
267
268 return 0;
269}
270
271static int check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
272 unsigned int offset)
273{
274 offset *= sizeof(u32);
275
276 if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset)
277 return -EINVAL;
278
279 return 0;
280}
281
282struct host1x_firewall {
283 struct host1x_job *job;
284 struct device *dev;
285
286 unsigned int num_relocs;
287 struct host1x_reloc *reloc;
288
289 struct host1x_bo *cmdbuf_id;
290 unsigned int offset;
291
292 u32 words;
293 u32 class;
294 u32 reg;
295 u32 mask;
296 u32 count;
297};
298
299static int check_mask(struct host1x_firewall *fw)
300{
301 u32 mask = fw->mask;
302 u32 reg = fw->reg;
303
304 while (mask) {
305 if (fw->words == 0)
306 return -EINVAL;
307
308 if (mask & 1) {
309 if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
310 bool bad_reloc = check_reloc(fw->reloc,
311 fw->cmdbuf_id,
312 fw->offset);
313 if (!fw->num_relocs || bad_reloc)
314 return -EINVAL;
315 fw->reloc++;
316 fw->num_relocs--;
317 }
318 fw->words--;
319 fw->offset++;
320 }
321 mask >>= 1;
322 reg++;
323 }
324
325 return 0;
326}
327
328static int check_incr(struct host1x_firewall *fw)
329{
330 u32 count = fw->count;
331 u32 reg = fw->reg;
332
333 while (fw) {
334 if (fw->words == 0)
335 return -EINVAL;
336
337 if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
338 bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id,
339 fw->offset);
340 if (!fw->num_relocs || bad_reloc)
341 return -EINVAL;
342 fw->reloc++;
343 fw->num_relocs--;
344 }
345 reg++;
346 fw->words--;
347 fw->offset++;
348 count--;
349 }
350
351 return 0;
352}
353
354static int check_nonincr(struct host1x_firewall *fw)
355{
356 int is_addr_reg = fw->job->is_addr_reg(fw->dev, fw->class, fw->reg);
357 u32 count = fw->count;
358
359 while (count) {
360 if (fw->words == 0)
361 return -EINVAL;
362
363 if (is_addr_reg) {
364 bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id,
365 fw->offset);
366 if (!fw->num_relocs || bad_reloc)
367 return -EINVAL;
368 fw->reloc++;
369 fw->num_relocs--;
370 }
371 fw->words--;
372 fw->offset++;
373 count--;
374 }
375
376 return 0;
377}
378
379static int validate(struct host1x_job *job, struct device *dev,
380 struct host1x_job_gather *g)
381{
382 u32 *cmdbuf_base;
383 int err = 0;
384 struct host1x_firewall fw;
385
386 fw.job = job;
387 fw.dev = dev;
388 fw.reloc = job->relocarray;
389 fw.num_relocs = job->num_relocs;
390 fw.cmdbuf_id = g->bo;
391
392 fw.offset = 0;
393 fw.class = 0;
394
395 if (!job->is_addr_reg)
396 return 0;
397
398 cmdbuf_base = host1x_bo_mmap(g->bo);
399 if (!cmdbuf_base)
400 return -ENOMEM;
401
402 fw.words = g->words;
403 while (fw.words && !err) {
404 u32 word = cmdbuf_base[fw.offset];
405 u32 opcode = (word & 0xf0000000) >> 28;
406
407 fw.mask = 0;
408 fw.reg = 0;
409 fw.count = 0;
410 fw.words--;
411 fw.offset++;
412
413 switch (opcode) {
414 case 0:
415 fw.class = word >> 6 & 0x3ff;
416 fw.mask = word & 0x3f;
417 fw.reg = word >> 16 & 0xfff;
418 err = check_mask(&fw);
419 if (err)
420 goto out;
421 break;
422 case 1:
423 fw.reg = word >> 16 & 0xfff;
424 fw.count = word & 0xffff;
425 err = check_incr(&fw);
426 if (err)
427 goto out;
428 break;
429
430 case 2:
431 fw.reg = word >> 16 & 0xfff;
432 fw.count = word & 0xffff;
433 err = check_nonincr(&fw);
434 if (err)
435 goto out;
436 break;
437
438 case 3:
439 fw.mask = word & 0xffff;
440 fw.reg = word >> 16 & 0xfff;
441 err = check_mask(&fw);
442 if (err)
443 goto out;
444 break;
445 case 4:
446 case 5:
447 case 14:
448 break;
449 default:
450 err = -EINVAL;
451 break;
452 }
453 }
454
455 /* No relocs should remain at this point */
456 if (fw.num_relocs)
457 err = -EINVAL;
458
459out:
460 host1x_bo_munmap(g->bo, cmdbuf_base);
461
462 return err;
463}
464
465static inline int copy_gathers(struct host1x_job *job, struct device *dev)
466{
467 size_t size = 0;
468 size_t offset = 0;
469 int i;
470
471 for (i = 0; i < job->num_gathers; i++) {
472 struct host1x_job_gather *g = &job->gathers[i];
473 size += g->words * sizeof(u32);
474 }
475
476 job->gather_copy_mapped = dma_alloc_writecombine(dev, size,
477 &job->gather_copy,
478 GFP_KERNEL);
479 if (!job->gather_copy_mapped) {
480 int err = PTR_ERR(job->gather_copy_mapped);
481 job->gather_copy_mapped = NULL;
482 return err;
483 }
484
485 job->gather_copy_size = size;
486
487 for (i = 0; i < job->num_gathers; i++) {
488 struct host1x_job_gather *g = &job->gathers[i];
489 void *gather;
490
491 gather = host1x_bo_mmap(g->bo);
492 memcpy(job->gather_copy_mapped + offset, gather + g->offset,
493 g->words * sizeof(u32));
494 host1x_bo_munmap(g->bo, gather);
495
496 g->base = job->gather_copy;
497 g->offset = offset;
498 g->bo = NULL;
499
500 offset += g->words * sizeof(u32);
501 }
502
503 return 0;
504}
505
506int host1x_job_pin(struct host1x_job *job, struct device *dev)
507{
508 int err;
509 unsigned int i, j;
510 struct host1x *host = dev_get_drvdata(dev->parent);
511 DECLARE_BITMAP(waitchk_mask, host1x_syncpt_nb_pts(host));
512
513 bitmap_zero(waitchk_mask, host1x_syncpt_nb_pts(host));
514 for (i = 0; i < job->num_waitchk; i++) {
515 u32 syncpt_id = job->waitchk[i].syncpt_id;
516 if (syncpt_id < host1x_syncpt_nb_pts(host))
517 set_bit(syncpt_id, waitchk_mask);
518 }
519
520 /* get current syncpt values for waitchk */
521 for_each_set_bit(i, waitchk_mask, host1x_syncpt_nb_pts(host))
522 host1x_syncpt_load(host->syncpt + i);
523
524 /* pin memory */
525 err = pin_job(job);
526 if (!err)
527 goto out;
528
529 /* patch gathers */
530 for (i = 0; i < job->num_gathers; i++) {
531 struct host1x_job_gather *g = &job->gathers[i];
532
533 /* process each gather mem only once */
534 if (g->handled)
535 continue;
536
537 g->base = job->gather_addr_phys[i];
538
539 for (j = 0; j < job->num_gathers; j++)
540 if (job->gathers[j].bo == g->bo)
541 job->gathers[j].handled = true;
542
543 err = 0;
544
545 if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
546 err = validate(job, dev, g);
547
548 if (err)
549 dev_err(dev, "Job invalid (err=%d)\n", err);
550
551 if (!err)
552 err = do_relocs(job, g->bo);
553
554 if (!err)
555 err = do_waitchks(job, host, g->bo);
556
557 if (err)
558 break;
559 }
560
561 if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && !err) {
562 err = copy_gathers(job, dev);
563 if (err) {
564 host1x_job_unpin(job);
565 return err;
566 }
567 }
568
569out:
570 wmb();
571
572 return err;
573}
574
575void host1x_job_unpin(struct host1x_job *job)
576{
577 unsigned int i;
578
579 for (i = 0; i < job->num_unpins; i++) {
580 struct host1x_job_unpin_data *unpin = &job->unpins[i];
581 host1x_bo_unpin(unpin->bo, unpin->sgt);
582 host1x_bo_put(unpin->bo);
583 }
584 job->num_unpins = 0;
585
586 if (job->gather_copy_size)
587 dma_free_writecombine(job->channel->dev, job->gather_copy_size,
588 job->gather_copy_mapped,
589 job->gather_copy);
590}
591
592/*
593 * Debug routine used to dump job entries
594 */
595void host1x_job_dump(struct device *dev, struct host1x_job *job)
596{
597 dev_dbg(dev, " SYNCPT_ID %d\n", job->syncpt_id);
598 dev_dbg(dev, " SYNCPT_VAL %d\n", job->syncpt_end);
599 dev_dbg(dev, " FIRST_GET 0x%x\n", job->first_get);
600 dev_dbg(dev, " TIMEOUT %d\n", job->timeout);
601 dev_dbg(dev, " NUM_SLOTS %d\n", job->num_slots);
602 dev_dbg(dev, " NUM_HANDLES %d\n", job->num_unpins);
603}
diff --git a/drivers/gpu/host1x/job.h b/drivers/gpu/host1x/job.h
new file mode 100644
index 000000000000..fba45f20458e
--- /dev/null
+++ b/drivers/gpu/host1x/job.h
@@ -0,0 +1,162 @@
1/*
2 * Tegra host1x Job
3 *
4 * Copyright (c) 2011-2013, NVIDIA Corporation.
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
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __HOST1X_JOB_H
20#define __HOST1X_JOB_H
21
22struct host1x_job_gather {
23 u32 words;
24 dma_addr_t base;
25 struct host1x_bo *bo;
26 int offset;
27 bool handled;
28};
29
30struct host1x_cmdbuf {
31 u32 handle;
32 u32 offset;
33 u32 words;
34 u32 pad;
35};
36
37struct host1x_reloc {
38 struct host1x_bo *cmdbuf;
39 u32 cmdbuf_offset;
40 struct host1x_bo *target;
41 u32 target_offset;
42 u32 shift;
43 u32 pad;
44};
45
46struct host1x_waitchk {
47 struct host1x_bo *bo;
48 u32 offset;
49 u32 syncpt_id;
50 u32 thresh;
51};
52
53struct host1x_job_unpin_data {
54 struct host1x_bo *bo;
55 struct sg_table *sgt;
56};
57
58/*
59 * Each submit is tracked as a host1x_job.
60 */
61struct host1x_job {
62 /* When refcount goes to zero, job can be freed */
63 struct kref ref;
64
65 /* List entry */
66 struct list_head list;
67
68 /* Channel where job is submitted to */
69 struct host1x_channel *channel;
70
71 u32 client;
72
73 /* Gathers and their memory */
74 struct host1x_job_gather *gathers;
75 unsigned int num_gathers;
76
77 /* Wait checks to be processed at submit time */
78 struct host1x_waitchk *waitchk;
79 unsigned int num_waitchk;
80 u32 waitchk_mask;
81
82 /* Array of handles to be pinned & unpinned */
83 struct host1x_reloc *relocarray;
84 unsigned int num_relocs;
85 struct host1x_job_unpin_data *unpins;
86 unsigned int num_unpins;
87
88 dma_addr_t *addr_phys;
89 dma_addr_t *gather_addr_phys;
90 dma_addr_t *reloc_addr_phys;
91
92 /* Sync point id, number of increments and end related to the submit */
93 u32 syncpt_id;
94 u32 syncpt_incrs;
95 u32 syncpt_end;
96
97 /* Maximum time to wait for this job */
98 unsigned int timeout;
99
100 /* Index and number of slots used in the push buffer */
101 unsigned int first_get;
102 unsigned int num_slots;
103
104 /* Copy of gathers */
105 size_t gather_copy_size;
106 dma_addr_t gather_copy;
107 u8 *gather_copy_mapped;
108
109 /* Check if register is marked as an address reg */
110 int (*is_addr_reg)(struct device *dev, u32 reg, u32 class);
111
112 /* Request a SETCLASS to this class */
113 u32 class;
114
115 /* Add a channel wait for previous ops to complete */
116 bool serialize;
117};
118/*
119 * Allocate memory for a job. Just enough memory will be allocated to
120 * accomodate the submit.
121 */
122struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
123 u32 num_cmdbufs, u32 num_relocs,
124 u32 num_waitchks);
125
126/*
127 * Add a gather to a job.
128 */
129void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id,
130 u32 words, u32 offset);
131
132/*
133 * Increment reference going to host1x_job.
134 */
135struct host1x_job *host1x_job_get(struct host1x_job *job);
136
137/*
138 * Decrement reference job, free if goes to zero.
139 */
140void host1x_job_put(struct host1x_job *job);
141
142/*
143 * Pin memory related to job. This handles relocation of addresses to the
144 * host1x address space. Handles both the gather memory and any other memory
145 * referred to from the gather buffers.
146 *
147 * Handles also patching out host waits that would wait for an expired sync
148 * point value.
149 */
150int host1x_job_pin(struct host1x_job *job, struct device *dev);
151
152/*
153 * Unpin memory related to job.
154 */
155void host1x_job_unpin(struct host1x_job *job);
156
157/*
158 * Dump contents of job to debug output.
159 */
160void host1x_job_dump(struct device *dev, struct host1x_job *job);
161
162#endif
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index 07fad1412bab..7e77e63da57b 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -300,6 +300,12 @@ bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
300 return (s32)(current_val - thresh) >= 0; 300 return (s32)(current_val - thresh) >= 0;
301} 301}
302 302
303/* remove a wait pointed to by patch_addr */
304int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr)
305{
306 return host1x_hw_syncpt_patch_wait(sp->host, sp, patch_addr);
307}
308
303int host1x_syncpt_init(struct host1x *host) 309int host1x_syncpt_init(struct host1x *host)
304{ 310{
305 struct host1x_syncpt *syncpt; 311 struct host1x_syncpt *syncpt;
@@ -319,6 +325,11 @@ int host1x_syncpt_init(struct host1x *host)
319 325
320 host1x_syncpt_restore(host); 326 host1x_syncpt_restore(host);
321 327
328 /* Allocate sync point to use for clearing waits for expired fences */
329 host->nop_sp = _host1x_syncpt_alloc(host, NULL, 0);
330 if (!host->nop_sp)
331 return -ENOMEM;
332
322 return 0; 333 return 0;
323} 334}
324 335
diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h
index 17c1616de100..c99806130f2e 100644
--- a/drivers/gpu/host1x/syncpt.h
+++ b/drivers/gpu/host1x/syncpt.h
@@ -27,6 +27,9 @@
27 27
28struct host1x; 28struct host1x;
29 29
30/* Reserved for replacing an expired wait with a NOP */
31#define HOST1X_SYNCPT_RESERVED 0
32
30struct host1x_syncpt { 33struct host1x_syncpt {
31 int id; 34 int id;
32 atomic_t min_val; 35 atomic_t min_val;
@@ -146,6 +149,9 @@ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
146 return sp->id < host1x_syncpt_nb_pts(sp->host); 149 return sp->id < host1x_syncpt_nb_pts(sp->host);
147} 150}
148 151
152/* Patch a wait by replacing it with a wait for syncpt 0 value 0 */
153int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr);
154
149/* Return id of the sync point */ 155/* Return id of the sync point */
150u32 host1x_syncpt_id(struct host1x_syncpt *sp); 156u32 host1x_syncpt_id(struct host1x_syncpt *sp);
151 157
diff --git a/include/trace/events/host1x.h b/include/trace/events/host1x.h
index 4bd2b0253d27..94db6a2c3540 100644
--- a/include/trace/events/host1x.h
+++ b/include/trace/events/host1x.h
@@ -37,6 +37,171 @@ DECLARE_EVENT_CLASS(host1x,
37 TP_printk("name=%s", __entry->name) 37 TP_printk("name=%s", __entry->name)
38); 38);
39 39
40DEFINE_EVENT(host1x, host1x_channel_open,
41 TP_PROTO(const char *name),
42 TP_ARGS(name)
43);
44
45DEFINE_EVENT(host1x, host1x_channel_release,
46 TP_PROTO(const char *name),
47 TP_ARGS(name)
48);
49
50DEFINE_EVENT(host1x, host1x_cdma_begin,
51 TP_PROTO(const char *name),
52 TP_ARGS(name)
53);
54
55DEFINE_EVENT(host1x, host1x_cdma_end,
56 TP_PROTO(const char *name),
57 TP_ARGS(name)
58);
59
60TRACE_EVENT(host1x_cdma_push,
61 TP_PROTO(const char *name, u32 op1, u32 op2),
62
63 TP_ARGS(name, op1, op2),
64
65 TP_STRUCT__entry(
66 __field(const char *, name)
67 __field(u32, op1)
68 __field(u32, op2)
69 ),
70
71 TP_fast_assign(
72 __entry->name = name;
73 __entry->op1 = op1;
74 __entry->op2 = op2;
75 ),
76
77 TP_printk("name=%s, op1=%08x, op2=%08x",
78 __entry->name, __entry->op1, __entry->op2)
79);
80
81TRACE_EVENT(host1x_cdma_push_gather,
82 TP_PROTO(const char *name, u32 mem_id,
83 u32 words, u32 offset, void *cmdbuf),
84
85 TP_ARGS(name, mem_id, words, offset, cmdbuf),
86
87 TP_STRUCT__entry(
88 __field(const char *, name)
89 __field(u32, mem_id)
90 __field(u32, words)
91 __field(u32, offset)
92 __field(bool, cmdbuf)
93 __dynamic_array(u32, cmdbuf, words)
94 ),
95
96 TP_fast_assign(
97 if (cmdbuf) {
98 memcpy(__get_dynamic_array(cmdbuf), cmdbuf+offset,
99 words * sizeof(u32));
100 }
101 __entry->cmdbuf = cmdbuf;
102 __entry->name = name;
103 __entry->mem_id = mem_id;
104 __entry->words = words;
105 __entry->offset = offset;
106 ),
107
108 TP_printk("name=%s, mem_id=%08x, words=%u, offset=%d, contents=[%s]",
109 __entry->name, __entry->mem_id,
110 __entry->words, __entry->offset,
111 __print_hex(__get_dynamic_array(cmdbuf),
112 __entry->cmdbuf ? __entry->words * 4 : 0))
113);
114
115TRACE_EVENT(host1x_channel_submit,
116 TP_PROTO(const char *name, u32 cmdbufs, u32 relocs, u32 waitchks,
117 u32 syncpt_id, u32 syncpt_incrs),
118
119 TP_ARGS(name, cmdbufs, relocs, waitchks, syncpt_id, syncpt_incrs),
120
121 TP_STRUCT__entry(
122 __field(const char *, name)
123 __field(u32, cmdbufs)
124 __field(u32, relocs)
125 __field(u32, waitchks)
126 __field(u32, syncpt_id)
127 __field(u32, syncpt_incrs)
128 ),
129
130 TP_fast_assign(
131 __entry->name = name;
132 __entry->cmdbufs = cmdbufs;
133 __entry->relocs = relocs;
134 __entry->waitchks = waitchks;
135 __entry->syncpt_id = syncpt_id;
136 __entry->syncpt_incrs = syncpt_incrs;
137 ),
138
139 TP_printk("name=%s, cmdbufs=%u, relocs=%u, waitchks=%d,"
140 "syncpt_id=%u, syncpt_incrs=%u",
141 __entry->name, __entry->cmdbufs, __entry->relocs, __entry->waitchks,
142 __entry->syncpt_id, __entry->syncpt_incrs)
143);
144
145TRACE_EVENT(host1x_channel_submitted,
146 TP_PROTO(const char *name, u32 syncpt_base, u32 syncpt_max),
147
148 TP_ARGS(name, syncpt_base, syncpt_max),
149
150 TP_STRUCT__entry(
151 __field(const char *, name)
152 __field(u32, syncpt_base)
153 __field(u32, syncpt_max)
154 ),
155
156 TP_fast_assign(
157 __entry->name = name;
158 __entry->syncpt_base = syncpt_base;
159 __entry->syncpt_max = syncpt_max;
160 ),
161
162 TP_printk("name=%s, syncpt_base=%d, syncpt_max=%d",
163 __entry->name, __entry->syncpt_base, __entry->syncpt_max)
164);
165
166TRACE_EVENT(host1x_channel_submit_complete,
167 TP_PROTO(const char *name, int count, u32 thresh),
168
169 TP_ARGS(name, count, thresh),
170
171 TP_STRUCT__entry(
172 __field(const char *, name)
173 __field(int, count)
174 __field(u32, thresh)
175 ),
176
177 TP_fast_assign(
178 __entry->name = name;
179 __entry->count = count;
180 __entry->thresh = thresh;
181 ),
182
183 TP_printk("name=%s, count=%d, thresh=%d",
184 __entry->name, __entry->count, __entry->thresh)
185);
186
187TRACE_EVENT(host1x_wait_cdma,
188 TP_PROTO(const char *name, u32 eventid),
189
190 TP_ARGS(name, eventid),
191
192 TP_STRUCT__entry(
193 __field(const char *, name)
194 __field(u32, eventid)
195 ),
196
197 TP_fast_assign(
198 __entry->name = name;
199 __entry->eventid = eventid;
200 ),
201
202 TP_printk("name=%s, event=%d", __entry->name, __entry->eventid)
203);
204
40TRACE_EVENT(host1x_syncpt_load_min, 205TRACE_EVENT(host1x_syncpt_load_min,
41 TP_PROTO(u32 id, u32 val), 206 TP_PROTO(u32 id, u32 val),
42 207
@@ -55,6 +220,33 @@ TRACE_EVENT(host1x_syncpt_load_min,
55 TP_printk("id=%d, val=%d", __entry->id, __entry->val) 220 TP_printk("id=%d, val=%d", __entry->id, __entry->val)
56); 221);
57 222
223TRACE_EVENT(host1x_syncpt_wait_check,
224 TP_PROTO(void *mem_id, u32 offset, u32 syncpt_id, u32 thresh, u32 min),
225
226 TP_ARGS(mem_id, offset, syncpt_id, thresh, min),
227
228 TP_STRUCT__entry(
229 __field(void *, mem_id)
230 __field(u32, offset)
231 __field(u32, syncpt_id)
232 __field(u32, thresh)
233 __field(u32, min)
234 ),
235
236 TP_fast_assign(
237 __entry->mem_id = mem_id;
238 __entry->offset = offset;
239 __entry->syncpt_id = syncpt_id;
240 __entry->thresh = thresh;
241 __entry->min = min;
242 ),
243
244 TP_printk("mem_id=%p, offset=%05x, id=%d, thresh=%d, current=%d",
245 __entry->mem_id, __entry->offset,
246 __entry->syncpt_id, __entry->thresh,
247 __entry->min)
248);
249
58#endif /* _TRACE_HOST1X_H */ 250#endif /* _TRACE_HOST1X_H */
59 251
60/* This part must be outside protection */ 252/* This part must be outside protection */