|
@@ -126,6 +126,8 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size)
|
|
|
hw_addr, cur_addr, next_addr);
|
|
|
return -1;
|
|
|
}
|
|
|
+ if ((cur_addr < hw_addr) && (next_addr >= hw_addr))
|
|
|
+ msleep(1);
|
|
|
} while ((cur_addr < hw_addr) && (next_addr >= hw_addr));
|
|
|
return 0;
|
|
|
}
|
|
@@ -416,27 +418,50 @@ static int via_hook_segment(drm_via_private_t * dev_priv,
|
|
|
int paused, count;
|
|
|
volatile uint32_t *paused_at = dev_priv->last_pause_ptr;
|
|
|
uint32_t reader,ptr;
|
|
|
+ uint32_t diff;
|
|
|
|
|
|
paused = 0;
|
|
|
via_flush_write_combine();
|
|
|
(void) *(volatile uint32_t *)(via_get_dma(dev_priv) -1);
|
|
|
+
|
|
|
*paused_at = pause_addr_lo;
|
|
|
via_flush_write_combine();
|
|
|
(void) *paused_at;
|
|
|
+
|
|
|
reader = *(dev_priv->hw_addr_ptr);
|
|
|
ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) +
|
|
|
dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4;
|
|
|
+
|
|
|
dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1;
|
|
|
|
|
|
- if ((ptr - reader) <= dev_priv->dma_diff ) {
|
|
|
- count = 10000000;
|
|
|
- while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--);
|
|
|
+ /*
|
|
|
+ * If there is a possibility that the command reader will
|
|
|
+ * miss the new pause address and pause on the old one,
|
|
|
+ * In that case we need to program the new start address
|
|
|
+ * using PCI.
|
|
|
+ */
|
|
|
+
|
|
|
+ diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
|
|
|
+ count = 10000000;
|
|
|
+ while(diff == 0 && count--) {
|
|
|
+ paused = (VIA_READ(0x41c) & 0x80000000);
|
|
|
+ if (paused)
|
|
|
+ break;
|
|
|
+ reader = *(dev_priv->hw_addr_ptr);
|
|
|
+ diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
|
|
|
}
|
|
|
|
|
|
+ paused = VIA_READ(0x41c) & 0x80000000;
|
|
|
+
|
|
|
if (paused && !no_pci_fire) {
|
|
|
reader = *(dev_priv->hw_addr_ptr);
|
|
|
- if ((ptr - reader) == dev_priv->dma_diff) {
|
|
|
-
|
|
|
+ diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff;
|
|
|
+ diff &= (dev_priv->dma_high - 1);
|
|
|
+ if (diff != 0 && diff < (dev_priv->dma_high >> 1)) {
|
|
|
+ DRM_ERROR("Paused at incorrect address. "
|
|
|
+ "0x%08x, 0x%08x 0x%08x\n",
|
|
|
+ ptr, reader, dev_priv->dma_diff);
|
|
|
+ } else if (diff == 0) {
|
|
|
/*
|
|
|
* There is a concern that these writes may stall the PCI bus
|
|
|
* if the GPU is not idle. However, idling the GPU first
|
|
@@ -577,6 +602,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
|
|
|
uint32_t pause_addr_lo, pause_addr_hi;
|
|
|
uint32_t jump_addr_lo, jump_addr_hi;
|
|
|
volatile uint32_t *last_pause_ptr;
|
|
|
+ uint32_t dma_low_save1, dma_low_save2;
|
|
|
|
|
|
agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr;
|
|
|
via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi,
|
|
@@ -603,8 +629,29 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv)
|
|
|
&pause_addr_lo, 0);
|
|
|
|
|
|
*last_pause_ptr = pause_addr_lo;
|
|
|
+ dma_low_save1 = dev_priv->dma_low;
|
|
|
|
|
|
- via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0);
|
|
|
+ /*
|
|
|
+ * Now, set a trap that will pause the regulator if it tries to rerun the old
|
|
|
+ * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause
|
|
|
+ * and reissues the jump command over PCI, while the regulator has already taken the jump
|
|
|
+ * and actually paused at the current buffer end).
|
|
|
+ * There appears to be no other way to detect this condition, since the hw_addr_pointer
|
|
|
+ * does not seem to get updated immediately when a jump occurs.
|
|
|
+ */
|
|
|
+
|
|
|
+ last_pause_ptr =
|
|
|
+ via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
|
|
|
+ &pause_addr_lo, 0) - 1;
|
|
|
+ via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi,
|
|
|
+ &pause_addr_lo, 0);
|
|
|
+ *last_pause_ptr = pause_addr_lo;
|
|
|
+
|
|
|
+ dma_low_save2 = dev_priv->dma_low;
|
|
|
+ dev_priv->dma_low = dma_low_save1;
|
|
|
+ via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0);
|
|
|
+ dev_priv->dma_low = dma_low_save2;
|
|
|
+ via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0);
|
|
|
}
|
|
|
|
|
|
|