/*-*-c++-*-*/
/* $Id: sst1init.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 $
**
*/

/*
** Initialization code for SST-1 board.
**
** NOTE: This code must compiled with optimizations DISABLED!!
**
** The following environment variables can optionally be used to alter
** functionality (A value of X is a "don't care"):
**
** Variable                    Value   Description
** --------                    -----   -----------
** SSTV2_ARCADE                  X       Allow special configs for arcade use
** SSTV2_ALLOC_AUX               {0,1}   Number of aux. buffers to allocate
** SSTV2_ALLOC_COLOR             {2,3}   Number of color buffers to allocate
** SSTV2_BACKPORCH               hex     Specify value of backPorch video register
** SSTV2_BOARDS                  val     Specify number of SST-1 boards in system
** SSTV2_CMDFIFO_DIRECT          X       Enable Direct Execution of CMDFIFO
** SSTV2_CMDFIFO_NOHOLES         X       Disable hole counting feature in CMDFIFO
** SSTV2_DEBUGDAC                X       Print out debug information for DAC
**                                       reads and writes
** SSTV2_DEVICEID                X       Specify the device ID value passed to
**                                       the PCI library
** SSTV2_DIMENSIONS              hex     Specify value of videoDimensions register
** SSTV2_FASTMEM_RAS_READS       X       Allow fast ras reads
** SSTV2_FASTPCIRD               X       Enable fast PCI reads
** SSTV2_FBICFG                  hex     Specify FBI configuration strapping bits
** SSTV2_FBI_MEMSIZE             val     Specify amount of frame buffer memory
** SSTV2_FT_CLKDEL               hex     Fbi-to-Trex clock delay value
** SSTV2_GAMMA                   float   Floating point value for gamma correction
** SSTV2_GRXCLK                  val     16 <= Frequency < 80 (50 is default)
** SSTV2_HSYNC                   hex     Specify value of hSync video register
** SSTV2_IGNORE_CLKDELAYS        X       Ignore calls to sst1InitSetClkDelays()
** SSTV2_IGNORE_IDLE             X       Ignore calls to sst1InitIdle*()
** SSTV2_IGNORE_INIT_GAMMA       X       Bypass sst1InitGamma call and return
** SSTV2_IGNORE_INIT_REGISTERS   X       Bypass sst1InitRegisters call and return
** SSTV2_IGNORE_INIT_VIDEO       X       Bypass sst1InitVideo call and return
** SSTV2_INITDEBUG               X       Enable debug output
** SSTV2_INITDEBUG_FILE          file    File to direct all debug output
** SSTV2_MEMFIFO                 0       Disable FBI Memory FIFO
** SSTV2_MEMFIFO                 1       Enable FBI Memory FIFO
** SSTV2_MEMFIFO_ENTRIES         hex     Set number of entries in memory fifo
** SSTV2_MEMFIFO_HWM             hex     Set memory fifo high water mark
** SSTV2_MEMFIFO_LFB             {0,1}   Specify LFB writes through Memory FIFO
** SSTV2_MEMFIFO_LWM             hex     Set memory fifo low water mark
** SSTV2_MEMFIFO_TEX             {0,1}   Specify Texture writes through Mem FIFO
** SSTV2_MEMOFFSET               hex     Specify value of memoffset video register
** SSTV2_NOCHECKHANG             X       Do not Check for FBI Hangs for texturing
** SSTV2_NODEVICEINFO            X       Ignore calls to sst1InitFillDeviceInfo()
** SSTV2_NOSHUTDOWN              X       Do not turn off monitor refresh on call
**                                       to sst1InitShutdown()
** SSTV2_NUM_TMUS                {1,2,3} Specify number of TMUs detected
** SSTV2_PCIFIFO_LWM             hex     Set pci fifo low water mark
** SSTV2_PFT_CLKDEL              hex     Preliminary Fbi-to-Trex clock delay value
** SSTV2_PTF0_CLKDEL             hex     Prelim TMU2Fbi clock delay value (TMU #0)
** SSTV2_PTF1_CLKDEL             hex     Prelim TMU2Fbi clock delay value (TMU #1)
** SSTV2_PTF2_CLKDEL             hex     Prelim TMU2Fbi clock delay value (TMU #2)
** SSTV2_SIPROCESS_CNTR          hex     Silicon process monitor PCI counter load
** SSTV2_SLIDETECT               {0,1}   Specify Scanline Interleaving detection
** SSTV2_SLIM_VIN_CLKDEL         hex     Specify FBI internal video clock delay
**                                       (SLI Master)
** SSTV2_SLIM_VOUT_CLKDEL        hex     Specify FBI external video clock delay
**                                       (SLI Master)
** SSTV2_SLIS_VIN_CLKDEL         hex     Specify FBI internal video clock delay
**                                       (SLI Slave)
** SSTV2_SLIS_VOUT_CLKDEL        hex     Specify FBI external video clock delay
**                                       (SLI Slave)
** SSTV2_SCREENREFRESH           {60,75, Select monitor refresh rate
**                               85,120}
** SSTV2_SCREENREZ               {512,   Select monitor resolution (512x384)
**                               512256, (512x256)
**                               640400, (640x400)
**                               640,    (640x480)
**                               800,    (800x600)
**                               856,    (856x600)
**                               960,    (960x720)
**                               1024}   (1024x768)
** SSTV2_SLOWMEM_RTW             X       Insert wait state for read-to-write
**                                       transitions
** SSTV2_SLOWMEM_WTR             X       Insert wait state for write-to-read
**                                       transitions
** SSTV2_SLOWPCIWR               X       Enable 1 wait-state PCI writes
** SSTV2_SLISWAP                 1       Use dac_data[0] for SLI swapping
** SSTV2_SWAPBOARDS              X       Swap order of mapping boards
** SSTV2_TEXMAP_DISABLE          X       Disable texture mapping
** SSTV2_TF0_CLKDEL              hex     Trex-to-Fbi clock delay value (TMU #0)
** SSTV2_TF1_CLKDEL              hex     Trex-to-Fbi clock delay value (TMU #1)
** SSTV2_TF2_CLKDEL              hex     Trex-to-Fbi clock delay value (TMU #2)
** SSTV2_TF_FIFO_THRESH          hex     Set TREX-to-FBI FIFO threshold
** SSTV2_TILESINX                hex     Specify value of tilesInX video register
** SSTV2_TMUCFG                  hex     Specify TMU configuration strapping bits
** SSTV2_TMUMEM                  X       Enable accurate determination of TMU mem
** SSTV2_TMU_MEMSIZE             val     Specify amount of texture memory
** SSTV2_TREX0INIT0              hex     Set trexInit0 register value - TMU #0
** SSTV2_TREX0INIT1              hex     Set trexInit1 register value - TMU #0
** SSTV2_TREX1INIT0              hex     Set trexInit0 register value - TMU #1
** SSTV2_TREX1INIT1              hex     Set trexInit1 register value - TMU #1
** SSTV2_TREX2INIT0              hex     Set trexInit0 register value - TMU #2
** SSTV2_TREX2INIT1              hex     Set trexInit1 register value - TMU #2
** SSTV2_VIN_CLKDEL              hex     Specify FBI internal video clock delay
** SSTV2_VOUT_CLKDEL             hex     Specify FBI external video clock delay
** SSTV2_VGA_PASS                {0,1}   Force VGA_PASS output to {0,1}
** SSTV2_VIDCLK2X                val     Video clock frequency (2x dot clock)
** SSTV2_VIDEO_24BPP             0       Select 16-bit video output
** SSTV2_VIDEO_24BPP             1       Select 24-bit video output
** SSTV2_VIDEO_DISABLE           X       Turn off Video/Monitor refresh in FBI
** SSTV2_VIDEO_FILTER_DISABLE    X       Disable video filtering
** SSTV2_VIDEO_FILTER_THRESHOLD  hex     Set video filtering threshold
** SSTV2_VIDEO_NOCLEAR           X       Do not clear buffers in sst1InitVideo()
** SSTV2_VIDEO_CLEARCOLOR        hex     Clear screen color
** SSTV2_VFIFO_THRESH            {0-31}  Select video fifo threshold
** SSTV2_VSYNC                   hex     Specify value of vSync video register
**
** VOODOO2_FILE                  name    Filename used in place of "voodoo2.ini"
** VOODOO2_PATH                  path    Path used to locate "voodoo2.ini" file
**
*/

