/******************************************************************************
 * RAGE128 Chapter 5 Sample Code                                              *
 *                                                                            *
 * CCEUTIL.C                                                                  *
 * This files contains the routines used to setup and maintain the            *
 * CCE subsystem and ring buffer.                                             *
 *                                                                            *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.             *
 ******************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include "regdef.h"
#include "main.h"
#include "defines.h"
#include "agp.h"
#include "cce.h"
#include "cceutil.h"

volatile BYTE readbuf[32 + sizeof (DWORD)];

static RBINFO RingBuf = {0};
static BUFINFO VertexBuf = {0};
static BUFINFO IndirectBuf = {0};

static int CCEFifoSize;
static BOOL CCEBMFlag, CCEAGPFlag, CCEVBFlag, CCEIBFlag;
static DWORD bm_save_state;
static DWORD CCERequestedMode;
static GART_INFO *PCIGartInfo = NULL;
static _AGP_INFO *AGP_Info = NULL;
static DWORD  read_ptr_offset = 0;
DWORD savebmchunk0val = 0;
DWORD savebmcommand = 0;

//static FILE *ccefile;

// CCE mode table.  Entries correspond to CCE operating mode constants as
// defined in CCE.H

static CCEModeTable CCEmode[] = {
    { PM4_BUFFER_CNTL_192PM4PIO,                 192, FALSE, FALSE, FALSE},
    { PM4_BUFFER_CNTL_192PM4BM,                  192,  TRUE, FALSE, FALSE},
    { PM4_BUFFER_CNTL_128PM4PIO_64INDBM,         128, FALSE, FALSE,  TRUE},
    { PM4_BUFFER_CNTL_128PM4BM_64INDBM,          128,  TRUE, FALSE,  TRUE},
    { PM4_BUFFER_CNTL_64PM4PIO_128INDBM,          64, FALSE, FALSE,  TRUE},
    { PM4_BUFFER_CNTL_64PM4BM_128INDBM,           64,  TRUE, FALSE,  TRUE},
    { PM4_BUFFER_CNTL_64PM4PIO_64VERBM_64INDBM,   64, FALSE,  TRUE,  TRUE},
    { PM4_BUFFER_CNTL_64PM4BM_64VERBM_64INDBM,    64,  TRUE,  TRUE,  TRUE},
    { PM4_BUFFER_CNTL_64PM4PIO_64VERPIO_64INDPIO, 64, FALSE,  TRUE,  TRUE}
};

static int CCESetupBusMaster (void);
int (*R128_CCESubmitPackets) (DWORD *, DWORD);
static int CCESubmitPacketsPIO (DWORD *, DWORD);
static int CCESubmitPacketsBM (DWORD *, DWORD);
static void CCELoadMicrocode (void);
static void CreateCCEBuffers (void);
int CCEWaitForIdle (void);
int CCEWaitForFifo (DWORD);

extern DWORD GetPhysical (DWORD);
extern GART_INFO *SetupPCIGARTTable (DWORD);


/******************************************************************************
 * R128_CCEInit                                                               *
 *  Function: Initializes the CCE microcode engine                            *
 *    Inputs: none.                                                           *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

int R128_CCEInit (int index)
{
    DWORD cce_buf_size, temp;

    // Load CCE Microcode

    CCELoadMicrocode ();

    // Validate CCEmode and set up necessary parameters

    if ((index < CCE_MODE_192PIO) ||
        (index > CCE_MODE_64PIO_64VERPIO_64INDPIO))
    {
        return (CCE_FAIL_INVALID_CCE_MODE);
    } // if

    // Perform a soft reset of the engine

    R128_ResetEngine ();

    CCEFifoSize = CCEmode[index].fifosize;
    CCEBMFlag = CCEmode[index].busmaster;
    if (CCEBMFlag)
    {
        R128_CCESubmitPackets = CCESubmitPacketsBM;
        if (CCESetupBusMaster ())
        {
            return (CCE_FAIL_BUS_MASTER_INIT);
        } // if
        cce_buf_size = RING_SIZE_LOG2;
        bm_save_state = regr (BUS_CNTL);
        regw (BUS_CNTL, (bm_save_state & ~BUS_MASTER_DIS));
    }
    else
    {
        R128_CCESubmitPackets = CCESubmitPacketsPIO;
        cce_buf_size = 0;
    } // if

    // Set the Rage 128 to requested CCE mode.
    CCERequestedMode = CCEmode[index].pm4buffermode + cce_buf_size;
    regw (PM4_BUFFER_CNTL, CCERequestedMode);

    temp = regr (PM4_BUFFER_ADDR);

    // Set the CCE to free running mode

    regw (PM4_MICRO_CNTL, PM4_MICRO_FREERUN);

	// Set vertex buffer and indirect buffer flags, and create if necessary.

    CCEVBFlag = CCEmode[index].vertexbuffer;
    CCEIBFlag = CCEmode[index].indirectbuffer;

    CreateCCEBuffers ();

    return (CCE_SUCCESS);
} // R128_CCEInit


/******************************************************************************
 * R128_CCEEnd                                                                *
 *  Function: Shuts down the CCE microcode engine                             *
 *    Inputs: waitmode                                                        *
 *   Outputs: none.                                                           *
 ******************************************************************************/

