/******************************************************************************
 * initeng.c                                                                  *
 *   GUI Engine initialization and related functions, including:              *
 *        void R128_InitEngine (void)                                         *
 *        void R128_WaitForIdle (void)                                        *
 *        void R128_WaitForFifo (DWORD entries)                               *
 *        void R128_ResetEngine (void)                                        *
 *        void R128_FlushPixelCache (void)                                    *
 *                                                                            *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.             *
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include "main.h"
#include "regdef.h"
#include "defines.h"

// Globals
DWORD term_count = 0;

void R128_InitEngine (void)
{
    DWORD temp;

    // insure 3D is disabled
    regw (SCALE_3D_CNTL, 0x00000000);

    // do an Engine Reset, just in case it's hung right now
    R128_ResetEngine ();

    // setup engine offset registers
    R128_WaitForFifo (1);
    regw (DEFAULT_OFFSET, 0x00000000);

    // setup engine pitch registers
    regw (DEFAULT_PITCH, (DWORD)R128_AdapterInfo.pitch);

    // set scissors to maximum size
    R128_WaitForFifo (1);
    regw (DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF);

    // Set the drawing controls registers.
    R128_WaitForFifo (1);
    temp = R128_GetBPPValue (R128_AdapterInfo.bpp);

    regw (DP_GUI_MASTER_CNTL, GMC_SRC_PITCH_OFFSET_DEFAULT |
                              GMC_DST_PITCH_OFFSET_DEFAULT |
                              GMC_SRC_CLIP_DEFAULT         |
                              GMC_DST_CLIP_DEFAULT         |
                              GMC_BRUSH_SOLIDCOLOR         |
                              (temp << 8)                  |
                              GMC_SRC_DSTCOLOR             |
                              GMC_BYTE_ORDER_MSB_TO_LSB    |
                              GMC_DP_CONVERSION_TEMP_6500  |
                              ROP3_PATCOPY                 |
                              GMC_DP_SRC_RECT              |
                              GMC_3D_FCN_EN_CLR            |
                              GMC_DST_CLR_CMP_FCN_CLEAR    |
                              GMC_AUX_CLIP_CLEAR           |
                              GMC_WRITE_MASK_SET);

    R128_WaitForFifo (8);

    // Clear the line drawing registers.
    regw (DST_BRES_ERR, 0);
    regw (DST_BRES_INC, 0);
    regw (DST_BRES_DEC, 0);

    // set brush color registers
    regw (DP_BRUSH_FRGD_CLR, 0xFFFFFFFF);
    regw (DP_BRUSH_BKGD_CLR, 0x00000000);

    // set source color registers
    regw (DP_SRC_FRGD_CLR, 0xFFFFFFFF);
    regw (DP_SRC_BKGD_CLR, 0x00000000);

    // default write mask
    regw (DP_WRITE_MASK, 0xFFFFFFFF);

    // Wait for all the writes to be completed before returning
    R128_WaitForIdle ();

    return;
} // R128_InitEngine ()


/******************************************************************************
 * R128_WaitForIdle - wait until engine active bit 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: NONE                                                            *
 ******************************************************************************/
void R128_WaitForIdle (void)
{
    WORD starttick, endtick;

    // Insure FIFO is empty before waiting for engine idle.
    R128_WaitForFifo (64);

    starttick = *((WORD *) (DOS_TICK_ADDRESS));
    endtick = starttick;
    while ((regr (GUI_STAT) & GUI_ACTIVE) != ENGINE_IDLE)
    {
        endtick = *((WORD *) (DOS_TICK_ADDRESS));
        if (abs (endtick - starttick) > IDLE_TIMEOUT)
        {
            // We should reset the engine at this point.
            R128_ResetEngine ();
        } // if
    } // while

    // flush the pixel cache
    R128_FlushPixelCache ();

    return;

} // R128_WaitForIdle ()


/******************************************************************************
 * R128_WaitForFifo - wait n empty FIFO entries.                              *
 *  Function: The FIFO contains upto 64 empty entries. The 'entries'          *
 *            value must be 1 to 64. This function implements the same        *
 *            timeout mechanism as the R128_WaitForIdle() function.           *
 *    Inputs: entries - number of entries spaces to wait for. Max - 64        *
 *   Outputs: NONE                                                            *
 ******************************************************************************/
void R128_WaitForFifo (DWORD entries)
{

    WORD starttick, endtick;

    starttick = *((WORD *) (DOS_TICK_ADDRESS));
    endtick = starttick;
    while ((regr (GUI_STAT) & 0x00000FFF) < entries)
    {
        endtick = *((WORD *) (DOS_TICK_ADDRESS));
        if (abs (endtick - starttick) > FIFO_TIMEOUT)
        {
            // we should reset the engine at this point.
            R128_ResetEngine ();
        } // if
    } // while

    return;

} // R128_WaitForFifo ()


/******************************************************************************
 * R128_ResetEngine - reset the Rage 128 GUI.                                 *
 *  Function: When the GUI becomes locked or hung, we must reset the engine   *
 *            to continue.  This involves resetting the engine clocks, and    *
 *            the engine itself.                                              *
 *    Inputs: NONE                                                            *
 *   Outputs: NONE                                                            *
 ******************************************************************************/
void R128_ResetEngine (void)
{
    DWORD save_genresetcntl, save_clockcntlindex, save_mclkcntl;

    // Flush the pixel cache
    R128_FlushPixelCache ();

    save_clockcntlindex = regr (CLOCK_CNTL_INDEX);
    save_mclkcntl = PLL_regr (MCLK_CNTL);

    // we must now force the engine clocks to active before
    // performing the engine reset.  We must turn them back on later...
    PLL_regw (MCLK_CNTL, save_mclkcntl | 0x00030000);

    // save GEN_RESET_CNTL register
    save_genresetcntl = regr (GEN_RESET_CNTL);

    // reset by setting bit, add read delay, then clear bit, add read delay
    regw (GEN_RESET_CNTL, save_genresetcntl | SOFT_RESET_GUI);
    regr (GEN_RESET_CNTL);
    regw (GEN_RESET_CNTL, save_genresetcntl & (~SOFT_RESET_GUI));
    regr (GEN_RESET_CNTL);

    // restore engine clocks
    PLL_regw (MCLK_CNTL, save_mclkcntl);

    // restore the two registers we changed
    regw (CLOCK_CNTL_INDEX, save_clockcntlindex);
    regw (GEN_RESET_CNTL, save_genresetcntl);

    term_count++; // for monitoring engine hangs
    return;

} // R128_ResetEngine

/**********************************************************************************
 *  R128_FlushPixelCache ()                                                       *
 *                                                                                *
 *  This function is required when reading back video memory after an engine      *
 *  operation to insure all data was written to the video memory from the pixel   *
 *  cache.                                                                        *
 *                                                                                *
 **********************************************************************************/
void R128_FlushPixelCache (void)
{
    WORD i;

    // initiate flush
    regw (PC_NGUI_CTLSTAT, regr (PC_NGUI_CTLSTAT) | 0x000000ff);

    // check for completion but limit looping to 16384 reads
    i = 0;
    while (((regr (PC_NGUI_CTLSTAT) & PC_BUSY) == PC_BUSY) && (i < 16384))
    {
        i++;
    }

    return;

} // R128_FlushPixelCache ()
