/*-*-c++-*-*/
/* $Id: util.c,v 1.2 2001-09-05 14:30:42 bird Exp $ */
/*
** THIS SOFTWARE IS SUBJECT TO COPYRIGHT PROTECTION AND IS OFFERED ONLY
** PURSUANT TO THE 3DFX GLIDE GENERAL PUBLIC LICENSE. THERE IS NO RIGHT
** TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX
** INTERACTIVE, INC. A COPY OF THIS LICENSE MAY BE OBTAINED FROM THE
** DISTRIBUTOR OR BY CONTACTING 3DFX INTERACTIVE INC(info@3dfx.com).
** THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
** EXPRESSED OR IMPLIED. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A
** FULL TEXT OF THE NON-WARRANTY PROVISIONS.
**
** USE, DUPLICATION OR DISCLOSURE BY THE GOVERNMENT IS SUBJECT TO
** RESTRICTIONS AS SET FORTH IN SUBDIVISION (C)(1)(II) OF THE RIGHTS IN
** TECHNICAL DATA AND COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013,
** AND/OR IN SIMILAR OR SUCCESSOR CLAUSES IN THE FAR, DOD OR NASA FAR
** SUPPLEMENT. UNPUBLISHED RIGHTS RESERVED UNDER THE COPYRIGHT LAWS OF
** THE UNITED STATES.
**
** COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED
**
** $Revision: 1.2 $
** $Date: 2001-09-05 14:30:42 $
**
** Utility routines for SST-1 Initialization code
**
*/
#pragma optimize ("",off)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#ifdef BUILD_FOR_SST1
#include <sst.h>
#else
#include <3dfx.h>
#include <cvgregs.h>
#include <cvgdefs.h>
#endif
#define FX_DLL_DEFINITION
#include <fxdll.h>
#include <sst1vid.h>
#include <sst1init.h>