void R128_CCEEnd (int waitmode)
{
    if (waitmode == CCE_END_WAIT)
    {
        // Wait for engine idle before ending.  It does not matter if the
        // engine fails to idle as we will reset it shortly.

        CCEWaitForIdle ();
    } // if

    if (CCEBMFlag)
    {
        CCENextPage (); // only necessary for PCI GART. Since we're
                        // shutting down who cares if not necessary when
                        // using AGP. 
                        
        CCEWaitForIdle ();

        // Signal CCE that we are done submitting bus-mastered packets

        regw (PM4_BUFFER_DL_WPTR, RingBuf.WriteIndex | PM4_BUFFER_DL_DONE);
    } // if

    // Stop the CCE microengine by setting it to single-stepping mode

    regw (PM4_MICRO_CNTL, 0x00000000);

    // Set the Rage 128 to standard PIO mode.

    regw (PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4);


    if (CCEBMFlag)
    {
        regw (BUS_CNTL, bm_save_state);

        if (CCEAGPFlag)
        {
            // Shut down AGP

            R128_EndAGP ();
        }
        else
        {
			// Disable PCI_GART.

			regw (PCI_GART_PAGE, 0x00000001);

			// Restore original BM_CHUNK_0_VAL value.

			regw (BM_CHUNK_0_VAL, savebmchunk0val);

			// Free PCI GART page memory.

            DPMI_freedosmem (PCIGartInfo->handle);

			// Free 'AGP space' memory.
			
            DPMI_freememory (PCIGartInfo->mem_handle);

        } // if
    } // if

    // Perform a soft reset of the engine.

    R128_ResetEngine ();

    return;
} // R128_CCEEnd