#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
#include <fxpci.h>
#define SST1INIT_ALLOCATE // Allocate variables in sst1init.h
#define FX_DLL_DEFINITION
#include <fxdll.h>
#include <sst1vid.h>
#include <sst1init.h>
#include "rcver.h"

#ifdef __WIN32__
#include <windows.h>
#endif

// Allow SourceSafe to track Revision
static char codeIdent[] = "@#%" VERSIONSTR ;

/*
** sst1InitMapBoard():
**  Find and map SST-1 board into virtual memory
**
**    Returns:
**      FxU32 pointer to base of SST-1 board if successful mapping occurs
**      FXFALSE if cannot map or find SST-1 board
**
*/
FX_EXPORT FxU32 * FX_CSTYLE sst1InitMapBoard(FxU32 BoardNumber)
{
    return(sst1InitMapBoardDirect(BoardNumber, FXTRUE));
}

// Use this flag to force a info clear when doing a
// sst1InitMapBoard() for the first time.  sst1InitShutdown()
// will also set this flag to TRUE to force a clear
// next time around.
static FxU32 clearBoardInfo = FXTRUE;

FX_EXPORT FxU32 * FX_CSTYLE sst1InitMapBoardDirect(FxU32 BoardNumber,
                                                   FxBool resetSLI)
{
    static FxU32 firstTime = 1;
    FxU32 vendorID = _3DFX_PCI_ID;     // 3Dfx Vendor ID
    FxU32 deviceID;                    // 0x0002 - Look for a Voodoo2 board (0xFFFF - Find any 3Dfx board)
    FxU32 sizeOfCard = 0x1000000;      // 16 MBytes of addr space for SST-1
    FxU32 *sstbase;
    FxU32 n;
    FxU32 j;

    if( GETENV( ("SSTV2_DEVICEID") ) )
      SSCANF(GETENV(("SSTV2_DEVICEID")), "%i", &deviceID);
    else
      deviceID = 0x0002;

    // Open PCI library (necessary for multiple calls to init routines, after
    // PCI library is closed by pciClose() call in sst1InitShutdown().
    //
    // NB: It is safe to do this even if we never called pciClose.
    pciOpen();

    if(firstTime) {
        // Make Watcom happy
        codeIdent[0] = '@';
        headersIdent[0] = '@';

        // Find "voodoo2.ini" file if it exists...
        sst1InitUseVoodooFile = sst1InitVoodooFile();

        if(!(boardsInSystem = sst1InitNumBoardsInSystem()))
            return(NULL);
    }

    if( clearBoardInfo ) {
        INIT_PRINTF(("sst1Init Routines"));
#ifdef FX_DLL_ENABLE
        INIT_PRINTF(("(DLL)"));
#endif
        INIT_PRINTF((": %s\n", VERSIONSTR));
        INIT_PRINTF(("sst1InitMapBoard(): BoardsInSystem = %d\n",
          boardsInSystem));

        // Clear board info structure
        sst1InitClearBoardInfo();

        clearBoardInfo = FXFALSE;

#if !DIRECTX
        /* dpc - 26 feb 1998
         * If glide is running on windows the a pciClose will close
         * the vxd etc. This is not functionally longer fatal, but w/o
         * it we will not be able to do things like set the caching on
         * the board's memory etc. This is bad.
         *
         * The actual cost of doing the re-mapping again is pretty low
         * because both the pci library and the init code are caching the
         * addresses of the board. However, the call to the pciLibrary
         * to map the board has the side affect of causing the vxd to be
         * loaded. It is this that is responsible for setting up caching.
         */
        firstTime = FXTRUE;
#endif /* !DIRECTX */
    }

    if(firstTime) {
        int boardLoopStart = 0;
        int boardLoopStop = SST1INIT_MAX_BOARDS;
        int boardLoopInc = 1;
        int index = 0;

        sst1InitClearBoardInfo();
        if(GETENV(("SSTV2_SWAPBOARDS"))) {
           boardLoopStart = SST1INIT_MAX_BOARDS-1;
           boardLoopStop = -1;
           boardLoopInc = -1;
        }

        for(j=0; j<SST1INIT_MAX_BOARDS; j++) {
           sst1BoardInfo[j].virtAddr[0] = (SstRegs *) NULL;
           sst1BoardInfo[j].physAddr[0] = (FxU32) NULL;
           sst1BoardInfo[j].deviceNumber = 0xdead;
           sst1BoardInfo[j].fbiRevision = 0xdead;
           sst1BoardInfo[j].deviceID = 0xdead;
           sst1BoardInfo[j].vendorID = 0xdead;
        }


        // Map all boards in the system
        // Check that scanline interleaving is not enabled...
        for(j = boardLoopStart; j != (FxU32)boardLoopStop; j += boardLoopInc) {
            sstbase = pciMapCardMulti(vendorID, deviceID, sizeOfCard,
                                      &sst1InitDeviceNumber, j, 0);
            if(sstbase) {
                sst1BoardInfo[index].virtAddr[0] = (SstRegs *) sstbase;
                PCICFG_RD(PCI_BASE_ADDRESS_0, sst1BoardInfo[index].physAddr[0]);
                sst1BoardInfo[index].deviceNumber = sst1InitDeviceNumber;
                {
                  FxU32 oldFbiRevision, newFbiRevision;

                  PCICFG_RD(PCI_REVISION_ID, oldFbiRevision);
                  PCICFG_RD(SST1_PCI_INIT_ENABLE, newFbiRevision);
                  sst1BoardInfo[index].fbiFab =
                    (newFbiRevision & SST_CHUCK_MFTG_ID) >>
                    SST_CHUCK_MFTG_ID_SHIFT;
                  newFbiRevision = (newFbiRevision & SST_CHUCK_REVISION_ID) >>
                    SST_CHUCK_REVISION_ID_SHIFT;
                  if(oldFbiRevision != 0x2 && newFbiRevision < 4)
                     return(NULL);
                  sst1BoardInfo[index].fbiRevision = newFbiRevision;
                }
                PCICFG_RD(PCI_DEVICE_ID, sst1BoardInfo[index].deviceID);
                PCICFG_RD(PCI_VENDOR_ID, sst1BoardInfo[index].vendorID);

                // Enable Memory accesses to SST-1
                // Must be set for PnP BIOS which do not enable memory mapped
                // accesses (sst1InitSliDetect reads memory mapped regs)
                PCICFG_WR(PCI_COMMAND, SST_PCIMEM_ACCESS_EN);
                index++;
            } else {
                FxU32 code = pciGetErrorCode();
                if (code != PCI_ERR_NOERR) {
#ifdef __WIN32__
                  MessageBox(NULL, pciGetErrorString(), NULL, MB_OK);
#endif // __WIN32__

                  INIT_PRINTF(("pciError(): %s", pciGetErrorString()));
                  exit(-1);
                }
            }
        }
    }

    if(resetSLI) {
       // Search through all known boards for SLI enabled...
       FxU32 k;
       SstRegs *sst;

       for(k=0; k<boardsInSystemReally; k++) {
          // Disable SLI if detected...
          if(!(sstbase = (FxU32 *) sst1BoardInfo[k].virtAddr[0]))
             return(NULL);
          sst1InitDeviceNumber = sst1BoardInfo[k].deviceNumber;
          sst1CurrentBoard = &sst1BoardInfo[k];
          sst = (SstRegs *) sstbase;

          if(IGET(sst->fbiInit1) & SST_EN_SCANLINE_INTERLEAVE) {
             INIT_PRINTF(("sst1InitMapBoard(): Disabling Scanline Interleaving (board #%d)...\n", (k+1)));
             // Disable SLI Snooping...
             PCICFG_RD(SST1_PCI_INIT_ENABLE, j);
             PCICFG_WR(SST1_PCI_INIT_ENABLE, j &
               ~(SST_SCANLINE_SLV_OWNPCI | SST_SCANLINE_SLI_SLV |
                 SST_SLI_SNOOP_EN | SST_SLI_SNOOP_MEMBASE));
             ISET(sst->fbiInit1, IGET(sst->fbiInit1) &
               ~SST_EN_SCANLINE_INTERLEAVE);
             sst1InitReturnStatus(sstbase);
          }

          // Tristate video output signals, so two boards connected as SLI
          // do not have contention problems when SSTV2_SLIDETECT=0
          if(firstTime)
             sst1InitVideoShutdown(sstbase, FXFALSE);
       }
    }
    firstTime = 0;

    // Max number of supported boards in system
    if(BoardNumber > SST1INIT_MAX_BOARDS) return(NULL);

    if(!(sstbase = (FxU32 *) sst1BoardInfo[BoardNumber].virtAddr[0]))
        return (NULL);

    INIT_PRINTF(("sst1InitMapBoard(): vAddr:0x%x pAddr:0x%x Dev:0x%x Board:%d\n",
        (FxU32) sst1BoardInfo[BoardNumber].virtAddr[0],
        (FxU32) sst1BoardInfo[BoardNumber].physAddr[0],
        (FxU32) sst1BoardInfo[BoardNumber].deviceNumber, BoardNumber));

    // Default settings
    sst1BoardInfo[BoardNumber].vgaPassthruDisable = SST_EN_VGA_PASSTHRU;
    sst1BoardInfo[BoardNumber].vgaPassthruEnable = 0x0;
    sst1BoardInfo[BoardNumber].fbiVideo16BPP = 0;

    if(GETENV(("SSTV2_VGA_PASS"))) {
        INIT_PRINTF(("sst1InitMapBoard(): Using SST_VGA_PASS=%d\n",
            ATOI(GETENV(("SSTV2_VGA_PASS")))));
        if(ATOI(GETENV(("SSTV2_VGA_PASS")))) {
            sst1BoardInfo[BoardNumber].vgaPassthruEnable = SST_EN_VGA_PASSTHRU;
            sst1BoardInfo[BoardNumber].vgaPassthruDisable = 0x0;
        } else {
            sst1BoardInfo[BoardNumber].vgaPassthruDisable = SST_EN_VGA_PASSTHRU;
            sst1BoardInfo[BoardNumber].vgaPassthruEnable = 0x0;
        }
    }

    return(sstbase);
}