/*
** sst1InitIdle():
**  Return idle condition of SST-1
**
**    Returns:
**      FXTRUE if SST-1 is idle (fifos are empty, graphics engines are idle)
**      FXFALSE if SST-1 has not been mapped
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitIdle(FxU32 *sstbase)
{
    if(!sstbase)
        return(FXFALSE);

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    if(!initIdleEnabled)
        return(FXTRUE);

    if(sst1CurrentBoard->fbiLfbLocked) {
        if(sst1CurrentBoard->sliSlaveVirtAddr == (FxU32 *) NULL)
            // SLI not enabled...
            sst1InitPciFifoIdleLoop(sstbase);
        else {
            // Check idle for Master...
            sst1InitPciFifoIdleLoop(sstbase);
            // Check idle for Slave...
            sst1InitPciFifoIdleLoop(sst1CurrentBoard->sliSlaveVirtAddr);
        }
    } else {
        if(sst1CurrentBoard->sliSlaveVirtAddr == (FxU32 *) NULL)
            // SLI not enabled...
            sst1InitIdleLoop(sstbase, FXTRUE);
        else {
            // Check idle for Master...
            sst1InitIdleLoop(sstbase, FXTRUE);

            // Check idle for Slave...
            // Note that the Slave does not need another NOP command, because
            // it will snoop the NOP command sent to the Master above.
            // Sending a NOP command to the Slave also confuses the callback
            // write routines for the command fifo which are not setup to
            // handle any other base addresses other than the Master's...
            sst1InitIdleLoop(sst1CurrentBoard->sliSlaveVirtAddr, FXFALSE);
        }
    }
    return(FXTRUE);
}

FX_EXPORT FxBool FX_CSTYLE sst1InitIdleWithTimeout(FxU32 *sstbase,
   FxU32 timeout)
{
    int retVal;

    if(!sstbase)
        return(FXFALSE);

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    if(sst1CurrentBoard->sliSlaveVirtAddr == (FxU32 *) NULL)
        // SLI not enabled...
        retVal = sst1InitIdleWithTimeoutLoop(sstbase, FXTRUE, timeout);
    else {
        // Check idle for Master...
        if(!sst1InitIdleWithTimeoutLoop(sstbase, FXTRUE, timeout))
           return(FXFALSE);

        // Check idle for Slave...
        // Note that the Slave does not need another NOP command, because
        // it will snoop the NOP command sent to the Master above.
        // Sending a NOP command to the Slave also confuses the callback
        // write routines for the command fifo which are not setup to
        // handle any other base addresses other than the Master's...
        retVal = sst1InitIdleWithTimeoutLoop(sst1CurrentBoard->sliSlaveVirtAddr,
          FXFALSE, timeout);
    }
    if(!retVal)
       return(FXFALSE);
    else
       return(FXTRUE);
}

FX_EXPORT FxBool FX_CSTYLE sst1InitIdleNoNOP(FxU32 *sstbase)
{
    if(!sstbase)
        return(FXFALSE);

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    if(!initIdleEnabled)
        return(FXTRUE);

    if(sst1CurrentBoard->fbiLfbLocked) {
        if(sst1CurrentBoard->sliSlaveVirtAddr == (FxU32 *) NULL)
            // SLI not enabled...
            sst1InitPciFifoIdleLoop(sstbase);
        else {
            // Check idle for Master...
            sst1InitPciFifoIdleLoop(sstbase);
            // Check idle for Slave...
            sst1InitPciFifoIdleLoop(sst1CurrentBoard->sliSlaveVirtAddr);
        }
    } else {
        if(sst1CurrentBoard->sliSlaveVirtAddr == (FxU32 *) NULL)
            // SLI not enabled...
            sst1InitIdleLoop(sstbase, FXFALSE);
        else {
            // Check idle for Master...
            sst1InitIdleLoop(sstbase, FXFALSE);
            // Check idle for Slave...
            sst1InitIdleLoop(sst1CurrentBoard->sliSlaveVirtAddr, FXFALSE);
        }
    }
    return(FXTRUE);
}

void sst1InitIdleLoop(FxU32 *sstbase, FxBool issueNOP)
{
    FxU32 cntr;
    SstRegs *sst = (SstRegs *) sstbase;

    if(issueNOP)
        ISET(sst->nopCMD, 0x0);

    cntr = 0;
    while(1) {
        if(!(sst1InitReturnStatus(sstbase) & SST_BUSY)) {
            if(++cntr >= 3)
                break;
        } else
            cntr = 0;
    }
}

int sst1InitIdleWithTimeoutLoop(FxU32 *sstbase, FxBool issueNOP, FxU32 timeout)
{
    FxU32 cntr, loop;
    SstRegs *sst = (SstRegs *) sstbase;

    if(issueNOP)
        ISET(sst->nopCMD, 0x0);

    cntr = loop = 0;
    while(++loop < timeout) {
        if(!(sst1InitReturnStatus(sstbase) & SST_BUSY)) {
            if(++cntr >= 3)
                break;
        } else
            cntr = 0;
    }
    if(loop >= timeout)
        return(0);
    else
        return(1);
}

void sst1InitPciFifoIdleLoop(FxU32 *sstbase)
{
    FxU32 cntr;

    cntr = 0;
    while(1) {
        if(((sst1InitReturnStatus(sstbase) & SST_FIFOLEVEL)) == 0x3f) {
            // Since sst1InitPciFifoIdleLoop is only called when we're trying
            // to "fake" idle during lfb locks, wait for 6 passes to give the
            // hardware time to drain
            if(++cntr >= 6)
                break;
        } else
            cntr = 0;
    }
}

/*
** sst1InitIdleFBI():
**  Return idle condition of FBI (ignoring idle status of TMU)
**
**    Returns:
**      FXTRUE if FBI is idle (fifos are empty, graphics engines are idle)
**      FXFALSE if FBI has not been mapped
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitIdleFBI(FxU32 *sstbase)
{
    FxU32 cntr;
    SstRegs *sst = (SstRegs *) sstbase;

    if(!sst)
        return(FXFALSE);

    ISET(sst->nopCMD, 0x0);
    cntr = 0;
    while(1) {
        if(!(sst1InitReturnStatus(sstbase) & SST_FBI_BUSY)) {
            if(++cntr >= 3)
                break;
        } else
            cntr = 0;
    }
    return(FXTRUE);
}

/*
** sst1InitIdleFBINoNOP():
**  Return idle condition of FBI (ignoring idle status of TMU)
**  sst1InitIdleFBINoNOP() differs from sst1InitIdleFBI() in that no NOP command
**  is issued to flush the graphics pipeline.
**
**    Returns:
**      FXTRUE if FBI is idle (fifos are empty, graphics engines are idle)
**      FXFALSE if FBI has not been mapped
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitIdleFBINoNOP(FxU32 *sstbase)
{
    FxU32 cntr;
    SstRegs *sst = (SstRegs *) sstbase;

    if(!sst)
        return(FXFALSE);

    // ISET(sst->nopCMD, 0x0);
    cntr = 0;
    while(1) {
        if(!(sst1InitReturnStatus(sstbase) & SST_FBI_BUSY)) {
            if(++cntr > 5)
                break;
        } else
            cntr = 0;
    }
    return(FXTRUE);
}

// Included so compiler doesn't optimize out loop code waiting on status bits
FX_EXPORT FxU32 FX_CSTYLE sst1InitReturnStatus(FxU32 *sstbase)
{
    SstRegs *sst = (SstRegs *) sstbase;

    return(IGET(sst->status));
}


/*
** sst1InitClearSwapPending():
**  Clear any swaps pending in the status register
**  NOTE: The video unit of FBI must be initialized before calling this routine
**
**    Returns:
**      FXTRUE
**
*/
FX_ENTRY FxBool FX_CALL sst1InitClearSwapPending(FxU32 *sstbase)
{
    SstRegs *sst = (SstRegs *) sstbase;
    FxU32 displayedBuffer, i;

    INIT_PRINTF(("sst1InitClearSwapPending() WARNING: Clearing pending swapbufferCMDs...\n"));

    sst1InitIdle(sstbase);
    displayedBuffer =
        (IGET(sst->status) & SST_DISPLAYED_BUFFER) >>
          SST_DISPLAYED_BUFFER_SHIFT;

    // Wait until vsync is inactive to guarantee that swaps queue in the
    // PCI fifo properly
    while(!(IGET(sst->status) & SST_VRETRACE) ||
      ((IGET(sst->vRetrace) & 0xfff) > 100) || ((IGET(sst->vRetrace) & 0xfff)
        < 10))
        ;

    // First swap syncs to Vsync...Subsequent ones do not...
    ISET(sst->swapbufferCMD, 0x1);
    ISET(sst->nopCMD, 0x0);
    for(i=0; i<17; i++) {
        ISET(sst->swapbufferCMD, 0x0);
        ISET(sst->nopCMD, 0x0);
    }
    if(displayedBuffer) {
        ISET(sst->swapbufferCMD, 0x0);
        ISET(sst->nopCMD, 0x0);
    }
    sst1InitIdle(sstbase);

    return(FXTRUE);
}

