/* 
** 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$ 
** $Date$ 
**
*/

/*
** 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
** --------                    -----   -----------
** SST_ARCADE                  X       Allow special configs for arcade use
** SST_BACKPORCH               hex     Specify value of backPorch video register
** SST_BOARDS                  val     Specify number of SST-1 boards in system
** SST_DEBUGDAC                X       Print out debug information for DAC
**                                     reads and writes
** SST_DIMENSIONS              hex     Specify value of videoDimensions register
** SST_FASTMEM                 1       Default. Enable fast memory timing
** SST_FASTMEM                 0       Disable fast memory timing
** SST_FASTPCIRD               X       Enable fast PCI reads
** SST_FBICFG                  hex     Specify FBI configuration strapping bits
** SST_FBIMEM_SIZE             val     Specify amount of frame buffer memory
** SST_FT_CLK_DEL              hex     Fbi-to-Trex clock delay value
** SST_GAMMA                   float   Floating point value for gamma correction
** SST_GRXCLK                  val     16 <= Frequency < 80 (50 is default)
** SST_HSYNC                   hex     Specify value of hSync video register
** SST_IGNORE_INIT_GAMMA       X       Bypass sst1InitGamma call and return
** SST_IGNORE_INIT_REGISTERS   X       Bypass sst1InitRegisters call and return
** SST_IGNORE_INIT_VIDEO       X       Bypass sst1InitVideo call and return
** SST_IGNORE_SLI_CHECK        X       Ignore checking for proper SLI install
** SST_INITDEBUG               X       Enable debug output
** SST_INITDEBUG_FILE          file    File to direct all debug output
** SST_MEMFIFO                 0       Disable FBI Memory FIFO
** SST_MEMFIFO                 1       Enable FBI Memory FIFO
** SST_MEMFIFO_ENTRIES         hex     Set number of entries in memory fifo
** SST_MEMFIFO_HWM             hex     Set memory fifo high water mark
** SST_MEMFIFO_LFB             0 or 1  Specify LFB writes through Memory FIFO
** SST_MEMFIFO_LWM             hex     Set memory fifo low water mark
** SST_MEMFIFO_TEX             0 or 1  Specify Texture writes through Mem FIFO
** SST_MEMOFFSET               hex     Specify value of memoffset video register
** SST_NOCHECKHANG             X       Do not Check for FBI Hangs for texturing
** SST_NODEVICEINFO            X       Ignore calls to sst1InitFillDeviceInfo()
** SST_NOSHUTDOWN              X       Do not turn off monitor refresh on call
**                                     to sst1InitShutdown()
** SST_PCIFIFO_LWM             hex     Set pci fifo low water mark
** SST_PFT_CLK_DEL             hex     Preliminary Fbi-to-Trex clock delay value
** SST_PTF0_CLK_DEL            hex     Prelim TMU2Fbi clock delay value (TMU #0)
** SST_PTF1_CLK_DEL            hex     Prelim TMU2Fbi clock delay value (TMU #1)
** SST_PTF2_CLK_DEL            hex     Prelim TMU2Fbi clock delay value (TMU #2)
** SST_SLIDETECT               0 or 1  Specify Scanline Interleaving detection
** SST_SLIM_VIN_CLKDEL         hex     Specify FBI internal video clock delay
**                                     (SLI Master)
** SST_SLIM_VOUT_CLKDEL        hex     Specify FBI external video clock delay
**                                     (SLI Master)
** SST_SLIS_VIN_CLKDEL         hex     Specify FBI internal video clock delay
**                                     (SLI Slave)
** SST_SLIS_VOUT_CLKDEL        hex     Specify FBI external video clock delay
**                                     (SLI Slave)
** SST_SCREENREFRESH           60      Select 60Hz monitor refresh rate
**                                     (overrides argument to sst1InitVideo)
** SST_SCREENREFRESH           75      Select 75Hz monitor refresh rate
** SST_SCREENREZ               512     Select 512x384 video resolution
**                                     (overrides argument to sst1InitVideo)
** SST_SCREENREZ               640     Select 640x480 video resolution
** SST_SCREENREZ               800     Select 800x600 video resolution
** SST_SLOWPCIWR               X       Enable 1 wait-state PCI writes
** SST_TEXMAP_DISABLE          X       Disable texture mapping
** SST_TF0_CLK_DEL             hex     Trex-to-Fbi clock delay value (TMU #0)
** SST_TF1_CLK_DEL             hex     Trex-to-Fbi clock delay value (TMU #1)
** SST_TF2_CLK_DEL             hex     Trex-to-Fbi clock delay value (TMU #2)
** SST_TF_FIFO_THRESH          hex     Set TREX-to-FBI FIFO threshold
** SST_TILESINX                hex     Specify value of tilesInX video register
** SST_TMUCFG                  hex     Specify TMU configuration strapping bits
** SST_TMUMEM                  X       Enable accurate determination of TMU mem
** SST_TMUMEM_SIZE             val     Specify amount of texture memory
** SST_TREX0INIT0              hex     Set trexInit0 register value - TMU #0
** SST_TREX0INIT1              hex     Set trexInit1 register value - TMU #0
** SST_TREX1INIT0              hex     Set trexInit0 register value - TMU #1
** SST_TREX1INIT1              hex     Set trexInit1 register value - TMU #1
** SST_TREX2INIT0              hex     Set trexInit0 register value - TMU #2
** SST_TREX2INIT1              hex     Set trexInit1 register value - TMU #2
** SST_TRIPLE_BUFFER           X       Enable Triple Buffering
** SST_VIN_CLKDEL              hex     Specify FBI internal video clock delay
** SST_VOUT_CLKDEL             hex     Specify FBI external video clock delay
** SST_VGA_PASS                0,1     Force VGA_PASS output to {0,1}
** SST_VIDCLK2X                val     Video clock frequency (2x dot clock)
** SST_VIDEO_24BPP             0       Select 16-bit video output
** SST_VIDEO_24BPP             1       Select 24-bit video output
** SST_VIDEO_DISABLE           X       Turn off Video/Monitor refresh in FBI
** SST_VIDEO_FILTER_DISABLE    X       Disable video filtering
** SST_VIDEO_FILTER_THRESHOLD  hex     Set video filtering threshold
** SST_VIDEO_NOCLEAR           X       Do not clear buffers in sst1InitVideo()
** SST_VIDEO_CLEARCOLOR        hex     Clear screen color
** SST_VFIFO_THRESH            0-31    Select video fifo threshold
** SST_VSYNC                   hex     Specify value of vSync video register
**
** VOODOO_FILE                 name    Filename used in place of "voodoo.ini"
** VOODOO_PATH                 path    Path used to locate "voodoo.ini" file
**
*/