/*
** sst1InitRegisters():
**  Initialize registers and memory and return to power-on state
**
**    Returns:
**      FXTRUE if successfully initializes SST-1
**      FXFALSE if cannot initialize SST-1
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitRegisters(FxU32 *sstbase)
{
    FxU32 n, tf_fifo_thresh;
    FxU32 ft_clkdel, tf0_clkdel, tf1_clkdel, tf2_clkdel;
    sst1ClkTimingStruct sstGrxClk;
    SstRegs *sst = (SstRegs *) sstbase;

    if(!sst)
        return(FXFALSE);

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

    if(GETENV(("SSTV2_IGNORE_INIT_REGISTERS"))) {
        INIT_PRINTF(("WARNING: Ignoring sst1InitRegisters()...\n"));
        sst1InitIdleFBINoNOP(sstbase);
        return(FXTRUE);
    }

    if(GETENV(("SSTV2_TEXMAP_DISABLE")))
       INIT_PRINTF(("sst1InitRegisters() WARNING: Disabling texture mapping\n"));
    // Open PCI library (necessary for multiple calls to init routines, after
    // PCI library is closed by pciClose() call in sst1InitShutdown()
    pciOpen();

    // Enable writes to the FBIINIT registers
    // Do not allow writes into the pci fifo until everything is reset
    PCICFG_WR(SST1_PCI_INIT_ENABLE, SST_INITWR_EN);

    // Reset Snoop registers to default values
    PCICFG_WR(SST1_PCI_BUS_SNOOP0, SST_PCI_BUS_SNOOP_DEFAULT);
    PCICFG_WR(SST1_PCI_BUS_SNOOP1, SST_PCI_BUS_SNOOP_DEFAULT);
    sst1InitReturnStatus(sstbase); // Stall - can't call IdleFbi because
    sst1InitReturnStatus(sstbase); // FBI could be hung at this stage
    sst1InitReturnStatus(sstbase);

    // Adjust Trex-to-Fbi FIFO
    if(GETENV(("SSTV2_TF_FIFO_THRESH")))
        SSCANF(GETENV(("SSTV2_TF_FIFO_THRESH")), "%i", &tf_fifo_thresh);
    else
        tf_fifo_thresh = 0x8;
    INIT_PRINTF(("sst1InitRegisters(): Setting TREX-to-FBI FIFO THRESHOLD to 0x%x...\n",
        tf_fifo_thresh));

    // Set ft_clkdel for 16 MHz bringup
    ft_clkdel = 0x8; // Okay for 16 MHz startup...
    if(GETENV(("SSTV2_PFT_CLKDEL")))
        SSCANF(GETENV(("SSTV2_PFT_CLKDEL")), "%i", &ft_clkdel);
    INIT_PRINTF(("sst1InitRegisters(): Setting PRELIM FT-CLK delay to 0x%x...\n", ft_clkdel));

    // SST_FBIINIT3_DEFAULT is set to disable texture mapping, so if
    // TMUs are hung, it will not affect FBI
    ISET(sst->fbiInit3,
       (SST_FBIINIT3_DEFAULT & ~(SST_FT_CLK_DEL_ADJ | SST_TF_FIFO_THRESH)) |
       (ft_clkdel << SST_FT_CLK_DEL_ADJ_SHIFT) |
       (tf_fifo_thresh << SST_TF_FIFO_THRESH_SHIFT));

    // Wait for Fbi-to-Trex clock delay value to propogate
    sst1InitReturnStatus(sstbase); // Stall - can't call IdleFbi because
    sst1InitReturnStatus(sstbase); // FBI could be hung at this stage
    sst1InitReturnStatus(sstbase);

    // Reset graphics and video units
    // Must reset video unit before graphics unit, otherwise video unit could
    // potentially hang waiting for the graphics unit to respond
    ISET(sst->fbiInit1, IGET(sst->fbiInit1) | SST_VIDEO_RESET);

    // Don't wait for idle because graphics could be hung...
    sst1InitReturnStatus(sstbase);
    sst1InitReturnStatus(sstbase);
    sst1InitReturnStatus(sstbase);
    ISET(sst->fbiInit0,
      IGET(sst->fbiInit0) | (SST_GRX_RESET | SST_PCI_FIFO_RESET));

    sst1InitIdleFBINoNOP(sstbase);

    // Unreset PCI FIFO...
    ISET(sst->fbiInit0, IGET(sst->fbiInit0) & ~SST_PCI_FIFO_RESET);
    sst1InitIdleFBINoNOP(sstbase);

    // Unreset graphics subsystem
    // THIS MUST BE PRESENT OTHERWISE THE PCI FIFO WILL NEVER DRAIN!
    ISET(sst->fbiInit0, IGET(sst->fbiInit0) & ~SST_GRX_RESET);
    sst1InitIdleFBINoNOP(sstbase);

    // Reset all FBI and TREX Init registers
    ISET(sst->fbiInit0, SST_FBIINIT0_DEFAULT);
    ISET(sst->fbiInit1, SST_FBIINIT1_DEFAULT);
    ISET(sst->fbiInit2, SST_FBIINIT2_DEFAULT);
    ISET(sst->fbiInit3,
       (SST_FBIINIT3_DEFAULT & ~(SST_FT_CLK_DEL_ADJ | SST_TF_FIFO_THRESH)) |
       (ft_clkdel << SST_FT_CLK_DEL_ADJ_SHIFT) |
       (tf_fifo_thresh << SST_TF_FIFO_THRESH_SHIFT));
    ISET(sst->fbiInit4, SST_FBIINIT4_DEFAULT);
    ISET(sst->fbiInit5, SST_FBIINIT5_DEFAULT);
    ISET(sst->fbiInit6, SST_FBIINIT6_DEFAULT);
    sst1CurrentBoard->fbiInit6 = SST_FBIINIT6_DEFAULT;
    ISET(sst->fbiInit7, SST_FBIINIT7_DEFAULT);
    sst1InitIdleFBINoNOP(sstbase);  // Wait until init regs are reset

    // Enable writes to the FBIINIT registers and allow pushes onto PCI FIFO
    PCICFG_WR(SST1_PCI_INIT_ENABLE, (SST_INITWR_EN | SST_PCI_FIFOWR_EN));

    // Determine DAC Type
    if(sst1InitDacDetect(sstbase) == FXFALSE) {
        INIT_PRINTF(("sst1InitRegisters(): Could not detect DAC...\n"));
        return(FXFALSE);
    }

    // set TREX0 init values
    if(GETENV(("SSTV2_TREX0INIT0"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX0INIT0 environment variable\n"));
        SSCANF(GETENV(("SSTV2_TREX0INIT0")), "%i",
            &sst1CurrentBoard->tmuInit0[0]);
    } else
        sst1CurrentBoard->tmuInit0[0] = SST_TREX0INIT0_DEFAULT;

    INIT_PRINTF(("sst1InitRegisters(): Storing TREX0INIT0=0x%x\n",
        sst1CurrentBoard->tmuInit0[0]));
    if(GETENV(("SSTV2_TREX0INIT1"))) {

        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX0INIT1 environment variable\n"));
        SSCANF(GETENV(("SSTV2_TREX0INIT1")), "%i",
            &sst1CurrentBoard->tmuInit1[0]);
    } else
        sst1CurrentBoard->tmuInit1[0] = SST_TREX0INIT1_DEFAULT;
    if(GETENV(("SSTV2_PTF0_CLKDEL"))) {
        SSCANF(GETENV(("SSTV2_PTF0_CLKDEL")), "%i", &tf0_clkdel);
        sst1CurrentBoard->tmuInit1[0] = (sst1CurrentBoard->tmuInit1[0] &
            ~SST_TEX_TF_CLK_DEL_ADJ) |
            (tf0_clkdel<<SST_TEX_TF_CLK_DEL_ADJ_SHIFT);
    }
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX0INIT1=0x%x\n",
        sst1CurrentBoard->tmuInit1[0]));

    // set TREX1 init values
    if(GETENV(("SSTV2_TREX1INIT0"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX1INIT0 environment variable\n"));
        SSCANF(GETENV(("SSTV2_TREX1INIT0")), "%i",
            &sst1CurrentBoard->tmuInit0[1]);
    } else
        sst1CurrentBoard->tmuInit0[1] = SST_TREX1INIT0_DEFAULT;
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX1INIT0=0x%x\n",
        sst1CurrentBoard->tmuInit0[1]));
    if(GETENV(("SSTV2_TREX1INIT1"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX1INIT1 environment variable\n"));
        SSCANF(GETENV(("SSTV2_TREX1INIT1")), "%i",
            &sst1CurrentBoard->tmuInit1[1]);
    } else
        sst1CurrentBoard->tmuInit1[1] = SST_TREX1INIT1_DEFAULT;
    if(GETENV(("SSTV2_PTF1_CLKDEL"))) {
        SSCANF(GETENV(("SSTV2_PTF1_CLKDEL")), "%i", &tf1_clkdel);
        sst1CurrentBoard->tmuInit1[1] = (sst1CurrentBoard->tmuInit1[1] &
            ~SST_TEX_TF_CLK_DEL_ADJ) |
            (tf1_clkdel<<SST_TEX_TF_CLK_DEL_ADJ_SHIFT);
    }
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX1INIT1=0x%x\n",
        sst1CurrentBoard->tmuInit1[1]));


    // set TREX2 init values
    if(GETENV(("SSTV2_TREX2INIT0"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX2INIT0 environment variable\n"));
        SSCANF(GETENV(("SSTV2_TREX2INIT0")), "%i",
            &sst1CurrentBoard->tmuInit0[2]);
    } else
        sst1CurrentBoard->tmuInit0[2] = SST_TREX2INIT0_DEFAULT;
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX2INIT0=0x%x\n",
        sst1CurrentBoard->tmuInit0[2]));
    if(GETENV(("SSTV2_TREX2INIT1"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX2INIT1 environment variable\n"));
        SSCANF(GETENV(("SSTV2_TREX2INIT1")), "%i",
            &sst1CurrentBoard->tmuInit1[2]);
    } else
        sst1CurrentBoard->tmuInit1[2] = SST_TREX2INIT1_DEFAULT;
    if(GETENV(("SSTV2_PTF2_CLKDEL"))) {
        SSCANF(GETENV(("SSTV2_PTF2_CLKDEL")), "%i", &tf2_clkdel);
        sst1CurrentBoard->tmuInit1[2] = (sst1CurrentBoard->tmuInit1[2] &
            ~SST_TEX_TF_CLK_DEL_ADJ) |
            (tf2_clkdel<<SST_TEX_TF_CLK_DEL_ADJ_SHIFT);
    }
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX2INIT1=0x%x\n",
        sst1CurrentBoard->tmuInit1[2]));

    // Set clock at 16 MHz to properly transmit TREX configuration registers
    // Note that setting the clock will automatically reset the TMUs...
    // sst1InitResetTmus() also will de-assert TEXMAP_DISABLE in fbiInit3
    // unless SSTV2_TEXMAP_DISABLE is set
    if(sst1InitComputeClkParams((float) 16.0, &sstGrxClk) == FXFALSE)
        return(FXFALSE);
    if(sst1InitSetGrxClk(sstbase, &sstGrxClk) == FXFALSE)
        return(FXFALSE);
    sst1CurrentBoard->initGrxClkDone = 0;

    // Set PCI wait-states
    if(GETENV(("SSTV2_SLOWPCIWR"))) {
        INIT_PRINTF(("sst1InitRegisters(): Setting up SLOW PCI Writes...\n"));
        ISET(sst->fbiInit1, IGET(sst->fbiInit1) | SST_PCI_WRWS_1);
    } else
        ISET(sst->fbiInit1, IGET(sst->fbiInit1) & ~SST_PCI_WRWS_1);

    if(GETENV(("SSTV2_FASTPCIRD"))) {
        INIT_PRINTF(("sst1InitRegisters(): Setting up FAST PCI Reads...\n"));
        ISET(sst->fbiInit4, IGET(sst->fbiInit4) & ~SST_PCI_RDWS_2);
    }
    sst1InitIdleFBINoNOP(sstbase);

    // Enable Linear frame buffer reads
    ISET(sst->fbiInit1, IGET(sst->fbiInit1) | SST_LFB_READ_EN);

    // Swapbuffer algorithm is based on VSync initially
    ISET(sst->fbiInit2, (IGET(sst->fbiInit2) & ~SST_SWAP_ALGORITHM) |
      SST_SWAP_VSYNC);

    // Enable LFB read-aheads
    ISET(sst->fbiInit4, IGET(sst->fbiInit4) | SST_EN_LFB_RDAHEAD);

    // Enable triangle alternate register mapping
    ISET(sst->fbiInit3, IGET(sst->fbiInit3) | SST_ALT_REGMAPPING);

    // Enable Endian Byte Swapping
    ISET(sst->fbiInit0, IGET(sst->fbiInit0) | SST_EN_ENDIAN_SWAPPING);

    // Setup DRAM Refresh
    ISET(sst->fbiInit2, (IGET(sst->fbiInit2) & ~SST_DRAM_REFRESH_CNTR) |
        SST_DRAM_REFRESH_16MS);
    sst1InitIdleFBINoNOP(sstbase);
    ISET(sst->fbiInit2, IGET(sst->fbiInit2) | SST_EN_DRAM_REFRESH);
    sst1InitIdleFBINoNOP(sstbase);

    {
       // Setup memory timings...
       FxU32 fbiInit1 = IGET(sst->fbiInit1);
       FxU32 fbiInit2 = IGET(sst->fbiInit2) |
          SST_EN_DRAM_RD_AHEAD_FIFO | SST_EN_FAST_RD_AHEAD_WR;

       if(GETENV(("SSTV2_SLOWMEM_WTR"))) {
          INIT_PRINTF(("sst1InitRegisters(): Running slow write-to-read memory timing...\n"));
          fbiInit1 |= SST_DISEN_RD_AHEAD_WR_RD;
       }
       if(GETENV(("SSTV2_SLOWMEM_RTW"))) {
          INIT_PRINTF(("sst1InitRegisters(): Running slow read-to-write memory timing...\n"));
          fbiInit2 &= ~SST_EN_FAST_RD_AHEAD_WR;
       }
       if(GETENV(("SSTV2_FASTMEM_RAS_READS"))) {
          INIT_PRINTF(("sst1InitRegisters(): Enabling fast RAS read memory timing...\n"));
          fbiInit2 |= SST_EN_FAST_RAS_READ;
       }
       ISET(sst->fbiInit1, fbiInit1);
       sst1InitReturnStatus(sstbase);
       ISET(sst->fbiInit2, fbiInit2);
       sst1InitReturnStatus(sstbase);
    }

    // Return all other registers to their power-on state
    sst1InitIdleFBINoNOP(sstbase);
    sst1InitSetResolution(sstbase, &SST_VREZ_640X480_60, 0);
    sst1InitIdleFBINoNOP(sstbase);

    // Get DRAMs up and running (refresh is already enabled)...
    ISET(sst->c1, 0x0);
    ISET(sst->c0, 0x0);
    ISET(sst->zaColor, 0x0);
    ISET(sst->clipLeftRight, 100); // draw into 50x50 area
    ISET(sst->clipBottomTop, 100);
    ISET(sst->fbzMode, SST_RGBWRMASK | SST_ZAWRMASK);
    ISET(sst->fastfillCMD, 0x0);   // Frontbuffer & Z/A
    ISET(sst->nopCMD, 0x1); // Clear fbistat registers after clearing screen
    sst1InitIdleFBINoNOP(sstbase);

    sst1InitRenderingRegisters(sstbase);
    sst1CurrentBoard->tmuRevision = 0xdead; // Force sst1InitFillDeviceInfo()
    if(sst1InitFillDeviceInfo(sstbase, sst1CurrentBoard) == FXFALSE) {
        INIT_PRINTF(("sst1InitRegisters(): ERROR filling DeviceInfo...\n"));

        return(FXFALSE);
    }

    sst1InitIdleFBINoNOP(sstbase);
    sst1InitRenderingRegisters(sstbase);
    sst1InitIdleFBINoNOP(sstbase);

    // LFB writes stored in memory FIFO?
    if(GETENV(("SSTV2_MEMFIFO_LFB")))
        n = ATOI(GETENV(("SSTV2_MEMFIFO_LFB")));
    else
        n = 1;
    if(n) {
        INIT_PRINTF(("sst1InitRegisters(): LFB Writes go through memory FIFO...\n"));
        ISET(sst->fbiInit0, IGET(sst->fbiInit0) | SST_EN_LFB_MEMFIFO);
        sst1InitIdleFBINoNOP(sstbase);
    }

    // Texture memory writes stored in memory FIFO?
    if(GETENV(("SSTV2_MEMFIFO_TEX")))
        n = ATOI(GETENV(("SSTV2_MEMFIFO_TEX")));
    else
        n = 1;
    if(n) {
        INIT_PRINTF(("sst1InitRegisters(): TEXTURE Writes go through memory FIFO...\n"));
        ISET(sst->fbiInit0, IGET(sst->fbiInit0) | SST_EN_TEX_MEMFIFO);
        sst1InitIdleFBINoNOP(sstbase);
    }

    ISET(sst->vRetrace, 0x0);
    ISET(sst->backPorch, 0x0);
    ISET(sst->videoDimensions, 0x0);
    ISET(sst->hSync, 0x0);
    ISET(sst->vSync, 0x0);
    ISET(sst->videoFilterRgbThreshold, 0x0);

    sst1InitIdleFBINoNOP(sstbase);  // Wait until init regs are reset

    // Update info structure for initEnable
    PCICFG_RD(SST1_PCI_INIT_ENABLE, sst1CurrentBoard->fbiInitEnable);

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

FX_EXPORT void FX_CSTYLE sst1InitRenderingRegisters(FxU32 *sstbase)
{
    FxU32 i;
    SstRegs *sst = (SstRegs *) sstbase;

    ISET(sst->vA.x, 0x0); ISET(sst->vA.y, 0x0);
    ISET(sst->vB.x, 0x0); ISET(sst->vB.y, 0x0);
    ISET(sst->vC.x, 0x0); ISET(sst->vC.y, 0x0);

    ISET(sst->r, 0x0); ISET(sst->g, 0x0);
    ISET(sst->b, 0x0); ISET(sst->z, 0x0);
    ISET(sst->a, 0x0); ISET(sst->s, 0x0);
    ISET(sst->t, 0x0); ISET(sst->w, 0x0);

    ISET(sst->drdx, 0x0); ISET(sst->dgdx, 0x0);
    ISET(sst->dbdx, 0x0); ISET(sst->dzdx, 0x0);
    ISET(sst->dadx, 0x0); ISET(sst->dsdx, 0x0);
    ISET(sst->dtdx, 0x0); ISET(sst->dwdx, 0x0);

    ISET(sst->drdy, 0x0); ISET(sst->dgdy, 0x0);
    ISET(sst->dbdy, 0x0); ISET(sst->dzdy, 0x0);
    ISET(sst->dady, 0x0); ISET(sst->dsdy, 0x0);
    ISET(sst->dtdy, 0x0); ISET(sst->dwdy, 0x0);

    ISET(sst->fbzColorPath, 0x0);
    ISET(sst->fogMode, 0x0);
    ISET(sst->alphaMode, 0x0);
    ISET(sst->fbzMode, 0x0);
    ISET(sst->lfbMode, 0x0);
    ISET(sst->clipLeftRight, 0x0);
    ISET(sst->clipBottomTop, 0x0);

    ISET(sst->fogColor, 0x0);
    ISET(sst->zaColor, 0x0);
    ISET(sst->chromaKey, 0x0);
    ISET(sst->chromaRange, 0x0);
    ISET(sst->stipple, 0x0);
    ISET(sst->c0, 0x0);
    ISET(sst->c1, 0x0);

    ISET(sst->sSetupMode, 0x0);
    ISET(sst->sVx, 0x0);
    ISET(sst->sVy, 0x0);
    ISET(sst->sARGB, 0x0);
    ISET(sst->sRed, 0x0);
    ISET(sst->sGreen, 0x0);
    ISET(sst->sBlue, 0x0);
    ISET(sst->sAlpha, 0x0);
    ISET(sst->sVz, 0x0);
    ISET(sst->sOowfbi, 0x0);
    ISET(sst->sOow0, 0x0);
    ISET(sst->sSow0, 0x0);
    ISET(sst->sTow0, 0x0);
    ISET(sst->sOow1, 0x0);
    ISET(sst->sSow1, 0x0);
    ISET(sst->sTow1, 0x0);

    ISET(sst->nopCMD, 0x3);   // Clear fbistat and trianglesOut registers

    for(i=0; i<32; i++)
        ISET(sst->fogTable[i], 0x0);

    ISET(sst->bltSrcBaseAddr, 0x0);
    ISET(sst->bltDstBaseAddr, 0x0);
    ISET(sst->bltXYstrides, 0x0);
    ISET(sst->bltSrcChromaRange, 0x0);
    ISET(sst->bltDstChromaRange, 0x0);
    ISET(sst->bltClipX, 0x0);
    ISET(sst->bltClipY, 0x0);
    ISET(sst->bltSrcXY, 0x0);
    ISET(sst->bltDstXY, 0x0);
    ISET(sst->bltSize, 0x0);
    ISET(sst->bltRop, 0x0);
    ISET(sst->bltColor, 0x0);
    ISET(sst->bltCommand, 0x0);

    // clear TMU registers
    ISET(sst->textureMode, 0x0);
    ISET(sst->tLOD, 0x0);
    ISET(sst->tDetail, 0x0);
    ISET(sst->texBaseAddr, 0x0);
    ISET(sst->texBaseAddr1, 0x0);
    ISET(sst->texBaseAddr2, 0x0);
    ISET(sst->texBaseAddr38, 0x0);

    for(i=0; i<12; i++)
        ISET(sst->nccTable0[i], 0x0);
    for(i=0; i<12; i++)
        ISET(sst->nccTable1[i], 0x0);
    sst1InitIdle(sstbase);

    ISET(sst->tchromaKey, 0x0);
    ISET(sst->tchromaRange, 0x0);

}

/*
** sst1InitShutdown():
**  Shutdown SST-1
**    Resets state of VGA_PASS to return monitor control back to the VGA
**    Powers-down the DAC
**    Resets the graphics clock in FBI to conserve power
**
**    Returns:
**      FXTRUE if successfully shuts down SST-1
**      FXFALSE if cannot shut down the SST-1
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitShutdown(FxU32 *sstbase)
{
    SstRegs *sstMaster = (SstRegs *) sstbase;
    SstRegs *sstSlave;
    SstRegs *sstPtr;
    FxU32 n;
    FxU32 sliEnabled;
    sst1ClkTimingStruct sstGrxClk;

    if(!sstbase)
        return(FXFALSE);

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

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

    sliEnabled = (sst1CurrentBoard->sliSlaveVirtAddr != (FxU32 *) NULL) ? 1 : 0;
    sstSlave = (SstRegs *) sst1CurrentBoard->sliSlaveVirtAddr;

#if 0
    // If the command fifo is enabled, we can't issue a NOP first, so wait
    // until the PCI fifo is empty...
    sst1InitPciFifoIdleLoop(sstbase);
    if(sst1CurrentBoard->sliSlaveVirtAddr)
       sst1InitPciFifoIdleLoop(sst1CurrentBoard->sliSlaveVirtAddr);
#else
    sst1InitIdle(sstbase);
#endif

    // Disable the command fifo if enabled...
    if(sst1CurrentBoard->fbiCmdFifoEn ||
       (sstMaster->fbiInit7 & SST_EN_CMDFIFO))
       sst1InitCmdFifo(sstbase, FXFALSE, (void *) NULL, (void *) NULL,
        (void *) NULL, NULL);

    sst1InitIdle(sstbase);

    if(sliEnabled && !GETENV(("SSTV2_NOSHUTDOWN"))) {
        if(sst1InitShutdownSli(sstbase) == FXFALSE)
            return(FXFALSE);
    }

    if(GETENV(("SSTV2_NOSHUTDOWN")))
        INIT_PRINTF(("sst1InitShutdown(): Bypassing shutdown with SSTV2_NOSHUTDOWN\n"));

    n = 0;
    while(!GETENV(("SSTV2_NOSHUTDOWN"))) {
        if(!n)
            sstPtr = sstMaster;
        else
            sstPtr = sstSlave;

        if(sst1InitCheckBoard((FxU32 *) sstPtr) == FXFALSE)
            return(FXFALSE);

        INIT_PRINTF(("sst1InitShutdown(): Shutting down SST-1 #%d...\n", n));

        // Reset video unit to guarantee no contentions on the memory bus
        // Blank DAC so VGA Passthru works properly
        ISET(sstPtr->fbiInit1,
          IGET(sstPtr->fbiInit1) | (SST_VIDEO_RESET | SST_VIDEO_BLANK_EN));
        // Turn off dram refresh to guarantee no contentions on the
        // memory bus
        ISET(sstPtr->fbiInit2, IGET(sstPtr->fbiInit2) & ~SST_EN_DRAM_REFRESH);

        // Reset graphics subsystem
        ISET(sstPtr->fbiInit0,
          IGET(sstPtr->fbiInit0) | (SST_GRX_RESET | SST_PCI_FIFO_RESET));
        sst1InitIdleFBINoNOP((FxU32 *) sstPtr);
        ISET(sstPtr->fbiInit0, IGET(sstPtr->fbiInit0) & ~SST_PCI_FIFO_RESET);

        sst1InitIdleFBINoNOP((FxU32 *) sstPtr);
        ISET(sstPtr->fbiInit0, IGET(sstPtr->fbiInit0) & ~SST_GRX_RESET);
        sst1InitIdleFBINoNOP((FxU32 *) sstPtr);

        // Turnaround VGA_PASS to allow VGA monitor
        sst1InitVgaPassCtrl((FxU32 *) sstPtr, 1);
        sst1InitIdleFBINoNOP((FxU32 *) sstPtr);

        // Set clock at 30 MHz to reduce power consumption...
        sst1InitComputeClkParams((float) 30.0, &sstGrxClk);
        if(sst1InitSetGrxClk((FxU32 *) sstPtr, &sstGrxClk) == FXFALSE)
          INIT_PRINTF(("sst1InitShutdown() WARNING: sst1InitSetGrxClk failed...Continuing...\n"));
        sst1CurrentBoard->initGrxClkDone = 0;

#ifndef __linux__
        pciUnmapPhysical((FxU32)sst1CurrentBoard->virtAddr[0],
             0x1000000UL);
#endif

        if((++n > 1) || !sliEnabled)
            break;
    }

    /* sst1InitIdle(sstbase);  */