/*
** sst1InitVgaPassCtrl():
**  Control VGA passthrough setting
**
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitVgaPassCtrl(FxU32 *sstbase, FxU32 enable)
{
    SstRegs *sst = (SstRegs *) sstbase;

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    if(enable) {
        // VGA controls monitor
        ISET(sst->fbiInit0, (IGET(sst->fbiInit0) & ~SST_EN_VGA_PASSTHRU) |
            sst1CurrentBoard->vgaPassthruEnable);
        ISET(sst->fbiInit1, IGET(sst->fbiInit1) | SST_VIDEO_BLANK_EN);
    } else {
        // SST-1 controls monitor
        ISET(sst->fbiInit0, (IGET(sst->fbiInit0) & ~SST_EN_VGA_PASSTHRU) |
            sst1CurrentBoard->vgaPassthruDisable);
        ISET(sst->fbiInit1, IGET(sst->fbiInit1) & ~SST_VIDEO_BLANK_EN);
    }

    return(FXTRUE);
}

/*
** sst1InitResetTmus():
**  Reset TMUs
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitResetTmus(FxU32 *sstbase)
{
   SstRegs *sst = (SstRegs *) sstbase;
   FxU32 fbiInit3, i, cntr = 0;
   int j;
   FxU32 allowTexturing = (GETENV(("SSTV2_TEXMAP_DISABLE"))) ? 0 : 1;

   if(!sst)
       return(FXFALSE);

   if(sst1InitCheckBoard(sstbase) == FXFALSE)
       return(FXFALSE);

   fbiInit3 = IGET(sst->fbiInit3);
   while(++cntr < 6) {
      // Ignore stalls on FT Bus
      ISET(sst->fbiInit3, fbiInit3 | SST_TEXMAP_DISABLE);
      for(i=0; i<3; i++) sst1InitReturnStatus(sstbase);

      // Set Default initialization values for all TMUs...
      for(j=0; j<3; j++) {
         ISET(SST_TREX(sst,j)->trexInit0, sst1CurrentBoard->tmuInit0[j]);
         for(i=0; i<3; i++) sst1InitReturnStatus(sstbase);
      }
      for(j=0; j<3; j++) {
         ISET(SST_TREX(sst,j)->trexInit1, sst1CurrentBoard->tmuInit1[j]);
         for(i=0; i<3; i++) sst1InitReturnStatus(sstbase);
      }

      // Reset TMU FIFOs and graphics core for all TMUs...
      for(j=0; j<3; j++) {
         ISET(SST_TREX(sst,j)->trexInit1, sst1CurrentBoard->tmuInit1[j] |
            SST_TEX_RESET_FIFO | SST_TEX_RESET_GRX);
         for(i=0; i<3; i++) sst1InitReturnStatus(sstbase);
      }
      // Allow reset to propogate
      for(i=0; i<10; i++) sst1InitReturnStatus(sstbase);

      // Deassert resets.  Start with upstream TMU and work down...
      for(j=2; j >= 0; j--) {
         ISET(SST_TREX(sst,j)->trexInit1, sst1CurrentBoard->tmuInit1[j]);
         // Allow unreset to propogate
         for(i=0; i<10; i++) sst1InitReturnStatus(sstbase);
      }

      // Reset in the downstream TMU may cause glitches in FBI's TF FIFO
      // Waiting for Idle in FBI will not stall on something in the TF FIFO,
      // as the TF FIFO empty signal is not part of FBI's "busy" bit
      sst1InitIdleFBI(sstbase);
      sst1InitResetFbi(sstbase);

      if(allowTexturing) {
         ISET(sst->fbiInit3, fbiInit3 & ~SST_TEXMAP_DISABLE);
         for(i=0; i<3; i++) sst1InitReturnStatus(sstbase);
      }

      if(sst1InitReturnStatus(sstbase) & SST_TREX_BUSY)
         INIT_PRINTF(("sst1InitResetTmus(): Could not reset TMUs.  Retry #%d...\n", cntr));
      else
         break;
   }
   if(cntr == 6) {
       INIT_PRINTF(("sst1InitResetTmus(): Could not reset TMUs...\n"));
       return(FXFALSE);
   }

   // Fix problem where first Texture downloads to TMU weren't being
   //  received properly
   ISET(*(long *) (0xf00000 + (long) sstbase), 0xdeadbeef);
   sst1InitIdle(sstbase);

   return(FXTRUE);
}

/*
** sst1InitResetFbi():
**  Reset FBI graphics engine and frontend PCI Fifo...
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitResetFbi(FxU32 *sstbase)
{
   SstRegs *sst = (SstRegs *) sstbase;

   ISET(sst->fbiInit0, IGET(sst->fbiInit0) | SST_GRX_RESET |
      SST_PCI_FIFO_RESET);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   ISET(sst->fbiInit0, IGET(sst->fbiInit0) & ~SST_PCI_FIFO_RESET);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   ISET(sst->fbiInit0, IGET(sst->fbiInit0) & ~SST_GRX_RESET);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);

   return(FXTRUE);
}

#if SET_BSWAP
#if __POWERPC__ && defined(__MWERKS__)
#define GET(s)       __lwbrx(s, 0 )
#define SET(d, s)    __stwbrx((s), (d), 0);
#else /* !__MWERKS__ */
#error "Define byte swapped macros for GET/SET"
#endif /* !__MWERKS__ */
#else /* !SET_BSWAP */
#ifdef GET
#undef GET
#endif
#ifdef SET
#undef SET
#endif
#define GET(s)       (*s)
#define SET(d, s)    ((*d) = s)
#endif /* !SET_BSWAP */

/*
** sst1InitWrite32():
**  Write 32-bit Word to specified address
**
*/
FX_EXPORT void FX_CSTYLE sst1InitWrite32(FxU32 *addr, FxU32 data)
{
  /* If the client software is using the command fifo then they are
   * responsible for passing a callback that can be used to put
   * register writes from the init code into the command fifo that
   * they are managing. However, some registers cannot be accessed via
   * the command fifo, and, inconveniently, these are not contiguously
   * allocated.
   */
  const FxU32 addrOffset = ((const FxU32)addr - (const FxU32)sst1CurrentBoard->virtAddr[0]);
  FxBool directWriteP = ((sst1CurrentBoard == NULL) ||
                         (sst1CurrentBoard->set32 == NULL) ||
                         sst1CurrentBoard->fbiLfbLocked ||
                         (addrOffset == 0x004) ||                            /* intrCtrl */
                         ((addrOffset >= 0x1E0) && (addrOffset <= 0x200)) || /* cmdFifoBase ... fbiInit4 */
                         ((addrOffset >= 0x208) && (addrOffset <= 0x224)) || /* backPorch ... vSync */
                         ((addrOffset >= 0x22C) && (addrOffset <= 0x23C)) || /* dacData ... borderColor */
                         ((addrOffset >= 0x244) && (addrOffset <= 0x24C)));  /* fbiInit5 ... fbiInit7 */

  if (directWriteP) {
    P6FENCE;
    SET(addr, data);
    P6FENCE;
  } else {
    (*sst1CurrentBoard->set32)(addr, data);
  }
}