/******************************************************************************
 * CCESetupBusMaster                                                          *
 *  Function: This function sets up the ring buffer for bus mastering.        *
 *    Inputs: none.                                                           *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

static int CCESetupBusMaster (void)
{
    DWORD temp;

    // For the sake of simplicity, put the ring buffer at the start of AGP
    // space, factoring in alignment restrictions.

    RingBuf.Offset = 0;

    // Align the offset to a 128-byte boundary.  Strictly speaking, since an
    // offset of zero was chosen, the following step is unnecessary, but it
    // is good form to perform this step in case the ring buffer needs to be
    // elsewhere.

    RingBuf.Offset = align (RingBuf.Offset, 128);
    read_ptr_offset = align ((DWORD) readbuf, 32);
    RingBuf.ReadIndexPtr = (DWORD *) read_ptr_offset;
    R128_Delay (1);
    RingBuf.ReadPtrPhysical = GetPhysical (read_ptr_offset);
    RingBuf.Size = RING_SIZE;
    RingBuf.WriteIndex = 0;

    CCEAGPFlag = R128_InitAGP (APERTURE_SIZE_4MB);
    if (CCEAGPFlag)
    {
        GetAGPINFO (&AGP_Info);
        RingBuf.LinearPtr = (DWORD *) (AGP_Info->LogicalAddress + RingBuf.Offset);
        regw (PCI_GART_PAGE, PCI_GART_DIS);
    }
    else
    {
        // No AGP available, use PCI GART mapping instead.

        if (!(PCIGartInfo = SetupPCIGARTTable (APERTURE_SIZE_4MB)))
        {
            // If even a PCI GART table is not available, then bus mastering
            // is not possible.

            return (CCE_FAIL_BUS_MASTER_INIT);
        } // if

        RingBuf.LinearPtr = PCIGartInfo->pointer +
                            (RingBuf.Offset/sizeof (DWORD));

        savebmchunk0val = regr (BM_CHUNK_0_VAL);
        temp = savebmchunk0val | (1L << 21) | (1L << 22) | (1L << 23);
        regw (BM_CHUNK_0_VAL, temp);

        // Write the GART page address.  Since this address is 4KB
        // aligned, bit 0 is cleared.  Hence, GART will be enabled.

        regw (PCI_GART_PAGE, PCIGartInfo->paddress);

    } // if

    // Set the start offset of the ring buffer.

    regw (PM4_BUFFER_OFFSET, (RingBuf.Offset + 0x02000000));

    regw (PM4_BUFFER_DL_WPTR, RingBuf.WriteIndex);
    regw (PM4_BUFFER_DL_RPTR, RingBuf.WriteIndex);

    // Put the physical address of read pointer into PM4_BUFFER_DL_RPTR_ADDR

    regw (PM4_BUFFER_DL_RPTR_ADDR, RingBuf.ReadPtrPhysical);

    // Set watermarks for CCE

    regw (PM4_BUFFER_WM_CNTL, (CCE_WATERMARK_K/64) << 24 |
                              (CCE_WATERMARK_N/4)  << 16 |
                              (CCE_WATERMARK_M/4)  <<  8 |
                              CCE_WATERMARK_L/4);

    temp = regr (PM4_BUFFER_ADDR);

    CCEWaitForIdle ();

    return (CCE_SUCCESS);
} // CCESetupBusMaster


/******************************************************************************
 * CCESubmitPacketsPIO                                                        *
 *  Function: This function submits packets to the CCE FIFO.  It determines   *
 *            if it can accept the packet that has been submitted, then it    *
 *            writes the data to the FIFO even and odd registers.  If an odd  *
 *            number of packets is submitted, a dummy (Type-2) packet is sent *
 *            to ensure that an even number of register writes is performed.  *
 *    Inputs: DWORD *ClientBuf - pointer to the data to be submitted          *
 *            DWORD DataSize - size of the packet submitted                   *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

static int CCESubmitPacketsPIO (DWORD *ClientBuf, DWORD DataSize)
{
    // Consume entries in the buffer two DWORDs at a time, splitting up the
    // writes to the even and odd registers.

    while (DataSize > 1)
    {
        if (CCEWaitForFifo (2))
        {
            return (CCE_FAIL_TIMEOUT);
        } // if
        regw (PM4_FIFO_DATA_EVEN, *ClientBuf++);
        regw (PM4_FIFO_DATA_ODD, *ClientBuf++);
        DataSize -= 2;
    } // while

    // At this point, DataSize should be either 0 or 1, handle odd packet
    // accordingly.

    if (DataSize & 1)
    {
        if (CCEWaitForFifo (2))
        {
            return (CCE_FAIL_TIMEOUT);
        } // if
        regw (PM4_FIFO_DATA_EVEN, *ClientBuf);  // Write final packet
        regw (PM4_FIFO_DATA_ODD, CCE_PACKET2);  // Write dummy packet to align
    } // if

    // N.B.  A more sophisticated packet submission algorithm might try to
    // reduce the number of times that CCEWaitForFifo () is called and still
    // handle packets that are larger than the maximum CCE FIFO size.  A
    // somewhat inefficient approach (waiting for 2 free entries each time
    // through the loop) is used above since it simplifies the example and
    // can handle arbitrary sized buffers.

    return (CCE_SUCCESS);
} // CCESubmitPacketsPIO


/******************************************************************************
 * CCESubmitPacketsBM                                                         *
 *  Function: This function is our ring buffer manager.  It determines if it  *
 *            can accept the packet that has been submitted, then it copies   *
 *            the data to the ring buffer.                                    *
 *    Inputs: DWORD *ClientBuf - pointer to the data to be submitted          *
 *            DWORD DataSize - size of the packet submitted                   *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

static int CCESubmitPacketsBM (DWORD *ClientBuf, DWORD DataSize)
{
    DWORD *tptr;
    WORD starttick, endtick;
    DWORD hwdbg;
    DWORD test;

    // We shall arbitrarily fail if the incoming packet is bigger than our
    // ring buffer.  A better algorithm would break up the incoming packet
    // into small enough chunks to feed to the buffer.

    if (DataSize >= RingBuf.Size)
    {
        return (CCE_FAIL_BAD_PACKET);
    } // if

    starttick = *((WORD *) (DOS_TICK_ADDRESS));
    endtick = starttick;

    tptr = RingBuf.LinearPtr + RingBuf.WriteIndex;
    while (DataSize > 0)
    {
        RingBuf.WriteIndex += 1;
        *tptr++ = *ClientBuf++;
        if (RingBuf.WriteIndex >= RingBuf.Size)
        {
            RingBuf.WriteIndex = 0;
            tptr = RingBuf.LinearPtr + RingBuf.WriteIndex;
        } // if
        while (RingBuf.WriteIndex == *(RingBuf.ReadIndexPtr))
        {
            endtick = *((WORD *) (DOS_TICK_ADDRESS));
            test = regr (PM4_BUFFER_DL_RPTR);
            if (abs (endtick - starttick) > FIFO_TIMEOUT)
            {
                // Attempt to reset CCE.  Not quite working yet...
                regw (PM4_MICRO_CNTL, 0x00000000);
                RingBuf.WriteIndex = 0;
                hwdbg = regr (HW_DEBUG);
                regw (HW_DEBUG, hwdbg | 0x00000040);
                regw (HW_DEBUG, hwdbg);
                R128_ResetEngine ();
                regw (PM4_BUFFER_CNTL, CCERequestedMode);
                regw (PM4_BUFFER_DL_WPTR, 0);
                regw (PM4_BUFFER_DL_RPTR, 0);
                regw (PM4_MICRO_CNTL, PM4_MICRO_FREERUN);
                return (CCE_FAIL_TIMEOUT);
            } // if
        } // while
        DataSize -= 1;
    } // while

    // The following test is a workaround for some early Rage 128 ASIC spins.
    // What sometimes happens is that, in bus-mastering mode, the CCE parser
    // may read up to 32 DWORDS beyond the end of the ring buffer memory before
    // it loops around.  This would occur if the ring buffer is empty, and a
    // small (<32 DWORD) packet that wraps around the end of the ring buffer
    // is submitted.  The solution is to simply copy the first 32 DWORDS in
    // the ring buffer to the area immediately beyond the end of the ring
    // buffer memory so that the parser will have valid packet data if it
    // overshoots.  This workaround has a negligible impact on performance
    // and will not affect Rage 128s where this problem has been corrected.

    if (RingBuf.WriteIndex < 32)
    {
        memcpy (RingBuf.LinearPtr + RingBuf.Size, RingBuf.LinearPtr,
                RingBuf.WriteIndex * sizeof (DWORD));
    } // if

    // Update pointer.

    regw (PM4_BUFFER_DL_WPTR, RingBuf.WriteIndex);
    return (CCE_SUCCESS);
} // CCESubmitPacketsBM


/******************************************************************************
 * R128_Flush                                                                 *
 *  Function: This function flushes pending writes to the CCE FIFO.           *
 *    Inputs: none.                                                           *
 *   Outputs: none.                                                           *
 ******************************************************************************/