#if !DIRECTX
    //
    // HACK alert.
    //
    // There's a pciClose(), but nobody calls it. This is needed by the
    // DOS DPMI services to close fxmemmap.vxd.
    //
    // Note that the call to pciClose must be after the shutdown sequence,
    // as the shutdown sequence uses PCI configuration reads/writes...
    //
    pciClose();
#endif /* !DIRECTX */

    INIT_PRINTF(("sst1InitShutdown(): Returning with status %d...\n", FXTRUE));
#ifdef INIT_OUTPUT
    if ( sst1InitMsgFile != stdout )
        fclose(sst1InitMsgFile);
#endif

    // Make sure that the board info structures are
    // cleared next time sst1InitMapBoard() is called.
    clearBoardInfo = FXTRUE;

    return(FXTRUE);
}

/*
** sst1InitCheckBoard():
**  Set internal global variables for multiple SST-1 support
**
*/
FX_EXPORT FxBool FX_CSTYLE sst1InitCheckBoard(FxU32 *sstbase)
{
    FxU32 n;
    FxBool returnVal = FXFALSE;

    for(n=0; n<boardsInSystem; n++) {
        if((FxU32 *) sst1BoardInfo[n].virtAddr[0] == sstbase) {
            sst1InitDeviceNumber = sst1BoardInfo[n].deviceNumber;
            sst1CurrentBoard = &sst1BoardInfo[n];
            returnVal = FXTRUE;
            n = boardsInSystem;
        }
    }
    return(returnVal);
}