/*
** sst1InitRead32():
**  Read 32-bit Word from specified address
**
*/
FX_EXPORT FxU32 FX_CSTYLE sst1InitRead32(FxU32 *addr)
{
    P6FENCE;
    return GET(addr);
}

/*
**
** sst1InitCmdFifo():

**   Setup Command Fifo.
**
**   The 'enable' parameter either enables or disables the Command Fifo.
**
**   Upon return, the 'virtAddrStart' parameter is filled with the starting
**   virtual address where command fifo packets should be stored.  The
**   'memAddrStart' parameter is filled with the address of where the command
**   fifo is stored in frame buffer memory.  The 'memAddrStart' value is often
**   used by the JMP command to jump to the beginning of the command fifo.
**   The 'size' parameter is filled with the size of the command fifo.
**   The values of 'virtAddrstart', 'memAddrStart' and 'size'
**   are dependent on the amount of frame buffer memory detected as well as the
**   video resolution setup by sst1InitVideo().  'virtAddrstart',
**   'memAddrStart' and 'size' are all specified in bytes.
**
*/
FX_ENTRY FxBool FX_CALL sst1InitCmdFifo(FxU32 *sstbase, FxBool enable,
  FxU32 *virtAddrStart, FxU32 *memAddrStart, FxU32 *size, FxSet32Proc set32Proc)
{
    SstRegs *sst = (SstRegs *) sstbase;
    FxU32 fbiInit, fifoStart, fifoSize;
    FxBool directExec = (GETENV(("SSTV2_CMDFIFO_DIRECT"))) ? FXTRUE : FXFALSE;
    FxBool disableHoles = (GETENV(("SSTV2_CMDFIFO_NOHOLES"))) ? FXTRUE : FXFALSE;

    if(!sst)
        return(FXFALSE);

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    if(sst1CurrentBoard->fbiCmdFifoEn || (IGET(sst->fbiInit7) & SST_EN_CMDFIFO))
        sst1InitIdleNoNOP(sstbase);
    else
        sst1InitIdle(sstbase);

    if(enable == FXFALSE) {
       // Remove any client set callbacks before continuing since
       // these must go straight to the hw.
       sst1CurrentBoard->set32 = NULL;
       sst1CurrentBoard->fbiCmdFifoEn = 0;

       // Disable Command Fifo
       INIT_PRINTF(("sst1InitCmdFifo(): Disabling Command Fifo...\n"));
       ISET(sst->fbiInit7, IGET(sst->fbiInit7) & ~SST_EN_CMDFIFO);

       // Reset graphics core to force disable to take effect...
       ISET(sst->fbiInit0, IGET(sst->fbiInit0) | SST_GRX_RESET |
         SST_EN_LFB_MEMFIFO | SST_EN_TEX_MEMFIFO);
       sst1InitReturnStatus(sstbase);
       sst1InitReturnStatus(sstbase);
       sst1InitReturnStatus(sstbase);
       ISET(sst->fbiInit0, IGET(sst->fbiInit0) & ~SST_GRX_RESET);

       if(sst1CurrentBoard->sliSlaveVirtAddr) {
          SstRegs *slaveSst = (SstRegs *) sst1CurrentBoard->sliSlaveVirtAddr;

          ISET(slaveSst->fbiInit7, IGET(slaveSst->fbiInit7) & ~SST_EN_CMDFIFO);
          // Reset graphics core to force disable to take effect...
          ISET(slaveSst->fbiInit0, IGET(slaveSst->fbiInit0) | SST_GRX_RESET |
            SST_EN_LFB_MEMFIFO | SST_EN_TEX_MEMFIFO);
          sst1InitReturnStatus(sst1CurrentBoard->sliSlaveVirtAddr);
          sst1InitReturnStatus(sst1CurrentBoard->sliSlaveVirtAddr);
          sst1InitReturnStatus(sst1CurrentBoard->sliSlaveVirtAddr);
          ISET(slaveSst->fbiInit0, IGET(slaveSst->fbiInit0) & ~SST_GRX_RESET);

          if(sst1InitCheckBoard(sst1CurrentBoard->sliSlaveVirtAddr) == FXFALSE)
              return(FXFALSE);
          sst1CurrentBoard->set32 = NULL;
          sst1CurrentBoard->fbiCmdFifoEn = 0;
       }
       return(FXTRUE);
    }

    sst1InitIdle(sstbase);
    fbiInit = IGET(sst->fbiInit4);
    fifoStart =
      (fbiInit & SST_MEM_FIFO_ROW_BASE) >> SST_MEM_FIFO_ROW_BASE_SHIFT;
    fifoStart <<= 12;  // Convert page-address into byte-address

    if(sst1CurrentBoard->fbiMemSize == 4)
      fifoSize = (1024 - (fifoStart >> 12)) << 12;
    else if(sst1CurrentBoard->fbiMemSize == 2)
      fifoSize = (512 - (fifoStart >> 12)) << 12;
    else
      fifoSize = (256 - (fifoStart >> 12)) << 12;
    if(fifoSize > (256<<10))
      fifoSize = (256<<10);

    *virtAddrStart =  (FxU32) (((FxU32) sstbase) + SST_CMDFIFO_ADDR);
    *memAddrStart = fifoStart;
    *size = fifoSize;

    if(!sst1InitCmdFifoDirect(sstbase, 0,
                              fifoStart, fifoSize,
                              directExec, disableHoles,
                              set32Proc)) {
      INIT_PRINTF(("sst1InitCmdFifo(): sst1InitCmdFifoDirect() failed...\n"));
      return(FXFALSE);
    }

    if(sst1CurrentBoard->sliSlaveVirtAddr) {
      if(!sst1InitCmdFifoDirect(sst1CurrentBoard->sliSlaveVirtAddr, 0,
                               fifoStart, fifoSize,
                               directExec, disableHoles,
                               set32Proc)) {
          INIT_PRINTF(("sst1InitCmdFifo(): sst1InitCmdFifoDirect() failed for SLI Slave...\n"));
          return(FXFALSE);
      }
    }

    INIT_PRINTF(("sst1InitCmdFifo() exiting with status %d...\n", FXTRUE));
    return(FXTRUE);
}

