blob: 5f9f66f350674527c844569c6ee9d86289e383e1 [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
30#include "nouveau_drv.h"
Francisco Jerez0c6c1c22010-09-22 00:58:54 +020031#include "nouveau_ramht.h"
Ben Skeggs6ee73862009-12-11 19:24:15 +100032#include "nouveau_dma.h"
33
Francisco Jerez27307232010-09-21 18:57:11 +020034#define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10)
Francisco Jerez0c6c1c22010-09-22 00:58:54 +020035#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17)
Ben Skeggs6ee73862009-12-11 19:24:15 +100036
37struct nouveau_fence {
38 struct nouveau_channel *channel;
39 struct kref refcount;
40 struct list_head entry;
41
42 uint32_t sequence;
43 bool signalled;
Francisco Jerez8ac38912010-09-21 20:49:39 +020044
45 void (*work)(void *priv, bool signalled);
46 void *priv;
Ben Skeggs6ee73862009-12-11 19:24:15 +100047};
48
Francisco Jerez0c6c1c22010-09-22 00:58:54 +020049struct nouveau_semaphore {
50 struct kref ref;
51 struct drm_device *dev;
52 struct drm_mm_node *mem;
53};
54
Ben Skeggs6ee73862009-12-11 19:24:15 +100055static inline struct nouveau_fence *
56nouveau_fence(void *sync_obj)
57{
58 return (struct nouveau_fence *)sync_obj;
59}
60
61static void
62nouveau_fence_del(struct kref *ref)
63{
64 struct nouveau_fence *fence =
65 container_of(ref, struct nouveau_fence, refcount);
66
67 kfree(fence);
68}
69
70void
71nouveau_fence_update(struct nouveau_channel *chan)
72{
Francisco Jerez27307232010-09-21 18:57:11 +020073 struct drm_device *dev = chan->dev;
74 struct nouveau_fence *tmp, *fence;
Ben Skeggs6ee73862009-12-11 19:24:15 +100075 uint32_t sequence;
76
Francisco Jerez3ba64622010-08-28 17:56:33 +020077 spin_lock(&chan->fence.lock);
78
Francisco Jerez27307232010-09-21 18:57:11 +020079 if (USE_REFCNT(dev))
Ben Skeggs6ee73862009-12-11 19:24:15 +100080 sequence = nvchan_rd32(chan, 0x48);
81 else
Ben Skeggs047d1d32010-05-31 12:00:43 +100082 sequence = atomic_read(&chan->fence.last_sequence_irq);
Ben Skeggs6ee73862009-12-11 19:24:15 +100083
84 if (chan->fence.sequence_ack == sequence)
Francisco Jerez3ba64622010-08-28 17:56:33 +020085 goto out;
Ben Skeggs6ee73862009-12-11 19:24:15 +100086 chan->fence.sequence_ack = sequence;
87
Francisco Jerez27307232010-09-21 18:57:11 +020088 list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
Ben Skeggs6ee73862009-12-11 19:24:15 +100089 sequence = fence->sequence;
90 fence->signalled = true;
91 list_del(&fence->entry);
Francisco Jerez8ac38912010-09-21 20:49:39 +020092
93 if (unlikely(fence->work))
94 fence->work(fence->priv, true);
95
Ben Skeggs6ee73862009-12-11 19:24:15 +100096 kref_put(&fence->refcount, nouveau_fence_del);
97
98 if (sequence == chan->fence.sequence_ack)
99 break;
100 }
Francisco Jerez3ba64622010-08-28 17:56:33 +0200101out:
Ben Skeggs047d1d32010-05-31 12:00:43 +1000102 spin_unlock(&chan->fence.lock);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000103}
104
105int
106nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,
107 bool emit)
108{
109 struct nouveau_fence *fence;
110 int ret = 0;
111
112 fence = kzalloc(sizeof(*fence), GFP_KERNEL);
113 if (!fence)
114 return -ENOMEM;
115 kref_init(&fence->refcount);
116 fence->channel = chan;
117
118 if (emit)
119 ret = nouveau_fence_emit(fence);
120
121 if (ret)
122 nouveau_fence_unref((void *)&fence);
123 *pfence = fence;
124 return ret;
125}
126
127struct nouveau_channel *
128nouveau_fence_channel(struct nouveau_fence *fence)
129{
130 return fence ? fence->channel : NULL;
131}
132
133int
134nouveau_fence_emit(struct nouveau_fence *fence)
135{
Ben Skeggs6ee73862009-12-11 19:24:15 +1000136 struct nouveau_channel *chan = fence->channel;
Francisco Jerez27307232010-09-21 18:57:11 +0200137 struct drm_device *dev = chan->dev;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000138 int ret;
139
140 ret = RING_SPACE(chan, 2);
141 if (ret)
142 return ret;
143
144 if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
Ben Skeggs6ee73862009-12-11 19:24:15 +1000145 nouveau_fence_update(chan);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000146
147 BUG_ON(chan->fence.sequence ==
148 chan->fence.sequence_ack - 1);
149 }
150
151 fence->sequence = ++chan->fence.sequence;
152
153 kref_get(&fence->refcount);
Ben Skeggs047d1d32010-05-31 12:00:43 +1000154 spin_lock(&chan->fence.lock);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000155 list_add_tail(&fence->entry, &chan->fence.pending);
Ben Skeggs047d1d32010-05-31 12:00:43 +1000156 spin_unlock(&chan->fence.lock);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000157
Francisco Jerez27307232010-09-21 18:57:11 +0200158 BEGIN_RING(chan, NvSubSw, USE_REFCNT(dev) ? 0x0050 : 0x0150, 1);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000159 OUT_RING(chan, fence->sequence);
160 FIRE_RING(chan);
161
162 return 0;
163}
164
165void
Francisco Jerez8ac38912010-09-21 20:49:39 +0200166nouveau_fence_work(struct nouveau_fence *fence,
167 void (*work)(void *priv, bool signalled),
168 void *priv)
169{
170 BUG_ON(fence->work);
171
172 spin_lock(&fence->channel->fence.lock);
173
174 if (fence->signalled) {
175 work(priv, true);
176 } else {
177 fence->work = work;
178 fence->priv = priv;
179 }
180
181 spin_unlock(&fence->channel->fence.lock);
182}
183
184void
Ben Skeggs6ee73862009-12-11 19:24:15 +1000185nouveau_fence_unref(void **sync_obj)
186{
187 struct nouveau_fence *fence = nouveau_fence(*sync_obj);
188
189 if (fence)
190 kref_put(&fence->refcount, nouveau_fence_del);
191 *sync_obj = NULL;
192}
193
194void *
195nouveau_fence_ref(void *sync_obj)
196{
197 struct nouveau_fence *fence = nouveau_fence(sync_obj);
198
199 kref_get(&fence->refcount);
200 return sync_obj;
201}
202
203bool
204nouveau_fence_signalled(void *sync_obj, void *sync_arg)
205{
206 struct nouveau_fence *fence = nouveau_fence(sync_obj);
207 struct nouveau_channel *chan = fence->channel;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000208
209 if (fence->signalled)
210 return true;
211
Ben Skeggs6ee73862009-12-11 19:24:15 +1000212 nouveau_fence_update(chan);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000213 return fence->signalled;
214}
215
216int
217nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
218{
219 unsigned long timeout = jiffies + (3 * DRM_HZ);
220 int ret = 0;
221
Ben Skeggs6ee73862009-12-11 19:24:15 +1000222 while (1) {
223 if (nouveau_fence_signalled(sync_obj, sync_arg))
224 break;
225
226 if (time_after_eq(jiffies, timeout)) {
227 ret = -EBUSY;
228 break;
229 }
230
Kulikov Vasiliy05991112010-07-26 12:23:54 +0400231 __set_current_state(intr ? TASK_INTERRUPTIBLE
232 : TASK_UNINTERRUPTIBLE);
Ben Skeggs6ee73862009-12-11 19:24:15 +1000233 if (lazy)
234 schedule_timeout(1);
235
236 if (intr && signal_pending(current)) {
Ben Skeggs9ddc8c52009-12-15 11:04:25 +1000237 ret = -ERESTARTSYS;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000238 break;
239 }
240 }
241
242 __set_current_state(TASK_RUNNING);
243
244 return ret;
245}
246
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200247static struct nouveau_semaphore *
248alloc_semaphore(struct drm_device *dev)
249{
250 struct drm_nouveau_private *dev_priv = dev->dev_private;
251 struct nouveau_semaphore *sema;
Francisco Jerez907af602010-10-09 04:02:09 +0200252 int ret;
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200253
254 if (!USE_SEMA(dev))
255 return NULL;
256
257 sema = kmalloc(sizeof(*sema), GFP_KERNEL);
258 if (!sema)
259 goto fail;
260
Francisco Jerez907af602010-10-09 04:02:09 +0200261 ret = drm_mm_pre_get(&dev_priv->fence.heap);
262 if (ret)
263 goto fail;
264
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200265 spin_lock(&dev_priv->fence.lock);
266 sema->mem = drm_mm_search_free(&dev_priv->fence.heap, 4, 0, 0);
267 if (sema->mem)
Francisco Jerez907af602010-10-09 04:02:09 +0200268 sema->mem = drm_mm_get_block_atomic(sema->mem, 4, 0);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200269 spin_unlock(&dev_priv->fence.lock);
270
271 if (!sema->mem)
272 goto fail;
273
274 kref_init(&sema->ref);
275 sema->dev = dev;
276 nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 0);
277
278 return sema;
279fail:
280 kfree(sema);
281 return NULL;
282}
283
284static void
285free_semaphore(struct kref *ref)
286{
287 struct nouveau_semaphore *sema =
288 container_of(ref, struct nouveau_semaphore, ref);
289 struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
290
291 spin_lock(&dev_priv->fence.lock);
292 drm_mm_put_block(sema->mem);
293 spin_unlock(&dev_priv->fence.lock);
294
295 kfree(sema);
296}
297
298static void
299semaphore_work(void *priv, bool signalled)
300{
301 struct nouveau_semaphore *sema = priv;
302 struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
303
304 if (unlikely(!signalled))
305 nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1);
306
307 kref_put(&sema->ref, free_semaphore);
308}
309
310static int
311emit_semaphore(struct nouveau_channel *chan, int method,
312 struct nouveau_semaphore *sema)
313{
314 struct drm_nouveau_private *dev_priv = sema->dev->dev_private;
315 struct nouveau_fence *fence;
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200316 bool smart = (dev_priv->card_type >= NV_50);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200317 int ret;
318
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200319 ret = RING_SPACE(chan, smart ? 8 : 4);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200320 if (ret)
321 return ret;
322
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200323 if (smart) {
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200324 BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
325 OUT_RING(chan, NvSema);
326 }
327 BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_OFFSET, 1);
328 OUT_RING(chan, sema->mem->start);
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200329
330 if (smart && method == NV_SW_SEMAPHORE_ACQUIRE) {
331 /*
332 * NV50 tries to be too smart and context-switch
333 * between semaphores instead of doing a "first come,
334 * first served" strategy like previous cards
335 * do.
336 *
337 * That's bad because the ACQUIRE latency can get as
338 * large as the PFIFO context time slice in the
339 * typical DRI2 case where you have several
340 * outstanding semaphores at the same moment.
341 *
342 * If we're going to ACQUIRE, force the card to
343 * context switch before, just in case the matching
344 * RELEASE is already scheduled to be executed in
345 * another channel.
346 */
347 BEGIN_RING(chan, NvSubSw, NV_SW_YIELD, 1);
348 OUT_RING(chan, 0);
349 }
350
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200351 BEGIN_RING(chan, NvSubSw, method, 1);
352 OUT_RING(chan, 1);
353
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200354 if (smart && method == NV_SW_SEMAPHORE_RELEASE) {
355 /*
356 * Force the card to context switch, there may be
357 * another channel waiting for the semaphore we just
358 * released.
359 */
360 BEGIN_RING(chan, NvSubSw, NV_SW_YIELD, 1);
361 OUT_RING(chan, 0);
362 }
363
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200364 /* Delay semaphore destruction until its work is done */
365 ret = nouveau_fence_new(chan, &fence, true);
366 if (ret)
367 return ret;
368
369 kref_get(&sema->ref);
370 nouveau_fence_work(fence, semaphore_work, sema);
371 nouveau_fence_unref((void *)&fence);
372
373 return 0;
374}
375
Ben Skeggs6ee73862009-12-11 19:24:15 +1000376int
Francisco Jerez27307232010-09-21 18:57:11 +0200377nouveau_fence_sync(struct nouveau_fence *fence,
378 struct nouveau_channel *wchan)
379{
380 struct nouveau_channel *chan = nouveau_fence_channel(fence);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200381 struct drm_device *dev = wchan->dev;
382 struct nouveau_semaphore *sema;
383 int ret;
Francisco Jerez27307232010-09-21 18:57:11 +0200384
385 if (likely(!fence || chan == wchan ||
386 nouveau_fence_signalled(fence, NULL)))
387 return 0;
388
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200389 sema = alloc_semaphore(dev);
390 if (!sema) {
391 /* Early card or broken userspace, fall back to
392 * software sync. */
393 return nouveau_fence_wait(fence, NULL, false, false);
394 }
395
Ben Skeggs08cd3d42010-10-12 08:01:59 +1000396 /* try to take chan's mutex, if we can't take it right away
Ben Skeggscff5c132010-10-06 16:16:59 +1000397 * we have to fallback to software sync to prevent locking
398 * order issues
399 */
Ben Skeggs08cd3d42010-10-12 08:01:59 +1000400 if (!mutex_trylock(&chan->mutex)) {
Ben Skeggscff5c132010-10-06 16:16:59 +1000401 free_semaphore(&sema->ref);
402 return nouveau_fence_wait(fence, NULL, false, false);
403 }
404
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200405 /* Make wchan wait until it gets signalled */
406 ret = emit_semaphore(wchan, NV_SW_SEMAPHORE_ACQUIRE, sema);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200407 if (ret)
408 goto out;
409
Francisco Jerez8af29cc2010-10-02 17:04:46 +0200410 /* Signal the semaphore from chan */
411 ret = emit_semaphore(chan, NV_SW_SEMAPHORE_RELEASE, sema);
Ben Skeggs08cd3d42010-10-12 08:01:59 +1000412 mutex_unlock(&chan->mutex);
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200413out:
414 kref_put(&sema->ref, free_semaphore);
415 return ret;
Francisco Jerez27307232010-09-21 18:57:11 +0200416}
417
418int
Ben Skeggs6ee73862009-12-11 19:24:15 +1000419nouveau_fence_flush(void *sync_obj, void *sync_arg)
420{
421 return 0;
422}
423
Ben Skeggs6ee73862009-12-11 19:24:15 +1000424int
Francisco Jerez27307232010-09-21 18:57:11 +0200425nouveau_fence_channel_init(struct nouveau_channel *chan)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000426{
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200427 struct drm_device *dev = chan->dev;
428 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez27307232010-09-21 18:57:11 +0200429 struct nouveau_gpuobj *obj = NULL;
430 int ret;
431
432 /* Create an NV_SW object for various sync purposes */
433 ret = nouveau_gpuobj_sw_new(chan, NV_SW, &obj);
434 if (ret)
435 return ret;
436
437 ret = nouveau_ramht_insert(chan, NvSw, obj);
438 nouveau_gpuobj_ref(NULL, &obj);
439 if (ret)
440 return ret;
441
442 ret = RING_SPACE(chan, 2);
443 if (ret)
444 return ret;
445 BEGIN_RING(chan, NvSubSw, 0, 1);
446 OUT_RING(chan, NvSw);
447
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200448 /* Create a DMA object for the shared cross-channel sync area. */
449 if (USE_SEMA(dev)) {
450 struct drm_mm_node *mem = dev_priv->fence.bo->bo.mem.mm_node;
451
452 ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
453 mem->start << PAGE_SHIFT,
454 mem->size << PAGE_SHIFT,
455 NV_DMA_ACCESS_RW,
456 NV_DMA_TARGET_VIDMEM, &obj);
457 if (ret)
458 return ret;
459
460 ret = nouveau_ramht_insert(chan, NvSema, obj);
461 nouveau_gpuobj_ref(NULL, &obj);
462 if (ret)
463 return ret;
464
465 ret = RING_SPACE(chan, 2);
466 if (ret)
467 return ret;
468 BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
469 OUT_RING(chan, NvSema);
470 }
471
Francisco Jerez27307232010-09-21 18:57:11 +0200472 FIRE_RING(chan);
473
Ben Skeggs6ee73862009-12-11 19:24:15 +1000474 INIT_LIST_HEAD(&chan->fence.pending);
475 spin_lock_init(&chan->fence.lock);
Ben Skeggs047d1d32010-05-31 12:00:43 +1000476 atomic_set(&chan->fence.last_sequence_irq, 0);
Francisco Jerez27307232010-09-21 18:57:11 +0200477
Ben Skeggs6ee73862009-12-11 19:24:15 +1000478 return 0;
479}
480
481void
Francisco Jerez27307232010-09-21 18:57:11 +0200482nouveau_fence_channel_fini(struct nouveau_channel *chan)
Ben Skeggs6ee73862009-12-11 19:24:15 +1000483{
Francisco Jerez27307232010-09-21 18:57:11 +0200484 struct nouveau_fence *tmp, *fence;
Ben Skeggs6ee73862009-12-11 19:24:15 +1000485
Francisco Jerez27307232010-09-21 18:57:11 +0200486 list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
Ben Skeggs6ee73862009-12-11 19:24:15 +1000487 fence->signalled = true;
488 list_del(&fence->entry);
Francisco Jerez8ac38912010-09-21 20:49:39 +0200489
490 if (unlikely(fence->work))
491 fence->work(fence->priv, false);
492
Ben Skeggs6ee73862009-12-11 19:24:15 +1000493 kref_put(&fence->refcount, nouveau_fence_del);
494 }
495}
496
Francisco Jerez0c6c1c22010-09-22 00:58:54 +0200497int
498nouveau_fence_init(struct drm_device *dev)
499{
500 struct drm_nouveau_private *dev_priv = dev->dev_private;
501 int ret;
502
503 /* Create a shared VRAM heap for cross-channel sync. */
504 if (USE_SEMA(dev)) {
505 ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM,
506 0, 0, false, true, &dev_priv->fence.bo);
507 if (ret)
508 return ret;
509
510 ret = nouveau_bo_pin(dev_priv->fence.bo, TTM_PL_FLAG_VRAM);
511 if (ret)
512 goto fail;
513
514 ret = nouveau_bo_map(dev_priv->fence.bo);
515 if (ret)
516 goto fail;
517
518 ret = drm_mm_init(&dev_priv->fence.heap, 0,
519 dev_priv->fence.bo->bo.mem.size);
520 if (ret)
521 goto fail;
522
523 spin_lock_init(&dev_priv->fence.lock);
524 }
525
526 return 0;
527fail:
528 nouveau_bo_unmap(dev_priv->fence.bo);
529 nouveau_bo_ref(NULL, &dev_priv->fence.bo);
530 return ret;
531}
532
533void
534nouveau_fence_fini(struct drm_device *dev)
535{
536 struct drm_nouveau_private *dev_priv = dev->dev_private;
537
538 if (USE_SEMA(dev)) {
539 drm_mm_takedown(&dev_priv->fence.heap);
540 nouveau_bo_unmap(dev_priv->fence.bo);
541 nouveau_bo_unpin(dev_priv->fence.bo);
542 nouveau_bo_ref(NULL, &dev_priv->fence.bo);
543 }
544}