/*
** sst1InitGetBaseAddr():
**  Return virtual base address to hardware
**  Returns NULL if board requested has not been mapped
**
*/
FX_EXPORT FxU32 * FX_CSTYLE sst1InitGetBaseAddr(FxU32 boardNum)
{
  SstRegs* sstBaseAddr = sst1BoardInfo[boardNum].virtAddr[0];
  sst1DeviceInfoStruct devInfo;

  if ((sstBaseAddr != NULL) &&
      sst1InitGetDeviceInfo((FxU32*)sstBaseAddr, &devInfo) &&
      devInfo.sliDetected &&
      !devInfo.monitorDetected) {

    SstRegs* tempAddr = NULL;

    if (boardNum > 0) {
      if (sst1BoardInfo[boardNum - 1].sliSlaveVirtAddr == (FxU32*)sstBaseAddr) {
        tempAddr = sst1BoardInfo[boardNum - 1].virtAddr[0];
      }
    }

    if ((tempAddr == NULL) && (boardNum < boardsInSystem)) {
      if (sst1BoardInfo[boardNum + 1].sliSlaveVirtAddr == (FxU32*)sstBaseAddr) {
        tempAddr = sst1BoardInfo[boardNum + 1].virtAddr[0];
      }
    }

    if (tempAddr != NULL) sstBaseAddr = tempAddr;
  }

  return (FxU32*)sstBaseAddr;
}