/*
**
** sst1InitCmdFifoDirect():
**   Explicitly initialize Command FIFO.  This routine is typically not
**   called directly from apps.
**
**   The 'start' and 'size' parameters are specified in bytes.
**   The 'which' parameter is not used, but is included for H3 compatibility.
**
*/
FX_ENTRY FxBool FX_CALL sst1InitCmdFifoDirect(FxU32 *sstbase, FxU32 which,
  FxU32 start, FxU32 size, FxBool directExec, FxBool disableHoles,
  FxSet32Proc set32Proc)
{
    SstRegs *sst = (SstRegs *) sstbase;
    FxU32 fbiInit7, initEnable, n;
    FxU32 pageStart = start >> 12;
    FxU32 pageEnd = (start+size-1) >> 12;

    if(!sst)
        return(FXFALSE);

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    // sst1Initidle() routines must be properly executed...
    initIdleEnabled = 1;

    INIT_PRINTF(("sst1InitCmdFifoDirect(): Start:0x%x bytes, Size:%d(0x%x) bytes\n",
      start, size, size));
    INIT_PRINTF(("sst1InitCmdFifoDirect(): DirectExec:%d, DisableHoles:%d\n",
      directExec, disableHoles));
    INIT_PRINTF(("sst1InitCmdFifoDirect(): pageStart:%d, pageEnd:%d\n",
      pageStart, pageEnd));
    INIT_PRINTF(("sst1InitCmdFifoDirect(): set32Proc: 0x%X\n",
                 (FxU32)set32Proc));

    if(sst1CurrentBoard->fbiCmdFifoEn || (IGET(sst->fbiInit7) & SST_EN_CMDFIFO))
        sst1InitIdleNoNOP(sstbase);
    else
        sst1InitIdle(sstbase);
    // Disable memory-backed fifo, and disallow lfb and texture writes
    // through command fifo...
    ISET(sst->fbiInit0, (IGET(sst->fbiInit0) &
      ~(SST_MEM_FIFO_EN | SST_EN_LFB_MEMFIFO | SST_EN_TEX_MEMFIFO)));
    sst1InitReturnStatus(sstbase);

    fbiInit7 = IGET(sst->fbiInit7);
    fbiInit7 &= ~(SST_EN_CMDFIFO |
       SST_EN_CMDFIFO_OFFSCREEN | SST_CMDFIFO_DISABLE_HOLES |
       SST_CMDFIFO_REGS_SYNC_WRITES | SST_CMDFIFO_REGS_SYNC_READS |
       SST_CMDFIFO_RDFETCH_THRESH | SST_CMDFIFO_PCI_TIMEOUT);
    fbiInit7 |= SST_CMDFIFO_REGS_SYNC_WRITES | SST_CMDFIFO_REGS_SYNC_READS |
       (0x10<<SST_CMDFIFO_RDFETCH_THRESH_SHIFT) |
       (127<<SST_CMDFIFO_PCI_TIMEOUT_SHIFT);
    ISET(sst->fbiInit7, fbiInit7);    // turn off the command fifo
    sst1InitReturnStatus(sstbase);

    // Disable all writes to the PCI fifo while we're setting up CMDFIFO
    PCICFG_RD(SST1_PCI_INIT_ENABLE, initEnable);
    PCICFG_WR(SST1_PCI_INIT_ENABLE,
       ((initEnable & ~(SST_INITWR_EN | SST_PCI_FIFOWR_EN)) | SST_INITWR_EN));

    // Setup command fifo...
    ISET(sst->cmdFifoBase, (pageEnd << SST_CMDFIFO_END_SHIFT) | pageStart);
    sst1InitReturnStatus(sstbase);  // prevent PCI bursts...
    ISET(sst->cmdFifoReadPtr, start);
    sst1InitReturnStatus(sstbase);
    ISET(sst->cmdFifoAmin, start-4);
    sst1InitReturnStatus(sstbase);
    ISET(sst->cmdFifoAmax, start-4);
    sst1InitReturnStatus(sstbase);
    ISET(sst->cmdFifoDepth, 0);
    sst1InitReturnStatus(sstbase);
    ISET(sst->cmdFifoHoles, 0);
    sst1InitReturnStatus(sstbase);
    sst1InitIdle(sstbase);

    // Turn on command fifo...
    ISET(sst->fbiInit7, fbiInit7 | SST_EN_CMDFIFO |
      (directExec ? 0 : SST_EN_CMDFIFO_OFFSCREEN) |
      (disableHoles ? SST_CMDFIFO_DISABLE_HOLES : 0));
    // Can't perform sst1InitIdle() here because it will generate
    // writes to the CMDFIFO since the CMDFIFO is now enabled...
    sst1InitReturnStatus(sstbase);

    // Enable writes to be pushed onto the CMDFIFO...
    PCICFG_WR(SST1_PCI_INIT_ENABLE,
       (initEnable | SST_INITWR_EN | SST_PCI_FIFOWR_EN));

    sst1CurrentBoard->fbiCmdFifoEn = 1;
    if(GETENV(("SSTV2_IGNORE_IDLE")))
       initIdleEnabled = 0;

    /* Set the client callbacks, if necessary. */
    sst1CurrentBoard->set32 = set32Proc;

    return(FXTRUE);
}

