diff options
Diffstat (limited to 'src/savage_driver.c')
-rw-r--r-- | src/savage_driver.c | 3282 |
1 files changed, 3282 insertions, 0 deletions
diff --git a/src/savage_driver.c b/src/savage_driver.c new file mode 100644 index 0000000..cdfe256 --- /dev/null +++ b/src/savage_driver.c @@ -0,0 +1,3282 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/savage/savage_driver.c,v 1.34 2003/02/25 04:08:21 dawes Exp $ */ +/* + * vim: sw=4 ts=8 ai ic: + * + * XFree86 4.0 S3 Savage driver + * + * Tim Roberts <timr@probo.com> + * Ani Joshi <ajoshi@unixbox.com> + * + * TODO: add credits for the 3.3.x authors... + * + */ + + +#include "xf86RAC.h" +#include "shadowfb.h" + +#include "globals.h" +#define DPMS_SERVER +#include "extensions/dpms.h" + +#ifdef XvExtension +#include "xf86xv.h" +#endif + +#include "savage_driver.h" +#include "savage_bci.h" + + + + +/* + * prototypes + */ +static void SavageEnableMMIO(ScrnInfoPtr pScrn); +static void SavageDisableMMIO(ScrnInfoPtr pScrn); + +static const OptionInfoRec * SavageAvailableOptions(int chipid, int busid); +static void SavageIdentify(int flags); +static Bool SavageProbe(DriverPtr drv, int flags); +static Bool SavagePreInit(ScrnInfoPtr pScrn, int flags); + +static Bool SavageEnterVT(int scrnIndex, int flags); +static void SavageLeaveVT(int scrnIndex, int flags); +static void SavageSave(ScrnInfoPtr pScrn); +static void SavageWriteMode(ScrnInfoPtr pScrn, vgaRegPtr, SavageRegPtr, Bool); + +static Bool SavageScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, + char **argv); +static int SavageInternalScreenInit(int scrnIndex, ScreenPtr pScreen); +static ModeStatus SavageValidMode(int index, DisplayModePtr mode, + Bool verbose, int flags); + +void SavageDGAInit(ScreenPtr); +static Bool SavageMapMMIO(ScrnInfoPtr pScrn); +static Bool SavageMapFB(ScrnInfoPtr pScrn); +static void SavageUnmapMem(ScrnInfoPtr pScrn, int All); +static Bool SavageModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode); +static Bool SavageCloseScreen(int scrnIndex, ScreenPtr pScreen); +static Bool SavageSaveScreen(ScreenPtr pScreen, int mode); +static void SavageLoadPalette(ScrnInfoPtr pScrn, int numColors, + int *indicies, LOCO *colors, + VisualPtr pVisual); +static void SavageLoadPaletteSavage4(ScrnInfoPtr pScrn, int numColors, + int *indicies, LOCO *colors, + VisualPtr pVisual); +static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1, + int min_n2, int max_n2, long freq_min, + long freq_max, unsigned int *mdiv, + unsigned int *ndiv, unsigned int *r); +void SavageGEReset(ScrnInfoPtr pScrn, int from_timeout, int line, char *file); +void SavagePrintRegs(ScrnInfoPtr pScrn); +static void SavageDPMS(ScrnInfoPtr pScrn, int mode, int flags); +static Bool SavageDDC1(int scrnIndex); +static unsigned int SavageDDC1Read(ScrnInfoPtr pScrn); +static void SavageProbeDDC(ScrnInfoPtr pScrn, int index); +static void SavageGetTvMaxSize(SavagePtr psav); +static Bool SavagePanningCheck(ScrnInfoPtr pScrn); + +extern ScrnInfoPtr gpScrn; + +#define iabs(a) ((int)(a)>0?(a):(-(a))) + +#define DRIVER_NAME "savage" +#define DRIVER_VERSION "1.1.26" +#define VERSION_MAJOR 1 +#define VERSION_MINOR 1 +#define PATCHLEVEL 26 +#define SAVAGE_VERSION ((VERSION_MAJOR << 24) | \ + (VERSION_MINOR << 16) | \ + PATCHLEVEL) + + +/*#define TRACEON*/ +#ifdef TRACEON +#define TRACE(prms) ErrorF prms +#else +#define TRACE(prms) +#endif + +DriverRec SAVAGE = +{ + SAVAGE_VERSION, + DRIVER_NAME, + SavageIdentify, + SavageProbe, + SavageAvailableOptions, + NULL, + 0 +}; + + +/* Supported chipsets */ + +static SymTabRec SavageChips[] = { + { PCI_CHIP_SAVAGE4, "Savage4" }, + { PCI_CHIP_SAVAGE3D, "Savage3D" }, + { PCI_CHIP_SAVAGE3D_MV, "Savage3D-MV" }, + { PCI_CHIP_SAVAGE2000, "Savage2000" }, + { PCI_CHIP_SAVAGE_MX_MV, "Savage/MX-MV" }, + { PCI_CHIP_SAVAGE_MX, "Savage/MX" }, + { PCI_CHIP_SAVAGE_IX_MV, "Savage/IX-MV" }, + { PCI_CHIP_SAVAGE_IX, "Savage/IX" }, + { PCI_CHIP_PROSAVAGE_PM, "ProSavage PM133" }, + { PCI_CHIP_PROSAVAGE_KM, "ProSavage KM133" }, + { PCI_CHIP_S3TWISTER_P, "ProSavage PN133" }, + { PCI_CHIP_S3TWISTER_K, "ProSavage KN133" }, + { PCI_CHIP_SUPSAV_MX128, "SuperSavage/MX 128" }, + { PCI_CHIP_SUPSAV_MX64, "SuperSavage/MX 64" }, + { PCI_CHIP_SUPSAV_MX64C, "SuperSavage/MX 64C" }, + { PCI_CHIP_SUPSAV_IX128SDR, "SuperSavage/IX 128" }, + { PCI_CHIP_SUPSAV_IX128DDR, "SuperSavage/IX 128" }, + { PCI_CHIP_SUPSAV_IX64SDR, "SuperSavage/IX 64" }, + { PCI_CHIP_SUPSAV_IX64DDR, "SuperSavage/IX 64" }, + { PCI_CHIP_SUPSAV_IXCSDR, "SuperSavage/IXC 64" }, + { PCI_CHIP_SUPSAV_IXCDDR, "SuperSavage/IXC 64" }, + { PCI_CHIP_PROSAVAGE_DDR, "ProSavage DDR" }, + { PCI_CHIP_PROSAVAGE_DDRK, "ProSavage DDR-K" }, + { -1, NULL } +}; + +static SymTabRec SavageChipsets[] = { + { S3_SAVAGE3D, "Savage3D" }, + { S3_SAVAGE4, "Savage4" }, + { S3_SAVAGE2000, "Savage2000" }, + { S3_SAVAGE_MX, "MobileSavage" }, + { S3_PROSAVAGE, "ProSavage" }, + { S3_SUPERSAVAGE, "SuperSavage" }, + { -1, NULL } +}; + +/* This table maps a PCI device ID to a chipset family identifier. */ + +static PciChipsets SavagePciChipsets[] = { + { S3_SAVAGE3D, PCI_CHIP_SAVAGE3D, RES_SHARED_VGA }, + { S3_SAVAGE3D, PCI_CHIP_SAVAGE3D_MV, RES_SHARED_VGA }, + { S3_SAVAGE4, PCI_CHIP_SAVAGE4, RES_SHARED_VGA }, + { S3_SAVAGE2000, PCI_CHIP_SAVAGE2000, RES_SHARED_VGA }, + { S3_SAVAGE_MX, PCI_CHIP_SAVAGE_MX_MV, RES_SHARED_VGA }, + { S3_SAVAGE_MX, PCI_CHIP_SAVAGE_MX, RES_SHARED_VGA }, + { S3_SAVAGE_MX, PCI_CHIP_SAVAGE_IX_MV, RES_SHARED_VGA }, + { S3_SAVAGE_MX, PCI_CHIP_SAVAGE_IX, RES_SHARED_VGA }, + { S3_PROSAVAGE, PCI_CHIP_PROSAVAGE_PM, RES_SHARED_VGA }, + { S3_PROSAVAGE, PCI_CHIP_PROSAVAGE_KM, RES_SHARED_VGA }, + { S3_PROSAVAGE, PCI_CHIP_S3TWISTER_P, RES_SHARED_VGA }, + { S3_PROSAVAGE, PCI_CHIP_S3TWISTER_K, RES_SHARED_VGA }, + { S3_PROSAVAGE, PCI_CHIP_PROSAVAGE_DDR, RES_SHARED_VGA }, + { S3_PROSAVAGE, PCI_CHIP_PROSAVAGE_DDRK, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_MX128, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_MX64, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_MX64C, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_IX128SDR, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_IX128DDR, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_IX64SDR, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_IX64DDR, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_IXCSDR, RES_SHARED_VGA }, + { S3_SUPERSAVAGE, PCI_CHIP_SUPSAV_IXCDDR, RES_SHARED_VGA }, + { -1, -1, RES_UNDEFINED } +}; + +typedef enum { + OPTION_PCI_BURST + ,OPTION_PCI_RETRY + ,OPTION_NOACCEL + ,OPTION_LCD_CENTER + ,OPTION_LCDCLOCK + ,OPTION_MCLK + ,OPTION_REFCLK + ,OPTION_SHOWCACHE + ,OPTION_SWCURSOR + ,OPTION_HWCURSOR + ,OPTION_SHADOW_FB + ,OPTION_ROTATE + ,OPTION_USEBIOS + ,OPTION_SHADOW_STATUS + ,OPTION_CRT_ONLY + ,OPTION_TV_ON + ,OPTION_TV_PAL + ,OPTION_FORCE_INIT +} SavageOpts; + + +static const OptionInfoRec SavageOptions[] = +{ + { OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_HWCURSOR, "HWCursor", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_SWCURSOR, "SWCursor", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_ROTATE, "Rotate", OPTV_ANYSTR, {0}, FALSE }, + { OPTION_USEBIOS, "UseBIOS", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_LCDCLOCK, "LCDClock", OPTV_FREQ, {0}, FALSE }, + { OPTION_SHADOW_STATUS, "ShadowStatus", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_CRT_ONLY, "CrtOnly", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_TV_ON, "TvOn", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_TV_PAL, "PAL", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_FORCE_INIT,"ForceInit", OPTV_BOOLEAN, {0}, FALSE }, + { -1, NULL, OPTV_NONE, {0}, FALSE } +}; + + +static const char *vgaHWSymbols[] = { + "vgaHWBlankScreen", + "vgaHWCopyReg", + "vgaHWGetHWRec", + "vgaHWGetIOBase", + "vgaHWGetIndex", + "vgaHWInit", + "vgaHWLock", + "vgaHWProtect", + "vgaHWRestore", + "vgaHWSave", + "vgaHWSaveScreen", + "vgaHWSetMmioFuncs", + "vgaHWSetStdFuncs", + "vgaHWUnmapMem", + "vgaHWddc1SetSpeed", +#if 0 + "vgaHWFreeHWRec", + "vgaHWMapMem", + "vgaHWUnlock", +#endif + NULL +}; + +static const char *ramdacSymbols[] = { + "xf86CreateCursorInfoRec", +#if 0 + "xf86DestroyCursorInfoRec", +#endif + "xf86InitCursor", + NULL +}; + +static const char *vbeSymbols[] = { + "VBEInit", + "vbeDoEDID", +#if 0 + "vbeFree", +#endif + NULL +}; + +static const char *vbeOptSymbols[] = { + "vbeModeInit", + "VBESetVBEMode", + "VBEGetVBEInfo", + "VBEFreeVBEInfo", + NULL +}; + +static const char *ddcSymbols[] = { + "xf86DoEDID_DDC1", + "xf86DoEDID_DDC2", + "xf86PrintEDID", + "xf86SetDDCproperties", + NULL +}; + +static const char *i2cSymbols[] = { + "xf86CreateI2CBusRec", + "xf86I2CBusInit", + NULL +}; + +static const char *xaaSymbols[] = { + "XAACopyROP", + "XAACopyROP_PM", + "XAACreateInfoRec", + "XAADestroyInfoRec", + "XAAFillSolidRects", + "XAAHelpPatternROP", + "XAAHelpSolidROP", + "XAAInit", + "XAAScreenIndex", + NULL +}; + +static const char *shadowSymbols[] = { + "ShadowFBInit", + NULL +}; + +static const char *int10Symbols[] = { + "xf86ExecX86int10", +#if 0 + "xf86FreeInt10", +#endif + "xf86InitInt10", + "xf86Int10AllocPages", + "xf86Int10FreePages", + "xf86int10Addr", + NULL +}; + +static const char *fbSymbols[] = { + "fbPictureInit", + "fbScreenInit", + NULL +}; + +#ifdef XFree86LOADER + +static MODULESETUPPROTO(SavageSetup); + +static XF86ModuleVersionInfo SavageVersRec = { + "savage", + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XF86_VERSION_CURRENT, + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, + ABI_CLASS_VIDEODRV, + ABI_VIDEODRV_VERSION, + MOD_CLASS_VIDEODRV, + {0, 0, 0, 0} +}; + +XF86ModuleData savageModuleData = { &SavageVersRec, SavageSetup, NULL }; + +static pointer SavageSetup(pointer module, pointer opts, int *errmaj, + int *errmin) +{ + static Bool setupDone = FALSE; + + if (!setupDone) { + setupDone = TRUE; + xf86AddDriver(&SAVAGE, module, 0); + LoaderRefSymLists(vgaHWSymbols, fbSymbols, ramdacSymbols, + xaaSymbols, shadowSymbols, vbeSymbols, vbeOptSymbols, + int10Symbols, i2cSymbols, ddcSymbols, NULL); + return (pointer) 1; + } else { + if (errmaj) + *errmaj = LDR_ONCEONLY; + return NULL; + } +} + +#endif /* XFree86LOADER */ + + +/* + * I'd rather have these wait macros be inline, but S3 has made it + * darned near impossible. The bit fields are in a different place in + * all three families, the status register has a different address in the + * three families, and even the idle vs busy sense flipped in the Sav2K. + */ + +static void +ResetBCI2K( SavagePtr psav ) +{ + CARD32 cob = INREG( 0x48c18 ); + /* if BCI is enabled and BCI is busy... */ + + if( + (cob & 0x00000008) && + ! (ALT_STATUS_WORD0 & 0x00200000) + ) + { + ErrorF( "Resetting BCI, stat = %08x...\n", ALT_STATUS_WORD0); + /* Turn off BCI */ + OUTREG( 0x48c18, cob & ~8 ); + usleep(10000); + /* Turn it back on */ + OUTREG( 0x48c18, cob ); + usleep(10000); + } +} + +static Bool +ShadowWait( SavagePtr psav ) +{ + BCI_GET_PTR; + int loop = 0; + + if( !psav->NoPCIRetry ) + return 0; + + psav->ShadowCounter = (psav->ShadowCounter + 1) & 0x7fff; + BCI_SEND( psav->dwBCIWait2DIdle ); + BCI_SEND( 0x98000000 + psav->ShadowCounter ); + + while( + (psav->ShadowVirtual[1] & 0x7fff) != psav->ShadowCounter && + (loop++ < MAXLOOP) + ) + ; + + return loop >= MAXLOOP; +} + +static Bool +ShadowWait1( SavagePtr psav, int v ) +{ + return ShadowWait( psav ); +} + + +/* Wait until "v" queue entries are free */ + +static int +WaitQueue3D( SavagePtr psav, int v ) +{ + int loop = 0; + int slots = MAXFIFO - v; + + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitQueue = ShadowWait1; + return ShadowWait(psav); + } + else + { + loop &= STATUS_WORD0; + while( ((STATUS_WORD0 & 0x0000ffff) > slots) && (loop++ < MAXLOOP)) + ; + } + return loop >= MAXLOOP; +} + +static int +WaitQueue4( SavagePtr psav, int v ) +{ + int loop = 0; + int slots = MAXFIFO - v; + + if( !psav->NoPCIRetry ) + return 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitQueue = ShadowWait1; + return ShadowWait(psav); + } + else + while( ((ALT_STATUS_WORD0 & 0x001fffff) > slots) && (loop++ < MAXLOOP)) + ; + return loop >= MAXLOOP; +} + +static int +WaitQueue2K( SavagePtr psav, int v ) +{ + int loop = 0; + int slots = MAXFIFO - v; + + if( !psav->NoPCIRetry ) + return 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitQueue = ShadowWait1; + return ShadowWait(psav); + } + else + while( ((ALT_STATUS_WORD0 & 0x000fffff) > slots) && (loop++ < MAXLOOP)) + ; + if( loop >= MAXLOOP ) + ResetBCI2K(psav); + return loop >= MAXLOOP; +} + +/* Wait until GP is idle and queue is empty */ + +static int +WaitIdleEmpty3D(SavagePtr psav) +{ + int loop = 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitIdleEmpty = ShadowWait; + return ShadowWait(psav); + } + loop &= STATUS_WORD0; + while( ((STATUS_WORD0 & 0x0008ffff) != 0x80000) && (loop++ < MAXLOOP) ) + ; + return loop >= MAXLOOP; +} + +static int +WaitIdleEmpty4(SavagePtr psav) +{ + int loop = 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitIdleEmpty = ShadowWait; + return ShadowWait(psav); + } + while( ((ALT_STATUS_WORD0 & 0x00a1ffff) != 0x00a00000) && (loop++ < MAXLOOP) ) + ; + return loop >= MAXLOOP; +} + +static int +WaitIdleEmpty2K(SavagePtr psav) +{ + int loop = 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitIdleEmpty = ShadowWait; + return ShadowWait(psav); + } + loop &= ALT_STATUS_WORD0; + while( ((ALT_STATUS_WORD0 & 0x009fffff) != 0) && (loop++ < MAXLOOP) ) + ; + if( loop >= MAXLOOP ) + ResetBCI2K(psav); + return loop >= MAXLOOP; +} + +/* Wait until GP is idle */ + +static int +WaitIdle3D(SavagePtr psav) +{ + int loop = 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitIdle = ShadowWait; + return ShadowWait(psav); + } + while( (!(STATUS_WORD0 & 0x00080000)) && (loop++ < MAXLOOP) ) + ; + return loop >= MAXLOOP; +} + +static int +WaitIdle4(SavagePtr psav) +{ + int loop = 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitIdle = ShadowWait; + return ShadowWait(psav); + } + while( (!(ALT_STATUS_WORD0 & 0x00800000)) && (loop++ < MAXLOOP) ) + ; + return loop >= MAXLOOP; +} + +static int +WaitIdle2K(SavagePtr psav) +{ + int loop = 0; + mem_barrier(); + if( psav->ShadowVirtual ) + { + psav->WaitIdle = ShadowWait; + return ShadowWait(psav); + } + loop &= ALT_STATUS_WORD0; + while( (ALT_STATUS_WORD0 & 0x00900000) && (loop++ < MAXLOOP) ) + ; + return loop >= MAXLOOP; +} + + +static Bool SavageGetRec(ScrnInfoPtr pScrn) +{ + if (pScrn->driverPrivate) + return TRUE; + + pScrn->driverPrivate = xnfcalloc(sizeof(SavageRec), 1); + return TRUE; +} + + +static void SavageFreeRec(ScrnInfoPtr pScrn) +{ + TRACE(( "SavageFreeRec(%x)\n", pScrn->driverPrivate )); + if (!pScrn->driverPrivate) + return; + xfree(pScrn->driverPrivate); + pScrn->driverPrivate = NULL; + SavageUnmapMem(pScrn, 1); +} + + +static const OptionInfoRec * SavageAvailableOptions(int chipid, int busid) +{ + return SavageOptions; +} + + +static void SavageIdentify(int flags) +{ + xf86PrintChipsets("SAVAGE", + "driver (version " DRIVER_VERSION ") for S3 Savage chipsets", + SavageChips); +} + + +static Bool SavageProbe(DriverPtr drv, int flags) +{ + int i; + GDevPtr *devSections = NULL; + int *usedChips; + int numDevSections; + int numUsed; + Bool foundScreen = FALSE; + + /* sanity checks */ + if ((numDevSections = xf86MatchDevice("savage", &devSections)) <= 0) + return FALSE; + if (xf86GetPciVideoInfo() == NULL) + return FALSE; + + numUsed = xf86MatchPciInstances("SAVAGE", PCI_VENDOR_S3, + SavageChipsets, SavagePciChipsets, + devSections, numDevSections, drv, + &usedChips); + if (devSections) + xfree(devSections); + devSections = NULL; + if (numUsed <= 0) + return FALSE; + + if (flags & PROBE_DETECT) + foundScreen = TRUE; + else + for (i=0; i<numUsed; i++) { + ScrnInfoPtr pScrn = xf86AllocateScreen(drv, 0); + + pScrn->driverVersion = (int)DRIVER_VERSION; + pScrn->driverName = DRIVER_NAME; + pScrn->name = "SAVAGE"; + pScrn->Probe = SavageProbe; + pScrn->PreInit = SavagePreInit; + pScrn->ScreenInit = SavageScreenInit; + pScrn->SwitchMode = SavageSwitchMode; + pScrn->AdjustFrame = SavageAdjustFrame; + pScrn->EnterVT = SavageEnterVT; + pScrn->LeaveVT = SavageLeaveVT; + pScrn->FreeScreen = NULL; + pScrn->ValidMode = SavageValidMode; + foundScreen = TRUE; + xf86ConfigActivePciEntity(pScrn, usedChips[i], SavagePciChipsets, + NULL, NULL, NULL, NULL, NULL); + } + + xfree(usedChips); + return foundScreen; +} + +static int LookupChipID( PciChipsets* pset, int ChipID ) +{ + /* Is there a function to do this for me? */ + while( pset->numChipset >= 0 ) + { + if( pset->PCIid == ChipID ) + return pset->numChipset; + pset++; + } + + return -1; +} + +static Bool SavagePreInit(ScrnInfoPtr pScrn, int flags) +{ + EntityInfoPtr pEnt; + SavagePtr psav; + MessageType from = X_DEFAULT; + int i; + ClockRangePtr clockRanges; + char *s = NULL; + unsigned char config1, m, n, n1, n2, sr8, cr66 = 0, tmp; + int mclk; + vgaHWPtr hwp; + int vgaCRIndex, vgaCRReg; + pointer ddc; + + TRACE(("SavagePreInit(%d)\n", flags)); + + gpScrn = pScrn; + + if (flags & PROBE_DETECT) { + SavageProbeDDC( pScrn, xf86GetEntityInfo(pScrn->entityList[0])->index ); + return TRUE; + } + + if (!xf86LoadSubModule(pScrn, "vgahw")) + return FALSE; + + xf86LoaderReqSymLists(vgaHWSymbols, NULL); + if (!vgaHWGetHWRec(pScrn)) + return FALSE; + +#if 0 + /* Here we can alter the number of registers saved and restored by the + * standard vgaHWSave and Restore routines. + */ + vgaHWSetRegCounts( pScrn, VGA_NUM_CRTC, VGA_NUM_SEQ, VGA_NUM_GFX, VGA_NUM_ATTR ); +#endif + + pScrn->monitor = pScrn->confScreen->monitor; + + /* + * We support depths of 8, 15, 16 and 24. + * We support bpp of 8, 16, and 32. + */ + + if (!xf86SetDepthBpp(pScrn, 8, 8, 8, Support32bppFb)) + return FALSE; + else { + int requiredBpp; + int altBpp = 0; + + switch (pScrn->depth) { + case 8: + case 16: + requiredBpp = pScrn->depth; + break; + case 15: + requiredBpp = 16; + break; + case 24: + requiredBpp = 32; + altBpp = 24; + break; + + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Given depth (%d) is not supported by this driver\n", + pScrn->depth); + return FALSE; + } + + if( + (pScrn->bitsPerPixel != requiredBpp) && + (pScrn->bitsPerPixel != altBpp) + ) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Depth %d must specify %d bpp; %d was given\n", + pScrn->depth, requiredBpp, pScrn->bitsPerPixel ); + return FALSE; + } + } + + xf86PrintDepthBpp(pScrn); + + if (pScrn->depth > 8) { + rgb zeros = {0, 0, 0}; + + if (!xf86SetWeight(pScrn, zeros, zeros)) + return FALSE; + else { + /* TODO check weight returned is supported */ + ; + } + } + + if (!xf86SetDefaultVisual(pScrn, -1)) { + return FALSE; + } else { + /* We don't currently support DirectColor at 16bpp */ + if (pScrn->bitsPerPixel == 16 && pScrn->defaultVisual != TrueColor) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given default visual" + " (%s) is not supported at depth %d\n", + xf86GetVisualName(pScrn->defaultVisual), pScrn->depth); + return FALSE; + } + } + + pScrn->progClock = TRUE; + + if (!SavageGetRec(pScrn)) + return FALSE; + psav = SAVPTR(pScrn); + + hwp = VGAHWPTR(pScrn); + vgaHWGetIOBase(hwp); + psav->vgaIOBase = hwp->IOBase; + + xf86CollectOptions(pScrn, NULL); + + if (pScrn->depth == 8) + pScrn->rgbBits = 8/*6*/; + + if (!(psav->Options = xalloc(sizeof(SavageOptions)))) + return FALSE; + memcpy(psav->Options, SavageOptions, sizeof(SavageOptions)); + xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, psav->Options); + + xf86GetOptValBool(psav->Options, OPTION_PCI_BURST, &psav->pci_burst); + + if (psav->pci_burst) { + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "Option: pci_burst - PCI burst read enabled\n"); + } + + psav->NoPCIRetry = 1; /* default */ + if (xf86ReturnOptValBool(psav->Options, OPTION_PCI_RETRY, FALSE)) { + if (xf86ReturnOptValBool(psav->Options, OPTION_PCI_BURST, FALSE)) { + psav->NoPCIRetry = 0; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Option: pci_retry\n"); + } else + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "\"pci_retry\" option requires \"pci_burst\"\n"); + } + + xf86GetOptValBool( psav->Options, OPTION_SHADOW_FB, &psav->shadowFB ); + if (psav->shadowFB) { + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Option: shadow FB enabled\n"); + } + + if ((s = xf86GetOptValString(psav->Options, OPTION_ROTATE))) { + if(!xf86NameCmp(s, "CW")) { + /* accel is disabled below for shadowFB */ + psav->shadowFB = TRUE; + psav->rotate = 1; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "Rotating screen clockwise - acceleration disabled\n"); + } else if(!xf86NameCmp(s, "CCW")) { + psav->shadowFB = TRUE; + psav->rotate = -1; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Rotating screen" + "counter clockwise - acceleration disabled\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "\"%s\" is not a valid" + "value for Option \"Rotate\"\n", s); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Valid options are \"CW\" or \"CCW\"\n"); + } + } + + if (xf86GetOptValBool(psav->Options, OPTION_NOACCEL, &psav->NoAccel)) + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "Option: NoAccel - Acceleration Disabled\n"); + + if (psav->shadowFB && !psav->NoAccel) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "HW acceleration not supported with \"shadowFB\".\n"); + psav->NoAccel = TRUE; + } + + if (pScrn->bitsPerPixel == 24 && !psav->NoAccel) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "HW acceleration not possible with depth 32 and bpp 24.\n"); + psav->NoAccel = TRUE; + } + + + /* + * The SWCursor setting takes priority over HWCursor. The default + * if neither is specified is HW, unless ShadowFB is specified, + * then SW. + */ + + from = X_DEFAULT; + psav->hwcursor = psav->shadowFB ? FALSE : TRUE; + if (xf86GetOptValBool(psav->Options, OPTION_HWCURSOR, &psav->hwcursor)) + from = X_CONFIG; + if (xf86ReturnOptValBool(psav->Options, OPTION_SWCURSOR, FALSE)) { + psav->hwcursor = FALSE; + from = X_CONFIG; + } + xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n", + psav->hwcursor ? "HW" : "SW"); + + from = X_DEFAULT; + psav->UseBIOS = TRUE; + if (xf86GetOptValBool(psav->Options, OPTION_USEBIOS, &psav->UseBIOS) ) + from = X_CONFIG; + xf86DrvMsg(pScrn->scrnIndex, from, "%ssing video BIOS to set modes\n", + psav->UseBIOS ? "U" : "Not u" ); + + psav->LCDClock = 0.0; + if( xf86GetOptValFreq( psav->Options, OPTION_LCDCLOCK, OPTUNITS_MHZ, &psav->LCDClock ) ) + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "Option: LCDClock %1.2f MHz\n", psav->LCDClock ); + + if( xf86GetOptValBool( psav->Options, OPTION_SHADOW_STATUS, &psav->ShadowStatus)) + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "Option: ShadowStatus enabled\n" ); + + + if( xf86GetOptValBool( psav->Options, OPTION_CRT_ONLY, &psav->CrtOnly)) + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "Option: CrtOnly enabled\n" ); + + if( xf86GetOptValBool( psav->Options, OPTION_TV_ON, &psav->TvOn)) { + psav->PAL = FALSE; + SavageGetTvMaxSize(psav); + } + + if( xf86GetOptValBool( psav->Options, OPTION_TV_PAL, &psav->PAL)) { + SavageGetTvMaxSize(psav); + psav->TvOn = TRUE; + } + + if( psav->TvOn ) + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "TV enabled in %s format\n", + psav->PAL ? "PAL" : "NTSC" ); + + psav->ForceInit = 0; + if( xf86GetOptValBool( psav->Options, OPTION_FORCE_INIT, &psav->ForceInit)) + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "Option: ForceInit enabled\n" ); + + /* Add more options here. */ + + if (pScrn->numEntities > 1) { + SavageFreeRec(pScrn); + return FALSE; + } + + pEnt = xf86GetEntityInfo(pScrn->entityList[0]); + if (pEnt->resources) { + xfree(pEnt); + SavageFreeRec(pScrn); + return FALSE; + } + psav->EntityIndex = pEnt->index; + + if (xf86LoadSubModule(pScrn, "int10")) { + xf86LoaderReqSymLists(int10Symbols, NULL); + psav->pInt10 = xf86InitInt10(pEnt->index); + } + + if (xf86LoadSubModule(pScrn, "vbe")) { + xf86LoaderReqSymLists(vbeSymbols, NULL); + psav->pVbe = VBEInit(psav->pInt10, pEnt->index); + } + + + psav->PciInfo = xf86GetPciInfoForEntity(pEnt->index); + xf86RegisterResources(pEnt->index, NULL, ResNone); + xf86SetOperatingState(resVgaIo, pEnt->index, ResUnusedOpr); + xf86SetOperatingState(resVgaMem, pEnt->index, ResDisableOpr); + + from = X_DEFAULT; + if (pEnt->device->chipset && *pEnt->device->chipset) { + pScrn->chipset = pEnt->device->chipset; + psav->ChipId = pEnt->device->chipID; + psav->Chipset = xf86StringToToken(SavageChipsets, pScrn->chipset); + from = X_CONFIG; + } else if (pEnt->device->chipID >= 0) { + psav->ChipId = pEnt->device->chipID; + psav->Chipset = LookupChipID(SavagePciChipsets, psav->ChipId); + pScrn->chipset = (char *)xf86TokenToString(SavageChipsets, + psav->Chipset); + from = X_CONFIG; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipID override: 0x%04X\n", + pEnt->device->chipID); + } else { + from = X_PROBED; + psav->ChipId = psav->PciInfo->chipType; + psav->Chipset = LookupChipID(SavagePciChipsets, psav->ChipId); + pScrn->chipset = (char *)xf86TokenToString(SavageChipsets, + psav->Chipset); + } + + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Chip: id %04x, \"%s\"\n", + psav->ChipId, xf86TokenToString( SavageChips, psav->ChipId ) ); + + if (pEnt->device->chipRev >= 0) { + psav->ChipRev = pEnt->device->chipRev; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n", + psav->ChipRev); + } else + psav->ChipRev = psav->PciInfo->chipRev; + + if (pEnt->device->videoRam != 0) + pScrn->videoRam = pEnt->device->videoRam; + + xfree(pEnt); + + /* maybe throw in some more sanity checks here */ + + xf86DrvMsg(pScrn->scrnIndex, from, "Engine: \"%s\"\n", pScrn->chipset); + + psav->PciTag = pciTag(psav->PciInfo->bus, psav->PciInfo->device, + psav->PciInfo->func); + + + if (!SavageMapMMIO(pScrn)) { + vbeFree(psav->pVbe); + return FALSE; + } + + vgaCRIndex = psav->vgaIOBase + 4; + vgaCRReg = psav->vgaIOBase + 5; + + xf86EnableIO(); + /* unprotect CRTC[0-7] */ + VGAOUT8(vgaCRIndex, 0x11); + tmp = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, tmp & 0x7f); + + /* unlock extended regs */ + VGAOUT16(vgaCRIndex, 0x4838); + VGAOUT16(vgaCRIndex, 0xa039); + VGAOUT16(0x3c4, 0x0608); + + VGAOUT8(vgaCRIndex, 0x40); + tmp = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, tmp & ~0x01); + + /* unlock sys regs */ + VGAOUT8(vgaCRIndex, 0x38); + VGAOUT8(vgaCRReg, 0x48); + + { + Gamma zeros = {0.0, 0.0, 0.0}; + + if (!xf86SetGamma(pScrn, zeros)) { + vbeFree(psav->pVbe); + return FALSE; + } + } + + /* Unlock system registers. */ + VGAOUT16(vgaCRIndex, 0x4838); + + /* Next go on to detect amount of installed ram */ + + VGAOUT8(vgaCRIndex, 0x36); /* for register CR36 (CONFG_REG1), */ + config1 = VGAIN8(vgaCRReg); /* get amount of vram installed */ + + /* Compute the amount of video memory and offscreen memory. */ + + if (!pScrn->videoRam) { + static unsigned char RamSavage3D[] = { 8, 4, 4, 2 }; + static unsigned char RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 }; + static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 }; + static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 16, 2 }; + + switch( psav->Chipset ) { + case S3_SAVAGE3D: + pScrn->videoRam = RamSavage3D[ (config1 & 0xC0) >> 6 ] * 1024; + break; + + case S3_SAVAGE4: + /* + * The Savage4 has one ugly special case to consider. On + * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB + * when it really means 8MB. Why do it the same when you + * can do it different... + */ + VGAOUT8(vgaCRIndex, 0x68); /* memory control 1 */ + if( (VGAIN8(vgaCRReg) & 0xC0) == (0x01 << 6) ) + RamSavage4[1] = 8; + + /*FALLTHROUGH*/ + + case S3_SAVAGE2000: + pScrn->videoRam = RamSavage4[ (config1 & 0xE0) >> 5 ] * 1024; + break; + + case S3_SAVAGE_MX: + case S3_SUPERSAVAGE: + pScrn->videoRam = RamSavageMX[ (config1 & 0x0E) >> 1 ] * 1024; + break; + + case S3_PROSAVAGE: + pScrn->videoRam = RamSavageNB[ (config1 & 0xE0) >> 5 ] * 1024; + break; + + default: + /* How did we get here? */ + pScrn->videoRam = 0; + break; + } + + psav->videoRambytes = pScrn->videoRam * 1024; + + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "probed videoram: %dk\n", + pScrn->videoRam); + } else { + psav->videoRambytes = pScrn->videoRam * 1024; + + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "videoram = %dk\n", + pScrn->videoRam); + } + + if( !pScrn->videoRam && psav->pVbe ) + { + /* If VBE is available, ask it about onboard memory. */ + + VbeInfoBlock* vib; + + vib = VBEGetVBEInfo( psav->pVbe ); + pScrn->videoRam = vib->TotalMemory * 64; + VBEFreeVBEInfo( vib ); + + /* VBE often cuts 64k off of the RAM total. */ + + if( pScrn->videoRam & 64 ) + pScrn->videoRam += 64; + + psav->videoRambytes = pScrn->videoRam * 1024; + } + + /* + * If we're running with acceleration, compute the command overflow + * buffer location. The command overflow buffer must END at a + * 4MB boundary; for all practical purposes, that means the very + * end of the frame buffer. + */ + + if( psav->NoAccel ) { + psav->CursorKByte = pScrn->videoRam - 4; + psav->cobIndex = 0; + psav->cobSize = 0; + psav->cobOffset = psav->videoRambytes; + } + else if( (S3_SAVAGE4_SERIES(psav->Chipset)) || + (S3_SUPERSAVAGE == psav->Chipset) ) { + /* + * The Savage4 and ProSavage have COB coherency bugs which render + * the buffer useless. COB seems to make the SuperSavage slower. + * We disable it. + */ + psav->CursorKByte = pScrn->videoRam - 4; + psav->cobIndex = 2; + psav->cobSize = 0x8000 << psav->cobIndex; + psav->cobOffset = psav->videoRambytes; + } + else + { + /* We use 128kB for the COB on all other chips. */ + + psav->cobSize = 1 << 17; + if (psav->Chipset == S3_SUPERSAVAGE) { + psav->cobIndex = 2; + } + else { + psav->cobIndex = 7; + } + psav->cobOffset = psav->videoRambytes - psav->cobSize; + } + + /* + * We place the cursor in high memory, just before the command overflow + * buffer. The cursor must be aligned on a 4k boundary. + */ + + psav->CursorKByte = (psav->cobOffset >> 10) - 4; + + /* reset graphics engine to avoid memory corruption */ + VGAOUT8(vgaCRIndex, 0x66); + cr66 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr66 | 0x02); + usleep(10000); + + VGAOUT8(vgaCRIndex, 0x66); + VGAOUT8(vgaCRReg, cr66 & ~0x02); /* clear reset flag */ + usleep(10000); + + /* Set status word positions based on chip type. */ + + switch( psav->Chipset ) { + case S3_SAVAGE3D: + case S3_SAVAGE_MX: + psav->WaitQueue = WaitQueue3D; + psav->WaitIdle = WaitIdle3D; + psav->WaitIdleEmpty = WaitIdleEmpty3D; + break; + + case S3_SAVAGE4: + case S3_PROSAVAGE: + case S3_SUPERSAVAGE: + psav->WaitQueue = WaitQueue4; + psav->WaitIdle = WaitIdle4; + psav->WaitIdleEmpty = WaitIdleEmpty4; + break; + + case S3_SAVAGE2000: + psav->WaitQueue = WaitQueue2K; + psav->WaitIdle = WaitIdle2K; + psav->WaitIdleEmpty = WaitIdleEmpty2K; + break; + } + + /* Do the DDC dance. */ + + if( psav->Chipset != S3_PROSAVAGE ) { + ddc = xf86LoadSubModule(pScrn, "ddc"); + if (ddc) { +#if 0 + xf86MonPtr pMon = NULL; +#endif + + xf86LoaderReqSymLists(ddcSymbols, NULL); +#if 0 +/* + * On many machines, the attempt to read DDC information via VBE puts the + * BIOS access into a state which prevents me from reading mode information. + * This is a complete mystery to me. + */ + if ((psav->pVbe) + && ((pMon = xf86PrintEDID(vbeDoEDID(psav->pVbe, ddc))) != NULL)) + xf86SetDDCproperties(pScrn,pMon); + else +#endif + if (!SavageDDC1(pScrn->scrnIndex)) { + if ( xf86LoadSubModule(pScrn, "i2c") ) { + xf86LoaderReqSymLists(i2cSymbols,NULL); + if (SavageI2CInit(pScrn)) { + unsigned char tmp; + + InI2CREG(psav,tmp); + OutI2CREG(psav,tmp | 0x13); + xf86SetDDCproperties(pScrn,xf86PrintEDID( + xf86DoEDID_DDC2(pScrn->scrnIndex,psav->I2C))); + OutI2CREG(psav,tmp); + } + } + } + } + } + + /* Savage ramdac speeds */ + pScrn->numClocks = 4; + pScrn->clock[0] = 250000; + pScrn->clock[1] = 250000; + pScrn->clock[2] = 220000; + pScrn->clock[3] = 220000; + + if (psav->dacSpeedBpp <= 0) { + if (pScrn->bitsPerPixel > 24) + psav->dacSpeedBpp = pScrn->clock[3]; + else if (pScrn->bitsPerPixel >= 24) + psav->dacSpeedBpp = pScrn->clock[2]; + else if ((pScrn->bitsPerPixel > 8) && (pScrn->bitsPerPixel < 24)) + psav->dacSpeedBpp = pScrn->clock[1]; + else if (pScrn->bitsPerPixel <= 8) + psav->dacSpeedBpp = pScrn->clock[0]; + } + + /* Set ramdac limits */ + psav->maxClock = psav->dacSpeedBpp; + + /* detect current mclk */ + VGAOUT8(0x3c4, 0x08); + sr8 = VGAIN8(0x3c5); + VGAOUT8(0x3c5, 0x06); + VGAOUT8(0x3c4, 0x10); + n = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x11); + m = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x08); + VGAOUT8(0x3c5, sr8); + m &= 0x7f; + n1 = n & 0x1f; + n2 = (n >> 5) & 0x03; + mclk = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100; + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected current MCLK value of %1.3f MHz\n", + mclk / 1000.0); + + psav->minClock = 10000; + + pScrn->maxHValue = 2048 << 3; /* 11 bits of h_total 8-pixel units */ + pScrn->maxVValue = 2048; /* 11 bits of v_total */ + pScrn->virtualX = pScrn->display->virtualX; + pScrn->virtualY = pScrn->display->virtualY; + + /* Check LCD panel information */ + + if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) && !psav->CrtOnly ) + { + unsigned char cr6b = hwp->readCrtc( hwp, 0x6b ); + + int panelX = (hwp->readSeq(hwp, 0x61) + + ((hwp->readSeq(hwp, 0x66) & 0x02) << 7) + 1) * 8; + int panelY = hwp->readSeq(hwp, 0x69) + + ((hwp->readSeq(hwp, 0x6e) & 0x70) << 4) + 1; + + char * sTechnology = "Unknown"; + + /* OK, I admit it. I don't know how to limit the max dot clock + * for LCD panels of various sizes. I thought I copied the formula + * from the BIOS, but many users have informed me of my folly. + * + * Instead, I'll abandon any attempt to automatically limit the + * clock, and add an LCDClock option to XF86Config. Some day, + * I should come back to this. + */ + + enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */ + ActiveCRT = 0x01, + ActiveLCD = 0x02, + ActiveTV = 0x04, + ActiveCRT2 = 0x20, + ActiveDUO = 0x80 + }; + + if( (hwp->readSeq( hwp, 0x39 ) & 0x03) == 0 ) + { + sTechnology = "TFT"; + } + else if( (hwp->readSeq( hwp, 0x30 ) & 0x01) == 0 ) + { + sTechnology = "DSTN"; + } + else + { + sTechnology = "STN"; + } + + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "%dx%d %s LCD panel detected %s\n", + panelX, panelY, sTechnology, + cr6b & ActiveLCD ? "and active" : "but not active"); + + if( cr6b & ActiveLCD ) { + /* If the LCD is active and panel expansion is enabled, */ + /* we probably want to kill the HW cursor. */ + + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "- Limiting video mode to %dx%d\n", + panelX, panelY ); + + psav->PanelX = panelX; + psav->PanelY = panelY; + + if( psav->LCDClock > 0.0 ) + { + psav->maxClock = psav->LCDClock * 1000.0; + xf86DrvMsg( pScrn->scrnIndex, X_CONFIG, + "- Limiting dot clock to %1.2f MHz\n", + psav->LCDClock ); + } + } + } + + clockRanges = xnfcalloc(sizeof(ClockRange),1); + clockRanges->next = NULL; + clockRanges->minClock = psav->minClock; + clockRanges->maxClock = psav->maxClock; + clockRanges->clockIndex = -1; + clockRanges->interlaceAllowed = TRUE; + clockRanges->doubleScanAllowed = TRUE; + clockRanges->ClockDivFactor = 1.0; + clockRanges->ClockMulFactor = 1.0; + + i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, clockRanges, NULL, + 256, 2048, 16 * pScrn->bitsPerPixel, + 128, 2048, + pScrn->virtualX, pScrn->virtualY, + psav->videoRambytes, LOOKUP_BEST_REFRESH); + + if (i == -1) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "xf86ValidateModes failure\n"); + SavageFreeRec(pScrn); + vbeFree(psav->pVbe); + return FALSE; + } + + xf86PruneDriverModes(pScrn); + + if (i == 0 || pScrn->modes == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); + SavageFreeRec(pScrn); + vbeFree(psav->pVbe); + return FALSE; + } + + if( psav->UseBIOS ) + { + /* Go probe the BIOS for all the modes and refreshes at this depth. */ + + if( psav->ModeTable ) + { + SavageFreeBIOSModeTable( psav, &psav->ModeTable ); + } + + psav->ModeTable = SavageGetBIOSModeTable( psav, pScrn->bitsPerPixel ); + + if( !psav->ModeTable || !psav->ModeTable->NumModes ) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to fetch any BIOS modes. Disabling BIOS.\n"); + psav->UseBIOS = FALSE; + } + else + /*if( xf86Verbose )*/ + { + SavageModeEntryPtr pmt; + + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "Found %d modes at this depth:\n", + psav->ModeTable->NumModes); + + for( + i = 0, pmt = psav->ModeTable->Modes; + i < psav->ModeTable->NumModes; + i++, pmt++ ) + { + int j; + ErrorF( " [%03x] %d x %d", + pmt->VesaMode, pmt->Width, pmt->Height ); + for( j = 0; j < pmt->RefreshCount; j++ ) + { + ErrorF( ", %dHz", pmt->RefreshRate[j] ); + } + ErrorF( "\n"); + } + } + } + + xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V); + pScrn->currentMode = pScrn->modes; + xf86PrintModes(pScrn); + xf86SetDpi(pScrn, 0, 0); + + if (xf86LoadSubModule(pScrn, "fb") == NULL) { + SavageFreeRec(pScrn); + vbeFree(psav->pVbe); + return FALSE; + } + + xf86LoaderReqSymLists(fbSymbols, NULL); + + if( !psav->NoAccel ) { + if( !xf86LoadSubModule(pScrn, "xaa") ) { + SavageFreeRec(pScrn); + vbeFree(psav->pVbe); + return FALSE; + } + xf86LoaderReqSymLists(xaaSymbols, NULL ); + } + + if (psav->hwcursor) { + if (!xf86LoadSubModule(pScrn, "ramdac")) { + SavageFreeRec(pScrn); + vbeFree(psav->pVbe); + return FALSE; + } + xf86LoaderReqSymLists(ramdacSymbols, NULL); + } + + if (psav->shadowFB) { + if (!xf86LoadSubModule(pScrn, "shadowfb")) { + SavageFreeRec(pScrn); + vbeFree(psav->pVbe); + return FALSE; + } + xf86LoaderReqSymLists(shadowSymbols, NULL); + } + vbeFree(psav->pVbe); + + return TRUE; +} + + +static Bool SavageEnterVT(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + TRACE(("SavageEnterVT(%d)\n", flags)); + + gpScrn = pScrn; + SavageEnableMMIO(pScrn); + SavageSave(pScrn); + return SavageModeInit(pScrn, pScrn->currentMode); +} + + +static void SavageLeaveVT(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + vgaRegPtr vgaSavePtr = &hwp->SavedReg; + SavageRegPtr SavageSavePtr = &psav->SavedReg; + + TRACE(("SavageLeaveVT(%d)\n", flags)); + gpScrn = pScrn; + SavageWriteMode(pScrn, vgaSavePtr, SavageSavePtr, FALSE); + SavageDisableMMIO(pScrn); +} + + +static void SavageSave(ScrnInfoPtr pScrn) +{ + unsigned char cr3a, cr53, cr66; + vgaHWPtr hwp = VGAHWPTR(pScrn); + vgaRegPtr vgaSavePtr = &hwp->SavedReg; + SavagePtr psav = SAVPTR(pScrn); + SavageRegPtr save = &psav->SavedReg; + unsigned short vgaCRReg = psav->vgaIOBase + 5; + unsigned short vgaCRIndex = psav->vgaIOBase + 4; + + TRACE(("SavageSave()\n")); + + VGAOUT16(vgaCRIndex, 0x4838); + VGAOUT16(vgaCRIndex, 0xa039); + VGAOUT16(0x3c4, 0x0608); + + VGAOUT8(vgaCRIndex, 0x66); + cr66 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr66 | 0x80); + VGAOUT8(vgaCRIndex, 0x3a); + cr3a = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr3a | 0x80); + VGAOUT8(vgaCRIndex, 0x53); + cr53 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr53 & 0x7f); + + if (xf86IsPrimaryPci(psav->PciInfo)) + vgaHWSave(pScrn, vgaSavePtr, VGA_SR_ALL); + else + vgaHWSave(pScrn, vgaSavePtr, VGA_SR_MODE); + + VGAOUT8(vgaCRIndex, 0x66); + VGAOUT8(vgaCRReg, cr66); + VGAOUT8(vgaCRIndex, 0x3a); + VGAOUT8(vgaCRReg, cr3a); + + VGAOUT8(vgaCRIndex, 0x66); + VGAOUT8(vgaCRReg, cr66); + VGAOUT8(vgaCRIndex, 0x3a); + VGAOUT8(vgaCRReg, cr3a); + + /* unlock extended seq regs */ + VGAOUT8(0x3c4, 0x08); + save->SR08 = VGAIN8(0x3c5); + VGAOUT8(0x3c5, 0x06); + + /* now save all the extended regs we need */ + VGAOUT8(vgaCRIndex, 0x31); + save->CR31 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x32); + save->CR32 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x34); + save->CR34 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x36); + save->CR36 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x3a); + save->CR3A = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x40); + save->CR40 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x42); + save->CR42 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x45); + save->CR45 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x50); + save->CR50 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x51); + save->CR51 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x53); + save->CR53 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x58); + save->CR58 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x60); + save->CR60 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x66); + save->CR66 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x67); + save->CR67 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x68); + save->CR68 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x69); + save->CR69 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x6f); + save->CR6F = VGAIN8(vgaCRReg); + + VGAOUT8(vgaCRIndex, 0x33); + save->CR33 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x86); + save->CR86 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x88); + save->CR88 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x90); + save->CR90 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x91); + save->CR91 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0xb0); + save->CRB0 = VGAIN8(vgaCRReg) | 0x80; + + /* extended mode timing regs */ + VGAOUT8(vgaCRIndex, 0x3b); + save->CR3B = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x3c); + save->CR3C = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x43); + save->CR43 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x5d); + save->CR5D = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x5e); + save->CR5E = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x65); + save->CR65 = VGAIN8(vgaCRReg); + + /* save seq extended regs for DCLK PLL programming */ + VGAOUT8(0x3c4, 0x0e); + save->SR0E = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x0f); + save->SR0F = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x10); + save->SR10 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x11); + save->SR11 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x12); + save->SR12 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x13); + save->SR13 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x29); + save->SR29 = VGAIN8(0x3c5); + + VGAOUT8(0x3c4, 0x15); + save->SR15 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x30); + save->SR30 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x18); + save->SR18 = VGAIN8(0x3c5); + VGAOUT8(0x3c4, 0x1b); + save->SR1B = VGAIN8(0x3c5); + + /* Save flat panel expansion regsters. */ + + if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) { + int i; + for( i = 0; i < 8; i++ ) { + VGAOUT8(0x3c4, 0x54+i); + save->SR54[i] = VGAIN8(0x3c5); + } + } + + VGAOUT8(vgaCRIndex, 0x66); + cr66 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr66 | 0x80); + VGAOUT8(vgaCRIndex, 0x3a); + cr3a = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr3a | 0x80); + + /* now save MIU regs */ + if( ! S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) { + save->MMPR0 = INREG(FIFO_CONTROL_REG); + save->MMPR1 = INREG(MIU_CONTROL_REG); + save->MMPR2 = INREG(STREAMS_TIMEOUT_REG); + save->MMPR3 = INREG(MISC_TIMEOUT_REG); + } + + VGAOUT8(vgaCRIndex, 0x3a); + VGAOUT8(vgaCRReg, cr3a); + VGAOUT8(vgaCRIndex, 0x66); + VGAOUT8(vgaCRReg, cr66); + + if (!psav->ModeStructInit) { + vgaHWCopyReg(&hwp->ModeReg, vgaSavePtr); + memcpy(&psav->ModeReg, save, sizeof(SavageRegRec)); + psav->ModeStructInit = TRUE; + } + +#if 0 + if (xf86GetVerbosity() > 1) + SavagePrintRegs(pScrn); +#endif + + return; +} + + +static void SavageWriteMode(ScrnInfoPtr pScrn, vgaRegPtr vgaSavePtr, + SavageRegPtr restore, Bool Entering) +{ + unsigned char tmp, cr3a, cr66, cr67; + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + int vgaCRIndex, vgaCRReg, vgaIOBase; + + + vgaIOBase = hwp->IOBase; + vgaCRIndex = vgaIOBase + 4; + vgaCRReg = vgaIOBase + 5; + + TRACE(("SavageWriteMode(%x)\n", restore->mode)); + + if( Entering && + (!S3_SAVAGE_MOBILE_SERIES(psav->Chipset) || (psav->ForceInit)) + ) + SavageInitialize2DEngine(pScrn); + + /* + * If we figured out a VESA mode number for this timing, just use + * the S3 BIOS to do the switching, with a few additional tweaks. + */ + + if( psav->UseBIOS && restore->mode > 0x13 ) + { + int width; + unsigned short cr6d; + unsigned short cr79 = 0; + + /* Set up the mode. Don't clear video RAM. */ + SavageSetVESAMode( psav, restore->mode | 0x8000, restore->refresh ); + + /* Restore the DAC. */ + vgaHWRestore(pScrn, vgaSavePtr, VGA_SR_CMAP); + + /* Unlock the extended registers. */ + +#if 0 + /* Which way is better? */ + hwp->writeCrtc( hwp, 0x38, 0x48 ); + hwp->writeCrtc( hwp, 0x39, 0xa0 ); + hwp->writeSeq( hwp, 0x08, 0x06 ); +#endif + + VGAOUT16(vgaCRIndex, 0x4838); + VGAOUT16(vgaCRIndex, 0xA039); + VGAOUT16(0x3c4, 0x0608); + + /* Enable linear addressing. */ + + VGAOUT16(vgaCRIndex, 0x1358); + + /* Disable old MMIO. */ + + VGAOUT8(vgaCRIndex, 0x53); + VGAOUT8(vgaCRReg, VGAIN8(vgaCRReg) & ~0x10); + + /* Set the color mode. */ + + VGAOUT8(vgaCRIndex, 0x67); + VGAOUT8(vgaCRReg, restore->CR67); + + /* Enable gamma correction. */ + + VGAOUT8(0x3c4, 0x1b); + if( (pScrn->bitsPerPixel == 32) && !psav->DGAactive ) + VGAOUT8(0x3c5, 0x28 ); + else + VGAOUT8(0x3c5, 0x00 ); + + /* We may need TV/panel fixups here. See s3bios.c line 2904. */ + + /* Set FIFO fetch delay. */ + VGAOUT8(vgaCRIndex, 0x85); + VGAOUT8(vgaCRReg, (VGAIN8(vgaCRReg) & 0xf8) | 0x03); + + /* Patch CR79. These values are magical. */ + + if( !S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) + { + VGAOUT8(vgaCRIndex, 0x6d); + cr6d = VGAIN8(vgaCRReg); + + cr79 = 0x04; + + if( pScrn->displayWidth >= 1024 ) + { + if(pScrn->bitsPerPixel == 32 ) + { + if( restore->refresh >= 130 ) + cr79 = 0x03; + else if( pScrn->displayWidth >= 1280 ) + cr79 = 0x02; + else if( + (pScrn->displayWidth == 1024) && + (restore->refresh >= 75) + ) + { + if( cr6d && LCD_ACTIVE ) + cr79 = 0x05; + else + cr79 = 0x08; + } + } + else if( pScrn->bitsPerPixel == 16) + { + +/* The windows driver uses 0x13 for 16-bit 130Hz, but I see terrible + * screen artifacts with that value. Let's keep it low for now. + * if( restore->refresh >= 130 ) + * cr79 = 0x13; + * else + */ + if( pScrn->displayWidth == 1024 ) + { + if( cr6d && LCD_ACTIVE ) + cr79 = 0x08; + else + cr79 = 0x0e; + } + } + } + } + + if( (psav->Chipset != S3_SAVAGE2000) && + !S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) + VGAOUT16(vgaCRIndex, (cr79 << 8) | 0x79); + + /* Make sure 16-bit memory access is enabled. */ + + VGAOUT16(vgaCRIndex, 0x0c31); + + /* Enable the graphics engine. */ + + VGAOUT16(vgaCRIndex, 0x0140); + + /* Handle the pitch. */ + + VGAOUT8(vgaCRIndex, 0x50); + VGAOUT8(vgaCRReg, VGAIN8(vgaCRReg) | 0xC1); + + width = (pScrn->displayWidth * (pScrn->bitsPerPixel / 8)) >> 3; + VGAOUT16(vgaCRIndex, ((width & 0xff) << 8) | 0x13 ); + VGAOUT16(vgaCRIndex, ((width & 0x300) << 4) | 0x51 ); + + /* Some non-S3 BIOSes enable block write even on non-SGRAM devices. */ + + switch( psav->Chipset ) + { + case S3_SAVAGE2000: + VGAOUT8(vgaCRIndex, 0x73); + VGAOUT8(vgaCRReg, VGAIN8(vgaCRReg) & 0xdf ); + break; + + case S3_SAVAGE3D: + case S3_SAVAGE4: + VGAOUT8(vgaCRIndex, 0x68); + if( !(VGAIN8(vgaCRReg) & 0x80) ) + { + /* Not SGRAM; disable block write. */ + VGAOUT8(vgaCRIndex, 0x88); + VGAOUT8(vgaCRReg, VGAIN8(vgaCRReg) | 0x10); + } + break; + } + + SavageInitialize2DEngine(pScrn); + SavageSetGBD(pScrn); + + VGAOUT16(vgaCRIndex, 0x0140); + + SavageSetGBD(pScrn); + + return; + } + + VGAOUT8(0x3c2, 0x23); + VGAOUT16(vgaCRIndex, 0x4838); + VGAOUT16(vgaCRIndex, 0xa039); + VGAOUT16(0x3c4, 0x0608); + + vgaHWProtect(pScrn, TRUE); + + /* will we be reenabling STREAMS for the new mode? */ + psav->STREAMSRunning = 0; + + /* reset GE to make sure nothing is going on */ + VGAOUT8(vgaCRIndex, 0x66); + if(VGAIN8(vgaCRReg) & 0x01) + SavageGEReset(pScrn,0,__LINE__,__FILE__); + + /* + * Some Savage/MX and /IX systems go nuts when trying to exit the + * server after WindowMaker has displayed a gradient background. I + * haven't been able to find what causes it, but a non-destructive + * switch to mode 3 here seems to eliminate the issue. + */ + + if( ((restore->CR31 & 0x0a) == 0) && psav->pInt10 ) { + SavageSetTextMode( psav ); + } + + VGAOUT8(vgaCRIndex, 0x67); + cr67 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, restore->CR67 & ~0x0c); /* no STREAMS yet */ + + /* restore extended regs */ + VGAOUT8(vgaCRIndex, 0x66); + VGAOUT8(vgaCRReg, restore->CR66); + VGAOUT8(vgaCRIndex, 0x3a); + VGAOUT8(vgaCRReg, restore->CR3A); + VGAOUT8(vgaCRIndex, 0x31); + VGAOUT8(vgaCRReg, restore->CR31); + VGAOUT8(vgaCRIndex, 0x32); + VGAOUT8(vgaCRReg, restore->CR32); + VGAOUT8(vgaCRIndex, 0x58); + VGAOUT8(vgaCRReg, restore->CR58); + VGAOUT8(vgaCRIndex, 0x53); + VGAOUT8(vgaCRReg, restore->CR53 & 0x7f); + + VGAOUT16(0x3c4, 0x0608); + + /* Restore DCLK registers. */ + + VGAOUT8(0x3c4, 0x0e); + VGAOUT8(0x3c5, restore->SR0E); + VGAOUT8(0x3c4, 0x0f); + VGAOUT8(0x3c5, restore->SR0F); + VGAOUT8(0x3c4, 0x29); + VGAOUT8(0x3c5, restore->SR29); + VGAOUT8(0x3c4, 0x15); + VGAOUT8(0x3c5, restore->SR15); + + /* Restore flat panel expansion regsters. */ + if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) { + int i; + for( i = 0; i < 8; i++ ) { + VGAOUT8(0x3c4, 0x54+i); + VGAOUT8(0x3c5, restore->SR54[i]); + } + } + + /* restore the standard vga regs */ + if (xf86IsPrimaryPci(psav->PciInfo)) + vgaHWRestore(pScrn, vgaSavePtr, VGA_SR_ALL); + else + vgaHWRestore(pScrn, vgaSavePtr, VGA_SR_MODE); + + /* extended mode timing registers */ + VGAOUT8(vgaCRIndex, 0x53); + VGAOUT8(vgaCRReg, restore->CR53); + VGAOUT8(vgaCRIndex, 0x5d); + VGAOUT8(vgaCRReg, restore->CR5D); + VGAOUT8(vgaCRIndex, 0x5e); + VGAOUT8(vgaCRReg, restore->CR5E); + VGAOUT8(vgaCRIndex, 0x3b); + VGAOUT8(vgaCRReg, restore->CR3B); + VGAOUT8(vgaCRIndex, 0x3c); + VGAOUT8(vgaCRReg, restore->CR3C); + VGAOUT8(vgaCRIndex, 0x43); + VGAOUT8(vgaCRReg, restore->CR43); + VGAOUT8(vgaCRIndex, 0x65); + VGAOUT8(vgaCRReg, restore->CR65); + + /* restore the desired video mode with cr67 */ + VGAOUT8(vgaCRIndex, 0x67); + VGAOUT8(vgaCRReg, restore->CR67 & ~0x0c); /* no STREAMS yet */ + + /* other mode timing and extended regs */ + VGAOUT8(vgaCRIndex, 0x34); + VGAOUT8(vgaCRReg, restore->CR34); + VGAOUT8(vgaCRIndex, 0x40); + VGAOUT8(vgaCRReg, restore->CR40); + VGAOUT8(vgaCRIndex, 0x42); + VGAOUT8(vgaCRReg, restore->CR42); + VGAOUT8(vgaCRIndex, 0x45); + VGAOUT8(vgaCRReg, restore->CR45); + VGAOUT8(vgaCRIndex, 0x50); + VGAOUT8(vgaCRReg, restore->CR50); + VGAOUT8(vgaCRIndex, 0x51); + VGAOUT8(vgaCRReg, restore->CR51); + + /* memory timings */ + VGAOUT8(vgaCRIndex, 0x36); + VGAOUT8(vgaCRReg, restore->CR36); + VGAOUT8(vgaCRIndex, 0x60); + VGAOUT8(vgaCRReg, restore->CR60); + VGAOUT8(vgaCRIndex, 0x68); + VGAOUT8(vgaCRReg, restore->CR68); + VGAOUT8(vgaCRIndex, 0x69); + VGAOUT8(vgaCRReg, restore->CR69); + VGAOUT8(vgaCRIndex, 0x6f); + VGAOUT8(vgaCRReg, restore->CR6F); + + VGAOUT8(vgaCRIndex, 0x33); + VGAOUT8(vgaCRReg, restore->CR33); + VGAOUT8(vgaCRIndex, 0x86); + VGAOUT8(vgaCRReg, restore->CR86); + VGAOUT8(vgaCRIndex, 0x88); + VGAOUT8(vgaCRReg, restore->CR88); + VGAOUT8(vgaCRIndex, 0x90); + VGAOUT8(vgaCRReg, restore->CR90); + VGAOUT8(vgaCRIndex, 0x91); + VGAOUT8(vgaCRReg, restore->CR91); + if( psav->Chipset == S3_SAVAGE4 ) + { + VGAOUT8(vgaCRIndex, 0xb0); + VGAOUT8(vgaCRReg, restore->CRB0); + } + + VGAOUT8(vgaCRIndex, 0x32); + VGAOUT8(vgaCRReg, restore->CR32); + + /* unlock extended seq regs */ + VGAOUT8(0x3c4, 0x08); + VGAOUT8(0x3c5, 0x06); + + /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates that + * we should leave the default SR10 and SR11 values there. + */ + if (restore->SR10 != 255) { + VGAOUT8(0x3c4, 0x10); + VGAOUT8(0x3c5, restore->SR10); + VGAOUT8(0x3c4, 0x11); + VGAOUT8(0x3c5, restore->SR11); + } + + /* restore extended seq regs for dclk */ + VGAOUT8(0x3c4, 0x0e); + VGAOUT8(0x3c5, restore->SR0E); + VGAOUT8(0x3c4, 0x0f); + VGAOUT8(0x3c5, restore->SR0F); + VGAOUT8(0x3c4, 0x12); + VGAOUT8(0x3c5, restore->SR12); + VGAOUT8(0x3c4, 0x13); + VGAOUT8(0x3c5, restore->SR13); + VGAOUT8(0x3c4, 0x29); + VGAOUT8(0x3c5, restore->SR29); + + VGAOUT8(0x3c4, 0x18); + VGAOUT8(0x3c5, restore->SR18); + VGAOUT8(0x3c4, 0x1b); + if( psav->DGAactive ) + VGAOUT8(0x3c5, restore->SR1B & ~0x28); + else + VGAOUT8(0x3c5, restore->SR1B); + + /* load new m, n pll values for dclk & mclk */ + VGAOUT8(0x3c4, 0x15); + tmp = VGAIN8(0x3c5) & ~0x21; + + VGAOUT8(0x3c5, tmp | 0x03); + VGAOUT8(0x3c5, tmp | 0x23); + VGAOUT8(0x3c5, tmp | 0x03); + VGAOUT8(0x3c5, restore->SR15); + usleep( 100 ); + + VGAOUT8(0x3c4, 0x30); + VGAOUT8(0x3c5, restore->SR30); + VGAOUT8(0x3c4, 0x08); + VGAOUT8(0x3c5, restore->SR08); + + /* now write out cr67 in full, possibly starting STREAMS */ + VerticalRetraceWait(psav); + VGAOUT8(vgaCRIndex, 0x67); +#if 0 + VGAOUT8(vgaCRReg, 0x50); + usleep(10000); + VGAOUT8(vgaCRIndex, 0x67); +#endif + VGAOUT8(vgaCRReg, restore->CR67); + + VGAOUT8(vgaCRIndex, 0x66); + cr66 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr66 | 0x80); + VGAOUT8(vgaCRIndex, 0x3a); + cr3a = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, cr3a | 0x80); + + if (Entering) + SavageGEReset(pScrn,0,__LINE__,__FILE__); + + if( !S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) + { + VerticalRetraceWait(psav); + OUTREG(FIFO_CONTROL_REG, restore->MMPR0); + OUTREG(MIU_CONTROL_REG, restore->MMPR1); + OUTREG(STREAMS_TIMEOUT_REG, restore->MMPR2); + OUTREG(MISC_TIMEOUT_REG, restore->MMPR3); + } + + /* If we're going into graphics mode and acceleration was enabled, */ + /* go set up the BCI buffer and the global bitmap descriptor. */ + +#if 0 + if( Entering && (!psav->NoAccel) ) + { + VGAOUT8(vgaCRIndex, 0x50); + VGAOUT8(vgaCRReg, VGAIN8(vgaCRReg) | 0xC1); + SavageInitialize2DEngine(pScrn); + } +#endif + + VGAOUT8(vgaCRIndex, 0x66); + VGAOUT8(vgaCRReg, cr66); + VGAOUT8(vgaCRIndex, 0x3a); + VGAOUT8(vgaCRReg, cr3a); + + if( Entering ) + SavageSetGBD(pScrn); + + vgaHWProtect(pScrn, FALSE); + + return; +} + + +static Bool SavageMapMMIO(ScrnInfoPtr pScrn) +{ + SavagePtr psav; + + TRACE(("SavageMapMMIO()\n")); + + psav = SAVPTR(pScrn); + + if( S3_SAVAGE3D_SERIES(psav->Chipset) ) { + psav->MmioBase = psav->PciInfo->memBase[0] + SAVAGE_NEWMMIO_REGBASE_S3; + psav->FrameBufferBase = psav->PciInfo->memBase[0]; + } + else { + psav->MmioBase = psav->PciInfo->memBase[0] + SAVAGE_NEWMMIO_REGBASE_S4; + psav->FrameBufferBase = psav->PciInfo->memBase[1]; + } + + xf86DrvMsg( pScrn->scrnIndex, X_PROBED, + "mapping MMIO @ 0x%x with size 0x%x\n", + psav->MmioBase, SAVAGE_NEWMMIO_REGSIZE); + + psav->MapBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO, psav->PciTag, + psav->MmioBase, + SAVAGE_NEWMMIO_REGSIZE); +#if 0 + psav->MapBaseDense = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO_32BIT, + psav->PciTag, + psav->PciInfo->memBase[0], + 0x8000); +#endif + if (!psav->MapBase) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Internal error: cound not map registers\n"); + return FALSE; + } + + psav->BciMem = psav->MapBase + 0x10000; + + SavageEnableMMIO(pScrn); + + return TRUE; +} + + + +static Bool SavageMapFB(ScrnInfoPtr pScrn) +{ + SavagePtr psav = SAVPTR(pScrn); + + TRACE(("SavageMapFB()\n")); + + xf86DrvMsg( pScrn->scrnIndex, X_PROBED, + "mapping framebuffer @ 0x%x with size 0x%x\n", + psav->FrameBufferBase, psav->videoRambytes); + + if (psav->videoRambytes) { + psav->FBBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER, + psav->PciTag, psav->FrameBufferBase, + psav->videoRambytes); + if (!psav->FBBase) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Internal error: could not map framebuffer\n"); + return FALSE; + } + psav->FBStart = psav->FBBase; + } + pScrn->memPhysBase = psav->PciInfo->memBase[0]; + pScrn->fbOffset = 0; + + return TRUE; +} + + +static void SavageUnmapMem(ScrnInfoPtr pScrn, int All) +{ + SavagePtr psav; + + psav = SAVPTR(pScrn); + + TRACE(("SavageUnmapMem(%x,%x)\n", psav->MapBase, psav->FBBase)); + + if (psav->PrimaryVidMapped) { + vgaHWUnmapMem(pScrn); + psav->PrimaryVidMapped = FALSE; + } + + SavageDisableMMIO(pScrn); + + if (All && psav->MapBase) { + xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->MapBase, + SAVAGE_NEWMMIO_REGSIZE); + psav->MapBase = 0; + } + + if (psav->FBBase) { + xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->FBBase, + psav->videoRambytes); + psav->FBBase = 0; + } + +#if 0 + xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->MapBaseDense, + 0x8000); +#endif + + return; +} + + +static Bool SavageScreenInit(int scrnIndex, ScreenPtr pScreen, + int argc, char **argv) +{ + ScrnInfoPtr pScrn; + SavagePtr psav; + EntityInfoPtr pEnt; + int ret; + + TRACE(("SavageScreenInit()\n")); + + pScrn = xf86Screens[pScreen->myNum]; + psav = SAVPTR(pScrn); + + pEnt = xf86GetEntityInfo(pScrn->entityList[0]); + psav->pVbe = VBEInit(NULL, pEnt->index); + + SavageEnableMMIO(pScrn); + + if (!SavageMapFB(pScrn)) + return FALSE; + + if( psav->ShadowStatus ) { + psav->ShadowPhysical = + psav->FrameBufferBase + psav->CursorKByte*1024 + 4096 - 32; + + psav->ShadowVirtual = (CARD32 *) + (psav->FBBase + psav->CursorKByte*1024 + 4096 - 32); + + xf86DrvMsg( pScrn->scrnIndex, X_PROBED, + "Shadow area physical %08x, linear %08x\n", + psav->ShadowPhysical, psav->ShadowVirtual ); + + psav->WaitQueue = ShadowWait1; + psav->WaitIdle = ShadowWait; + psav->WaitIdleEmpty = ShadowWait; + + if( psav->Chipset == S3_SAVAGE2000 ) + psav->dwBCIWait2DIdle = 0xc0040000; + else + psav->dwBCIWait2DIdle = 0xc0020000; + } + psav->ShadowCounter = 0; + + SavageSave(pScrn); + + vgaHWBlankScreen(pScrn, TRUE); + + if (!SavageModeInit(pScrn, pScrn->currentMode)) + return FALSE; + + miClearVisualTypes(); + + if (pScrn->bitsPerPixel == 16) { + if (!miSetVisualTypes(pScrn->depth, TrueColorMask, + pScrn->rgbBits, pScrn->defaultVisual)) + return FALSE; + if (!miSetPixmapDepths ()) + return FALSE; + } else { + if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth), + pScrn->rgbBits, pScrn->defaultVisual)) + return FALSE; + if (!miSetPixmapDepths ()) + return FALSE; + } + + ret = SavageInternalScreenInit(scrnIndex, pScreen); + if (!ret) + return FALSE; + + xf86SetBlackWhitePixels(pScreen); + + if (pScrn->bitsPerPixel > 8) { + VisualPtr visual; + + visual = pScreen->visuals + pScreen->numVisuals; + while (--visual >= pScreen->visuals) { + if ((visual->class | DynamicClass) == DirectColor) { + visual->offsetRed = pScrn->offset.red; + visual->offsetGreen = pScrn->offset.green; + visual->offsetBlue = pScrn->offset.blue; + visual->redMask = pScrn->mask.red; + visual->greenMask = pScrn->mask.green; + visual->blueMask = pScrn->mask.blue; + } + } + } + + /* must be after RGB ordering fixed */ + fbPictureInit (pScreen, 0, 0); + + if( !psav->NoAccel ) { + SavageInitAccel(pScreen); + } + + miInitializeBackingStore(pScreen); + xf86SetBackingStore(pScreen); + + if( !psav->shadowFB ) + SavageDGAInit(pScreen); + + miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); + + if (psav->hwcursor) + if (!SavageHWCursorInit(pScreen)) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Hardware cursor initialization failed\n"); + + if (psav->shadowFB) { + RefreshAreaFuncPtr refreshArea = SavageRefreshArea; + + if(psav->rotate) { + if (!psav->PointerMoved) { + psav->PointerMoved = pScrn->PointerMoved; + pScrn->PointerMoved = SavagePointerMoved; + } + + switch(pScrn->bitsPerPixel) { + case 8: refreshArea = SavageRefreshArea8; break; + case 16: refreshArea = SavageRefreshArea16; break; + case 24: refreshArea = SavageRefreshArea24; break; + case 32: refreshArea = SavageRefreshArea32; break; + } + } + + ShadowFBInit(pScreen, refreshArea); + } + + if (!miCreateDefColormap(pScreen)) + return FALSE; + + if (psav->Chipset == S3_SAVAGE4) { + if (!xf86HandleColormaps(pScreen, 256, 6, SavageLoadPaletteSavage4, + NULL, + CMAP_RELOAD_ON_MODE_SWITCH + | CMAP_PALETTED_TRUECOLOR + )) + return FALSE; + } else { + if (!xf86HandleColormaps(pScreen, 256, 6, SavageLoadPalette, NULL, + CMAP_RELOAD_ON_MODE_SWITCH + | CMAP_PALETTED_TRUECOLOR + )) + return FALSE; + } + + vgaHWBlankScreen(pScrn, FALSE); + + psav->CloseScreen = pScreen->CloseScreen; + pScreen->SaveScreen = SavageSaveScreen; + pScreen->CloseScreen = SavageCloseScreen; + + if (xf86DPMSInit(pScreen, SavageDPMS, 0) == FALSE) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DPMS initialization failed\n"); + +#ifdef XvExtension + if( !psav->NoAccel && !SavagePanningCheck(pScrn) ) + SavageInitVideo( pScreen ); +#endif + + if (serverGeneration == 1) + xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); + + return TRUE; +} + + +static int SavageInternalScreenInit(int scrnIndex, ScreenPtr pScreen) +{ + int ret = TRUE; + ScrnInfoPtr pScrn; + SavagePtr psav; + int width, height, displayWidth; + unsigned char *FBStart; + + TRACE(("SavageInternalScreenInit()\n")); + + pScrn = xf86Screens[pScreen->myNum]; + psav = SAVPTR(pScrn); + + displayWidth = pScrn->displayWidth; + + if (psav->rotate) { + height = pScrn->virtualX; + width = pScrn->virtualY; + } else { + width = pScrn->virtualX; + height = pScrn->virtualY; + } + + + if(psav->shadowFB) { + psav->ShadowPitch = BitmapBytePad(pScrn->bitsPerPixel * width); + psav->ShadowPtr = xalloc(psav->ShadowPitch * height); + displayWidth = psav->ShadowPitch / (pScrn->bitsPerPixel >> 3); + FBStart = psav->ShadowPtr; + } else { + psav->ShadowPtr = NULL; + FBStart = psav->FBStart; + } + + ret = fbScreenInit(pScreen, FBStart, width, height, + pScrn->xDpi, pScrn->yDpi, + displayWidth, + pScrn->bitsPerPixel); + return ret; +} + + +static ModeStatus SavageValidMode(int index, DisplayModePtr pMode, + Bool verbose, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[index]; + SavagePtr psav = SAVPTR(pScrn); + + TRACE(("SavageValidMode\n")); + + /* We prohibit modes bigger than the LCD panel. */ + /* TODO We should do this only if the panel is active. */ + + if( psav->TvOn ) + { + if( pMode->HDisplay > psav->TVSizeX ) + return MODE_VIRTUAL_X; + + if( pMode->VDisplay > psav->TVSizeY ) + return MODE_VIRTUAL_Y; + + } + if( + !psav->CrtOnly && + psav->PanelX && + ( + (pMode->HDisplay > psav->PanelX) || + (pMode->VDisplay > psav->PanelY) + ) + ) + return MODE_PANEL; + + return MODE_OK; +} + +static Bool SavageModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode) +{ + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + int width, dclk, i, j; /*, refresh; */ + unsigned int m, n, r; + unsigned char tmp = 0; + SavageRegPtr new = &psav->ModeReg; + vgaRegPtr vganew = &hwp->ModeReg; + int vgaCRIndex, vgaCRReg, vgaIOBase; + + vgaIOBase = hwp->IOBase; + vgaCRIndex = vgaIOBase + 4; + vgaCRReg = vgaIOBase + 5; + + TRACE(("SavageModeInit(%dx%d, %dHz)\n", + mode->HDisplay, mode->VDisplay, mode->Clock)); + +#if 0 + ErrorF("Clock = %d, HDisplay = %d, HSStart = %d\n", + mode->Clock, mode->HDisplay, mode->HSyncStart); + ErrorF("HSEnd = %d, HSkew = %d\n", + mode->HSyncEnd, mode->HSkew); + ErrorF("VDisplay - %d, VSStart = %d, VSEnd = %d\n", + mode->VDisplay, mode->VSyncStart, mode->VSyncEnd); + ErrorF("VTotal = %d\n", + mode->VTotal); + ErrorF("HDisplay = %d, HSStart = %d\n", + mode->CrtcHDisplay, mode->CrtcHSyncStart); + ErrorF("HSEnd = %d, HSkey = %d\n", + mode->CrtcHSyncEnd, mode->CrtcHSkew); + ErrorF("VDisplay - %d, VSStart = %d, VSEnd = %d\n", + mode->CrtcVDisplay, mode->CrtcVSyncStart, mode->CrtcVSyncEnd); + ErrorF("VTotal = %d\n", + mode->CrtcVTotal); +#endif + + + + if (pScrn->bitsPerPixel == 8) + psav->HorizScaleFactor = 1; + else if (pScrn->bitsPerPixel == 16) + psav->HorizScaleFactor = 1; /* I don't think we ever want 2 */ + else + psav->HorizScaleFactor = 1; + + if (psav->HorizScaleFactor == 2) + if (!mode->CrtcHAdjusted) { + mode->CrtcHDisplay *= 2; + mode->CrtcHSyncStart *= 2; + mode->CrtcHSyncEnd *= 2; + mode->CrtcHTotal *= 2; + mode->CrtcHSkew *= 2; + mode->CrtcHAdjusted = TRUE; + } + + if (!vgaHWInit(pScrn, mode)) + return FALSE; + + new->mode = 0; + + /* We need to set CR67 whether or not we use the BIOS. */ + + dclk = mode->Clock; + new->CR67 = 0x00; + + switch( pScrn->depth ) { + case 8: + if( (psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000) ) + new->CR67 = 0x10; /* 8bpp, 2 pixels/clock */ + else + new->CR67 = 0x00; /* 8bpp, 1 pixel/clock */ + break; + case 15: + if( + S3_SAVAGE_MOBILE_SERIES(psav->Chipset) || + ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000)) + ) + new->CR67 = 0x30; /* 15bpp, 2 pixel/clock */ + else + new->CR67 = 0x20; /* 15bpp, 1 pixels/clock */ + break; + case 16: + if( + S3_SAVAGE_MOBILE_SERIES(psav->Chipset) || + ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000)) + ) + new->CR67 = 0x50; /* 16bpp, 2 pixel/clock */ + else + new->CR67 = 0x40; /* 16bpp, 1 pixels/clock */ + break; + case 24: + if (pScrn->bitsPerPixel == 24 ) + new->CR67 = 0x70; + else + new->CR67 = 0xd0; + break; + } + + if( psav->UseBIOS ) { + int refresh; + SavageModeEntryPtr pmt; + + /* Scan through our BIOS list to locate the closest valid mode. */ + + /* If we ever break 4GHz clocks on video boards, we'll need to + * change this. + */ + + refresh = (mode->Clock * 1000) / (mode->HTotal * mode->VTotal); + +#ifdef EXTENDED_DEBUG + ErrorF( "Desired refresh rate = %dHz\n", refresh ); +#endif + + for( i = 0, pmt = psav->ModeTable->Modes; + i < psav->ModeTable->NumModes; + i++, pmt++ ) + { + if( (pmt->Width == mode->HDisplay) && + (pmt->Height == mode->VDisplay) ) + { + int jDelta = 99; + int jBest = 0; + + /* We have an acceptable mode. Find a refresh rate. */ + + new->mode = pmt->VesaMode; + for( j = 0; j < pmt->RefreshCount; j++ ) + { + if( pmt->RefreshRate[j] == refresh ) + { + /* Exact match. */ + jBest = j; + break; + } + else if( iabs(pmt->RefreshRate[j] - refresh) < jDelta ) + { + jDelta = iabs(pmt->RefreshRate[j] - refresh); + jBest = j; + } + } + + new->refresh = pmt->RefreshRate[jBest]; + break; + } + } + + if( new->mode ) { + /* Success: we found a match in the BIOS. */ + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "Chose mode %x at %dHz.\n", new->mode, new->refresh ); + } + else { + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "No suitable BIOS mode found for %dx%d %dMHz.\n", + mode->HDisplay, mode->VDisplay, mode->Clock/1000 ); + } + } + + if( !new->mode ) { + /* + * Either BIOS use is disabled, or we failed to find a suitable + * match. Fall back to traditional register-crunching. + */ + + VGAOUT8(vgaCRIndex, 0x3a); + tmp = VGAIN8(vgaCRReg); + if (psav->pci_burst) + new->CR3A = (tmp & 0x7f) | 0x15; + else + new->CR3A = tmp | 0x95; + + new->CR53 = 0x00; + new->CR31 = 0x8c; + new->CR66 = 0x89; + + VGAOUT8(vgaCRIndex, 0x58); + new->CR58 = VGAIN8(vgaCRReg) & 0x80; + new->CR58 |= 0x13; + +#if 0 + VGAOUT8(vgaCRIndex, 0x55); + new->CR55 = VGAIN8(vgaCRReg); + if (psav->hwcursor) + new->CR55 |= 0x10; +#endif + + new->SR15 = 0x03 | 0x80; + new->SR18 = 0x00; + +/* VGAOUT8(0x3c4, 0x1b); + new->SR1B = VGAIN8(0x3c5); + if( pScrn->depth == 24 ) + new->SR1B |= 0x28; +*/ + if( pScrn->depth == 24 ) + new->SR1B = 0x28; + else + new->SR1B = 0x00; + + + new->CR43 = new->CR45 = new->CR65 = 0x00; + + VGAOUT8(vgaCRIndex, 0x40); + new->CR40 = VGAIN8(vgaCRReg) & ~0x01; + + new->MMPR0 = 0x010400; + new->MMPR1 = 0x00; + new->MMPR2 = 0x0808; + new->MMPR3 = 0x08080810; + + if (psav->fifo_aggressive || psav->fifo_moderate || + psav->fifo_conservative) { + new->MMPR1 = 0x0200; + new->MMPR2 = 0x1808; + new->MMPR3 = 0x08081810; + } + + if (psav->MCLK <= 0) { + new->SR10 = 255; + new->SR11 = 255; + } + + psav->NeedSTREAMS = FALSE; + + SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, + &m, &n, &r); + new->SR12 = (r << 6) | (n & 0x3f); + new->SR13 = m & 0xff; + new->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2; + + if (psav->fifo_moderate) { + if (pScrn->bitsPerPixel < 24) + new->MMPR0 -= 0x8000; + else + new->MMPR0 -= 0x4000; + } else if (psav->fifo_aggressive) { + if (pScrn->bitsPerPixel < 24) + new->MMPR0 -= 0xc000; + else + new->MMPR0 -= 0x6000; + } + + if (mode->Flags & V_INTERLACE) + new->CR42 = 0x20; + else + new->CR42 = 0x00; + + new->CR34 = 0x10; + + i = ((((mode->CrtcHTotal >> 3) - 5) & 0x100) >> 8) | + ((((mode->CrtcHDisplay >> 3) - 1) & 0x100) >> 7) | + ((((mode->CrtcHSyncStart >> 3) - 1) & 0x100) >> 6) | + ((mode->CrtcHSyncStart & 0x800) >> 7); + + if ((mode->CrtcHSyncEnd >> 3) - (mode->CrtcHSyncStart >> 3) > 64) + i |= 0x08; + if ((mode->CrtcHSyncEnd >> 3) - (mode->CrtcHSyncStart >> 3) > 32) + i |= 0x20; + j = (vganew->CRTC[0] + ((i & 0x01) << 8) + + vganew->CRTC[4] + ((i & 0x10) << 4) + 1) / 2; + if (j - (vganew->CRTC[4] + ((i & 0x10) << 4)) < 4) { + if (vganew->CRTC[4] + ((i & 0x10) << 4) + 4 <= + vganew->CRTC[0] + ((i & 0x01) << 8)) + j = vganew->CRTC[4] + ((i & 0x10) << 4) + 4; + else + j = vganew->CRTC[0] + ((i & 0x01) << 8) + 1; + } + + new->CR3B = j & 0xff; + i |= (j & 0x100) >> 2; + new->CR3C = (vganew->CRTC[0] + ((i & 0x01) << 8)) / 2; + new->CR5D = i; + new->CR5E = (((mode->CrtcVTotal - 2) & 0x400) >> 10) | + (((mode->CrtcVDisplay - 1) & 0x400) >> 9) | + (((mode->CrtcVSyncStart) & 0x400) >> 8) | + (((mode->CrtcVSyncStart) & 0x400) >> 6) | 0x40; + width = (pScrn->displayWidth * (pScrn->bitsPerPixel / 8)) >> 3; + new->CR91 = vganew->CRTC[19] = 0xff & width; + new->CR51 = (0x300 & width) >> 4; + new->CR90 = 0x80 | (width >> 8); + vganew->MiscOutReg |= 0x0c; + + /* Set frame buffer description. */ + + if (pScrn->bitsPerPixel <= 8) + new->CR50 = 0; + else if (pScrn->bitsPerPixel <= 16) + new->CR50 = 0x10; + else + new->CR50 = 0x30; + + if (pScrn->displayWidth == 640) + new->CR50 |= 0x40; + else if (pScrn->displayWidth == 800) + new->CR50 |= 0x80; + else if (pScrn->displayWidth == 1024) + new->CR50 |= 0x00; + else if (pScrn->displayWidth == 1152) + new->CR50 |= 0x01; + else if (pScrn->displayWidth == 1280) + new->CR50 |= 0xc0; + else if (pScrn->displayWidth == 1600) + new->CR50 |= 0x81; + else + new->CR50 |= 0xc1; /* Use GBD */ + + if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) + new->CR33 = 0x00; + else + new->CR33 = 0x08; + + vganew->CRTC[0x17] = 0xeb; + + new->CR67 |= 1; + + VGAOUT8(vgaCRIndex, 0x36); + new->CR36 = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x68); + new->CR68 = VGAIN8(vgaCRReg); + new->CR69 = 0; + VGAOUT8(vgaCRIndex, 0x6f); + new->CR6F = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRIndex, 0x88); + new->CR86 = VGAIN8(vgaCRReg) | 0x08; + VGAOUT8(vgaCRIndex, 0xb0); + new->CRB0 = VGAIN8(vgaCRReg) | 0x80; + } + + pScrn->vtSema = TRUE; + + /* do it! */ + SavageWriteMode(pScrn, vganew, new, TRUE); + SavageAdjustFrame(pScrn->scrnIndex, pScrn->frameX0, pScrn->frameY0, 0); + + return TRUE; +} + + +static Bool SavageCloseScreen(int scrnIndex, ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + vgaRegPtr vgaSavePtr = &hwp->SavedReg; + SavageRegPtr SavageSavePtr = &psav->SavedReg; + + TRACE(("SavageCloseScreen\n")); + + if (psav->pVbe) + vbeFree(psav->pVbe); + psav->pVbe = NULL; + + if( psav->AccelInfoRec ) { + XAADestroyInfoRec( psav->AccelInfoRec ); + psav->AccelInfoRec = NULL; + } + + if( psav->DGAModes ) { + xfree( psav->DGAModes ); + psav->DGAModes = NULL; + psav->numDGAModes = 0; + } + + if (pScrn->vtSema) { + SavageWriteMode(pScrn, vgaSavePtr, SavageSavePtr, FALSE); + vgaHWLock(hwp); + SavageUnmapMem(pScrn, 0); + } + + pScrn->vtSema = FALSE; + pScreen->CloseScreen = psav->CloseScreen; + + return (*pScreen->CloseScreen)(scrnIndex, pScreen); +} + + +static Bool SavageSaveScreen(ScreenPtr pScreen, int mode) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TRACE(("SavageSaveScreen(0x%x)\n", mode)); + + if( pScrn->vtSema && SAVPTR(pScrn)->hwcursor && SAVPTR(pScrn)->hwc_on) { + + if( xf86IsUnblank(mode) ) + SavageShowCursor( pScrn ); + else + SavageHideCursor( pScrn ); + SAVPTR(pScrn)->hwc_on = TRUE; + } + + return vgaHWSaveScreen(pScreen, mode); +} + + +void SavageAdjustFrame(int scrnIndex, int x, int y, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + int Base; + int vgaCRIndex, vgaCRReg, vgaIOBase; + vgaIOBase = hwp->IOBase; + vgaCRIndex = vgaIOBase + 4; + vgaCRReg = vgaIOBase + 5; + + TRACE(("SavageAdjustFrame(%d,%d,%x)\n", x, y, flags)); + + if (psav->ShowCache && y) + y += pScrn->virtualY - 1; + + Base = ((y * pScrn->displayWidth + (x&~1)) * + (pScrn->bitsPerPixel / 8)) >> 2; + /* now program the start address registers */ + VGAOUT16(vgaCRIndex, (Base & 0x00ff00) | 0x0c); + VGAOUT16(vgaCRIndex, ((Base & 0x00ff) << 8) | 0x0d); + VGAOUT8(vgaCRIndex, 0x69); + VGAOUT8(vgaCRReg, (Base & 0x7f0000) >> 16); + + return; +} + + +Bool SavageSwitchMode(int scrnIndex, DisplayModePtr mode, int flags) +{ + TRACE(("SavageSwitchMode\n")); + return SavageModeInit(xf86Screens[scrnIndex], mode); +} + + +void SavageEnableMMIO(ScrnInfoPtr pScrn) +{ + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + int vgaCRIndex, vgaCRReg; + unsigned char val; + + TRACE(("SavageEnableMMIO\n")); + + vgaHWSetStdFuncs(hwp); + vgaHWSetMmioFuncs(hwp, psav->MapBase, 0x8000); + val = VGAIN8(0x3c3); + VGAOUT8(0x3c3, val | 0x01); + val = VGAIN8(VGA_MISC_OUT_R); + VGAOUT8(VGA_MISC_OUT_W, val | 0x01); + vgaCRIndex = psav->vgaIOBase + 4; + vgaCRReg = psav->vgaIOBase + 5; + + if( psav->Chipset >= S3_SAVAGE4 ) + { + VGAOUT8(vgaCRIndex, 0x40); + val = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, val | 1); + } + + return; +} + + +void SavageDisableMMIO(ScrnInfoPtr pScrn) +{ + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + int vgaCRIndex, vgaCRReg; + unsigned char val; + + TRACE(("SavageDisableMMIO\n")); + + vgaCRIndex = psav->vgaIOBase + 4; + vgaCRReg = psav->vgaIOBase + 5; + + if( psav->Chipset >= S3_SAVAGE4 ) + { + VGAOUT8(vgaCRIndex, 0x40); + val = VGAIN8(vgaCRReg); + VGAOUT8(vgaCRReg, val | 1); + } + + vgaHWSetStdFuncs(hwp); + + return; +} + +void SavageLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indicies, + LOCO *colors, VisualPtr pVisual) +{ + SavagePtr psav = SAVPTR(pScrn); + int i, index; + + for (i=0; i<numColors; i++) { + index = indicies[i]; + VGAOUT8(0x3c8, index); + VGAOUT8(0x3c9, colors[index].red); + VGAOUT8(0x3c9, colors[index].green); + VGAOUT8(0x3c9, colors[index].blue); + } +} + +#define inStatus1() (hwp->readST01( hwp )) + +void SavageLoadPaletteSavage4(ScrnInfoPtr pScrn, int numColors, int *indicies, + LOCO *colors, VisualPtr pVisual) +{ + SavagePtr psav = SAVPTR(pScrn); + int i, index; + + vgaHWPtr hwp = VGAHWPTR(pScrn); + int vgaCRIndex, vgaCRReg, vgaIOBase; + vgaIOBase = hwp->IOBase; + vgaCRIndex = vgaIOBase + 4; + vgaCRReg = vgaIOBase + 5; + VerticalRetraceWait(psav); + + for (i=0; i<numColors; i++) { + if (!(inStatus1()) & 0x08) + VerticalRetraceWait(psav); + index = indicies[i]; + VGAOUT8(0x3c8, index); + VGAOUT8(0x3c9, colors[index].red); + VGAOUT8(0x3c9, colors[index].green); + VGAOUT8(0x3c9, colors[index].blue); + } +} + + + +static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1, + + /* Make sure linear addressing is enabled after the BIOS call. */ + /* Note that we must use an I/O port to do this. */ + int min_n2, int max_n2, long freq_min, + long freq_max, unsigned int *mdiv, + unsigned int *ndiv, unsigned int *r) +{ + double ffreq, ffreq_min, ffreq_max; + double div, diff, best_diff; + unsigned int m; + unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2; + + ffreq = freq / 1000.0 / BASE_FREQ; + ffreq_max = freq_max / 1000.0 / BASE_FREQ; + ffreq_min = freq_min / 1000.0 / BASE_FREQ; + + if (ffreq < ffreq_min / (1 << max_n2)) { + ErrorF("invalid frequency %1.3f Mhz\n", + ffreq*BASE_FREQ); + ffreq = ffreq_min / (1 << max_n2); + } + if (ffreq > ffreq_max / (1 << min_n2)) { + ErrorF("invalid frequency %1.3f Mhz\n", + ffreq*BASE_FREQ); + ffreq = ffreq_max / (1 << min_n2); + } + + /* work out suitable timings */ + + best_diff = ffreq; + + for (n2=min_n2; n2<=max_n2; n2++) { + for (n1=min_n1+2; n1<=max_n1+2; n1++) { + m = (int)(ffreq * n1 * (1 << n2) + 0.5); + if (m < min_m+2 || m > 127+2) + continue; + div = (double)(m) / (double)(n1); + if ((div >= ffreq_min) && + (div <= ffreq_max)) { + diff = ffreq - div / (1 << n2); + if (diff < 0.0) + diff = -diff; + if (diff < best_diff) { + best_diff = diff; + best_m = m; + best_n1 = n1; + best_n2 = n2; + } + } + } + } + + *ndiv = best_n1 - 2; + *r = best_n2; + *mdiv = best_m - 2; +} + + +void SavageGEReset(ScrnInfoPtr pScrn, int from_timeout, int line, char *file) +{ + unsigned char cr66; + int r, success = 0; + CARD32 fifo_control = 0, miu_control = 0; + CARD32 streams_timeout = 0, misc_timeout = 0; + vgaHWPtr hwp = VGAHWPTR(pScrn); + SavagePtr psav = SAVPTR(pScrn); + int vgaCRIndex, vgaCRReg, vgaIOBase; + + TRACE(("SavageGEReset(%d,%s)\n", line, file)); + + vgaIOBase = hwp->IOBase; + vgaCRIndex = vgaIOBase + 4; + vgaCRReg = vgaIOBase + 5; + + if (from_timeout) { + if (psav->GEResetCnt++ < 10 || xf86GetVerbosity() > 1) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "SavageGEReset called from %s line %d\n", file, line); + } else + psav->WaitIdleEmpty(psav); + + if (from_timeout && !S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) { + fifo_control = INREG(FIFO_CONTROL_REG); + miu_control = INREG(MIU_CONTROL_REG); + streams_timeout = INREG(STREAMS_TIMEOUT_REG); + misc_timeout = INREG(MISC_TIMEOUT_REG); + } + + VGAOUT8(vgaCRIndex, 0x66); + cr66 = VGAIN8(vgaCRReg); + + usleep(10000); + for (r=1; r<10; r++) { + VGAOUT8(vgaCRReg, cr66 | 0x02); + usleep(10000); + VGAOUT8(vgaCRReg, cr66 & ~0x02); + usleep(10000); + + if (!from_timeout) + psav->WaitIdleEmpty(psav); + OUTREG(DEST_SRC_STR, psav->Bpl << 16 | psav->Bpl); + + usleep(10000); + switch(psav->Chipset) { + case S3_SAVAGE3D: + case S3_SAVAGE_MX: + success = (STATUS_WORD0 & 0x0008ffff) == 0x00080000; + break; + case S3_SAVAGE4: + case S3_PROSAVAGE: + case S3_SUPERSAVAGE: + success = (ALT_STATUS_WORD0 & 0x0081ffff) == 0x00800000; + break; + case S3_SAVAGE2000: + success = (ALT_STATUS_WORD0 & 0x008fffff) == 0; + break; + } + if(!success) { + usleep(10000); + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "restarting S3 graphics engine reset %2d ...\n", r); + } + else + break; + } + + /* At this point, the FIFO is empty and the engine is idle. */ + + if (from_timeout && !S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) { + OUTREG(FIFO_CONTROL_REG, fifo_control); + OUTREG(MIU_CONTROL_REG, miu_control); + OUTREG(STREAMS_TIMEOUT_REG, streams_timeout); + OUTREG(MISC_TIMEOUT_REG, misc_timeout); + } + + OUTREG(SRC_BASE, 0); + OUTREG(DEST_BASE, 0); + OUTREG(CLIP_L_R, ((0) << 16) | pScrn->displayWidth); + OUTREG(CLIP_T_B, ((0) << 16) | psav->ScissB); + OUTREG(MONO_PAT_0, ~0); + OUTREG(MONO_PAT_1, ~0); + + SavageSetGBD(pScrn); +} + + + +/* This function is used to debug, it prints out the contents of s3 regs */ + +void +SavagePrintRegs(ScrnInfoPtr pScrn) +{ + SavagePtr psav = SAVPTR(pScrn); + unsigned char i; + int vgaCRIndex = 0x3d4; + int vgaCRReg = 0x3d5; + + ErrorF( "SR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF" ); + + for( i = 0; i < 0x70; i++ ) { + if( !(i % 16) ) + ErrorF( "\nSR%xx ", i >> 4 ); + VGAOUT8( 0x3c4, i ); + ErrorF( " %02x", VGAIN8(0x3c5) ); + } + + ErrorF( "\n\nCR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF" ); + + for( i = 0; i < 0xB7; i++ ) { + if( !(i % 16) ) + ErrorF( "\nCR%xx ", i >> 4 ); + VGAOUT8( vgaCRIndex, i ); + ErrorF( " %02x", VGAIN8(vgaCRReg) ); + } + + ErrorF("\n\n"); +} + + +static void SavageDPMS(ScrnInfoPtr pScrn, int mode, int flags) +{ + SavagePtr psav = SAVPTR(pScrn); + unsigned char sr8 = 0x00, srd = 0x00; + + TRACE(("SavageDPMS(%d,%x)\n", mode, flags)); + + VGAOUT8(0x3c4, 0x08); + sr8 = VGAIN8(0x3c5); + sr8 |= 0x06; + VGAOUT8(0x3c5, sr8); + + VGAOUT8(0x3c4, 0x0d); + srd = VGAIN8(0x3c5); + + srd &= 0x03; + + switch (mode) { + case DPMSModeOn: + break; + case DPMSModeStandby: + srd |= 0x10; + break; + case DPMSModeSuspend: + srd |= 0x40; + break; + case DPMSModeOff: + srd |= 0x50; + break; + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid DPMS mode %d\n", mode); + break; + } + + VGAOUT8(0x3c4, 0x0d); + VGAOUT8(0x3c5, srd); + + return; +} + + +static unsigned int +SavageDDC1Read(ScrnInfoPtr pScrn) +{ + register vgaHWPtr hwp = VGAHWPTR(pScrn); + register unsigned char tmp; + SavagePtr psav = SAVPTR(pScrn); + + VerticalRetraceWait(psav); + + InI2CREG(psav,tmp); + while (hwp->readST01(hwp)&0x8) {}; + while (!(hwp->readST01(hwp)&0x8)) {}; + + return ((unsigned int) (tmp & 0x08)); +} + +static Bool +SavageDDC1(int scrnIndex) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + SavagePtr psav = SAVPTR(pScrn); + unsigned char tmp; + Bool success = FALSE; + xf86MonPtr pMon; + + /* initialize chipset */ + InI2CREG(psav,tmp); + OutI2CREG(psav,tmp | 0x12); + + if ((pMon = xf86PrintEDID( + xf86DoEDID_DDC1(scrnIndex,vgaHWddc1SetSpeed,SavageDDC1Read))) != NULL) + success = TRUE; + xf86SetDDCproperties(pScrn,pMon); + + /* undo initialization */ + OutI2CREG(psav,tmp); + return success; +} + + +static void +SavageProbeDDC(ScrnInfoPtr pScrn, int index) +{ + vbeInfoPtr pVbe; + if (xf86LoadSubModule(pScrn, "vbe")) { + pVbe = VBEInit(NULL,index); + ConfiguredMonitor = vbeDoEDID(pVbe, NULL); + vbeFree(pVbe); + } +} + + +static void +SavageGetTvMaxSize(SavagePtr psav) +{ + if( psav->PAL ) { + psav->TVSizeX = 800; + psav->TVSizeY = 600; + } + else { + psav->TVSizeX = 640; + psav->TVSizeY = 480; + } +} + + +static Bool +SavagePanningCheck(ScrnInfoPtr pScrn) +{ + SavagePtr psav = SAVPTR(pScrn); + DisplayModePtr pMode; + + pMode = pScrn->currentMode; + psav->iResX = pMode->CrtcHDisplay; + psav->iResY = pMode->CrtcVDisplay; + if( psav->iResX < pScrn->virtualX || psav->iResY < pScrn->virtualY ) + return TRUE; + else + return FALSE; +} + + |