diff options
author | Francisco Jerez <currojerez@riseup.net> | 2010-09-21 18:58:54 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-10-04 19:59:16 -0400 |
commit | 0c6c1c2fb8b0fd4340f78db20ee7f35d2a810907 (patch) | |
tree | 2df3b8c3454d39d1f4b5aa59ffe76736a0c96678 /drivers/gpu/drm/nouveau | |
parent | 8ac3891b48906b38db4b153c2d0d55db2ef81aee (diff) |
drm/nouveau: Use semaphores to handle inter-channel sync in hardware.
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dma.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fence.c | 195 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 11 |
4 files changed, 212 insertions, 3 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h index 8b05c15866d..d578c21d3c8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.h +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h | |||
@@ -72,6 +72,7 @@ enum { | |||
72 | NvGdiRect = 0x8000000c, | 72 | NvGdiRect = 0x8000000c, |
73 | NvImageBlit = 0x8000000d, | 73 | NvImageBlit = 0x8000000d, |
74 | NvSw = 0x8000000e, | 74 | NvSw = 0x8000000e, |
75 | NvSema = 0x8000000f, | ||
75 | 76 | ||
76 | /* G80+ display objects */ | 77 | /* G80+ display objects */ |
77 | NvEvoVRAM = 0x01000000, | 78 | NvEvoVRAM = 0x01000000, |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index d4f049d42ed..a308c132c19 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -622,6 +622,12 @@ struct drm_nouveau_private { | |||
622 | atomic_t validate_sequence; | 622 | atomic_t validate_sequence; |
623 | } ttm; | 623 | } ttm; |
624 | 624 | ||
625 | struct { | ||
626 | spinlock_t lock; | ||
627 | struct drm_mm heap; | ||
628 | struct nouveau_bo *bo; | ||
629 | } fence; | ||
630 | |||
625 | int fifo_alloc_count; | 631 | int fifo_alloc_count; |
626 | struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR]; | 632 | struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR]; |
627 | 633 | ||
@@ -1237,6 +1243,8 @@ extern int nouveau_bo_sync_gpu(struct nouveau_bo *, struct nouveau_channel *); | |||
1237 | 1243 | ||
1238 | /* nouveau_fence.c */ | 1244 | /* nouveau_fence.c */ |
1239 | struct nouveau_fence; | 1245 | struct nouveau_fence; |
1246 | extern int nouveau_fence_init(struct drm_device *); | ||
1247 | extern void nouveau_fence_fini(struct drm_device *); | ||
1240 | extern int nouveau_fence_channel_init(struct nouveau_channel *); | 1248 | extern int nouveau_fence_channel_init(struct nouveau_channel *); |
1241 | extern void nouveau_fence_channel_fini(struct nouveau_channel *); | 1249 | extern void nouveau_fence_channel_fini(struct nouveau_channel *); |
1242 | extern void nouveau_fence_update(struct nouveau_channel *); | 1250 | extern void nouveau_fence_update(struct nouveau_channel *); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index fbb2c3b2623..f42675cc9d1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c | |||
@@ -28,9 +28,11 @@ | |||
28 | #include "drm.h" | 28 | #include "drm.h" |
29 | 29 | ||
30 | #include "nouveau_drv.h" | 30 | #include "nouveau_drv.h" |
31 | #include "nouveau_ramht.h" | ||
31 | #include "nouveau_dma.h" | 32 | #include "nouveau_dma.h" |
32 | 33 | ||
33 | #define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10) | 34 | #define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10) |
35 | #define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17) | ||
34 | 36 | ||
35 | struct nouveau_fence { | 37 | struct nouveau_fence { |
36 | struct nouveau_channel *channel; | 38 | struct nouveau_channel *channel; |
@@ -44,6 +46,12 @@ struct nouveau_fence { | |||
44 | void *priv; | 46 | void *priv; |
45 | }; | 47 | }; |
46 | 48 | ||
49 | struct nouveau_semaphore { | ||
50 | struct kref ref; | ||
51 | struct drm_device *dev; | ||
52 | struct drm_mm_node *mem; | ||
53 | }; | ||
54 | |||
47 | static inline struct nouveau_fence * | 55 | static inline struct nouveau_fence * |
48 | nouveau_fence(void *sync_obj) | 56 | nouveau_fence(void *sync_obj) |
49 | { | 57 | { |
@@ -236,17 +244,128 @@ nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) | |||
236 | return ret; | 244 | return ret; |
237 | } | 245 | } |
238 | 246 | ||
247 | static struct nouveau_semaphore * | ||
248 | alloc_semaphore(struct drm_device *dev) | ||
249 | { | ||
250 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
251 | struct nouveau_semaphore *sema; | ||
252 | |||
253 | if (!USE_SEMA(dev)) | ||
254 | return NULL; | ||
255 | |||
256 | sema = kmalloc(sizeof(*sema), GFP_KERNEL); | ||
257 | if (!sema) | ||
258 | goto fail; | ||
259 | |||
260 | spin_lock(&dev_priv->fence.lock); | ||
261 | sema->mem = drm_mm_search_free(&dev_priv->fence.heap, 4, 0, 0); | ||
262 | if (sema->mem) | ||
263 | sema->mem = drm_mm_get_block(sema->mem, 4, 0); | ||
264 | spin_unlock(&dev_priv->fence.lock); | ||
265 | |||
266 | if (!sema->mem) | ||
267 | goto fail; | ||
268 | |||
269 | kref_init(&sema->ref); | ||
270 | sema->dev = dev; | ||
271 | nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 0); | ||
272 | |||
273 | return sema; | ||
274 | fail: | ||
275 | kfree(sema); | ||
276 | return NULL; | ||
277 | } | ||
278 | |||
279 | static void | ||
280 | free_semaphore(struct kref *ref) | ||
281 | { | ||
282 | struct nouveau_semaphore *sema = | ||
283 | container_of(ref, struct nouveau_semaphore, ref); | ||
284 | struct drm_nouveau_private *dev_priv = sema->dev->dev_private; | ||
285 | |||
286 | spin_lock(&dev_priv->fence.lock); | ||
287 | drm_mm_put_block(sema->mem); | ||
288 | spin_unlock(&dev_priv->fence.lock); | ||
289 | |||
290 | kfree(sema); | ||
291 | } | ||
292 | |||
293 | static void | ||
294 | semaphore_work(void *priv, bool signalled) | ||
295 | { | ||
296 | struct nouveau_semaphore *sema = priv; | ||
297 | struct drm_nouveau_private *dev_priv = sema->dev->dev_private; | ||
298 | |||
299 | if (unlikely(!signalled)) | ||
300 | nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1); | ||
301 | |||
302 | kref_put(&sema->ref, free_semaphore); | ||
303 | } | ||
304 | |||
305 | static int | ||
306 | emit_semaphore(struct nouveau_channel *chan, int method, | ||
307 | struct nouveau_semaphore *sema) | ||
308 | { | ||
309 | struct drm_nouveau_private *dev_priv = sema->dev->dev_private; | ||
310 | struct nouveau_fence *fence; | ||
311 | int ret; | ||
312 | |||
313 | ret = RING_SPACE(chan, dev_priv->card_type >= NV_50 ? 6 : 4); | ||
314 | if (ret) | ||
315 | return ret; | ||
316 | |||
317 | if (dev_priv->card_type >= NV_50) { | ||
318 | BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1); | ||
319 | OUT_RING(chan, NvSema); | ||
320 | } | ||
321 | BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_OFFSET, 1); | ||
322 | OUT_RING(chan, sema->mem->start); | ||
323 | BEGIN_RING(chan, NvSubSw, method, 1); | ||
324 | OUT_RING(chan, 1); | ||
325 | |||
326 | /* Delay semaphore destruction until its work is done */ | ||
327 | ret = nouveau_fence_new(chan, &fence, true); | ||
328 | if (ret) | ||
329 | return ret; | ||
330 | |||
331 | kref_get(&sema->ref); | ||
332 | nouveau_fence_work(fence, semaphore_work, sema); | ||
333 | nouveau_fence_unref((void *)&fence); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
239 | int | 338 | int |
240 | nouveau_fence_sync(struct nouveau_fence *fence, | 339 | nouveau_fence_sync(struct nouveau_fence *fence, |
241 | struct nouveau_channel *wchan) | 340 | struct nouveau_channel *wchan) |
242 | { | 341 | { |
243 | struct nouveau_channel *chan = nouveau_fence_channel(fence); | 342 | struct nouveau_channel *chan = nouveau_fence_channel(fence); |
343 | struct drm_device *dev = wchan->dev; | ||
344 | struct nouveau_semaphore *sema; | ||
345 | int ret; | ||
244 | 346 | ||
245 | if (likely(!fence || chan == wchan || | 347 | if (likely(!fence || chan == wchan || |
246 | nouveau_fence_signalled(fence, NULL))) | 348 | nouveau_fence_signalled(fence, NULL))) |
247 | return 0; | 349 | return 0; |
248 | 350 | ||
249 | return nouveau_fence_wait(fence, NULL, false, false); | 351 | sema = alloc_semaphore(dev); |
352 | if (!sema) { | ||
353 | /* Early card or broken userspace, fall back to | ||
354 | * software sync. */ | ||
355 | return nouveau_fence_wait(fence, NULL, false, false); | ||
356 | } | ||
357 | |||
358 | /* Signal the semaphore from chan */ | ||
359 | ret = emit_semaphore(chan, NV_SW_SEMAPHORE_RELEASE, sema); | ||
360 | if (ret) | ||
361 | goto out; | ||
362 | |||
363 | /* Make wchan wait until it gets signalled */ | ||
364 | ret = emit_semaphore(wchan, NV_SW_SEMAPHORE_ACQUIRE, sema); | ||
365 | |||
366 | out: | ||
367 | kref_put(&sema->ref, free_semaphore); | ||
368 | return ret; | ||
250 | } | 369 | } |
251 | 370 | ||
252 | int | 371 | int |
@@ -258,6 +377,8 @@ nouveau_fence_flush(void *sync_obj, void *sync_arg) | |||
258 | int | 377 | int |
259 | nouveau_fence_channel_init(struct nouveau_channel *chan) | 378 | nouveau_fence_channel_init(struct nouveau_channel *chan) |
260 | { | 379 | { |
380 | struct drm_device *dev = chan->dev; | ||
381 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
261 | struct nouveau_gpuobj *obj = NULL; | 382 | struct nouveau_gpuobj *obj = NULL; |
262 | int ret; | 383 | int ret; |
263 | 384 | ||
@@ -277,6 +398,30 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) | |||
277 | BEGIN_RING(chan, NvSubSw, 0, 1); | 398 | BEGIN_RING(chan, NvSubSw, 0, 1); |
278 | OUT_RING(chan, NvSw); | 399 | OUT_RING(chan, NvSw); |
279 | 400 | ||
401 | /* Create a DMA object for the shared cross-channel sync area. */ | ||
402 | if (USE_SEMA(dev)) { | ||
403 | struct drm_mm_node *mem = dev_priv->fence.bo->bo.mem.mm_node; | ||
404 | |||
405 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, | ||
406 | mem->start << PAGE_SHIFT, | ||
407 | mem->size << PAGE_SHIFT, | ||
408 | NV_DMA_ACCESS_RW, | ||
409 | NV_DMA_TARGET_VIDMEM, &obj); | ||
410 | if (ret) | ||
411 | return ret; | ||
412 | |||
413 | ret = nouveau_ramht_insert(chan, NvSema, obj); | ||
414 | nouveau_gpuobj_ref(NULL, &obj); | ||
415 | if (ret) | ||
416 | return ret; | ||
417 | |||
418 | ret = RING_SPACE(chan, 2); | ||
419 | if (ret) | ||
420 | return ret; | ||
421 | BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1); | ||
422 | OUT_RING(chan, NvSema); | ||
423 | } | ||
424 | |||
280 | FIRE_RING(chan); | 425 | FIRE_RING(chan); |
281 | 426 | ||
282 | INIT_LIST_HEAD(&chan->fence.pending); | 427 | INIT_LIST_HEAD(&chan->fence.pending); |
@@ -302,3 +447,51 @@ nouveau_fence_channel_fini(struct nouveau_channel *chan) | |||
302 | } | 447 | } |
303 | } | 448 | } |
304 | 449 | ||
450 | int | ||
451 | nouveau_fence_init(struct drm_device *dev) | ||
452 | { | ||
453 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
454 | int ret; | ||
455 | |||
456 | /* Create a shared VRAM heap for cross-channel sync. */ | ||
457 | if (USE_SEMA(dev)) { | ||
458 | ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, | ||
459 | 0, 0, false, true, &dev_priv->fence.bo); | ||
460 | if (ret) | ||
461 | return ret; | ||
462 | |||
463 | ret = nouveau_bo_pin(dev_priv->fence.bo, TTM_PL_FLAG_VRAM); | ||
464 | if (ret) | ||
465 | goto fail; | ||
466 | |||
467 | ret = nouveau_bo_map(dev_priv->fence.bo); | ||
468 | if (ret) | ||
469 | goto fail; | ||
470 | |||
471 | ret = drm_mm_init(&dev_priv->fence.heap, 0, | ||
472 | dev_priv->fence.bo->bo.mem.size); | ||
473 | if (ret) | ||
474 | goto fail; | ||
475 | |||
476 | spin_lock_init(&dev_priv->fence.lock); | ||
477 | } | ||
478 | |||
479 | return 0; | ||
480 | fail: | ||
481 | nouveau_bo_unmap(dev_priv->fence.bo); | ||
482 | nouveau_bo_ref(NULL, &dev_priv->fence.bo); | ||
483 | return ret; | ||
484 | } | ||
485 | |||
486 | void | ||
487 | nouveau_fence_fini(struct drm_device *dev) | ||
488 | { | ||
489 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
490 | |||
491 | if (USE_SEMA(dev)) { | ||
492 | drm_mm_takedown(&dev_priv->fence.heap); | ||
493 | nouveau_bo_unmap(dev_priv->fence.bo); | ||
494 | nouveau_bo_unpin(dev_priv->fence.bo); | ||
495 | nouveau_bo_ref(NULL, &dev_priv->fence.bo); | ||
496 | } | ||
497 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 75bce914e7b..ed7757f1408 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c | |||
@@ -639,9 +639,13 @@ nouveau_card_init(struct drm_device *dev) | |||
639 | /* what about PVIDEO/PCRTC/PRAMDAC etc? */ | 639 | /* what about PVIDEO/PCRTC/PRAMDAC etc? */ |
640 | 640 | ||
641 | if (!engine->graph.accel_blocked) { | 641 | if (!engine->graph.accel_blocked) { |
642 | ret = nouveau_card_init_channel(dev); | 642 | ret = nouveau_fence_init(dev); |
643 | if (ret) | 643 | if (ret) |
644 | goto out_irq; | 644 | goto out_irq; |
645 | |||
646 | ret = nouveau_card_init_channel(dev); | ||
647 | if (ret) | ||
648 | goto out_fence; | ||
645 | } | 649 | } |
646 | 650 | ||
647 | ret = nouveau_backlight_init(dev); | 651 | ret = nouveau_backlight_init(dev); |
@@ -652,6 +656,8 @@ nouveau_card_init(struct drm_device *dev) | |||
652 | drm_kms_helper_poll_init(dev); | 656 | drm_kms_helper_poll_init(dev); |
653 | return 0; | 657 | return 0; |
654 | 658 | ||
659 | out_fence: | ||
660 | nouveau_fence_fini(dev); | ||
655 | out_irq: | 661 | out_irq: |
656 | drm_irq_uninstall(dev); | 662 | drm_irq_uninstall(dev); |
657 | out_display: | 663 | out_display: |
@@ -695,7 +701,8 @@ static void nouveau_card_takedown(struct drm_device *dev) | |||
695 | 701 | ||
696 | nouveau_backlight_exit(dev); | 702 | nouveau_backlight_exit(dev); |
697 | 703 | ||
698 | if (dev_priv->channel) { | 704 | if (!engine->graph.accel_blocked) { |
705 | nouveau_fence_fini(dev); | ||
699 | nouveau_channel_free(dev_priv->channel); | 706 | nouveau_channel_free(dev_priv->channel); |
700 | dev_priv->channel = NULL; | 707 | dev_priv->channel = NULL; |
701 | } | 708 | } |