/*
**
** sst1InitLfbLock():
**   Used to workaround a hw bug when performing lfb and texture writes when
**   the command fifo is enabled.  The routines sst1InitLfbLock() and
**   sst1InitLfbUnlock() dynamically disable the command fifo so that lfb
**   and texture accesses do not pass through the command fifo.
**   WARNING: No register writes of any kind may be performed between a
**   sst1InitLfbLock() and sst1InitLfbUnlock() pair -- only lfb reads and
**   writes are allowed.
*/
FX_ENTRY FxBool FX_CALL sst1InitLfbLock(FxU32* sstbase)
{
   if(!sstbase)
       return(FXFALSE);

   if(sst1InitCheckBoard(sstbase) == FXFALSE)
       return(FXFALSE);

   // Idle the hardware...
   sst1InitIdle(sstbase);

   if(sst1InitLfbLockDirect(sstbase) == FXFALSE)
       return(FXFALSE);

   if(sst1CurrentBoard->sliSlaveVirtAddr) {
       // SLI Enabled
       if(sst1InitLfbLockDirect(sst1CurrentBoard->sliSlaveVirtAddr) == FXFALSE)
           return(FXFALSE);
   }

   return(FXTRUE);
}

FX_ENTRY FxBool FX_CALL sst1InitLfbLockDirect(FxU32* sstbase)
{
   SstRegs *sst = (SstRegs *) sstbase;

   if(!sst)
       return(FXFALSE);

   if(sst1InitCheckBoard(sstbase) == FXFALSE)
       return(FXFALSE);

   if((!(IGET(sst->fbiInit7) & SST_EN_CMDFIFO) &&
     !sst1CurrentBoard->fbiCmdFifoEn) || sst1CurrentBoard->fbiLfbLocked)
       return(FXTRUE);

   // Force direct writes...
   sst1CurrentBoard->fbiLfbLocked = 1;

   ISET(sst->fbiInit7, IGET(sst->fbiInit7) & ~SST_EN_CMDFIFO_OFFSCREEN);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);

   return(FXTRUE);
}

/*
**
** sst1InitLfbUnlock():
**   Used to workaround a hw bug when performing lfb and texture writes when
**   the command fifo is enabled.  The routines sst1InitLfbLock() and
**   sst1InitLfbUnlock() dynamically disable the command fifo so that lfb
**   and texture accesses do not pass through the command fifo.
**   WARNING: No register writes of any kind may be performed between a
**   sst1InitLfbLock() and sst1InitLfbUnlock() pair -- only lfb reads and
**   writes are allowed.
*/

FX_ENTRY FxBool FX_CALL sst1InitLfbUnlock(FxU32* sstbase)
{
   if(!sstbase)
       return(FXFALSE);

   if(sst1InitCheckBoard(sstbase) == FXFALSE)
       return(FXFALSE);

   // Idle the hardware.  Since lfb lock is set, a NOP will not be issued, and
   // sst1InitPciFifoIdleLoop() will be used instead of sst1InitIdleLoop()...
   sst1InitIdle(sstbase);

   if(sst1CurrentBoard->sliSlaveVirtAddr) {
       // SLI Enabled
       if(sst1InitLfbUnlockDirect(sst1CurrentBoard->sliSlaveVirtAddr) ==
         FXFALSE)
           return(FXFALSE);
   }
   if(sst1InitLfbUnlockDirect(sstbase) == FXFALSE)
       return(FXFALSE);

   // After the command fifo is re-enabled, make sure the chip is really idle...
   sst1InitIdle(sstbase);

   return(FXTRUE);
}

FX_ENTRY FxBool FX_CALL sst1InitLfbUnlockDirect(FxU32* sstbase)
{
   SstRegs *sst = (SstRegs *) sstbase;

   if(!sst)
       return(FXFALSE);

   if(sst1InitCheckBoard(sstbase) == FXFALSE)
       return(FXFALSE);

   if(!sst1CurrentBoard->fbiCmdFifoEn || !sst1CurrentBoard->fbiLfbLocked)
       return(FXTRUE);

   ISET(sst->fbiInit7, IGET(sst->fbiInit7) | SST_EN_CMDFIFO_OFFSCREEN);

   sst1CurrentBoard->fbiLfbLocked = 0;

   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);
   sst1InitReturnStatus(sstbase);

   return(FXTRUE);
}

/*
**
** sst1InitPrintInitRegs():
**   Print Initialization Registers
**
*/
FX_EXPORT void FX_CSTYLE sst1InitPrintInitRegs(FxU32 *sstbase)
{
    SstRegs *sst = (SstRegs *) sstbase;

    INIT_PRINTF(("FBI Initialization Registers:\n"));
    INIT_PRINTF(("-----------------------------\n"));
    INIT_PRINTF(("fbiInit0:0x%08x  fbiInit1:0x%08x\n",
      IGET(sst->fbiInit0), IGET(sst->fbiInit1)));
    INIT_PRINTF(("fbiInit2:0x%08x  fbiInit3:0x%08x\n",
      IGET(sst->fbiInit2), IGET(sst->fbiInit3)));
    INIT_PRINTF(("fbiInit4:0x%08x  fbiInit5:0x%08x\n",
      IGET(sst->fbiInit4), IGET(sst->fbiInit5)));
    INIT_PRINTF(("fbiInit6:0x%08x  fbiInit7:0x%08x\n",
      IGET(sst->fbiInit6), IGET(sst->fbiInit7)));
    INIT_PRINTF(("videoDimensions:0x%08x\n\n",
      IGET(sst->videoDimensions)));
    INIT_PRINTF(("FBI Command Fifo Registers:\n"));
    INIT_PRINTF(("---------------------------\n"));
    INIT_PRINTF(("cmdFifoBase:    0x%08x\tcmdFifoBump:0x%x\n",
      IGET(sst->cmdFifoBase), IGET(sst->cmdFifoBump)));
    INIT_PRINTF(("cmdFifoReadPtr: 0x%08x\tcmdFifoAmin:0x%x\n",
      IGET(sst->cmdFifoReadPtr), IGET(sst->cmdFifoAmin)));
    INIT_PRINTF(("cmdFifoAmax:    0x%08x\tcmdFifoDepth:0x%x\n",
      IGET(sst->cmdFifoAmax), IGET(sst->cmdFifoDepth)));
    INIT_PRINTF(("cmdFifoHoles:   0x%08x\n", IGET(sst->cmdFifoHoles)));
}

