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:
Rudolf Cornelissen
2005-01-24 16:14:20 +00:00
parent e01fcf6917
commit 11e38e920f
@@ -813,25 +813,85 @@ static void nv_start_dma(void)
* The hardware FIFO state is checked by the DMA hardware automatically. */
static status_t nv_acc_fifofree_dma(uint16 cmd_size)
{
/* check if the DMA buffer has enough room for the command */
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... */
uint32 dmaget;
/* update the current free space we have left in the DMA buffer */
si->engine.dma.free = si->engine.dma.max - si->engine.dma.current;
/* we'd better check for timeouts on the DMA engine as it's theoretically
* 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;