diff options
Diffstat (limited to 'drivers/char/drm/via_dma.c')
-rw-r--r-- | drivers/char/drm/via_dma.c | 111 |
1 files changed, 46 insertions, 65 deletions
diff --git a/drivers/char/drm/via_dma.c b/drivers/char/drm/via_dma.c index c0539c6299cf..13a9c5ca4593 100644 --- a/drivers/char/drm/via_dma.c +++ b/drivers/char/drm/via_dma.c | |||
@@ -252,7 +252,7 @@ static int via_dma_init(DRM_IOCTL_ARGS) | |||
252 | break; | 252 | break; |
253 | case VIA_DMA_INITIALIZED: | 253 | case VIA_DMA_INITIALIZED: |
254 | retcode = (dev_priv->ring.virtual_start != NULL) ? | 254 | retcode = (dev_priv->ring.virtual_start != NULL) ? |
255 | 0 : DRM_ERR(EFAULT); | 255 | 0 : DRM_ERR(EFAULT); |
256 | break; | 256 | break; |
257 | default: | 257 | default: |
258 | retcode = DRM_ERR(EINVAL); | 258 | retcode = DRM_ERR(EINVAL); |
@@ -432,56 +432,34 @@ static int via_hook_segment(drm_via_private_t * dev_priv, | |||
432 | { | 432 | { |
433 | int paused, count; | 433 | int paused, count; |
434 | volatile uint32_t *paused_at = dev_priv->last_pause_ptr; | 434 | volatile uint32_t *paused_at = dev_priv->last_pause_ptr; |
435 | uint32_t reader,ptr; | ||
435 | 436 | ||
437 | paused = 0; | ||
436 | via_flush_write_combine(); | 438 | via_flush_write_combine(); |
437 | while (!*(via_get_dma(dev_priv) - 1)) ; | 439 | (void) *(volatile uint32_t *)(via_get_dma(dev_priv) -1); |
438 | *dev_priv->last_pause_ptr = pause_addr_lo; | 440 | *paused_at = pause_addr_lo; |
439 | via_flush_write_combine(); | 441 | via_flush_write_combine(); |
440 | 442 | (void) *paused_at; | |
441 | /* | 443 | reader = *(dev_priv->hw_addr_ptr); |
442 | * The below statement is inserted to really force the flush. | 444 | ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) + |
443 | * Not sure it is needed. | 445 | dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; |
444 | */ | ||
445 | |||
446 | while (!*dev_priv->last_pause_ptr) ; | ||
447 | dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; | 446 | dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; |
448 | while (!*dev_priv->last_pause_ptr) ; | ||
449 | 447 | ||
450 | paused = 0; | 448 | if ((ptr - reader) <= dev_priv->dma_diff ) { |
451 | count = 20; | 449 | count = 10000000; |
452 | 450 | while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--); | |
453 | while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--) ; | ||
454 | if ((count <= 8) && (count >= 0)) { | ||
455 | uint32_t rgtr, ptr; | ||
456 | rgtr = *(dev_priv->hw_addr_ptr); | ||
457 | ptr = ((volatile char *)dev_priv->last_pause_ptr - | ||
458 | dev_priv->dma_ptr) + dev_priv->dma_offset + | ||
459 | (uint32_t) dev_priv->agpAddr + 4 - CMDBUF_ALIGNMENT_SIZE; | ||
460 | if (rgtr <= ptr) { | ||
461 | DRM_ERROR | ||
462 | ("Command regulator\npaused at count %d, address %x, " | ||
463 | "while current pause address is %x.\n" | ||
464 | "Please mail this message to " | ||
465 | "<unichrome-devel@lists.sourceforge.net>\n", count, | ||
466 | rgtr, ptr); | ||
467 | } | ||
468 | } | 451 | } |
469 | 452 | ||
470 | if (paused && !no_pci_fire) { | 453 | if (paused && !no_pci_fire) { |
471 | uint32_t rgtr, ptr; | 454 | reader = *(dev_priv->hw_addr_ptr); |
472 | uint32_t ptr_low; | 455 | if ((ptr - reader) == dev_priv->dma_diff) { |
473 | 456 | ||
474 | count = 1000000; | 457 | /* |
475 | while ((VIA_READ(VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY) | 458 | * There is a concern that these writes may stall the PCI bus |
476 | && count--) ; | 459 | * if the GPU is not idle. However, idling the GPU first |
460 | * doesn't make a difference. | ||
461 | */ | ||
477 | 462 | ||
478 | rgtr = *(dev_priv->hw_addr_ptr); | ||
479 | ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) + | ||
480 | dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; | ||
481 | |||
482 | ptr_low = (ptr > 3 * CMDBUF_ALIGNMENT_SIZE) ? | ||
483 | ptr - 3 * CMDBUF_ALIGNMENT_SIZE : 0; | ||
484 | if (rgtr <= ptr && rgtr >= ptr_low) { | ||
485 | VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); | 463 | VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); |
486 | VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi); | 464 | VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi); |
487 | VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo); | 465 | VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo); |
@@ -494,6 +472,9 @@ static int via_hook_segment(drm_via_private_t * dev_priv, | |||
494 | static int via_wait_idle(drm_via_private_t * dev_priv) | 472 | static int via_wait_idle(drm_via_private_t * dev_priv) |
495 | { | 473 | { |
496 | int count = 10000000; | 474 | int count = 10000000; |
475 | |||
476 | while (!(VIA_READ(VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && count--); | ||
477 | |||
497 | while (count-- && (VIA_READ(VIA_REG_STATUS) & | 478 | while (count-- && (VIA_READ(VIA_REG_STATUS) & |
498 | (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | | 479 | (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | |
499 | VIA_3D_ENG_BUSY))) ; | 480 | VIA_3D_ENG_BUSY))) ; |
@@ -537,6 +518,9 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) | |||
537 | uint32_t end_addr, end_addr_lo; | 518 | uint32_t end_addr, end_addr_lo; |
538 | uint32_t command; | 519 | uint32_t command; |
539 | uint32_t agp_base; | 520 | uint32_t agp_base; |
521 | uint32_t ptr; | ||
522 | uint32_t reader; | ||
523 | int count; | ||
540 | 524 | ||
541 | dev_priv->dma_low = 0; | 525 | dev_priv->dma_low = 0; |
542 | 526 | ||
@@ -554,7 +538,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) | |||
554 | &pause_addr_hi, &pause_addr_lo, 1) - 1; | 538 | &pause_addr_hi, &pause_addr_lo, 1) - 1; |
555 | 539 | ||
556 | via_flush_write_combine(); | 540 | via_flush_write_combine(); |
557 | while (!*dev_priv->last_pause_ptr) ; | 541 | (void) *(volatile uint32_t *)dev_priv->last_pause_ptr; |
558 | 542 | ||
559 | VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); | 543 | VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); |
560 | VIA_WRITE(VIA_REG_TRANSPACE, command); | 544 | VIA_WRITE(VIA_REG_TRANSPACE, command); |
@@ -566,6 +550,24 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) | |||
566 | DRM_WRITEMEMORYBARRIER(); | 550 | DRM_WRITEMEMORYBARRIER(); |
567 | VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); | 551 | VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); |
568 | VIA_READ(VIA_REG_TRANSPACE); | 552 | VIA_READ(VIA_REG_TRANSPACE); |
553 | |||
554 | dev_priv->dma_diff = 0; | ||
555 | |||
556 | count = 10000000; | ||
557 | while (!(VIA_READ(0x41c) & 0x80000000) && count--); | ||
558 | |||
559 | reader = *(dev_priv->hw_addr_ptr); | ||
560 | ptr = ((volatile char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) + | ||
561 | dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; | ||
562 | |||
563 | /* | ||
564 | * This is the difference between where we tell the | ||
565 | * command reader to pause and where it actually pauses. | ||
566 | * This differs between hw implementation so we need to | ||
567 | * detect it. | ||
568 | */ | ||
569 | |||
570 | dev_priv->dma_diff = ptr - reader; | ||
569 | } | 571 | } |
570 | 572 | ||
571 | static void via_pad_cache(drm_via_private_t * dev_priv, int qwords) | 573 | static void via_pad_cache(drm_via_private_t * dev_priv, int qwords) |
@@ -592,7 +594,6 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) | |||
592 | uint32_t pause_addr_lo, pause_addr_hi; | 594 | uint32_t pause_addr_lo, pause_addr_hi; |
593 | uint32_t jump_addr_lo, jump_addr_hi; | 595 | uint32_t jump_addr_lo, jump_addr_hi; |
594 | volatile uint32_t *last_pause_ptr; | 596 | volatile uint32_t *last_pause_ptr; |
595 | uint32_t dma_low_save1, dma_low_save2; | ||
596 | 597 | ||
597 | agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; | 598 | agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; |
598 | via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, | 599 | via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, |
@@ -619,31 +620,11 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) | |||
619 | &pause_addr_lo, 0); | 620 | &pause_addr_lo, 0); |
620 | 621 | ||
621 | *last_pause_ptr = pause_addr_lo; | 622 | *last_pause_ptr = pause_addr_lo; |
622 | dma_low_save1 = dev_priv->dma_low; | ||
623 | |||
624 | /* | ||
625 | * Now, set a trap that will pause the regulator if it tries to rerun the old | ||
626 | * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause | ||
627 | * and reissues the jump command over PCI, while the regulator has already taken the jump | ||
628 | * and actually paused at the current buffer end). | ||
629 | * There appears to be no other way to detect this condition, since the hw_addr_pointer | ||
630 | * does not seem to get updated immediately when a jump occurs. | ||
631 | */ | ||
632 | 623 | ||
633 | last_pause_ptr = | 624 | via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0); |
634 | via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, | ||
635 | &pause_addr_lo, 0) - 1; | ||
636 | via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, | ||
637 | &pause_addr_lo, 0); | ||
638 | *last_pause_ptr = pause_addr_lo; | ||
639 | |||
640 | dma_low_save2 = dev_priv->dma_low; | ||
641 | dev_priv->dma_low = dma_low_save1; | ||
642 | via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0); | ||
643 | dev_priv->dma_low = dma_low_save2; | ||
644 | via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); | ||
645 | } | 625 | } |
646 | 626 | ||
627 | |||
647 | static void via_cmdbuf_rewind(drm_via_private_t * dev_priv) | 628 | static void via_cmdbuf_rewind(drm_via_private_t * dev_priv) |
648 | { | 629 | { |
649 | via_cmdbuf_jump(dev_priv); | 630 | via_cmdbuf_jump(dev_priv); |