#pragma optimize ("",off)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sst.h>
#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)
{
    static FxU32 firstTime = 1;
    FxU32 sizeOfCard = 0x1000000;      /* 16 MBytes of addr space for SST-1 */
    FxU32 *sst;
    volatile Sstregs *sstptr;
    FxU32 n, j;

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

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

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

    if(BoardNumber == 0) {
        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();
    }

    if(firstTime) {
        firstTime = 0;
        sst1InitClearBoardInfo();

        /* Map all boards in the system */
        /* Check that scanline interleaving is not enabled... */
        for(j=0; j<SST1INIT_MAX_BOARDS; j++) {
          FxU32 vendorID = _3DFX_PCI_ID;     /* 3Dfx Vendor ID */
          FxU32 deviceID = 1;                /* Find only sst1 boards */

#ifndef DIRECTX
            sst = pciMapCardMulti(vendorID, deviceID, sizeOfCard,
                &sst1InitDeviceNumber, j, 0);
#else
            sst = pciMapCardMulti(vendorID, deviceID, sizeOfCard, 
                &sst1InitDeviceNumber, j, 0);
#endif /* DIRECTX */
            if(sst) {
                sst1BoardInfo[j].virtAddr = sst;
                PCICFG_RD(PCI_BASE_ADDRESS_0, sst1BoardInfo[j].physAddr);
                sst1BoardInfo[j].deviceNumber = sst1InitDeviceNumber;
                sst1BoardInfo[j].numBoardInsystem = j;
                PCICFG_RD(PCI_REVISION_ID, sst1BoardInfo[j].fbiRevision);
                PCICFG_RD(PCI_DEVICE_ID, sst1BoardInfo[j].deviceID);
                PCICFG_RD(PCI_VENDOR_ID, sst1BoardInfo[j].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);
                sstptr = (Sstregs *) sst;
                /* Check SLI not enabled... */
                if((IGET(sstptr->fbiInit1) & SST_EN_SCANLINE_INTERLEAVE) &&
                    sst1InitSliDetect(sst)) {
                    INIT_PRINTF(("sst1InitMapBoard(): Scanline Interleave detected at startup for board=%d\n", j));
                    INIT_PRINTF(("                    System reboot required...\n"));
                    return(NULL);
                }
            } else {
                FxU32 code = pciGetErrorCode();
                if (code == PCI_ERR_WRONGVXD) {
#ifdef __WIN32__
                    MessageBox(NULL, pciGetErrorString(), NULL, MB_OK);
#endif /* __WIN32__ */
                    exit(1);
                }
                sst1BoardInfo[j].virtAddr = (FxU32 *) NULL;
                sst1BoardInfo[j].physAddr = (FxU32) NULL;
                sst1BoardInfo[j].deviceNumber = 0xdead;
                sst1BoardInfo[j].numBoardInsystem = 0xdead;
                sst1BoardInfo[j].fbiRevision = 0xdead;
                sst1BoardInfo[j].deviceID = 0xdead;
                sst1BoardInfo[j].vendorID = 0xdead;
            }
        }
    }

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

    if(!(sst = sst1BoardInfo[BoardNumber].virtAddr))
        return (NULL);

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

    sstptr = (Sstregs *) sst;

    /* Default settings */
    if((IGET(sstptr->fbiInit3) & SST_FBI_VGA_PASS_POWERON) &&
       (sst1BoardInfo[BoardNumber].fbiRevision > 0)) {
        sst1BoardInfo[BoardNumber].vgaPassthruDisable = 0x0;
        sst1BoardInfo[BoardNumber].vgaPassthruEnable = SST_EN_VGA_PASSTHRU;
        sst1BoardInfo[BoardNumber].fbiRegulatorPresent = 0x1;
    } else {
        sst1BoardInfo[BoardNumber].vgaPassthruDisable = SST_EN_VGA_PASSTHRU;
        sst1BoardInfo[BoardNumber].vgaPassthruEnable = 0x0;
        sst1BoardInfo[BoardNumber].fbiRegulatorPresent = 0x0;
    }
    sst1BoardInfo[BoardNumber].fbiVideo16BPP = 0;

    if(GETENV(("SST_VGA_PASS"))) {
        INIT_PRINTF(("sst1InitMapBoard(): Using SST_VGA_PASS=%d\n",
            ATOI(GETENV(("SST_VGA_PASS")))));
        if(ATOI(GETENV(("SST_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(sst);
}

/*
** 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_clk_del, tf0_clk_del, tf1_clk_del, tf2_clk_del;
    sst1ClkTimingStruct sstGrxClk;
    volatile Sstregs *sst = (Sstregs *) sstbase;

    if(!sst)
        return(FXFALSE);

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

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

    /* Disable video clock */
    PCICFG_WR(SST1_PCI_VCLK_DISABLE, 0x0);

    /* 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 */
#if 0
    PCICFG_WR(SST1_PCI_BUS_SNOOP0, SST_PCI_BUS_SNOOP_DEFAULT);
    PCICFG_WR(SST1_PCI_BUS_SNOOP1, SST_PCI_BUS_SNOOP_DEFAULT);
#else
    PCICFG_WR(SST1_PCI_BUS_SNOOP_0, SST_PCI_BUS_SNOOP_DEFAULT);
    PCICFG_WR(SST1_PCI_BUS_SNOOP_1, SST_PCI_BUS_SNOOP_DEFAULT);
#endif
    sst1InitReturnStatus(sstbase); /* Stall - can't call IdleFbi because */
                                   /* FBI could be hung at this stage */
    sst1InitReturnStatus(sstbase);
    sst1InitReturnStatus(sstbase);
    
    /* Adjust Fbi-to-Trex clock delay value */
    /* Adjust Trex-to-Fbi FIFO */
    if(GETENV(("SST_TF_FIFO_THRESH")))
        SSCANF(GETENV(("SST_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_clk_del for 16 MHz bringup */
    if(sst1CurrentBoard->fbiRevision == 2)
        /* .5 micron */
        ft_clk_del = 0x8; /* Okay for 16 MHz startup... */
    else
        /* .6 micron */
        ft_clk_del = 0xa; /* Okay for 16 MHz startup... */
    if(GETENV(("SST_PFT_CLK_DEL")))
        SSCANF(GETENV(("SST_PFT_CLK_DEL")), "%i", &ft_clk_del);
    INIT_PRINTF(("sst1InitRegisters(): Setting PRELIM FT-CLK delay to 0x%x...\n", ft_clk_del));
    ISET(sst->fbiInit3,
        (SST_FBIINIT3_DEFAULT & ~(SST_FT_CLK_DEL_ADJ | SST_TF_FIFO_THRESH)) |
        (ft_clk_del << 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 */
                                   /* FBI could be hung at this stage */

    sst1InitReturnStatus(sstbase);
    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 b/c grx 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_clk_del << SST_FT_CLK_DEL_ADJ_SHIFT) |
        (tf_fifo_thresh << SST_TF_FIFO_THRESH_SHIFT));
    ISET(sst->fbiInit4, SST_FBIINIT4_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(("SST_TREX0INIT0"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX0INIT0 environment variable\n"));
        SSCANF(GETENV(("SST_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(("SST_TREX0INIT1"))) {

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

    ISET(SST_TREX(sst,0)->trexInit0, sst1CurrentBoard->tmuInit0[0]);
    sst1InitIdleFBINoNOP(sstbase);
    ISET(SST_TREX(sst,0)->trexInit1, sst1CurrentBoard->tmuInit1[0]);
    sst1InitIdleFBINoNOP(sstbase);

    /* set TREX1 init values */
    if(GETENV(("SST_TREX1INIT0"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX1INIT0 environment variable\n"));
        SSCANF(GETENV(("SST_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(("SST_TREX1INIT1"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX1INIT1 environment variable\n"));
        SSCANF(GETENV(("SST_TREX1INIT1")), "%i",
            &sst1CurrentBoard->tmuInit1[1]);
    } else
        sst1CurrentBoard->tmuInit1[1] = SST_TREX1INIT1_DEFAULT;
    if(GETENV(("SST_PTF1_CLK_DEL"))) {
        SSCANF(GETENV(("SST_PTF1_CLK_DEL")), "%i", &tf1_clk_del);
        sst1CurrentBoard->tmuInit1[1] = (sst1CurrentBoard->tmuInit1[1] &
            ~SST_TEX_TF_CLK_DEL_ADJ) |
            (tf1_clk_del<<SST_TEX_TF_CLK_DEL_ADJ_SHIFT);
    }
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX1INIT1=0x%x\n",
        sst1CurrentBoard->tmuInit1[1]));

    ISET(SST_TREX(sst,1)->trexInit0, sst1CurrentBoard->tmuInit0[1]);
    sst1InitIdleFBINoNOP(sstbase);
    ISET(SST_TREX(sst,1)->trexInit1, sst1CurrentBoard->tmuInit1[1]);
    sst1InitIdleFBINoNOP(sstbase);

    /* set TREX2 init values */
    if(GETENV(("SST_TREX2INIT0"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX2INIT0 environment variable\n"));
        SSCANF(GETENV(("SST_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(("SST_TREX2INIT1"))) {
        INIT_PRINTF(("sst1InitRegisters(): Using SST_TREX2INIT1 environment variable\n"));
        SSCANF(GETENV(("SST_TREX2INIT1")), "%i",
            &sst1CurrentBoard->tmuInit1[2]);
    } else
        sst1CurrentBoard->tmuInit1[2] = SST_TREX2INIT1_DEFAULT;
    if(GETENV(("SST_PTF2_CLK_DEL"))) {
        SSCANF(GETENV(("SST_PTF2_CLK_DEL")), "%i", &tf2_clk_del);
        sst1CurrentBoard->tmuInit1[2] = (sst1CurrentBoard->tmuInit1[2] &
            ~SST_TEX_TF_CLK_DEL_ADJ) |
            (tf2_clk_del<<SST_TEX_TF_CLK_DEL_ADJ_SHIFT);
    }
    INIT_PRINTF(("sst1InitRegisters(): Storing TREX2INIT1=0x%x\n", 
        sst1CurrentBoard->tmuInit1[2]));

    ISET(SST_TREX(sst,2)->trexInit0, sst1CurrentBoard->tmuInit0[2]);
    sst1InitIdleFBINoNOP(sstbase);
    ISET(SST_TREX(sst,2)->trexInit1, sst1CurrentBoard->tmuInit1[2]);
    sst1InitIdleFBINoNOP(sstbase);

    /* Set clock at 16 MHz to sense TREX configuration... */
    if(sst1InitComputeClkParams((float) 16.0, &sstGrxClk) == FXFALSE)
        return(FXFALSE);
    if(sst1InitSetGrxClk(sstbase, &sstGrxClk) == FXFALSE)
        return(FXFALSE);

    /* Set PCI wait-states */
    if(GETENV(("SST_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(("SST_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);

#if 0
    /* Enable Endian Byte Swapping */
    /* Do not enable byte swapping due to potential bug in FBI... */
    ISET(sst->fbiInit0, IGET(sst->fbiInit0) | SST_EN_ENDIAN_SWAPPING);
#endif

    /* 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);

    if(GETENV(("SST_FASTMEM")))
        n = ATOI(GETENV(("SST_FASTMEM")));
    else
        n = 1;

    if(n) {
        INIT_PRINTF(("sst1InitRegisters(): Setting up FAST DRAM Configuration\n"));
        ISET(sst->fbiInit2, IGET(sst->fbiInit2) | (SST_EN_FAST_RAS_READ |
          SST_EN_DRAM_RD_AHEAD_FIFO | SST_EN_FAST_RD_AHEAD_WR));
    } else
        ISET(sst->fbiInit1, IGET(sst->fbiInit1) | SST_DISEN_RD_AHEAD_WR_RD);

    /* 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);
    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(("SST_MEMFIFO_LFB")))
        n = ATOI(GETENV(("SST_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(("SST_MEMFIFO_TEX")))
        n = ATOI(GETENV(("SST_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 */

    if(GETENV(("SST_TEXMAP_DISABLE"))) {
        INIT_PRINTF(("sst1InitRegisters() WARNING: Disabling texture mapping\n"));
        ISET(sst->fbiInit3, IGET(sst->fbiInit3) | SST_TEXMAP_DISABLE);
        sst1InitIdleFBINoNOP(sstbase);
    }
    INIT_PRINTF(("sst1InitRegisters(): exiting with status %d...\n", FXTRUE));
    return(FXTRUE);
}

FX_EXPORT void FX_CSTYLE sst1InitRenderingRegisters(FxU32 *sstbase)
{
    FxU32 i;
    volatile 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->stipple, 0x0);
    ISET(sst->c0, 0x0);
    ISET(sst->c1, 0x0);

    ISET(sst->nopCMD, 0x1);   /* Clear fbistat registers */

    for(i=0; i<32; i++)
        ISET(sst->fogTable[i], 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);
        ISET(sst->nccTable1[i], 0x0);
    }
    sst1InitIdle(sstbase);
}

/*
** 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)
{
    volatile Sstregs *sstMaster = (Sstregs *) sstbase;
    volatile Sstregs *sstSlave = (Sstregs *) sst1InitSliSlaveVirtAddr;
    volatile Sstregs *sstPtr;
    FxU32 n;
    FxU32 SliEnable = sst1InitSliEnabled;
    sst1ClkTimingStruct sstGrxClk;

    if(!sstbase)
        return(FXFALSE);

    /* Wait for the SST-1 to finish rendering... */
    sst1InitIdle(sstbase);

    if(sst1InitSliEnabled) {
        if(sst1InitShutdownSli(sstbase) == FXFALSE)
            return(FXFALSE);
    }
#ifdef __DOS32__
    /* 
     * HACK alert. -MS
     *
     * There's a pciClose(), but nobody calls it. This is needed by the
     * DOS DPMI services to close fxmemmap.vxd.
     * 
     * We need to move this to the "appropriate" place, wherever that may be.
     */
    pciClose();
#endif

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

    n = 0;
    while(!GETENV(("SST_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)
            return(FXFALSE);
        sst1CurrentBoard->initGrxClkDone = 0;
        
        if((++n > 1) || !SliEnable)
            break;
    }
    sst1InitIdle(sstbase);
    INIT_PRINTF(("sst1InitShutdown(): Returning with status %d...\n", FXTRUE));
#ifdef INIT_OUTPUT
    if ( sst1InitMsgFile != stdout && sst1InitMsgFile )
        fclose(sst1InitMsgFile);
#endif
    return(FXTRUE);
}

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

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

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

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

    for(n=0; n<SST1INIT_MAX_BOARDS; n++) {
        //sst1BoardInfo[n].virtAddr = (FxU32 *) NULL;
        //sst1BoardInfo[n].physAddr = (FxU32) NULL;
        //sst1BoardInfo[n].deviceNumber = 0xdead;
        //sst1BoardInfo[n].vendorID = 0xdead;
        //sst1BoardInfo[n].deviceID = 0xdead;
        //sst1BoardInfo[n].fbiRevision = 0xdead;
        sst1BoardInfo[n].fbiBoardID = 0xdead;
        sst1BoardInfo[n].fbiVideo16BPP = 0xdead;
        sst1BoardInfo[n].fbiVideoWidth = 0xdead;
        sst1BoardInfo[n].fbiVideoHeight = 0xdead;
        sst1BoardInfo[n].fbiVideoRefresh = 0xdead;
        sst1BoardInfo[n].fbiMemoryFifoEn = 0x0;
        sst1BoardInfo[n].sstSliDetect = 0;
        sst1BoardInfo[n].tmuRevision = 0xdead;
        sst1BoardInfo[n].numberTmus = 0xdead;
        sst1BoardInfo[n].tmuConfig = 0xdead;
        sst1BoardInfo[n].fbiMemSize = 0x0;
        for(j=0; j<MAX_NUM_TMUS; j++) {
            sst1BoardInfo[n].tmuMemSize[j] = 0x0;
            sst1BoardInfo[n].tmuInit0[j] = 0xdead;
            sst1BoardInfo[n].tmuInit1[j] = 0xdead;
        }
        sst1BoardInfo[n].initGrxClkDone = 0;
        sst1BoardInfo[n].vgaPassthruDisable = 0;
        sst1BoardInfo[n].vgaPassthruEnable = 0;
        sst1BoardInfo[n].fbiDacType = 0xdead;
        sst1BoardInfo[n].memFifoStatusLwm = 0xdead;
        //sst1BoardInfo[n].numBoardInsystem = 0xdead;
        sst1BoardInfo[n].grxClkFreq = 0xdead;
        sst1BoardInfo[n].fbiMemSpeed = 50;
        sst1BoardInfo[n].tmuMemSpeed = 50;
        sst1BoardInfo[n].tmuClkFixed = 0;
        sst1BoardInfo[n].fbiRegulatorPresent = 0;
        sst1BoardInfo[n].fbiTripleBufferingEnabled = FXFALSE;
    }
}

/*
** 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 = 1;                /* Find only sst1 boards */
    FxU32 numBoards, j, n;

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

static FxU32 
  mtrr;

/*
** sst1InitCachingOn
**
** Sets up memory caching on P6 systems.
**
*/
FX_EXPORT void FX_CSTYLE 
sst1InitCachingOn(void)
{
  FxU32 
    physAddr;
  FxBool
    res;

  /* Turn on Write combining for wrap 1-n  */
  pciGetConfigData(PCI_BASE_ADDRESS_0, sst1InitDeviceNumber, &physAddr);

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

  /* Set up USWC MTRR */
  res = pciFindMTRRMatch(physAddr, 0x1000000,
                         PciMemTypeWriteCombining, &mtrr);

  if (!res)
    res = pciFindFreeMTRR(&mtrr);
  else
    return;                   /* It's already there.  We're done. */
  
  if (res)
    pciSetMTRR(mtrr, physAddr, 0x1000000, PciMemTypeWriteCombining);

} /* sst1InitSetCacheType */


#pragma optimize ("",on)