void R128_Flush (void)
{
    if (CCEBMFlag)
        regw (PM4_BUFFER_DL_WPTR, RingBuf.WriteIndex | PM4_BUFFER_DL_DONE);
}


/******************************************************************************
 * CCELoadMicrocode                                                           *
 *  Function: This function loads the microcode into the CCE microengine.     *
 *    Inputs: none.                                                           *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

static void CCELoadMicrocode (void)
{
    int i;

    // Wait for engine idle before loading the microcode.

    R128_WaitForIdle ();

    // Set starting address for writing the microcode.

    regw (PM4_MICROCODE_ADDR, 0);

    for (i = 0; i < 256; i += 1)
    {
        // The microcode write address will automatically increment after
        // every microcode data high/low pair.  Note that the high DWORD
        // must be written first for address autoincrement to work correctly.

        regw (PM4_MICROCODE_DATAH, CCE_Microcode[i][0]);
        regw (PM4_MICROCODE_DATAL, CCE_Microcode[i][1]);
    } // for

    return;
} // CCELoadMicrocode


/******************************************************************************
 * CCEWaitForIdle - wait until CCE microengine is idle.                       *
 *  Function: This function uses the DOS tick counter to serve as a           *
 *            timeout clock. If the engine is in a lockup condition,          *
 *            the busy bit may stay set. In this case, a timeout will         *
 *            occur, an error message will occur, and the program will        *
 *            terminate.                                                      *
 *    Inputs: NONE                                                            *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

int CCEWaitForIdle (void)
{
    WORD starttick, endtick;
    DWORD temp;

    // Ensure FIFO is empty before waiting for engine idle.

    if (CCEWaitForFifo (CCEFifoSize))
    {
        return (CCE_FAIL_TIMEOUT);
    } // if

    starttick = *((WORD *) (DOS_TICK_ADDRESS));
    endtick = starttick;
    while (((temp = regr (PM4_STAT)) & (GUI_ACTIVE | PM4_BUSY)) != ENGINE_IDLE)
    {
        endtick = *((WORD *) (DOS_TICK_ADDRESS));
        if (abs (endtick - starttick) > IDLE_TIMEOUT)
        {
            return (CCE_FAIL_TIMEOUT);
        } // if
    } // while

    // flush the pixel cache
    R128_FlushPixelCache ();

    return (CCE_SUCCESS);
} // CCEWaitForIdle


/******************************************************************************
 * CCEWaitForFifo - wait n empty CCE FIFO entries.                            *
 *  Function: The FIFO contains up to 192 empty entries, depending on current *
 *            CCE configuration (see PM4_BUFFER_CNTL). The 'entries' value    *
 *            must be 1 to 192. This function implements the same timeout     *
 *            mechanism as the CCEWaitForIdle() function.                     *
 *    Inputs: entries - number of entries spaces to wait for. Max - 192       *
 *   Outputs: CCE Status code.  See cce.h for details.                        *
 ******************************************************************************/