/*
**
** sst1InitMeasureSiProcess():
**   Use silicon process register to measure silicon performance
**
*/
FX_EXPORT FxU32 FX_CSTYLE sst1InitMeasureSiProcess(FxU32 *sstbase, FxU32 which)
{
    FxU32 n, siProcess, nandOsc, norOsc;
    FxU32 pciCntrLoad = 0xfff;
    FxU32 cntr;

    if(sst1InitCheckBoard(sstbase) == FXFALSE)
        return(FXFALSE);

    if(GETENV(("SSTV2_SIPROCESS_CNTR"))) {
        SSCANF(GETENV(("SSTV2_SIPROCESS_CNTR")), "%i", &pciCntrLoad);
        INIT_PRINTF(("sst1InitMeasureSiProcess(): Using PCI Counter preload value of 0x%x...\n", pciCntrLoad));
    }

    if(!which) {
       ////////////////////////////////
       // Test NAND oscillator tree...
       ////////////////////////////////
       PCICFG_WR(SST1_PCI_SIPROCESS,
        (pciCntrLoad<<SST_SIPROCESS_PCI_CNTR_SHIFT) |
         SST_SIPROCESS_OSC_CNTR_RESET_N | SST_SIPROCESS_OSC_NAND_SEL);

       // Allow oscillator to run...
       PCICFG_RD(SST1_PCI_SIPROCESS, siProcess);
       PCICFG_WR(SST1_PCI_SIPROCESS,
        (pciCntrLoad<<SST_SIPROCESS_PCI_CNTR_SHIFT) |
         SST_SIPROCESS_OSC_CNTR_RUN | SST_SIPROCESS_OSC_NAND_SEL);

       // Loop until PCI counter decrements to 0
       cntr = 0 ;
       do {
          ++cntr;
          PCICFG_RD(SST1_PCI_SIPROCESS, siProcess);
       } while(siProcess & SST_SIPROCESS_PCI_CNTR);

       PCICFG_RD(SST1_PCI_SIPROCESS, siProcess);
       siProcess &= SST_SIPROCESS_OSC_CNTR;
       nandOsc = siProcess;
       if(nandOsc < 3000)
          nandOsc <<= 1; // Running on 66 MHz PCI...
       sst1CurrentBoard->fbiNandTree = nandOsc;

       INIT_PRINTF(("sst1InitInfo(): NAND-tree: %d\n", nandOsc));
    } else {
       ////////////////////////////////
       // Test NOR oscillator tree...
       ////////////////////////////////
       PCICFG_WR(SST1_PCI_SIPROCESS,
        (pciCntrLoad<<SST_SIPROCESS_PCI_CNTR_SHIFT) |
         SST_SIPROCESS_OSC_CNTR_RESET_N | SST_SIPROCESS_OSC_NOR_SEL);

       // Allow oscillator to run...
       PCICFG_RD(SST1_PCI_SIPROCESS, siProcess);
       PCICFG_WR(SST1_PCI_SIPROCESS,
       (pciCntrLoad<<SST_SIPROCESS_PCI_CNTR_SHIFT) |
         SST_SIPROCESS_OSC_CNTR_RUN | SST_SIPROCESS_OSC_NOR_SEL);

       // Loop until PCI counter decrements to 0
       cntr = 0 ;
       do {
          ++cntr;
          PCICFG_RD(SST1_PCI_SIPROCESS, siProcess);
       } while(siProcess & SST_SIPROCESS_PCI_CNTR);

       PCICFG_RD(SST1_PCI_SIPROCESS, siProcess);
       siProcess &= SST_SIPROCESS_OSC_CNTR;
       norOsc = siProcess;
       if(norOsc < 3000)
          norOsc <<= 1; // Running on 66 MHz PCI...
       sst1CurrentBoard->fbiNorTree = norOsc;

       INIT_PRINTF(("sst1InitInfo(): NOR-tree : %d\n", norOsc));
    }
    return(siProcess);
}

