|
@@ -252,7 +252,7 @@ static int via_dma_init(DRM_IOCTL_ARGS)
|
|
|
break;
|
|
|
case VIA_DMA_INITIALIZED:
|
|
|
retcode = (dev_priv->ring.virtual_start != NULL) ?
|
|
|
- 0 : DRM_ERR(EFAULT);
|
|
|
+ 0 : DRM_ERR(EFAULT);
|
|
|
break;
|
|
|
default:
|
|
|
retcode = DRM_ERR(EINVAL);
|
|
@@ -432,56 +432,34 @@ 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;
|
|
|
|
|
|
+ paused = 0;
|
|
|
via_flush_write_combine();
|
|
|
- while (!*(via_get_dma(dev_priv) - 1)) ;
|
|
|
- *dev_priv->last_pause_ptr = pause_addr_lo;
|
|
|
+ (void) *(volatile uint32_t *)(via_get_dma(dev_priv) -1);
|
|
|
+ *paused_at = pause_addr_lo;
|
|
|
via_flush_write_combine();
|
|
|
-
|
|
|
- /*
|
|
|
- * The below statement is inserted to really force the flush.
|
|
|
- * Not sure it is needed.
|
|
|
- */
|
|
|
-
|
|
|
- while (!*dev_priv->last_pause_ptr) ;
|
|
|
+ (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;
|
|
|
- while (!*dev_priv->last_pause_ptr) ;
|
|
|
|
|
|
- paused = 0;
|
|
|
- count = 20;
|
|
|
-
|
|
|
- while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--) ;
|
|
|
- if ((count <= 8) && (count >= 0)) {
|
|
|
- uint32_t rgtr, ptr;
|
|
|
- rgtr = *(dev_priv->hw_addr_ptr);
|
|
|
- ptr = ((volatile char *)dev_priv->last_pause_ptr -
|
|
|
- dev_priv->dma_ptr) + dev_priv->dma_offset +
|
|
|
- (uint32_t) dev_priv->agpAddr + 4 - CMDBUF_ALIGNMENT_SIZE;
|
|
|
- if (rgtr <= ptr) {
|
|
|
- DRM_ERROR
|
|
|
- ("Command regulator\npaused at count %d, address %x, "
|
|
|
- "while current pause address is %x.\n"
|
|
|
- "Please mail this message to "
|
|
|
- "<unichrome-devel@lists.sourceforge.net>\n", count,
|
|
|
- rgtr, ptr);
|
|
|
- }
|
|
|
+ if ((ptr - reader) <= dev_priv->dma_diff ) {
|
|
|
+ count = 10000000;
|
|
|
+ while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--);
|
|
|
}
|
|
|
|
|
|
if (paused && !no_pci_fire) {
|
|
|
- uint32_t rgtr, ptr;
|
|
|
- uint32_t ptr_low;
|
|
|
+ reader = *(dev_priv->hw_addr_ptr);
|
|
|
+ if ((ptr - reader) == dev_priv->dma_diff) {
|
|
|
|
|
|
- count = 1000000;
|
|
|
- while ((VIA_READ(VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY)
|
|
|
- && count--) ;
|
|
|
+ /*
|
|
|
+ * There is a concern that these writes may stall the PCI bus
|
|
|
+ * if the GPU is not idle. However, idling the GPU first
|
|
|
+ * doesn't make a difference.
|
|
|
+ */
|
|
|
|
|
|
- rgtr = *(dev_priv->hw_addr_ptr);
|
|
|
- ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) +
|
|
|
- dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4;
|
|
|
-
|
|
|
- ptr_low = (ptr > 3 * CMDBUF_ALIGNMENT_SIZE) ?
|
|
|
- ptr - 3 * CMDBUF_ALIGNMENT_SIZE : 0;
|
|
|
- if (rgtr <= ptr && rgtr >= ptr_low) {
|
|
|
VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16));
|
|
|
VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_hi);
|
|
|
VIA_WRITE(VIA_REG_TRANSPACE, pause_addr_lo);
|
|
@@ -494,6 +472,9 @@ static int via_hook_segment(drm_via_private_t * dev_priv,
|
|
|
static int via_wait_idle(drm_via_private_t * dev_priv)
|
|
|
{
|
|
|
int count = 10000000;
|
|
|
+
|
|
|
+ while (!(VIA_READ(VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && count--);
|
|
|
+
|
|
|
while (count-- && (VIA_READ(VIA_REG_STATUS) &
|
|
|
(VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY |
|
|
|
VIA_3D_ENG_BUSY))) ;
|
|
@@ -537,6 +518,9 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv)
|
|
|
uint32_t end_addr, end_addr_lo;
|
|
|
uint32_t command;
|
|
|
uint32_t agp_base;
|
|
|
+ uint32_t ptr;
|
|
|
+ uint32_t reader;
|
|
|
+ int count;
|
|
|
|
|
|
dev_priv->dma_low = 0;
|
|
|
|
|
@@ -554,7 +538,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv)
|
|
|
&pause_addr_hi, &pause_addr_lo, 1) - 1;
|
|
|
|
|
|
via_flush_write_combine();
|
|
|
- while (!*dev_priv->last_pause_ptr) ;
|
|
|
+ (void) *(volatile uint32_t *)dev_priv->last_pause_ptr;
|
|
|
|
|
|
VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16));
|
|
|
VIA_WRITE(VIA_REG_TRANSPACE, command);
|
|
@@ -566,6 +550,24 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv)
|
|
|
DRM_WRITEMEMORYBARRIER();
|
|
|
VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK);
|
|
|
VIA_READ(VIA_REG_TRANSPACE);
|
|
|
+
|
|
|
+ dev_priv->dma_diff = 0;
|
|
|
+
|
|
|
+ count = 10000000;
|
|
|
+ while (!(VIA_READ(0x41c) & 0x80000000) && count--);
|
|
|
+
|
|
|
+ reader = *(dev_priv->hw_addr_ptr);
|
|
|
+ ptr = ((volatile char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) +
|
|
|
+ dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This is the difference between where we tell the
|
|
|
+ * command reader to pause and where it actually pauses.
|
|
|
+ * This differs between hw implementation so we need to
|
|
|
+ * detect it.
|
|
|
+ */
|
|
|
+
|
|
|
+ dev_priv->dma_diff = ptr - reader;
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
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,
|
|
@@ -619,31 +620,11 @@ 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;
|
|
|
-
|
|
|
- /*
|
|
|
- * 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);
|
|
|
+ via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
static void via_cmdbuf_rewind(drm_via_private_t * dev_priv)
|
|
|
{
|
|
|
via_cmdbuf_jump(dev_priv);
|