diff options
author | Jose Fonseca <jrfonseca@users.sourceforge.net> | 2002-06-06 09:15:50 +0000 |
---|---|---|
committer | Jose Fonseca <jrfonseca@users.sourceforge.net> | 2002-06-06 09:15:50 +0000 |
commit | fe8e3bcca99ec39e30bb33918e8af75afb60af36 (patch) | |
tree | 8565d04830cc55e7232c4569d5e5a8c4e7c8d566 /linux | |
parent | c7f975325e87cdead26c4f92cfd6ac79f7164cd4 (diff) |
The bus master operation is now always checked on the wait loops, fixing
the lockups experienced in the NO_BATCH_DISPATCH code path. All other
code paths besides NO_BATCH_DISPATCH are broken. do_dma_flush waits for
completion of all DMA buffers and not just idle engine.
Diffstat (limited to 'linux')
-rw-r--r-- | linux/mach64_dma.c | 122 | ||||
-rw-r--r-- | linux/mach64_drv.h | 122 |
2 files changed, 69 insertions, 175 deletions
diff --git a/linux/mach64_dma.c b/linux/mach64_dma.c index 92e78155..27fa9e63 100644 --- a/linux/mach64_dma.c +++ b/linux/mach64_dma.c @@ -27,6 +27,7 @@ * Gareth Hughes <gareth@valinux.com> * Frank C. Earl <fearl@airmail.net> * Leif Delgass <ldelgass@retinalburn.net> + * José Fonseca <j_r_fonseca@yahoo.co.uk> */ #include "mach64.h" @@ -180,52 +181,43 @@ int mach64_wait_ring( drm_mach64_private_t *dev_priv, int n ) } /* Wait until all DMA requests have been processed... */ -int mach64_do_wait_for_dma( drm_mach64_private_t *dev_priv ) +int mach64_do_dma_flush( drm_mach64_private_t *dev_priv ) { - int i, ret; - - /* Assume we timeout... */ - ret = -EBUSY; + drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; + int i; - for ( i = 0 ; i < dev_priv->usec_timeout; i++ ) - { - if ( list_empty(&dev_priv->dma_queue) ) - { - ret = mach64_do_wait_for_idle( dev_priv ); - break; + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { + mach64_update_ring_snapshot( dev_priv ); + if ( ring->head == ring->tail ) { + if (i > 0) { + DRM_DEBUG( "do_dma_idle: %d usecs\n", i ); + } + return 0; } udelay( 1 ); } - if (ret != 0) - DRM_INFO( "do_wait_for_dma failed! GUI_STAT=0x%08x\n", MACH64_READ( MACH64_GUI_STAT ) ); - - return ret; + DRM_INFO( "%s failed! GUI_STAT=0x%08x\n", __FUNCTION__, + MACH64_READ( MACH64_GUI_STAT ) ); + return -EBUSY; } int mach64_do_dma_idle( drm_mach64_private_t *dev_priv ) { int ret; -#if MACH64_NO_BATCH_DISPATCH - u32 reg; -#endif /* wait for completion */ - if ( (ret = mach64_do_wait_for_idle( dev_priv )) < 0 ) { + if ( (ret = mach64_do_dma_flush( dev_priv )) < 0 ) { DRM_ERROR( "%s failed BM_GUI_TABLE=0x%08x tail: %d\n", __FUNCTION__, MACH64_READ(MACH64_BM_GUI_TABLE), dev_priv->ring.tail ); return ret; } -#if MACH64_NO_BATCH_DISPATCH - reg = MACH64_READ( MACH64_BUS_CNTL ); /* Disable bus-mastering, but keep block 1 registers enabled */ - MACH64_WRITE( MACH64_BUS_CNTL, reg | MACH64_BUS_MASTER_DIS | MACH64_BUS_EXT_REG_EN ); + MACH64_WRITE( MACH64_BUS_CNTL, MACH64_READ( MACH64_BUS_CNTL ) | MACH64_BUS_MASTER_DIS | MACH64_BUS_EXT_REG_EN ); MACH64_WRITE( MACH64_SRC_CNTL, 0 ); - return 0; -#else + /* clean up after pass */ mach64_do_release_used_buffers( dev_priv ); return 0; -#endif } /* Reset the engine. This will stop the DMA if it is running. @@ -1038,40 +1030,34 @@ static int mach64_do_dispatch_pseudo_dma( drm_mach64_private_t *dev_priv ) #endif /* MACH64_NO_BATCH_DISPATCH */ -int mach64_do_dma_flush( drm_mach64_private_t *dev_priv ) +int mach64_dma_start( drm_mach64_private_t *dev_priv ) { #if MACH64_NO_BATCH_DISPATCH drm_mach64_descriptor_ring_t *ring = &dev_priv->ring; - UPDATE_RING_HEAD( dev_priv, ring ); - - if ( ring->tail != ring->head && - !(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE) ) { - - if (ring->head_addr < ring->start_addr || - ring->head_addr > ring->start_addr + (ring->size - 4*sizeof(u32))) { - DRM_ERROR("Bad address in BM_GUI_TABLE: 0x%08x\n", ring->head_addr); - return -EINVAL; - } - + if ( (MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE) ) + return 0; + + if ( !(MACH64_READ(MACH64_SRC_CNTL) & MACH64_SRC_BM_ENABLE) ) { /* enable bus mastering and block 1 registers */ MACH64_WRITE( MACH64_BUS_CNTL, ( MACH64_READ(MACH64_BUS_CNTL) & ~MACH64_BUS_MASTER_DIS ) | MACH64_BUS_EXT_REG_EN ); - /* reset descriptor table ring head */ - MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, - ( ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB ) ); /* enable GUI-master operation */ MACH64_WRITE( MACH64_SRC_CNTL, MACH64_SRC_BM_ENABLE | MACH64_SRC_BM_SYNC | MACH64_SRC_BM_OP_SYSTEM_TO_REG ); - /* kick off the transfer */ - MACH64_WRITE( MACH64_DST_HEIGHT_WIDTH, 0 ); - DRM_DEBUG( "%s: new dispatch: head_addr: 0x%08x head: %d tail: %d space: %d\n", - __FUNCTION__, - ring->head_addr, ring->head, ring->tail, ring->space); } + + if ( ring->head_addr < ring->start_addr || + ring->head_addr > ring->start_addr + (ring->size - 4 * sizeof(u32)) ) { + DRM_ERROR("Bad address in BM_GUI_TABLE: 0x%08x\n", ring->head_addr); + return -EINVAL; + } + + UPDATE_RING_HEAD( dev_priv, ring ); + return 0; #else DRM_DEBUG("%s\n", __FUNCTION__); @@ -1211,8 +1197,7 @@ int mach64_dma_flush( struct inode *inode, struct file *filp, VB_AGE_TEST_WITH_RETURN( dev_priv ); - DMAFLUSH( dev_priv ); - return 0; + return mach64_do_dma_flush( dev_priv ); } int mach64_engine_reset( struct inode *inode, struct file *filp, @@ -1303,11 +1288,8 @@ drm_buf_t *mach64_freelist_get( drm_mach64_private_t *dev_priv ) int t; if ( list_empty(&dev_priv->free_list) ) { -#if !MACH64_USE_BUFFER_AGING u32 head, tail, ofs; -#else - u32 done_age = 0; -#endif + if ( list_empty( &dev_priv->pending ) ) { /* All 3 lists should never be empty - this is here for debugging */ if ( list_empty( &dev_priv->dma_queue ) ) { @@ -1320,16 +1302,8 @@ drm_buf_t *mach64_freelist_get( drm_mach64_private_t *dev_priv ) } } -#if MACH64_NO_BATCH_DISPATCH - /* Make sure we haven't gone idle */ - mach64_do_dma_flush( dev_priv ); -#endif - -#if !MACH64_USE_BUFFER_AGING tail = ring->tail; -#endif for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) { -#if !MACH64_USE_BUFFER_AGING UPDATE_RING_HEAD( dev_priv, ring ); head = ring->head; @@ -1339,16 +1313,12 @@ drm_buf_t *mach64_freelist_get( drm_mach64_private_t *dev_priv ) DRM_DEBUG( "%s: idle engine, freed all buffers.\n", __FUNCTION__ ); goto _freelist_entry_found; } -#else - done_age = MACH64_READ( MACH64_LAST_DISPATCH_REG ); -#endif /* Look for a completed buffer and bail out of the loop * as soon as we find one -- don't waste time trying * to free extra bufs here, leave that to do_release_used_buffers */ list_for_each_safe(ptr, tmp, &dev_priv->pending) { entry = list_entry(ptr, drm_mach64_freelist_t, list); -#if !MACH64_USE_BUFFER_AGING ofs = entry->ring_ofs; if ( (head < tail && (ofs < head || ofs >= tail)) || (head > tail && (ofs < head && ofs >= tail)) ) { @@ -1359,24 +1329,10 @@ drm_buf_t *mach64_freelist_get( drm_mach64_private_t *dev_priv ) DRM_DEBUG( "%s: freed processed buffer (head=%d tail=%d buf ring ofs=%d).\n", __FUNCTION__, head, tail, ofs ); goto _freelist_entry_found; } -#else - if (entry->age <= done_age && done_age > 0) { - /* found a processed buffer */ - entry->buf->pending = 0; - list_del(ptr); - list_add_tail(ptr, &dev_priv->free_list); - DRM_DEBUG( "%s: freed processed buffer (buffer age: %d last dispatch reg: %d last_dispatch: %d\n", __FUNCTION__, entry->age, done_age, dev_priv->sarea_priv->last_dispatch ); - goto _freelist_entry_found; - } -#endif } udelay( 1 ); } -#if !MACH64_USE_BUFFER_AGING DRM_ERROR( "timeout waiting for buffers: ring head_addr: 0x%08x head: %d tail: %d last cmd ofs %d\n", ring->head_addr, ring->head, ring->tail, ring->last_cmd_ofs ); -#else - DRM_ERROR( "timeout waiting for buffers: last dispatch reg: %d last_dispatch: %d\n", done_age, dev_priv->sarea_priv->last_dispatch ); -#endif return NULL; } @@ -1454,18 +1410,6 @@ static int mach64_dma_get_buffers( drm_device_t *dev, drm_dma_t *d ) return 0; } -/* - Through some pretty thorough testing, it has been found that the - RagePRO engine will pretty much ignore any "commands" sent - via the gui-master pathway that aren't gui operations (the register - gets set, but the actions that are normally associated with the setting - of those said registers doesn't happen.). So, it's safe to send us - buffers of gui commands from userspace (altering the buffer in mid- - execution will at worst scribble all over the screen and pushing - bogus commands will have no apparent effect...) - - FCE (03-08-2002) -*/ int mach64_dma_buffers( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { diff --git a/linux/mach64_drv.h b/linux/mach64_drv.h index 8e2c5068..c1335ff9 100644 --- a/linux/mach64_drv.h +++ b/linux/mach64_drv.h @@ -45,7 +45,7 @@ #define MACH64_USE_BUFFER_AGING 0 #define MACH64_USE_FRAME_AGING 0 -#define MACH64_NO_BATCH_DISPATCH 0 +#define MACH64_NO_BATCH_DISPATCH 1 #define MACH64_DEFAULT_MODE MACH64_MODE_DMA_ASYNC @@ -98,7 +98,7 @@ typedef struct drm_mach64_private { drm_mach64_descriptor_ring_t ring; struct list_head free_list; /* Free-list head */ - struct list_head placeholders; /* Free-list placeholder list */ + struct list_head placeholders; /* Free-list placeholder list */ struct list_head pending; /* Pending submission placeholder */ struct list_head dma_queue; /* Submission queue head */ @@ -147,6 +147,8 @@ extern int mach64_do_release_used_buffers( drm_mach64_private_t *dev_priv ); extern void mach64_dump_engine_info( drm_mach64_private_t *dev_priv ); extern int mach64_do_engine_reset( drm_mach64_private_t *dev_priv ); +extern int mach64_dma_start( drm_mach64_private_t *dev_priv ); + extern int mach64_do_dma_idle( drm_mach64_private_t *dev_priv ); extern int mach64_do_dma_flush( drm_mach64_private_t *dev_priv ); extern int mach64_do_cleanup_dma( drm_device_t *dev ); @@ -454,19 +456,28 @@ extern int mach64_dma_blit( struct inode *inode, struct file *filp, * Misc helper macros */ -#define UPDATE_RING_HEAD( dev_priv, ring ) \ -do { \ - int idle = 0; \ - if (!(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE)) idle = 1; \ - (ring)->head_addr = (MACH64_READ(MACH64_BM_GUI_TABLE) & 0xfffffff0); \ - /* If not idle, BM_GUI_TABLE points one descriptor past the current head */ \ - if ( !idle ) { \ - if ((ring)->head_addr == (ring)->start_addr) \ - (ring)->head_addr += ((ring)->size - (sizeof(u32)*4)); \ - else \ - (ring)->head_addr -= (sizeof(u32)*4); \ - } \ - (ring)->head = ((ring)->head_addr - (ring)->start_addr) / sizeof(u32); \ +#define UPDATE_RING_HEAD( dev_priv, ring ) \ +do { \ + int gui_active = MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE; \ + (ring)->head_addr = (MACH64_READ(MACH64_BM_GUI_TABLE) & 0xfffffff0); \ + if ( gui_active ) { \ + /* If not idle, BM_GUI_TABLE points one descriptor past the current head */ \ + if ((ring)->head_addr == (ring)->start_addr) \ + (ring)->head_addr += (ring)->size - 4 * sizeof(u32)*4; \ + else \ + (ring)->head_addr -= 4 * sizeof(u32); \ + } \ + (ring)->head = ((ring)->head_addr - (ring)->start_addr) / sizeof(u32); \ + if ( !gui_active && (ring)->head != (ring)->tail ) { \ + /* reset descriptor table ring head */ \ + MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, \ + ( (ring)->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB ) ); \ + /* kick off the transfer */ \ + MACH64_WRITE( MACH64_DST_HEIGHT_WIDTH, 0 ); \ + DRM_DEBUG( "%s: new dispatch: head_addr: 0x%08x head: %d tail: %d space: %d\n", \ + __FUNCTION__, \ + (ring)->head_addr, (ring)->head, (ring)->tail, (ring)->space); \ + } \ } while (0) static inline void @@ -579,8 +590,7 @@ do { \ * DMA descriptor ring macros */ -#define RING_LOCALS \ - int write; unsigned int tail_mask; u32 *ring; +#define RING_LOCALS int tail, write; unsigned int mask; volatile u32 *ring; #define RING_WRITE_OFS write @@ -601,8 +611,8 @@ do { \ } \ dev_priv->ring.space -= (n) * sizeof(u32); \ ring = (u32 *) dev_priv->ring.start; \ - write = dev_priv->ring.tail; \ - tail_mask = dev_priv->ring.tail_mask; \ + tail = write = dev_priv->ring.tail; \ + mask = dev_priv->ring.tail_mask; \ } while (0) #define OUT_RING( x ) \ @@ -612,41 +622,22 @@ do { \ (unsigned int)(x), write ); \ } \ ring[write++] = cpu_to_le32( x ); \ - write &= tail_mask; \ + write &= mask; \ } while (0) #define ADVANCE_RING() \ do { \ if ( MACH64_VERBOSE ) { \ DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ - write, dev_priv->ring.tail ); \ + write, tail ); \ } \ + ring[(write - 2) & mask] |= cpu_to_le32( DMA_EOL ); \ + wmb(); \ + ring[(tail - 2) & mask] &= cpu_to_le32( ~DMA_EOL ); \ + wmb(); \ dev_priv->ring.tail = write; \ } while (0) -#define COMMIT_RING() \ -do { \ - int last_cmd_ofs; u32 last_cmd; \ - UPDATE_RING_HEAD( dev_priv, &dev_priv->ring ); \ - DRM_DEBUG("COMMIT_RING() head: %d tail: %d last_cmd_ofs: %d\n", \ - dev_priv->ring.head, dev_priv->ring.tail, \ - dev_priv->ring.last_cmd_ofs); \ - \ - last_cmd_ofs = dev_priv->ring.tail - 2; \ - if (last_cmd_ofs < 0) \ - last_cmd_ofs = dev_priv->ring.tail_mask - 1; \ - last_cmd = ring[last_cmd_ofs] & ~DMA_EOL; \ - /* set EOL flag */ \ - ring[last_cmd_ofs] = cpu_to_le32( last_cmd | DMA_EOL ); \ - wmb(); \ - /* clear EOL flag from previous ring tail */ \ - ring[dev_priv->ring.last_cmd_ofs] = \ - cpu_to_le32( dev_priv->ring.last_cmd ); \ - dev_priv->ring.last_cmd_ofs = last_cmd_ofs; \ - dev_priv->ring.last_cmd = last_cmd; \ - mach64_do_dma_flush( dev_priv ); \ -} while(0) - /* ================================================================ * DMA macros @@ -690,8 +681,6 @@ do { \ buf->used += 8; \ } while (0) -#if MACH64_NO_BATCH_DISPATCH - #define DMAADVANCE( dev_priv ) \ do { \ struct list_head *ptr; \ @@ -716,8 +705,6 @@ do { \ list_add_tail(ptr, &dev_priv->pending); \ \ ADD_BUF_TO_RING( dev_priv, entry ); \ - COMMIT_RING(); \ - \ } while (0) #define ADD_BUF_TO_RING( dev_priv, entry ) \ @@ -757,44 +744,7 @@ do { \ OUT_RING( 0 ); \ \ ADVANCE_RING(); \ + mach64_dma_start( dev_priv ); \ } while(0) -#else - -#define DMAADVANCE( dev_priv ) \ -do { \ - struct list_head *ptr; \ - drm_mach64_freelist_t *entry; \ - \ - if ( MACH64_VERBOSE ) { \ - DRM_INFO( "DMAADVANCE() in %s\n", __FUNCTION__ ); \ - } \ - \ - if (list_empty(&dev_priv->placeholders)) { \ - DRM_ERROR( "%s: empty placeholder list in DMAADVANCE()\n", \ - __FUNCTION__ ); \ - return -EFAULT; \ - } \ - \ - /* Add the buffer to the DMA queue */ \ - ptr = dev_priv->placeholders.next; \ - list_del(ptr); \ - entry = list_entry(ptr, drm_mach64_freelist_t, list); \ - entry->buf = buf; \ - entry->buf->waiting = 1; \ - list_add_tail(ptr, &dev_priv->dma_queue); \ - \ -} while (0) -#endif /* MACH64_NO_BATCH_DISPATCH */ - -#define DMAFLUSH( dev_priv ) \ -do { \ - int ret; \ - if ( MACH64_VERBOSE ) { \ - DRM_INFO( "DMAFLUSH() in %s\n", __FUNCTION__ ); \ - } \ - if ((ret=mach64_do_dma_flush( dev_priv )) < 0) \ - return ret; \ -} while (0) - #endif /* __MACH64_DRV_H__ */ |