diff options
author | Thomas Hellstrom <unichrome@shipmail.org> | 2004-10-30 13:01:48 +0000 |
---|---|---|
committer | Thomas Hellstrom <unichrome@shipmail.org> | 2004-10-30 13:01:48 +0000 |
commit | 8264e2c8aa6b2811bca0e45c50ac3328d14bfbf7 (patch) | |
tree | 10e5cf5c496a985b444718fafa8d84e7c84e8eed /shared-core | |
parent | 816a2917099b3ff214dc357c5b41984f34902cf3 (diff) |
Some stabilizing work to the DMA ring-buffer code. Temporarily replaced the
rewind jump with a reinitialization. This makes the code stable on
CLE266 and KM400.
Diffstat (limited to 'shared-core')
-rw-r--r-- | shared-core/via_dma.c | 218 | ||||
-rw-r--r-- | shared-core/via_drv.h | 6 |
2 files changed, 130 insertions, 94 deletions
diff --git a/shared-core/via_dma.c b/shared-core/via_dma.c index 574858b9..a0fc8fd4 100644 --- a/shared-core/via_dma.c +++ b/shared-core/via_dma.c @@ -16,6 +16,14 @@ #include "via_drv.h" #define VIA_2D_CMD 0xF0000000 +#define via_flush_write_combine() DRM_MEMORYBARRIER() + +#define VIA_OUT_RING_QW(w1,w2) \ + *vb++ = (w1); \ + *vb++ = (w2); \ + dev_priv->dma_low += 8; + + static void via_cmdbuf_start(drm_via_private_t * dev_priv); static void via_cmdbuf_pause(drm_via_private_t * dev_priv); @@ -23,6 +31,7 @@ static void via_cmdbuf_reset(drm_via_private_t * dev_priv); static void via_cmdbuf_rewind(drm_via_private_t * dev_priv); static int via_wait_idle(drm_via_private_t * dev_priv); + /* * This function needs to be extended whenever a new command set * is implemented. Currently it works only for the 2D engine @@ -55,7 +64,7 @@ static int via_check_command_stream(const uint32_t * buf, unsigned int size) if ((offset > ((0x3FF >> 2) | VIA_2D_CMD)) && (offset < ((0xC00 >> 2) | VIA_2D_CMD))) { DRM_ERROR - ("Attempt to access Burst Command / 3D Area.\n"); + ("Attempt to access Burst Command / 3D Area.\n"); return DRM_ERR(EINVAL); } else if (offset > ((0xDFF >> 2) | VIA_2D_CMD)) { DRM_ERROR("Attempt to access DMA or VGA registers.\n"); @@ -81,12 +90,7 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) uint32_t count; hw_addr_ptr = dev_priv->hw_addr_ptr; cur_addr = dev_priv->dma_low; - /* At high resolution (i.e. 1280x1024) and with high workload within - * a short commmand stream, the following test will fail. It may be - * that the engine is too busy to update hw_addr. Therefore, add - * a large 64KB window between buffer head and tail. - */ - next_addr = cur_addr + size + 64 * 1024; + next_addr = cur_addr + size; count = 1000000; /* How long is this? */ do { hw_addr = *hw_addr_ptr - agp_base; @@ -122,7 +126,7 @@ int via_dma_cleanup(drm_device_t * dev) { if (dev->dev_private) { drm_via_private_t *dev_priv = - (drm_via_private_t *) dev->dev_private; + (drm_via_private_t *) dev->dev_private; if (dev_priv->ring.virtual_start) { via_cmdbuf_reset(dev_priv); @@ -321,7 +325,7 @@ static int via_dispatch_pci_cmdbuffer(drm_device_t * dev, if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size)) return DRM_ERR(EFAULT); ret = - via_parse_pci_cmdbuffer(dev, dev_priv->pci_buf, cmd->size); + via_parse_pci_cmdbuffer(dev, dev_priv->pci_buf, cmd->size); } return ret; } @@ -375,20 +379,16 @@ int via_pci_cmdbuffer(DRM_IOCTL_ARGS) dev_priv->dma_low +=8; \ } -static uint32_t via_swap_count = 0; - static inline uint32_t *via_align_buffer(drm_via_private_t * dev_priv, uint32_t * vb, int qw_count) { for (; qw_count > 0; --qw_count) { - *vb++ = (0xcc000000 | (dev_priv->dma_low & 0xffffff)); - *vb++ = (0xdd400000 | via_swap_count); - dev_priv->dma_low += 8; + VIA_OUT_RING_QW(HC_DUMMY, HC_DUMMY); } - via_swap_count = (via_swap_count + 1) & 0xffff; return vb; } + /* * This function is used internally by ring buffer mangement code. * @@ -399,6 +399,58 @@ static inline uint32_t *via_get_dma(drm_via_private_t * dev_priv) return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low); } +/* + * Hooks a segment of data into the tail of the ring-buffer by + * modifying the pause address stored in the buffer itself. If + * the regulator has already paused, restart it. + */ +static int via_hook_segment(drm_via_private_t *dev_priv, + uint32_t pause_addr_hi, uint32_t pause_addr_lo, + int no_pci_fire) +{ + int paused, count; + + via_flush_write_combine(); + while(! *(via_get_dma(dev_priv)-1)); + *dev_priv->last_pause_ptr = 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); + dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; + + paused = 0; + count = 3; + + while (!(paused = (VIA_READ(0x41c) & 0x80000000)) && count--); + if ((count < 1) && (count >= 0)) { + uint32_t rgtr, ptr; + rgtr = *(dev_priv->hw_addr_ptr); + ptr = ((char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) + + dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4 - 0x100; + 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 (paused && !no_pci_fire) { + 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); + } + return paused; +} + + + static int via_wait_idle(drm_via_private_t * dev_priv) { int count = 10000000; @@ -408,29 +460,50 @@ static int via_wait_idle(drm_via_private_t * dev_priv) return count; } -static inline void via_dummy_bitblt(drm_via_private_t * dev_priv) +static uint32_t *via_align_cmd(drm_via_private_t * dev_priv, uint32_t cmd_type, + uint32_t addr, uint32_t *cmd_addr_hi, + uint32_t *cmd_addr_lo) { - uint32_t *vb = via_get_dma(dev_priv); - /* GEDST */ - SetReg2DAGP(0x0C, (0 | (0 << 16))); - /* GEWD */ - SetReg2DAGP(0x10, 0 | (0 << 16)); - /* BITBLT */ - SetReg2DAGP(0x0, 0x1 | 0x2000 | 0xAA000000); + uint32_t agp_base; + uint32_t cmd_addr, addr_lo, addr_hi; + uint32_t *vb; + uint32_t qw_pad_count; + + via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE); + + vb = via_get_dma(dev_priv); + VIA_OUT_RING_QW( HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | + (VIA_REG_TRANSPACE >> 2), HC_ParaType_PreCR << 16); + agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; + qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) - + ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3); + + + cmd_addr = (addr) ? addr : + agp_base + dev_priv->dma_low - 8 + (qw_pad_count << 3); + addr_lo = ((HC_SubA_HAGPBpL << 24) | cmd_type | + (cmd_addr & 0xffffff)); + addr_hi = ((HC_SubA_HAGPBpH << 24) | (cmd_addr >> 24)); + + vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1); + VIA_OUT_RING_QW(*cmd_addr_hi = addr_hi, + *cmd_addr_lo = addr_lo); + return vb; } + + + static void via_cmdbuf_start(drm_via_private_t * dev_priv) { - uint32_t agp_base; - uint32_t pause_addr, pause_addr_lo, pause_addr_hi; + uint32_t pause_addr_lo, pause_addr_hi; uint32_t start_addr, start_addr_lo; uint32_t end_addr, end_addr_lo; - uint32_t qw_pad_count; uint32_t command; - uint32_t *vb; + uint32_t agp_base; + dev_priv->dma_low = 0; - vb = via_get_dma(dev_priv); agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; start_addr = agp_base; @@ -441,25 +514,12 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) command = ((HC_SubA_HAGPCMNT << 24) | (start_addr >> 24) | ((end_addr & 0xff000000) >> 16)); - *vb++ = HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | - (VIA_REG_TRANSPACE >> 2); - *vb++ = (HC_ParaType_PreCR << 16); - dev_priv->dma_low += 8; + dev_priv->last_pause_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, + &pause_addr_hi, & pause_addr_lo) - 1; - qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) - - ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3); - - pause_addr = agp_base + dev_priv->dma_low - 8 + (qw_pad_count << 3); - pause_addr_lo = ((HC_SubA_HAGPBpL << 24) | - HC_HAGPBpID_PAUSE | (pause_addr & 0xffffff)); - pause_addr_hi = ((HC_SubA_HAGPBpH << 24) | (pause_addr >> 24)); - - vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1); - - *vb++ = pause_addr_hi; - *vb++ = pause_addr_lo; - dev_priv->dma_low += 8; - dev_priv->last_pause_ptr = vb - 1; + via_flush_write_combine(); + while(! *dev_priv->last_pause_ptr); VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); VIA_WRITE(VIA_REG_TRANSPACE, command); @@ -471,6 +531,17 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); } +static inline void via_dummy_bitblt(drm_via_private_t * dev_priv) +{ + uint32_t *vb = via_get_dma(dev_priv); + /* GEDST */ + SetReg2DAGP(0x0C, (0 | (0 << 16))); + /* GEWD */ + SetReg2DAGP(0x10, 0 | (0 << 16)); + /* BITBLT */ + SetReg2DAGP(0x0, 0x1 | 0x2000 | 0xAA000000); +} + static void via_cmdbuf_jump(drm_via_private_t * dev_priv) { @@ -490,7 +561,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) * regulator for a moment seems to solve the problem. */ via_cmdbuf_wait(dev_priv, 48); - via_dummy_bitblt(dev_priv); + via_dummy_bitblt(dev_priv); via_cmdbuf_wait(dev_priv, 2 * CMDBUF_ALIGNMENT_SIZE); @@ -563,62 +634,27 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) *vb++ = pause_addr_hi; *vb++ = pause_addr_lo; dev_priv->dma_low += 8; + via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0); +} - *dev_priv->last_pause_ptr = jump_addr_lo; - dev_priv->last_pause_ptr = vb - 1; - if (VIA_READ(0x41c) & 0x80000000) { - VIA_WRITE(VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); - VIA_WRITE(VIA_REG_TRANSPACE, jump_addr_hi); - VIA_WRITE(VIA_REG_TRANSPACE, jump_addr_lo); - } -} static void via_cmdbuf_rewind(drm_via_private_t * dev_priv) { - via_cmdbuf_pause(dev_priv); - via_cmdbuf_jump(dev_priv); + /* via_cmdbuf_jump(dev_priv); */ + via_cmdbuf_reset(dev_priv); + via_cmdbuf_start(dev_priv); } static void via_cmdbuf_flush(drm_via_private_t * dev_priv, uint32_t cmd_type) { - uint32_t agp_base; - uint32_t pause_addr, pause_addr_lo, pause_addr_hi; - uint32_t *vb; - uint32_t qw_pad_count; + uint32_t pause_addr_lo, pause_addr_hi; - via_cmdbuf_wait(dev_priv, 0x200); - - vb = via_get_dma(dev_priv); - *vb++ = HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | - (VIA_REG_TRANSPACE >> 2); - *vb++ = (HC_ParaType_PreCR << 16); - dev_priv->dma_low += 8; - - agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; - qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) - - ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3); - - pause_addr = agp_base + dev_priv->dma_low - 8 + (qw_pad_count << 3); - pause_addr_lo = ((HC_SubA_HAGPBpL << 24) | cmd_type | - (pause_addr & 0xffffff)); - pause_addr_hi = ((HC_SubA_HAGPBpH << 24) | (pause_addr >> 24)); - - vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1); - - *vb++ = pause_addr_hi; - *vb++ = pause_addr_lo; - dev_priv->dma_low += 8; - *dev_priv->last_pause_ptr = pause_addr_lo; - dev_priv->last_pause_ptr = vb - 1; - - if (VIA_READ(0x41c) & 0x80000000) { - 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); - } + via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo); + via_hook_segment( dev_priv, pause_addr_hi, pause_addr_lo, 0); } + static void via_cmdbuf_pause(drm_via_private_t * dev_priv) { via_cmdbuf_flush(dev_priv, HC_HAGPBpID_PAUSE); diff --git a/shared-core/via_drv.h b/shared-core/via_drv.h index 3d3f093f..9e21f5b5 100644 --- a/shared-core/via_drv.h +++ b/shared-core/via_drv.h @@ -28,11 +28,11 @@ #define DRIVER_NAME "via" #define DRIVER_DESC "VIA Unichrome" -#define DRIVER_DATE "20041012" +#define DRIVER_DATE "20041030" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_PATCHLEVEL 2 typedef struct drm_via_ring_buffer { drm_map_t map; @@ -50,7 +50,7 @@ typedef struct drm_via_private { unsigned int dma_low; unsigned int dma_high; unsigned int dma_offset; - uint32_t *last_pause_ptr; + volatile uint32_t *last_pause_ptr; volatile uint32_t *hw_addr_ptr; drm_via_ring_buffer_t ring; char pci_buf[VIA_PREALLOCATED_PCI_SIZE]; |