mirror of
https://gitlab.freedesktop.org/mesa/drm.git
synced 2025-12-26 13:30:12 +01:00
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.
This commit is contained in:
parent
c7f975325e
commit
fe8e3bcca9
2 changed files with 69 additions and 175 deletions
|
|
@ -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;
|
||||
drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
|
||||
int i;
|
||||
|
||||
/* Assume we timeout... */
|
||||
ret = -EBUSY;
|
||||
|
||||
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 )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue