added checking to prevent DMA buffer overfills: it turns out for instance NV11 can't keep up with BeRoMeter and the engine crashes otherwise. It turns out NV11 is currently only working correctly in 32bit color mode BTW.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11005 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -813,25 +813,85 @@ static void nv_start_dma(void)
|
|||||||
* The hardware FIFO state is checked by the DMA hardware automatically. */
|
* The hardware FIFO state is checked by the DMA hardware automatically. */
|
||||||
static status_t nv_acc_fifofree_dma(uint16 cmd_size)
|
static status_t nv_acc_fifofree_dma(uint16 cmd_size)
|
||||||
{
|
{
|
||||||
/* check if the DMA buffer has enough room for the command */
|
uint32 dmaget;
|
||||||
if (si->engine.dma.free < cmd_size)
|
|
||||||
{
|
|
||||||
/* not enough room left, so instruct DMA engine to reset the buffer when
|
|
||||||
* it's done */
|
|
||||||
si->engine.dma.cmdbuffer[si->engine.dma.current++] = 0x20000000;
|
|
||||||
/* reset our buffer pointer, so new commands will be placed at the
|
|
||||||
* beginning of the buffer. */
|
|
||||||
si->engine.dma.current = 0;
|
|
||||||
/* tell the engine to fetch the remaining command(s) in the DMA buffer
|
|
||||||
* that where not executed before. */
|
|
||||||
nv_start_dma();
|
|
||||||
/* fixme if needed:
|
|
||||||
* we are assuming here that a large part in the beginning of the DMA
|
|
||||||
* cmd buffer is already processed. If this is not true, we need to wait
|
|
||||||
* here until the engine becomes idle or so... */
|
|
||||||
|
|
||||||
/* update the current free space we have left in the DMA buffer */
|
/* we'd better check for timeouts on the DMA engine as it's theoretically
|
||||||
si->engine.dma.free = si->engine.dma.max - si->engine.dma.current;
|
* breakable by malfunctioning software */
|
||||||
|
uint16 cnt = 0;
|
||||||
|
|
||||||
|
/* check if the DMA buffer has enough room for the command.
|
||||||
|
* note:
|
||||||
|
* engine.dma.free is 'cached' */
|
||||||
|
while ((si->engine.dma.free < cmd_size) && (cnt < 10000) && (err < 3))
|
||||||
|
{
|
||||||
|
/* see where the engine is currently fetching from the buffer */
|
||||||
|
dmaget = ((NV_REG32(NVACC_FIFO + NV_GENERAL_DMAGET +
|
||||||
|
si->engine.fifo.handle[(si->engine.fifo.ch_ptr[NV_ROP5_SOLID])])) >> 2);
|
||||||
|
|
||||||
|
/* snooze a bit so I do not hammer the bus */
|
||||||
|
snooze (100);
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
/* where's the engine fetching viewed from us issuing? */
|
||||||
|
if (si->engine.dma.put >= dmaget)
|
||||||
|
{
|
||||||
|
/* engine is fetching 'behind us', the last piece of the buffer is free */
|
||||||
|
|
||||||
|
/* note the 'updated' free space we have in the DMA buffer */
|
||||||
|
si->engine.dma.free = si->engine.dma.max - si->engine.dma.current;
|
||||||
|
/* if it's enough after all we exit this routine immediately. Else: */
|
||||||
|
if (si->engine.dma.free < cmd_size)
|
||||||
|
{
|
||||||
|
/* not enough room left, so instruct DMA engine to reset the buffer
|
||||||
|
* when it's reaching the end of it */
|
||||||
|
si->engine.dma.cmdbuffer[si->engine.dma.current++] = 0x20000000;
|
||||||
|
/* reset our buffer pointer, so new commands will be placed at the
|
||||||
|
* beginning of the buffer. */
|
||||||
|
si->engine.dma.current = 0;
|
||||||
|
/* tell the engine to fetch the remaining command(s) in the DMA buffer
|
||||||
|
* that where not executed before. */
|
||||||
|
nv_start_dma();
|
||||||
|
|
||||||
|
/* NOW the engine is fetching 'in front of us', so the first piece
|
||||||
|
* of the buffer is free */
|
||||||
|
|
||||||
|
/* note the updated current free space we have in the DMA buffer */
|
||||||
|
si->engine.dma.free = dmaget - si->engine.dma.current;
|
||||||
|
/* mind this pittfall:
|
||||||
|
* as the engine is DMA triggered for fetching chunks every 128 bytes,
|
||||||
|
* we may not touch that much at the end of our free space!
|
||||||
|
* (confirmed NV11 when it's buffer is full for a longer period of
|
||||||
|
* time, tested with BeRoMeter 1.2.6.) */
|
||||||
|
if (si->engine.dma.free < 32)
|
||||||
|
si->engine.dma.free = 0;
|
||||||
|
else
|
||||||
|
si->engine.dma.free -= 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* engine is fetching 'in front of us', so the first piece of the buffer
|
||||||
|
* is free */
|
||||||
|
|
||||||
|
/* note the updated current free space we have in the DMA buffer */
|
||||||
|
si->engine.dma.free = dmaget - si->engine.dma.current;
|
||||||
|
/* mind this pittfall:
|
||||||
|
* as the engine is DMA triggered for fetching chunks every 128 bytes,
|
||||||
|
* we may not touch that much at the end of our free space!
|
||||||
|
* (confirmed NV11 when it's buffer is full for a longer period of
|
||||||
|
* time, tested with BeRoMeter 1.2.6.) */
|
||||||
|
if (si->engine.dma.free < 32)
|
||||||
|
si->engine.dma.free = 0;
|
||||||
|
else
|
||||||
|
si->engine.dma.free -= 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* log timeout if we had one */
|
||||||
|
if (cnt == 10000)
|
||||||
|
{
|
||||||
|
err++;
|
||||||
|
LOG(4,("ACC_DMA: wait_idle DMA timeout #%d, engine trouble!\n", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|||||||
Reference in New Issue
Block a user