diff options
author | Ben Skeggs <skeggsb@gmail.com> | 2007-12-10 15:16:05 +1100 |
---|---|---|
committer | Ben Skeggs <skeggsb@gmail.com> | 2007-12-10 15:17:20 +1100 |
commit | 3b2598c70bce098ea8c43ff37862bcce1663514b (patch) | |
tree | c6fc30b811f4aa4045562fb271c9426785022e06 /src/mesa | |
parent | 7d8368790fabc19e51add0fd9f1d1f85b7803cdf (diff) |
nouveau: fix elusive dma bug
In some situations WAIT_RING would get called while the GPU was processing
data from outside the "master" ring, which caused dma.free to be updated
incorrectly and much fun was had.
WAIT_RING will now wait until it reads GET values from within the main ring
buffer before calculating free space.
Diffstat (limited to 'src/mesa')
-rw-r--r-- | src/mesa/drivers/dri/nouveau_winsys/nouveau_dma.c | 54 |
1 files changed, 32 insertions, 22 deletions
diff --git a/src/mesa/drivers/dri/nouveau_winsys/nouveau_dma.c b/src/mesa/drivers/dri/nouveau_winsys/nouveau_dma.c index 6ec390ec76..5739e0010d 100644 --- a/src/mesa/drivers/dri/nouveau_winsys/nouveau_dma.c +++ b/src/mesa/drivers/dri/nouveau_winsys/nouveau_dma.c @@ -28,14 +28,29 @@ #include "nouveau_dma.h" #include "nouveau_local.h" -#define READ_GET(ch) ((*(ch)->get - (ch)->dma.base) >> 2) -#define WRITE_PUT(ch, val) do { \ - volatile int dum; \ - NOUVEAU_DMA_BARRIER; \ - dum=READ_GET(ch); \ - *(ch)->put = (((val) << 2) + (ch)->dma.base); \ - NOUVEAU_DMA_BARRIER; \ -} while(0) +static __inline__ uint32_t +READ_GET(struct nouveau_channel_priv *nvchan) +{ + return ((*nvchan->get - nvchan->dma.base) >> 2); +} + +static __inline__ void +WRITE_PUT(struct nouveau_channel_priv *nvchan, uint32_t val) +{ + uint32_t put = ((val << 2) + nvchan->dma.base); + volatile int dum; + + NOUVEAU_DMA_BARRIER; + dum = READ_GET(nvchan); + + *nvchan->put = put; + nvchan->dma.put = val; +#ifdef NOUVEAU_DMA_TRACE + NOUVEAU_MSG("WRITE_PUT %d/0x%08x\n", nvchan->drm.channel, put); +#endif + + NOUVEAU_DMA_BARRIER; +} void nouveau_dma_channel_init(struct nouveau_channel *userchan) @@ -57,6 +72,8 @@ nouveau_dma_channel_init(struct nouveau_channel *userchan) return - EBUSY; \ } while(0) +#define IN_MASTER_RING(chan, ptr) ((ptr) <= (chan)->dma.max) + int nouveau_dma_wait(struct nouveau_channel *userchan, int size) { @@ -67,7 +84,11 @@ nouveau_dma_wait(struct nouveau_channel *userchan, int size) t_start = NOUVEAU_TIME_MSEC(); while (chan->dma.free < size) { + CHECK_TIMEOUT(); + get = READ_GET(chan); + if (!IN_MASTER_RING(chan, get)) + continue; if (chan->dma.put >= get) { chan->dma.free = chan->dma.max - chan->dma.cur; @@ -86,6 +107,8 @@ nouveau_dma_wait(struct nouveau_channel *userchan, int size) do { CHECK_TIMEOUT(); get = READ_GET(chan); + if (!IN_MASTER_RING(chan, get)) + continue; } while (get <= RING_SKIPS); } @@ -96,8 +119,6 @@ nouveau_dma_wait(struct nouveau_channel *userchan, int size) } else { chan->dma.free = get - chan->dma.cur - 1; } - - CHECK_TIMEOUT(); } return 0; @@ -135,9 +156,6 @@ void nouveau_dma_kickoff(struct nouveau_channel *userchan) { struct nouveau_channel_priv *chan = nouveau_channel(userchan); - uint32_t put_offset; - int i; - volatile int dum; if (chan->dma.cur == chan->dma.put) return; @@ -165,13 +183,5 @@ nouveau_dma_kickoff(struct nouveau_channel *userchan) } #endif - put_offset = (chan->dma.cur << 2) + chan->dma.base; -#ifdef NOUVEAU_DMA_TRACE - NOUVEAU_MSG("FIRE_RING %d/0x%08x\n", chan->drm.channel, put_offset); -#endif - chan->dma.put = chan->dma.cur; - NOUVEAU_DMA_BARRIER; - dum = READ_GET(chan); - *chan->put = put_offset; - NOUVEAU_DMA_BARRIER; + WRITE_PUT(chan, chan->dma.cur); } |