blob: f26177ac27e73f5e85c849959ff63e4ec0b6b57c [file] [log] [blame]
Ben Skeggs6ee73862009-12-11 19:24:15 +10001/*
2 * Copyright (C) 2007 Ben Skeggs.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#include "drmP.h"
28#include "drm.h"
29
Marcin Slusarzbd35fe52011-03-09 14:22:19 +010030#include <linux/ktime.h>
31#include <linux/hrtimer.h>
32
Ben Skeggs6ee73862009-12-11 19:24:15 +100033#include "nouveau_drv.h"
Francisco Jerez0c6c1c22010-09-22 00:58:54 +020034#include "nouveau_ramht.h"
Ben Skeggs20abd162012-04-30 11:33:43 -050035#include "nouveau_software.h"
Ben Skeggs6ee73862009-12-11 19:24:15 +100036#include "nouveau_dma.h"
37
Francisco Jerez27307232010-09-21 18:57:11 +020038#define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10)
Ben Skeggscb1d7712011-01-28 13:44:32 +100039#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17)
Ben Skeggs6ee73862009-12-11 19:24:15 +100040
41struct nouveau_fence {
42 struct nouveau_channel *channel;
43 struct kref refcount;
44 struct list_head entry;
45
46 uint32_t sequence;
47 bool signalled;
Marcin Slusarz695b95b2012-04-25 23:20:33 +020048 unsigned long timeout;
Francisco Jerez8ac38912010-09-21 20:49:39 +020049
50 void (*work)(void *priv, bool signalled);
51 void *priv;
Ben Skeggs6ee73862009-12-11 19:24:15 +100052};
53
Francisco Jerez0c6c1c22010-09-22 00:58:54 +020054struct nouveau_semaphore {
55 struct kref ref;
56 struct drm_device *dev;
57 struct drm_mm_node *mem;
58};
59
Ben Skeggs6ee73862009-12-11 19:24:15 +100060static inline struct nouveau_fence *
61nouveau_fence(void *sync_obj)
62{
63 return (struct nouveau_fence *)sync_obj;
64}
65
66static void
67nouveau_fence_del(struct kref *ref)
68{
69 struct nouveau_fence *fence =
70 container_of(ref, struct nouveau_fence, refcount);
71
Francisco Jerez2a6789a2010-10-18 03:56:14 +020072 nouveau_channel_ref(NULL, &fence->channel);
Ben Skeggs6ee73862009-12-11 19:24:15 +100073 kfree(fence);
74}
75
76void
77nouveau_fence_update(struct nouveau_channel *chan)
78{
Francisco Jerez27307232010-09-21 18:57:11 +020079 struct drm_device *dev = chan->dev;
80 struct nouveau_fence *tmp, *fence;
Ben Skeggs6ee73862009-12-11 19:24:15 +100081 uint32_t sequence;
82
Francisco Jerez3ba64622010-08-28 17:56:33 +020083 spin_lock(&chan->fence.lock);
84
Francisco Jerez937c3472010-12-08 02:35:45 +010085 /* Fetch the last sequence if the channel is still up and running */
86 if (likely(!list_empty(&chan->fence.pending))) {
87 if (USE_REFCNT(dev))
88 sequence = nvchan_rd32(chan, 0x48);
89 else
90 sequence = atomic_read(&chan->fence.last_sequence_irq);
Ben Skeggs6ee73862009-12-11 19:24:15 +100091
Francisco Jerez937c3472010-12-08 02:35:45 +010092 if (chan->fence.sequence_ack == sequence)
93 goto out;
94 chan->fence.sequence_ack = sequence;
95 }
Ben Skeggs6ee73862009-12-11 19:24:15 +100096
Francisco Jerez27307232010-09-21 18:57:11 +020097 list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
Ben Skeggsb08abd42012-03-21 13:51:03 +100098 if (fence->sequence > chan->fence.sequence_ack)
99 break;
100
Ben Skeggs6ee73862009-12-11 19:24:15 +1000101 fence->signalled = true;
102 list_del(&fence->entry);
Ben Skeggsb08abd42012-03-21 13:51:03 +1000103 if (fence->work)
Francisco Jerez8ac38912010-09-21 20:49:39 +0200104 fence->work(fence->priv, true);
105
Ben Skeggs6ee73862009-12-11 19:24:15 +1000106 kref_put(&fence->refcount, nouveau_fence_del);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000107 }
Ben Skeggsb08abd42012-03-21 13:51:03 +1000108
Francisco Jerez3ba64622010-08-28 17:56:33 +0200109out:
Ben Skeggs047d1d32010-05-31 12:00:43 +1000110 spin_unlock(&chan->fence.lock);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000111}
112
113int
114nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,
115 bool emit)
116{
117 struct nouveau_fence *fence;
118 int ret = 0;
119
120 fence = kzalloc(sizeof(*fence), GFP_KERNEL);
121 if (!fence)
122 return -ENOMEM;
123 kref_init(&fence->refcount);
Francisco Jerez2a6789a2010-10-18 03:56:14 +0200124 nouveau_channel_ref(chan, &fence->channel);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000125
126 if (emit)
127 ret = nouveau_fence_emit(fence);
128
129 if (ret)
Marcin Slusarz382d62e2010-10-20 21:50:24 +0200130 nouveau_fence_unref(&fence);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000131 *pfence = fence;
132 return ret;
133}
134
135struct nouveau_channel *
136nouveau_fence_channel(struct nouveau_fence *fence)
137{
Francisco Jerez2b478add2010-10-18 03:56:40 +0200138 return fence ? nouveau_channel_get_unlocked(fence->channel) : NULL;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000139}
140
141int
142nouveau_fence_emit(struct nouveau_fence *fence)
143{
Ben Skeggs6ee73862009-12-11 19:24:15 +1000144 struct nouveau_channel *chan = fence->channel;
Francisco Jerez27307232010-09-21 18:57:11 +0200145 struct drm_device *dev = chan->dev;
Ben Skeggs529c4952010-11-24 10:30:22 +1000146 struct drm_nouveau_private *dev_priv = dev->dev_private;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000147 int ret;
148
149 ret = RING_SPACE(chan, 2);
150 if (ret)
151 return ret;
152
153 if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
Ben Skeggs6ee73862009-12-11 19:24:15 +1000154 nouveau_fence_update(chan);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000155
156 BUG_ON(chan->fence.sequence ==
157 chan->fence.sequence_ack - 1);
158 }
159
160 fence->sequence = ++chan->fence.sequence;
161
162 kref_get(&fence->refcount);
Ben Skeggs047d1d32010-05-31 12:00:43 +1000163 spin_lock(&chan->fence.lock);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000164 list_add_tail(&fence->entry, &chan->fence.pending);
Ben Skeggs047d1d32010-05-31 12:00:43 +1000165 spin_unlock(&chan->fence.lock);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000166
Ben Skeggs529c4952010-11-24 10:30:22 +1000167 if (USE_REFCNT(dev)) {
168 if (dev_priv->card_type < NV_C0)
Ben Skeggs6d597022012-04-01 21:09:13 +1000169 BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
Ben Skeggs529c4952010-11-24 10:30:22 +1000170 else
Ben Skeggs6d597022012-04-01 21:09:13 +1000171 BEGIN_NVC0(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
Ben Skeggs529c4952010-11-24 10:30:22 +1000172 } else {
Ben Skeggs6d597022012-04-01 21:09:13 +1000173 BEGIN_NV04(chan, NvSubSw, 0x0150, 1);
Ben Skeggs529c4952010-11-24 10:30:22 +1000174 }
175 OUT_RING (chan, fence->sequence);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000176 FIRE_RING(chan);
Marcin Slusarz695b95b2012-04-25 23:20:33 +0200177 fence->timeout = jiffies + 3 * DRM_HZ;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000178
179 return 0;
180}
181
182void
Francisco Jerez8ac38912010-09-21 20:49:39 +0200183nouveau_fence_work(struct nouveau_fence *fence,
184 void (*work)(void *priv, bool signalled),
185 void *priv)
186{
187 BUG_ON(fence->work);
188
189 spin_lock(&fence->channel->fence.lock);
190
191 if (fence->signalled) {
192 work(priv, true);
193 } else {
194 fence->work = work;
195 fence->priv = priv;
196 }
197
198 spin_unlock(&fence->channel->fence.lock);
199}
200
201void
Ben Skeggs875ac342012-04-30 12:51:48 +1000202nouveau_fence_unref(struct nouveau_fence **pfence)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000203{
Ben Skeggs875ac342012-04-30 12:51:48 +1000204 if (*pfence)
205 kref_put(&(*pfence)->refcount, nouveau_fence_del);
206 *pfence = NULL;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000207}
208
Ben Skeggs875ac342012-04-30 12:51:48 +1000209struct nouveau_fence *
210nouveau_fence_ref(struct nouveau_fence *fence)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000211{
Ben Skeggs6ee73862009-12-11 19:24:15 +1000212 kref_get(&fence->refcount);
Ben Skeggs875ac342012-04-30 12:51:48 +1000213 return fence;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000214}
215
216bool
Ben Skeggs875ac342012-04-30 12:51:48 +1000217nouveau_fence_signalled(struct nouveau_fence *fence)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000218{
Ben Skeggs6ee73862009-12-11 19:24:15 +1000219 struct nouveau_channel *chan = fence->channel;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000220
221 if (fence->signalled)
222 return true;
223
Ben Skeggs6ee73862009-12-11 19:24:15 +1000224 nouveau_fence_update(chan);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000225 return fence->signalled;
226}
227
228int
Ben Skeggs875ac342012-04-30 12:51:48 +1000229nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000230{
Marcin Slusarzbd35fe52011-03-09 14:22:19 +0100231 unsigned long sleep_time = NSEC_PER_MSEC / 1000;
232 ktime_t t;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000233 int ret = 0;
234
Ben Skeggs875ac342012-04-30 12:51:48 +1000235 while (!nouveau_fence_signalled(fence)) {
236 if (time_after_eq(jiffies, fence->timeout)) {
Ben Skeggs6ee73862009-12-11 19:24:15 +1000237 ret = -EBUSY;
238 break;
239 }
240
Ben Skeggs875ac342012-04-30 12:51:48 +1000241 __set_current_state(intr ? TASK_INTERRUPTIBLE :
242 TASK_UNINTERRUPTIBLE);
Marcin Slusarzbd35fe52011-03-09 14:22:19 +0100243 if (lazy) {
244 t = ktime_set(0, sleep_time);
245 schedule_hrtimeout(&t, HRTIMER_MODE_REL);
246 sleep_time *= 2;
247 if (sleep_time > NSEC_PER_MSEC)
248 sleep_time = NSEC_PER_MSEC;
249 }
Ben Skeggs6ee73862009-12-11 19:24:15 +1000250
251 if (intr && signal_pending(current)) {
Ben Skeggs9ddc8c52009-12-15 11:04:25 +1000252 ret = -ERESTARTSYS;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000253 break;
254 }
255 }
256
257 __set_current_state(TASK_RUNNING);
258
259 return ret;
260}
261
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200262static struct nouveau_semaphore *
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000263semaphore_alloc(struct drm_device *dev)
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200264{
265 struct drm_nouveau_private *dev_priv = dev->dev_private;
266 struct nouveau_semaphore *sema;
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000267 int size = (dev_priv->chipset < 0x84) ? 4 : 16;
268 int ret, i;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200269
270 if (!USE_SEMA(dev))
271 return NULL;
272
273 sema = kmalloc(sizeof(*sema), GFP_KERNEL);
274 if (!sema)
275 goto fail;
276
Francisco Jerez907af602010-10-09 04:02:09 +0200277 ret = drm_mm_pre_get(&dev_priv->fence.heap);
278 if (ret)
279 goto fail;
280
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200281 spin_lock(&dev_priv->fence.lock);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000282 sema->mem = drm_mm_search_free(&dev_priv->fence.heap, size, 0, 0);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200283 if (sema->mem)
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000284 sema->mem = drm_mm_get_block_atomic(sema->mem, size, 0);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200285 spin_unlock(&dev_priv->fence.lock);
286
287 if (!sema->mem)
288 goto fail;
289
290 kref_init(&sema->ref);
291 sema->dev = dev;
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000292 for (i = sema->mem->start; i < sema->mem->start + size; i += 4)
293 nouveau_bo_wr32(dev_priv->fence.bo, i / 4, 0);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200294
295 return sema;
296fail:
297 kfree(sema);
298 return NULL;
299}
300
301static void
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000302semaphore_free(struct kref *ref)
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200303{
304 struct nouveau_semaphore *sema =
305 container_of(ref, struct nouveau_semaphore, ref);
306 struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
307
308 spin_lock(&dev_priv->fence.lock);
309 drm_mm_put_block(sema->mem);
310 spin_unlock(&dev_priv->fence.lock);
311
312 kfree(sema);
313}
314
315static void
316semaphore_work(void *priv, bool signalled)
317{
318 struct nouveau_semaphore *sema = priv;
319 struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
320
321 if (unlikely(!signalled))
322 nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1);
323
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000324 kref_put(&sema->ref, semaphore_free);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200325}
326
327static int
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000328semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200329{
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000330 struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
331 struct nouveau_fence *fence = NULL;
Ben Skeggsd02836b2011-06-07 15:21:23 +1000332 u64 offset = chan->fence.vma.offset + sema->mem->start;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200333 int ret;
334
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000335 if (dev_priv->chipset < 0x84) {
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000336 ret = RING_SPACE(chan, 4);
Ben Skeggsec238022011-02-02 14:57:05 +1000337 if (ret)
338 return ret;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200339
Ben Skeggs6d597022012-04-01 21:09:13 +1000340 BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 3);
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000341 OUT_RING (chan, NvSema);
Ben Skeggsd02836b2011-06-07 15:21:23 +1000342 OUT_RING (chan, offset);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000343 OUT_RING (chan, 1);
Ben Skeggscb1d7712011-01-28 13:44:32 +1000344 } else
345 if (dev_priv->chipset < 0xc0) {
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000346 ret = RING_SPACE(chan, 7);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000347 if (ret)
348 return ret;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200349
Ben Skeggs6d597022012-04-01 21:09:13 +1000350 BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000351 OUT_RING (chan, chan->vram_handle);
Ben Skeggs6d597022012-04-01 21:09:13 +1000352 BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
Ben Skeggse3b7ed52011-02-02 13:21:57 +1000353 OUT_RING (chan, upper_32_bits(offset));
354 OUT_RING (chan, lower_32_bits(offset));
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000355 OUT_RING (chan, 1);
356 OUT_RING (chan, 1); /* ACQUIRE_EQ */
Ben Skeggscb1d7712011-01-28 13:44:32 +1000357 } else {
Ben Skeggscb1d7712011-01-28 13:44:32 +1000358 ret = RING_SPACE(chan, 5);
359 if (ret)
360 return ret;
361
Ben Skeggs6d597022012-04-01 21:09:13 +1000362 BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
Ben Skeggscb1d7712011-01-28 13:44:32 +1000363 OUT_RING (chan, upper_32_bits(offset));
364 OUT_RING (chan, lower_32_bits(offset));
365 OUT_RING (chan, 1);
366 OUT_RING (chan, 0x1001); /* ACQUIRE_EQ */
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200367 }
368
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200369 /* Delay semaphore destruction until its work is done */
370 ret = nouveau_fence_new(chan, &fence, true);
371 if (ret)
372 return ret;
373
374 kref_get(&sema->ref);
375 nouveau_fence_work(fence, semaphore_work, sema);
Marcin Slusarz382d62e2010-10-20 21:50:24 +0200376 nouveau_fence_unref(&fence);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000377 return 0;
378}
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200379
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000380static int
381semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
382{
383 struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
384 struct nouveau_fence *fence = NULL;
Ben Skeggsd02836b2011-06-07 15:21:23 +1000385 u64 offset = chan->fence.vma.offset + sema->mem->start;
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000386 int ret;
387
388 if (dev_priv->chipset < 0x84) {
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000389 ret = RING_SPACE(chan, 5);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000390 if (ret)
391 return ret;
392
Ben Skeggs6d597022012-04-01 21:09:13 +1000393 BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000394 OUT_RING (chan, NvSema);
Ben Skeggsd02836b2011-06-07 15:21:23 +1000395 OUT_RING (chan, offset);
Ben Skeggs6d597022012-04-01 21:09:13 +1000396 BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000397 OUT_RING (chan, 1);
Ben Skeggscb1d7712011-01-28 13:44:32 +1000398 } else
399 if (dev_priv->chipset < 0xc0) {
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000400 ret = RING_SPACE(chan, 7);
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000401 if (ret)
402 return ret;
403
Ben Skeggs6d597022012-04-01 21:09:13 +1000404 BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000405 OUT_RING (chan, chan->vram_handle);
Ben Skeggs6d597022012-04-01 21:09:13 +1000406 BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
Ben Skeggse3b7ed52011-02-02 13:21:57 +1000407 OUT_RING (chan, upper_32_bits(offset));
408 OUT_RING (chan, lower_32_bits(offset));
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000409 OUT_RING (chan, 1);
410 OUT_RING (chan, 2); /* RELEASE */
Ben Skeggscb1d7712011-01-28 13:44:32 +1000411 } else {
Ben Skeggscb1d7712011-01-28 13:44:32 +1000412 ret = RING_SPACE(chan, 5);
413 if (ret)
414 return ret;
415
Ben Skeggs6d597022012-04-01 21:09:13 +1000416 BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
Ben Skeggscb1d7712011-01-28 13:44:32 +1000417 OUT_RING (chan, upper_32_bits(offset));
418 OUT_RING (chan, lower_32_bits(offset));
419 OUT_RING (chan, 1);
420 OUT_RING (chan, 0x1002); /* RELEASE */
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000421 }
422
423 /* Delay semaphore destruction until its work is done */
424 ret = nouveau_fence_new(chan, &fence, true);
425 if (ret)
426 return ret;
427
428 kref_get(&sema->ref);
429 nouveau_fence_work(fence, semaphore_work, sema);
430 nouveau_fence_unref(&fence);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200431 return 0;
432}
433
Ben Skeggs6ee73862009-12-11 19:24:15 +1000434int
Francisco Jerez27307232010-09-21 18:57:11 +0200435nouveau_fence_sync(struct nouveau_fence *fence,
436 struct nouveau_channel *wchan)
437{
438 struct nouveau_channel *chan = nouveau_fence_channel(fence);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200439 struct drm_device *dev = wchan->dev;
440 struct nouveau_semaphore *sema;
Francisco Jerez2b478add2010-10-18 03:56:40 +0200441 int ret = 0;
Francisco Jerez27307232010-09-21 18:57:11 +0200442
Francisco Jerez2b478add2010-10-18 03:56:40 +0200443 if (likely(!chan || chan == wchan ||
Marcin Slusarz382d62e2010-10-20 21:50:24 +0200444 nouveau_fence_signalled(fence)))
Francisco Jerez2b478add2010-10-18 03:56:40 +0200445 goto out;
Francisco Jerez27307232010-09-21 18:57:11 +0200446
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000447 sema = semaphore_alloc(dev);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200448 if (!sema) {
449 /* Early card or broken userspace, fall back to
450 * software sync. */
Marcin Slusarz382d62e2010-10-20 21:50:24 +0200451 ret = nouveau_fence_wait(fence, true, false);
Francisco Jerez2b478add2010-10-18 03:56:40 +0200452 goto out;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200453 }
454
Ben Skeggs08cd3d42010-10-12 08:01:59 +1000455 /* try to take chan's mutex, if we can't take it right away
Ben Skeggscff5c132010-10-06 16:16:59 +1000456 * we have to fallback to software sync to prevent locking
457 * order issues
458 */
Ben Skeggs08cd3d42010-10-12 08:01:59 +1000459 if (!mutex_trylock(&chan->mutex)) {
Marcin Slusarz382d62e2010-10-20 21:50:24 +0200460 ret = nouveau_fence_wait(fence, true, false);
Francisco Jerez2b478add2010-10-18 03:56:40 +0200461 goto out_unref;
Ben Skeggscff5c132010-10-06 16:16:59 +1000462 }
463
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200464 /* Make wchan wait until it gets signalled */
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000465 ret = semaphore_acquire(wchan, sema);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200466 if (ret)
Francisco Jerez2b478add2010-10-18 03:56:40 +0200467 goto out_unlock;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200468
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200469 /* Signal the semaphore from chan */
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000470 ret = semaphore_release(chan, sema);
Francisco Jerez2b478add2010-10-18 03:56:40 +0200471
472out_unlock:
Ben Skeggs08cd3d42010-10-12 08:01:59 +1000473 mutex_unlock(&chan->mutex);
Francisco Jerez2b478add2010-10-18 03:56:40 +0200474out_unref:
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000475 kref_put(&sema->ref, semaphore_free);
Francisco Jerez2b478add2010-10-18 03:56:40 +0200476out:
477 if (chan)
478 nouveau_channel_put_unlocked(&chan);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200479 return ret;
Francisco Jerez27307232010-09-21 18:57:11 +0200480}
481
482int
Marcin Slusarz382d62e2010-10-20 21:50:24 +0200483__nouveau_fence_flush(void *sync_obj, void *sync_arg)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000484{
485 return 0;
486}
487
Ben Skeggs6ee73862009-12-11 19:24:15 +1000488int
Francisco Jerez27307232010-09-21 18:57:11 +0200489nouveau_fence_channel_init(struct nouveau_channel *chan)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000490{
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200491 struct drm_device *dev = chan->dev;
492 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez27307232010-09-21 18:57:11 +0200493 struct nouveau_gpuobj *obj = NULL;
494 int ret;
495
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000496 if (dev_priv->card_type < NV_C0) {
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000497 ret = RING_SPACE(chan, 2);
498 if (ret)
499 return ret;
Francisco Jerez27307232010-09-21 18:57:11 +0200500
Ben Skeggs6d597022012-04-01 21:09:13 +1000501 BEGIN_NV04(chan, NvSubSw, NV01_SUBCHAN_OBJECT, 1);
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000502 OUT_RING (chan, NvSw);
503 FIRE_RING (chan);
504 }
Francisco Jerez27307232010-09-21 18:57:11 +0200505
Ben Skeggsb16a5a12011-06-17 23:41:54 +1000506 /* Setup area of memory shared between all channels for x-chan sync */
Ben Skeggse3b7ed52011-02-02 13:21:57 +1000507 if (USE_SEMA(dev) && dev_priv->chipset < 0x84) {
Ben Skeggsa8b214f2010-12-03 09:05:20 +1000508 struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200509
Ben Skeggs91a8f1e2011-07-25 20:26:19 +1000510 ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY,
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200511 mem->start << PAGE_SHIFT,
Ben Skeggsa8b214f2010-12-03 09:05:20 +1000512 mem->size, NV_MEM_ACCESS_RW,
Ben Skeggs7f4a1952010-11-16 11:50:09 +1000513 NV_MEM_TARGET_VRAM, &obj);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200514 if (ret)
515 return ret;
516
517 ret = nouveau_ramht_insert(chan, NvSema, obj);
518 nouveau_gpuobj_ref(NULL, &obj);
519 if (ret)
520 return ret;
Ben Skeggscfd8be02011-08-23 10:23:11 +1000521 } else
522 if (USE_SEMA(dev)) {
Ben Skeggsd02836b2011-06-07 15:21:23 +1000523 /* map fence bo into channel's vm */
524 ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm,
525 &chan->fence.vma);
526 if (ret)
527 return ret;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200528 }
529
Ben Skeggs047d1d32010-05-31 12:00:43 +1000530 atomic_set(&chan->fence.last_sequence_irq, 0);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000531 return 0;
532}
533
534void
Francisco Jerez27307232010-09-21 18:57:11 +0200535nouveau_fence_channel_fini(struct nouveau_channel *chan)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000536{
Ben Skeggsd02836b2011-06-07 15:21:23 +1000537 struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
Francisco Jerez27307232010-09-21 18:57:11 +0200538 struct nouveau_fence *tmp, *fence;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000539
Francisco Jerez889fa932010-10-18 03:57:19 +0200540 spin_lock(&chan->fence.lock);
Francisco Jerez27307232010-09-21 18:57:11 +0200541 list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
Ben Skeggs6ee73862009-12-11 19:24:15 +1000542 fence->signalled = true;
543 list_del(&fence->entry);
Francisco Jerez8ac38912010-09-21 20:49:39 +0200544
545 if (unlikely(fence->work))
546 fence->work(fence->priv, false);
547
Ben Skeggs6ee73862009-12-11 19:24:15 +1000548 kref_put(&fence->refcount, nouveau_fence_del);
549 }
Francisco Jerez889fa932010-10-18 03:57:19 +0200550 spin_unlock(&chan->fence.lock);
Ben Skeggsd02836b2011-06-07 15:21:23 +1000551
552 nouveau_bo_vma_del(dev_priv->fence.bo, &chan->fence.vma);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000553}
554
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200555int
556nouveau_fence_init(struct drm_device *dev)
557{
558 struct drm_nouveau_private *dev_priv = dev->dev_private;
Ben Skeggsc3b90a72011-01-28 12:08:29 +1000559 int size = (dev_priv->chipset < 0x84) ? 4096 : 16384;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200560 int ret;
561
562 /* Create a shared VRAM heap for cross-channel sync. */
563 if (USE_SEMA(dev)) {
Ben Skeggs7375c952011-06-07 14:21:29 +1000564 ret = nouveau_bo_new(dev, size, 0, TTM_PL_FLAG_VRAM,
Dave Airlie22b33e82012-04-02 11:53:06 +0100565 0, 0, NULL, &dev_priv->fence.bo);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200566 if (ret)
567 return ret;
568
569 ret = nouveau_bo_pin(dev_priv->fence.bo, TTM_PL_FLAG_VRAM);
570 if (ret)
571 goto fail;
572
573 ret = nouveau_bo_map(dev_priv->fence.bo);
574 if (ret)
575 goto fail;
576
577 ret = drm_mm_init(&dev_priv->fence.heap, 0,
578 dev_priv->fence.bo->bo.mem.size);
579 if (ret)
580 goto fail;
581
582 spin_lock_init(&dev_priv->fence.lock);
583 }
584
585 return 0;
586fail:
587 nouveau_bo_unmap(dev_priv->fence.bo);
588 nouveau_bo_ref(NULL, &dev_priv->fence.bo);
589 return ret;
590}
591
592void
593nouveau_fence_fini(struct drm_device *dev)
594{
595 struct drm_nouveau_private *dev_priv = dev->dev_private;
596
597 if (USE_SEMA(dev)) {
598 drm_mm_takedown(&dev_priv->fence.heap);
599 nouveau_bo_unmap(dev_priv->fence.bo);
600 nouveau_bo_unpin(dev_priv->fence.bo);
601 nouveau_bo_ref(NULL, &dev_priv->fence.bo);
602 }
603}