/*
** sst1InitClearBoardInfo():
**  Clear board configuration information
**
*/
void sst1InitClearBoardInfo(void)
{
    FxU32 n, j;

    for(n=0; n<SST1INIT_MAX_BOARDS; n++) {
        sst1BoardInfo[n].size = sizeof(sst1DeviceInfoStruct);
        //sst1BoardInfo[n].virtAddr[0] = (SstRegs *) NULL;
        //sst1BoardInfo[n].physAddr[0] = (FxU32) NULL;
        //sst1BoardInfo[n].deviceNumber = 0xdead;
        //sst1BoardInfo[n].vendorID = 0xdead;
        //sst1BoardInfo[n].deviceID = 0xdead;

        //sst1BoardInfo[n].fbiRevision = 0xdead;
        //sst1BoardInfo[n].fbiFab = 0xdead;
        sst1BoardInfo[n].fbiBoardID = 0xdead;
        sst1BoardInfo[n].fbiVideo16BPP = 0xdead;
        sst1BoardInfo[n].fbiVideoWidth = 0xdead;
        sst1BoardInfo[n].fbiVideoHeight = 0xdead;
        sst1BoardInfo[n].fbiVideoRefresh = 0xdead;
        sst1BoardInfo[n].fbiVideoColBuffs = 0xdead;
        sst1BoardInfo[n].fbiVideoAuxBuffs = 0xdead;
        sst1BoardInfo[n].fbiVideoMemOffset = 0xdead;
        sst1BoardInfo[n].fbiVideoTilesInX = 0xdead;
        sst1BoardInfo[n].fbiVideoStruct = (sst1VideoTimingStruct *) NULL;
        sst1BoardInfo[n].fbiVideoDacType = 0xdead;
        sst1BoardInfo[n].fbiMemoryFifoEn = 0x0;
        sst1BoardInfo[n].fbiCmdFifoEn = 0x0;
        sst1BoardInfo[n].fbiLfbLocked = 0x0;
        sst1BoardInfo[n].fbiConfig = 0xdead;
        sst1BoardInfo[n].fbiGrxClkFreq = 0xdead;
        sst1BoardInfo[n].fbiMemSize = 0x0;
        sst1BoardInfo[n].fbiInitGammaDone = 0x0;
        sst1BoardInfo[n].fbiGammaRed = (double) 0.0;
        sst1BoardInfo[n].fbiGammaGreen = (double) 0.0;
        sst1BoardInfo[n].fbiGammaBlue = (double) 0.0;

        sst1BoardInfo[n].tmuRevision = 0xdead;
        sst1BoardInfo[n].numberTmus = 0xdead;
        sst1BoardInfo[n].tmuConfig = 0xdead;
        sst1BoardInfo[n].tmuGrxClkFreq = 0xdead;

        for(j=0; j<MAX_NUM_TMUS; j++) {
            sst1BoardInfo[n].tmuFab[j] = 0xdead;
            sst1BoardInfo[n].tmuMemSize[j] = 0x0;
            sst1BoardInfo[n].tmuInit0[j] = 0xdead;
            sst1BoardInfo[n].tmuInit1[j] = 0xdead;
        }
        sst1BoardInfo[n].fbiInit6 = 0;
        sst1BoardInfo[n].fbiInitEnable = 0;

        sst1BoardInfo[n].sliDetected = 0;
        sst1BoardInfo[n].monitorDetected = 0;
        sst1BoardInfo[n].sliSlaveVirtAddr = (FxU32 *) NULL;
        sst1BoardInfo[n].initGrxClkDone = 0;
        sst1BoardInfo[n].vgaPassthruDisable = 0;
        sst1BoardInfo[n].vgaPassthruEnable = 0;
        sst1BoardInfo[n].memFifoStatusLwm = 0xdead;
    }
}

