Skip to content

Commit

Permalink
drm/via: attempt again to stabilise the AGP DMA command submission.
Browse files Browse the repository at this point in the history
It's worth remembering that all new bright ideas on how to make this command reader work properly and according to docs will probably fail :( Bring in some old code.

Also allow a larger SG-DMA download stride, and remove unnecessary waits for
command regulators pauses.

Signed-off-by: Dave Airlie <airlied@redhat.com>
  • Loading branch information
Thomas Hellstrom authored and Dave Airlie committed Mar 17, 2008
1 parent 9df5808 commit f0fb6d7
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 7 deletions.
59 changes: 53 additions & 6 deletions drivers/char/drm/via_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}


Expand Down
2 changes: 1 addition & 1 deletion drivers/char/drm/via_dmablit.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ via_build_sg_info(struct drm_device *dev, drm_via_sg_info_t *vsg, drm_via_dmabli
* (Not a big limitation anyway.)
*/

if ((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) {
if ((xfer->mem_stride - xfer->line_length) > 2*PAGE_SIZE) {
DRM_ERROR("Too large system memory stride. Stride: %d, "
"Length: %d\n", xfer->mem_stride, xfer->line_length);
return -EINVAL;
Expand Down

0 comments on commit f0fb6d7

Please sign in to comment.