/*
**
** sst1InitCalcTClkDelay()
** Calculate optimal TF/TT Clock delay values
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitCalcTClkDelay(FxU32 *sstbase,
   FxU32 tmuNumber, FxU32 tClkDelay)
{
   SstRegs *sst = (SstRegs *) sstbase;
   volatile FxU32 *texAddr;

   if(sst1InitCheckBoard(sstbase) == FXFALSE)
      return(FXFALSE);

   ISET(sst->tLOD, 0x0);
   ISET(sst->tDetail, 0x0);
   ISET(sst->texBaseAddr, 0x0);
   ISET(sst->fogMode, 0x0);
   ISET(sst->alphaMode, 0x0);
   ISET(sst->fbzMode, SST_ENCHROMAKEY);
   ISET(sst->fbzColorPath, SST_RGBSEL_TREXOUT | SST_CC_PASS | SST_ENTEXTUREMAP);
   //texAddr = (numTmu<<(21-2)) + (FxU32 *) SST_TEX_ADDRESS(sst);
   texAddr = (0<<(21-2)) + (FxU32 *) SST_TEX_ADDRESS(sst);
   ISET(texAddr[0], 0x0);
   texAddr = (1<<(21-2)) + (FxU32 *) SST_TEX_ADDRESS(sst);
   ISET(texAddr[0], 0x0);
   texAddr = (2<<(21-2)) + (FxU32 *) SST_TEX_ADDRESS(sst);
   ISET(texAddr[0], 0x0);

   ISET(SST_TREX(sst,tmuNumber)->trexInit1,
      (sst1CurrentBoard->tmuInit1[tmuNumber] & ~SST_TEX_TF_CLK_DEL_ADJ) |
      (tClkDelay<<SST_TEX_TF_CLK_DEL_ADJ_SHIFT));

   if(sst1InitIdleWithTimeout(sstbase, 10000) == FXFALSE)
      return(FXFALSE);

   // Reset pixel stat registers
   ISET(sst->nopCMD, 0x3);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0xff0000);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0x00ffff);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0xf0f0f0);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0x0f0f0f);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0x55aa55);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0xaa55aa);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0x5a5a5a);
   sst1InitCheckTmuMemConst(sstbase, tmuNumber, 0xa5a5a5);

   if(sst1InitIdleWithTimeout(sstbase, 10000) == FXFALSE)
      return(FXFALSE);

   if(IGET(sst->stats.fbiChromaFail))
      return(FXFALSE);
   else
      return(FXTRUE);
}

#define COLOR32TOCOLOR24_6666(X)       \
      ((((X >> 2) & 0x3F) << 0) |      \
       (((X >> 10) & 0x3F) << 6) |     \
       (((X >> 18) & 0x3F) << 12) |    \
       (((X >> 26) & 0x3F) << 18))
#define COLOR24_6666TOCOLOR32(X)                                  \
      (((((X >> 0) & 0x3F) << 2) | (((X >> 4) & 0x3) << 0)) |     \
       ((((X >> 6) & 0x3F) << 10) | (((X >> 10) & 0x3) << 8)) |   \
       ((((X >> 12) & 0x3F) << 18) | (((X >> 16) & 0x3) << 16)) | \
       ((((X >> 18) & 0x3F) << 26) | (((X >> 22) & 0x3) << 24)))

void sst1InitCheckTmuMemConst(FxU32 *sstbase, FxU32 tmuNumber,
   FxU32 dataExpect32)
{
   SstRegs *sst = (SstRegs *) sstbase;
   FxU32 dataExpect6666 = COLOR32TOCOLOR24_6666(dataExpect32);
   FxU32 dataExpect6666To32 = COLOR24_6666TOCOLOR32(dataExpect6666) & 0xffffff;

   ISET(sst->chromaKey, dataExpect6666To32);
   ISET(sst->chromaRange, dataExpect6666To32 | SST_ENCHROMARANGE |
      SST_CHROMARANGE_BLUE_EX | SST_CHROMARANGE_GREEN_EX |
      SST_CHROMARANGE_RED_EX | SST_CHROMARANGE_BLOCK_OR);

   if(!tmuNumber) {
      ISET(sst->textureMode, SST_P8_ARGB6666 | SST_TC_REPLACE | SST_TCA_ZERO);
   } else if(tmuNumber == 1) {
      // Force downstream TMU to passthrough upstream data
      ISET(SST_TREX(sst,0)->textureMode, SST_P8_ARGB6666 | SST_TC_PASS |
        SST_TCA_PASS);
      ISET(SST_TREX(sst,1)->textureMode, SST_P8_ARGB6666 | SST_TC_REPLACE |
        SST_TCA_ZERO);
      ISET(SST_TREX(sst,2)->textureMode, SST_P8_ARGB6666 | SST_TC_REPLACE |
        SST_TCA_ZERO);
   } else {
      // Force downstream TMUs to passthrough upstream data
      ISET(SST_TREX(sst,0)->textureMode, SST_P8_ARGB6666 | SST_TC_PASS |
        SST_TCA_PASS);
      ISET(SST_TREX(sst,1)->textureMode, SST_P8_ARGB6666 | SST_TC_PASS |
        SST_TCA_PASS);
      ISET(SST_TREX(sst,2)->textureMode, SST_P8_ARGB6666 | SST_TC_REPLACE |
        SST_TCA_ZERO);
   }
   // Specify color in palette entry 0...
   //sst->nccTable0[4]=BIT(31) | ((index>>1)<<24) | (palette[index] & 0xffffff);
   ISET(sst->nccTable0[4], BIT(31) | COLOR32TOCOLOR24_6666(dataExpect32));
   sst1InitDrawRectUsingTris(sstbase, 0, 0, 128);

   return;
}

void sst1InitDrawRectUsingTris(FxU32 *sstbase, FxU32 x, FxU32 y, FxU32 tSize)
{
   SstRegs *sst = (SstRegs *) sstbase;

   ISET(sst->vA.x, (x<<SST_XY_FRACBITS));
   ISET(sst->vA.y, (y<<SST_XY_FRACBITS));
   ISET(sst->vB.x, ((x+tSize)<<SST_XY_FRACBITS));
   ISET(sst->vB.y, (y<<SST_XY_FRACBITS));
   ISET(sst->vC.x, ((x+tSize)<<SST_XY_FRACBITS));
   ISET(sst->vC.y, ((y+tSize)<<SST_XY_FRACBITS));
   ISET(sst->s, 0);
   ISET(sst->t, 0);
   ISET(sst->w, 0);
   ISET(sst->r, (0xff<<SST_XY_INTBITS));
   ISET(sst->g, 0);
   ISET(sst->b, 0);
   ISET(sst->dsdx, 0);
   ISET(sst->dtdx, 0);
   ISET(sst->dwdx, 0);
   ISET(sst->dsdy, 0);
   ISET(sst->dtdy, 0);
   ISET(sst->dwdy, 0);
   ISET(sst->triangleCMD, 0);
   ISET(sst->vB.x, (x<<SST_XY_FRACBITS));
   ISET(sst->vB.y, ((y+tSize)<<SST_XY_FRACBITS));
   ISET(sst->triangleCMD, 0xFFFFFFFF);
}

#pragma optimize ("",on)