/*
** sst1InitNumBoardsInSystem():
**  Returns the number of SST-1 boards in the system
**
*/
FX_ENTRY FxU32 FX_CSTYLE sst1InitNumBoardsInSystem(void)
{
    FxU32 vendorID = _3DFX_PCI_ID;     // 3Dfx Vendor ID
    FxU32 deviceID;                    // 0x0002 - Find Voodoo2 boards ( 0xFFFF - Find any 3Dfx board)
    FxU32 j, n;

    if( GETENV( ("SSTV2_DEVICEID") ) )
      deviceID = ATOI( GETENV( "SSTV2_DEVICEID" ) );
    else
      deviceID = 0x0002;

    boardsInSystemReally = 0;
    for(j=0; j<SST1INIT_MAX_BOARDS; j++) {
       if(pciFindCardMulti(vendorID, deviceID, &n, j))
           boardsInSystemReally++;
    }
    if(GETENV(("SSTV2_BOARDS")))
       return(ATOI(GETENV(("SSTV2_BOARDS"))));
    else
       return(boardsInSystemReally);
}

/*
** sst1InitCaching
**
** Sets up memory caching on P6 systems for the entire 16mb virtual
** address space of the card.
**
*/
FX_ENTRY FxBool FX_CSTYLE
sst1InitCaching(FxU32* sstBase, FxBool enableP)
{
  FxBool retVal = sst1InitCheckBoard(sstBase);

  if (!retVal) return FXFALSE;

  if (enableP && (GETENV("SSTV2_IGNORE_CACHING") == NULL)) {
    FxU32 physAddr;

    /* Get the board's base. Isn't this the same as what we
     * cary around in sst1CurrentBoard->physAddr[0]?
     */
    pciGetConfigData(PCI_BASE_ADDRESS_0, sst1CurrentBoard->deviceNumber, &physAddr);

    // For some reason, there sometimes is a 008 at the end of the
    // physical address, so mask that puppy RTF out
    physAddr &= 0xfffff000;

    /* dpc - 23 jan 1998 - Warning!!!!!
     * We are no longer mapping the entire board range as uswc because
     * this causes weird problems on some systems. We are now mapping
     * the 3d register area as uncacheable (since no one should be
     * writing here anyway, except to bump the swapbufferCMD register)
     * and the command fifo range and lfb range as uswc.
     *
     * NB: It is still unclear why this is happening because the
     * memory page containing the registers is never really explicitly
     * written to until we are shutting down so changing its caching
     * characteristics should have no bearing on anything.  */
    {
#define kCacheSizeWriteCombine (0x08UL << 20UL)
#define kCacheSizeUncacheable  (0x1000UL)
      FxBool hasWC = pciFindMTRRMatch(physAddr, kCacheSizeWriteCombine,
                                      PciMemTypeWriteCombining,
                                      &sst1CurrentBoard->mtrrWriteCombine);
      FxBool hasUC = pciFindMTRRMatch(physAddr, kCacheSizeUncacheable,
                                      PciMemTypeUncacheable,
                                      &sst1CurrentBoard->mtrrUncacheable);

      if (!hasWC) {
        if (pciFindFreeMTRR(&sst1CurrentBoard->mtrrWriteCombine)) {
          hasWC = pciSetMTRR(sst1CurrentBoard->mtrrWriteCombine,
                             physAddr, kCacheSizeWriteCombine,
                             PciMemTypeWriteCombining);
          if (!hasWC) INIT_PRINTF(("sst1InitCaching(): Could not set wc mtrr\n"));
        } else {
          INIT_PRINTF(("sst1InitCaching(): Could not find wc mtrr\n"));
        }
      } else {
        INIT_PRINTF(("sst1InitCaching(): Already have wc\n"));
      }

      if (!hasUC) {
        if (pciFindFreeMTRR(&sst1CurrentBoard->mtrrUncacheable)) {
          hasUC = pciSetMTRR(sst1CurrentBoard->mtrrUncacheable,
                             physAddr, kCacheSizeUncacheable,
                             PciMemTypeUncacheable);
          if (!hasUC) INIT_PRINTF(("sst1InitCaching(): Could not set uc mtrr\n"));
        } else {
          INIT_PRINTF(("sst1InitCaching(): Could not find uc mtrr\n"));
        }
      } else {
        INIT_PRINTF(("sst1InitCaching(): Already have uc\n"));
      }

      /* We only succeed if we have them both since having only uswc
       * seems to cause problems.
       */
      retVal = (hasWC && hasUC);
      if (!retVal) goto __errExit;

      INIT_PRINTF(("sst1InitCaching(): wc(0x%X) : uc(0x%X)\n",
                   sst1CurrentBoard->mtrrWriteCombine,
                   sst1CurrentBoard->mtrrUncacheable));
    }
  } else {
    /* This always succeeds */
    retVal = FXTRUE;

__errExit:
    if (sst1CurrentBoard->mtrrUncacheable != 0) {
      pciSetMTRR(sst1CurrentBoard->mtrrUncacheable, 0, 0, 0);
      sst1CurrentBoard->mtrrUncacheable = 0;
    }

    if (sst1CurrentBoard->mtrrWriteCombine != 0) {
      pciSetMTRR(sst1CurrentBoard->mtrrWriteCombine, 0, 0, 0);
      sst1CurrentBoard->mtrrWriteCombine = 0;
    }
  }

  return retVal;
} // sst1InitSetCaching

#pragma optimize ("",on)