int CCEWaitForFifo (DWORD entries)
{
    WORD starttick, endtick;
    DWORD temp;

    starttick = *((WORD *) (DOS_TICK_ADDRESS));
    endtick = starttick;
    while (((temp = regr (PM4_STAT)) & 0x00000FFF) < entries)
    {
        endtick = *((WORD *) (DOS_TICK_ADDRESS));
        if (abs (endtick - starttick) > FIFO_TIMEOUT)
        {
            return (CCE_FAIL_TIMEOUT);
        } // if
    } // while

    return (CCE_SUCCESS);
} // CCEWaitForFifo


/****************************************************************************
 * CreateBuffer - Create a logical buffer.                                  *
 *  Function: This function creates a logical description of a linear       *
 *            buffer as described by the offset and size parameters. It is  *
 *            used to create the vertex and indirect buffers in response to *
 *             CreateCCEBuffers function.                                   * 
 *    Inputs: BUFINFO* pbuf - pointer to the buffer's BUFINFO structure.    *
 *            DWORD offset - BYTE offset of the buffer from the base memory.*
 *            DWORD size - size of the buffer in BYTEs.                     *
 *   Outputs: None.                                                         *
 ****************************************************************************/

static void CreateBuffer (BUFINFO* pbuf, DWORD offset, DWORD size)
{
    if (CCEBMFlag)
    {
        if (CCEAGPFlag)
        {
            pbuf->LinearPtr = (DWORD *) ((DWORD)AGP_Info->BytePointer + offset);
        }
        else
        {
            pbuf->LinearPtr = (DWORD *) ((DWORD)PCIGartInfo->pointer + offset);
        }
    }

	pbuf->Offset = offset;
    pbuf->Size = size;
}


/****************************************************************************
 * CreateCCEBuffers - Create Vertex and/or Indirect buffers.                *
 *  Function: This function places the indirect buffer and/or vertex after  *
 *            the ring buffer in AGP or PCI GART space. If both are created,*
 *            the vertex buffer is placed after the indirect buffer. The    *
 *            buffer offsets are set at the next 4k boundary following the  *
 *            previous buffer.                                              *
 *    Inputs: None.                                                         *
 *   Outputs: None.                                                         *
 ****************************************************************************/

static void CreateCCEBuffers (void)
{
	DWORD offset, lastoffset;
	DWORD size;

	// Zero structures

	memset (&VertexBuf, 0, sizeof (BUFINFO));
	memset (&IndirectBuf, 0, sizeof (BUFINFO));

	lastoffset = RingBuf.Offset + (RingBuf.Size << 2);

    // Create an indirect buffer if needed and place it at
    // next 4K offset after end of ring buffer.

    if (CCEIBFlag)
    {
        offset = align (lastoffset + 4096, 4096);
		size = INDIRECT_BUF_SIZE;

		CreateBuffer (&IndirectBuf, offset, size);

		lastoffset = offset + size;
    }

    // Create vertex buffer if needed and place it at next
    // 4K offset after ring or indirect buffer.

    if (CCEVBFlag)
    {
        offset = align (lastoffset + 4096, 4096);
		size = VERTEX_BUF_SIZE;

		CreateBuffer (&VertexBuf, offset, size);

		lastoffset = offset + size;
    }
}


/****************************************************************************
 * R128_GetVertexBufferPtr - return a pointer to the top of the vertex      *
 *            buffer.                                                       *
 *  Function: This function returns the VertexBuf.LinearPtr member.         *
 *    Inputs: None.                                                         *
 *   Outputs: DWORD pointer to the top of the vertex buffer.                *
 ****************************************************************************/

DWORD* R128_GetVertexBufferPtr (void)
{
    return (VertexBuf.LinearPtr);
}


/****************************************************************************
 * R128_GetVertexBufferOffset - return offset to the top of the vertex      *
 *            buffer.                                                       *
 *  Function: This function returns the VertexBuf.Offset member.            *
 *    Inputs: None.                                                         *
 *   Outputs: DWORD offset to the top of the vertex buffer.                 *
 ****************************************************************************/

DWORD R128_GetVertexBufferOffset (void)
{
    return (VertexBuf.Offset);
}


/****************************************************************************
 * R128_GetVertexBufferSize - return offset to the top of the vertex        *
 *            buffer.                                                       *
 *  Function: This function returns the VertexBuf.Size member.              *
 *    Inputs: None.                                                         *
 *   Outputs: DWORD size of the vertex buffer.                              *
 ****************************************************************************/

DWORD R128_GetVertexBufferSize (void)
{
    return (VertexBuf.Size);
}


/****************************************************************************
 * R128_GetIndirectBufferPtr - return a pointer to the top of the indirect  *
 *            buffer.                                                       *
 *  Function: This function returns the IndirectBuf.LinearPtr member.       *
 *    Inputs: None.                                                         *
 *   Outputs: DWORD pointer to the top of the indirect buffer.              *
 ****************************************************************************/

DWORD* R128_GetIndirectBufferPtr (void)
{
    return (IndirectBuf.LinearPtr);
}


/****************************************************************************
 * R128_GetIndirectBufferOffset - return offset to the top of the indirect  *
 *            buffer.                                                       *
 *  Function: This function returns the IndirectBuf.Offset member.          *
 *    Inputs: None.                                                         *
 *   Outputs: DWORD offset to the top of the indirect buffer.               *
 ****************************************************************************/

DWORD R128_GetIndirectBufferOffset (void)
{
    return (IndirectBuf.Offset);
}


/****************************************************************************
 * R128_GetIndirectBufferSize - return size of the indirect buffer.         *
 *  Function: This function returns the IndirectBuf.Size member.            *
 *    Inputs: None.                                                         *
 *   Outputs: DWORD size of the indirect buffer.                            *
 ****************************************************************************/

DWORD R128_GetIndirectBufferSize (void)
{
    return (IndirectBuf.Size);
}


/****************************************************************************
 * CCENextPage - Write 4K plus two DWORDs of Type-2 packets (NOP) into      *
 *            ring buffer and submit.                                       *
 *  Function: This function is as a workaround for a PCI GART entry         *
 *            hardware bug in the Rage 128 ASIC.                            * 
 *    Inputs: None.                                                         *
 *   Outputs: None.                                                         *
 ****************************************************************************/

void CCENextPage (void)
{
    DWORD *tptr;
    DWORD i;

    tptr = RingBuf.LinearPtr + RingBuf.WriteIndex;

    for (i=0; i < 1026; i++)
    {
        *tptr++ = CCE_PACKET2;

        RingBuf.WriteIndex += 1;

        if (RingBuf.WriteIndex >= RingBuf.Size)
        {
            RingBuf.WriteIndex = 0;
            tptr = RingBuf.LinearPtr + RingBuf.WriteIndex;
        } // if
    } // for

    // Update pointer.

    regw (PM4_BUFFER_DL_WPTR, RingBuf.WriteIndex);
}



