/* $XFree86: xc/programs/Xserver/hw/xfree86/vga256/drivers/s3_svga/s3ramdacs.c,v 1.1.2.6 1998/10/18 20:42:32 hohndel Exp $ */
/*
 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
 * 
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Thomas Roell not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Thomas Roell makes no
 * representations about the suitability of this software for any purpose. It
 * is provided "as is" without express or implied warranty.
 * 
 * THOMAS ROELL AND KEVIN E. MARTIN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL THOMAS ROELL OR KEVIN E. MARTIN BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * Author:  Thomas Roell, roell@informatik.tu-muenchen.de
 * 
 * Rewritten for the 8514/A by Kevin E. Martin (martin@cs.unc.edu)
 * 
 * Header: /home/src/xfree86/mit/server/ddx/xf86/accel/s3/RCS/s3.c,v 2.0
 * 1993/02/22 05:58:13 jon Exp
 * 
 * Modified by Amancio Hasty and Jon Tombs
 *
 * Rather severely reorganized by Mark Vojkovich (mvojkovi@ucsd.edu)
 * 
 */

#include "misc.h"
#include "pixmapstr.h"
#include "fontstruct.h"
#include "s3.h"
#include "s3reg.h"
#include "xf86_HWlib.h"
#include "xf86_PCI.h"
#define XCONFIG_FLAGS_ONLY
#include "xf86_Config.h"
#include "s3Bt485.h"
#define S3_SERVER
#include "Ti302X.h"
#include "IBMRGB.h"
#include "s3ELSA.h"

extern Bool pixMuxPossible;
extern Bool allowPixMuxInterlace;
extern Bool allowPixMuxSwitching;
extern int nonMuxMaxClock;
extern int nonMuxMaxMemory;
extern int pixMuxMinWidth;
extern int pixMuxMinClock;
extern Bool pixMuxLimitedWidths;
static unsigned char *find_bios_string(int, char *, char *);
static int s3DetectMIRO_20SV_Rev();
static void Probe_ELSA();

static Bool NORMAL_Probe();
static Bool S3_TRIO32_Probe();
static Bool S3_TRIO64_Probe();
static Bool S3_TRIO64V2_Probe();
static Bool S3_TRIO64V_Probe();
static Bool TI3026_Probe();
static Bool TI3030_Probe();
static Bool TI3020_Probe();
static Bool TI3025_Probe();
static Bool BT485_Probe();
static Bool ATT20C505_Probe();
static Bool ATT22C498_Probe();
static Bool ATT498_Probe();
static Bool ATT20C409_Probe();
static Bool SC15025_Probe();
static Bool STG1700_Probe();
static Bool STG1703_Probe();
static Bool IBMRGB524_Probe();
static Bool IBMRGB525_Probe();
static Bool IBMRGB528_Probe();
static Bool S3_SDAC_Probe();
static Bool S3_GENDAC_Probe();
static Bool ATT20C490_Probe();
static Bool SS2410_Probe();
static Bool SC1148x_Probe();
static Bool Null_Probe() {return FALSE;}

static int BT485_SERIES_PreInit();
static int TI3020_3025_PreInit();
static int ATT409_498_PreInit();
static int SC15025_PreInit();
static int STG17xx_PreInit();
static int S3_SDAC_GENDAC_PreInit();
static int S3_TRIO_PreInit();
static int TI3030_3026_PreInit();
static int IBMRGB52x_PreInit();
static int MISC_HI_COLOR_PreInit();
static int NORMAL_PreInit();
static int Null_PreInit() {return 0;}


static void Null_Restore(vgaS3Ptr restore){}
static void S3_TRIO_Restore(vgaS3Ptr restore);
static void TI3030_3026_Restore(vgaS3Ptr restore);
static void TI3020_3025_Restore(vgaS3Ptr restore);
static void BT485_Restore(vgaS3Ptr restore);
static void ATT409_498_Restore(vgaS3Ptr restore);
static void SC15025_Restore(vgaS3Ptr restore);
static void STG17xx_Restore(vgaS3Ptr restore);
static void IBMRGB52x_Restore(vgaS3Ptr restore);
static void S3_SDAC_GENDAC_Restore(vgaS3Ptr restore);
static void SC1148x_Restore(vgaS3Ptr restore);
static void SS2410_Restore(vgaS3Ptr restore);
static void ATT20C490_Restore(vgaS3Ptr restore);

static void Null_Save(vgaS3Ptr save){}
static void S3_TRIO_Save(vgaS3Ptr save);
static void TI3030_3026_Save(vgaS3Ptr save);
static void TI3020_3025_Save(vgaS3Ptr save);
static void BT485_Save(vgaS3Ptr save);
static void ATT409_498_Save(vgaS3Ptr save);
static void SC15025_Save(vgaS3Ptr save);
static void STG17xx_Save(vgaS3Ptr save);
static void IBMRGB52x_Save(vgaS3Ptr save);
static void S3_SDAC_GENDAC_Save(vgaS3Ptr save);
static void SC1148x_Save(vgaS3Ptr save);
static void SS2410_Save(vgaS3Ptr save);
static void ATT20C490_Save(vgaS3Ptr save);

static int Null_Init(DisplayModePtr mode){return 1;}
static int SC1148x_Init(DisplayModePtr);
static int ATT20C490_Init(DisplayModePtr);
static int ATT409_498_Init(DisplayModePtr);
static int STG17xx_Init(DisplayModePtr);
static int S3_SDAC_Init(DisplayModePtr);
static int S3_GENDAC_Init(DisplayModePtr);
static int S3_TRIO_Init(DisplayModePtr);
static int BT485_Init(DisplayModePtr);
static int TI3020_3025_Init(DisplayModePtr);
static int TI3030_3026_Init(DisplayModePtr);
static int IBMRGB52x_Init(DisplayModePtr);
static int SC15025_Init(DisplayModePtr);
static int SS2410_Init(DisplayModePtr);


extern pointer vgaNewVideoState;
#define new ((vgaHWPtr)vgaNewVideoState)


Bool  (*s3ClockSelectFunc) ();
static Bool LegendClockSelect();
static Bool s3ClockSelect();
static Bool icd2061ClockSelect();
static Bool s3GendacClockSelect();
static Bool ti3025ClockSelect();
static Bool ti3026ClockSelect();
static Bool IBMRGBClockSelect();
static void s3ProgramTi3025Clock(
#if NeedFunctionPrototypes
	int clk,
	unsigned char n,
	unsigned char m,
	unsigned char p
#endif
);
static Bool ch8391ClockSelect();
static Bool att409ClockSelect();
static Bool STG1703ClockSelect();
static Bool Gloria8ClockSelect();

/* 
	Miscellaneous ramblings...  This is pretty much the same
 	as in the S3 server.  I cleaned up a few things though.
	All this code should be revaluated to determine how much
	of it is really needed and how much is just an old fix
	for a nolonger existant problem.  I'm considering getting
	rid of the stg1700 and stg1703 entries and just making
	an stg170x entry.  The two are identical except for the
	clock and the server can distinguish the between the two
	by the clock option being set or not.

				MArk
*/


/* NOTE:  This order must be the same as the #define order in
	s3.h !!!!!!!! */
s3RamdacInfo s3Ramdacs[] = {
	/*   DacName,  DacSpeeds, DacProbe(), PreInit(), 
		DacRestore(vgaS3Ptr save), DacSave(),  DacInit() */
/* 0 */		{"normal", {110000,0,0,0}, NORMAL_Probe, NORMAL_PreInit, 	
			Null_Restore, Null_Save, Null_Init},
/* 1 */		{"s3_trio32", {135000,80000,0,50000}, S3_TRIO32_Probe, 
			S3_TRIO_PreInit, S3_TRIO_Restore, S3_TRIO_Save, 
			S3_TRIO_Init},
/* 2 */		{"s3_trio64", {135000,80000,0,50000}, S3_TRIO64_Probe, 
			S3_TRIO_PreInit, S3_TRIO_Restore,S3_TRIO_Save,
			S3_TRIO_Init},
/* 3 */		{"ti3026", {135000,135000,135000,135000}, 
			TI3026_Probe, TI3030_3026_PreInit, TI3030_3026_Restore, 
			TI3030_3026_Save, TI3030_3026_Init},
/* 4 */		{"ti3030", {175000,175000,175000,175000}, TI3030_Probe, 	
			TI3030_3026_PreInit, TI3030_3026_Restore, 
			TI3030_3026_Save, TI3030_3026_Init},
/* 5 */		{"ti3020", {135000,135000,135000,135000}, TI3020_Probe, 
			TI3020_3025_PreInit, TI3020_3025_Restore, 
			TI3020_3025_Save, TI3020_3025_Init},
/* 6 */		{"ti3025", {135000,135000,135000,135000}, TI3025_Probe, 
			TI3020_3025_PreInit, TI3020_3025_Restore, 
			TI3020_3025_Save, TI3020_3025_Init},
/* 7 */		{"Bt485", {135000,135000,135000,135000}, BT485_Probe, 
			BT485_SERIES_PreInit, BT485_Restore, BT485_Save,
 			BT485_Init},
/* 8 */		{"att20c505", {135000,135000,135000,135000}, ATT20C505_Probe, 
			BT485_SERIES_PreInit,BT485_Restore, BT485_Save, 
			BT485_Init},
/* 9 */		{"att22c498", {135000,135000,135000,135000}, ATT22C498_Probe, 
			ATT409_498_PreInit,ATT409_498_Restore, 
			ATT409_498_Save,ATT409_498_Init},
/* 10 */	{"att20c498", {135000,135000,135000,135000}, ATT498_Probe,
	 		ATT409_498_PreInit, ATT409_498_Restore, 
			ATT409_498_Save, ATT409_498_Init},
/* 11 */	{"att20c409", {135000,135000,135000,135000}, ATT20C409_Probe, 
			ATT409_498_PreInit, ATT409_498_Restore, ATT409_498_Save,
			ATT409_498_Init},
/* 12 */	{"sc15025", {110000,110000,110000,110000}, SC15025_Probe, 
			SC15025_PreInit, SC15025_Restore, SC15025_Save, 
			SC15025_Init},
/* 13 */	{"stg1700", {135000,135000,135000,135000}, STG1700_Probe, 
			STG17xx_PreInit, STG17xx_Restore, STG17xx_Save, 
			STG17xx_Init},
/* 14 */	{"stg1703", {135000,135000,135000,135000}, STG1703_Probe, 
			STG17xx_PreInit, STG17xx_Restore, STG17xx_Save, 
			STG17xx_Init},
/* 15 */	{"ibm_rgb524", {170000,170000,170000,170000}, IBMRGB524_Probe,
			IBMRGB52x_PreInit,IBMRGB52x_Restore,IBMRGB52x_Save,
			IBMRGB52x_Init},
/* 16 */	{"ibm_rgb525", {170000,170000,170000,170000}, IBMRGB525_Probe, 
			IBMRGB52x_PreInit,IBMRGB52x_Restore,IBMRGB52x_Save,
			IBMRGB52x_Init},
/* 17 */	{"ibm_rgb528", {170000,170000,170000,170000}, IBMRGB528_Probe,
			IBMRGB52x_PreInit,IBMRGB52x_Restore,IBMRGB52x_Save,
			IBMRGB52x_Init},
/* 18 */	{"s3_sdac", {135000,110000,0,55000}, S3_SDAC_Probe,
		 	S3_SDAC_GENDAC_PreInit,S3_SDAC_GENDAC_Restore,
			S3_SDAC_GENDAC_Save,S3_SDAC_Init},
/* 19 */	{"s3_gendac", {110000,55000,0,27500}, S3_GENDAC_Probe,
		 	S3_SDAC_GENDAC_PreInit,S3_SDAC_GENDAC_Restore,
			S3_SDAC_GENDAC_Save,S3_GENDAC_Init},
/* 20 */	{"att20c490", {110000,110000,110000,110000}, ATT20C490_Probe, 
			MISC_HI_COLOR_PreInit,ATT20C490_Restore,ATT20C490_Save,
			ATT20C490_Init},
/* 21 */	{"ss2410", {110000,110000,110000,110000}, SS2410_Probe, 
			MISC_HI_COLOR_PreInit, SS2410_Restore, SS2410_Save, 
			SS2410_Init},
/* 22 */	{"sc1148x", {110000,110000,110000,110000}, SC1148x_Probe,
			MISC_HI_COLOR_PreInit,SC1148x_Restore,SC1148x_Save,
			SC1148x_Init},
/* 23 */	{"s3_trio64v2", {170000,170000,0,135000}, S3_TRIO64V2_Probe, 
			S3_TRIO_PreInit, S3_TRIO_Restore,S3_TRIO_Save,
			S3_TRIO_Init},
/* 24 */	{"s3_trio64v+", {135000,95000,0,57000}, S3_TRIO64V_Probe, 
			S3_TRIO_PreInit, S3_TRIO_Restore,S3_TRIO_Save,
			S3_TRIO_Init},
/* 25 */	{NULL, {0,0,0,0}, Null_Probe, Null_PreInit, Null_Restore, 
			Null_Save,Null_Init}
}; 

#if 0

static Bool TEMPLATE_PreInit()
{
    /* Verify that depth is supported by ramdac */
   
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */

    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */

    /* Make any necessary clock alterations due to multiplexing,
	clock doubling, etc...  s3Probe will do some last minute
	clock sanity checks when we return */

	return 1;
}

#endif

static void OtherClocksSetup();


/*************************************************************\
			
			BT485_DAC

\*************************************************************/

static Bool BT485_Probe()
{	
   /*
    * Probe for the bloody thing.  Set 0x3C6 to a bogus value, then
    * try to get the Bt485 status register.  If it's there, then we will
    * get something else back from this port.
    */

    Bool found = FALSE;
    unsigned char tmp, tmp2, saveCR43;
    int card_id;

    /*quick check*/
    if (!S3_928_ONLY(s3ChipId) && !S3_964_SERIES(s3ChipId))
	return FALSE;

    outb(vgaCRIndex, 0x43);
    saveCR43 = inb(vgaCRReg);
    outb(vgaCRReg, saveCR43 & ~0x02);

    tmp = inb(0x3C6);
    outb(0x3C6, 0x0F);
    tmp2 = s3InBtStatReg();
    if ((tmp2 & 0x80) && ((tmp2 & 0xF0) != 0xD0)){
       found = TRUE;
       ErrorF("%s %s: Detected a BrookTree Bt485 RAMDAC\n",
                    XCONFIG_PROBED, vga256InfoRec.name);

       card_id = s3DetectMIRO_20SV_Rev(vga256InfoRec.BIOSbase);
       if (card_id > 1) {
	  ErrorF("%s %s: MIRO 20SV Rev.2 or newer detected.\n",
		 XCONFIG_PROBED, vga256InfoRec.name);
	  if (!OFLG_ISSET(OPTION_S3_964_BT485_VCLK, &vga256InfoRec.options))
	     ErrorF("\tplease use Option \"s3_964_bt485_vclk\"\n");
       }

       /* If it is a Bt485 and no clockchip is specified in the 
         XF86Config, set clockchips for SPEA Mercury / Mercury P64 */

       if (!OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions) &&
            OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options)) {
            if (S3_964_SERIES(s3ChipId)) {
                OFLG_SET(CLOCK_OPTION_ICD2061A, &vga256InfoRec.clockOptions);
                OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
       	        s3ClockChipProbed = XCONFIG_PROBED;
           } else if (S3_928_ONLY(s3ChipId)) {
                OFLG_SET(CLOCK_OPTION_SC11412, &vga256InfoRec.clockOptions);
                OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
      	        s3ClockChipProbed = XCONFIG_PROBED;
           }
       }    
     }
     outb(0x3C6, tmp);

     outb(vgaCRIndex, 0x43);
     outb(vgaCRReg, saveCR43);

     return found;
}


static int BT485_SERIES_PreInit()
{
    /* Verify that depth is supported by ramdac */
	/* all are supported */
   
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
   if ( OFLG_ISSET(OPTION_STB_PEGASUS, &vga256InfoRec.options) ||
	OFLG_ISSET(OPTION_NUMBER_NINE, &vga256InfoRec.options) ||
	OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options) ||
	OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) ||
	S3_964_SERIES(s3ChipId) || S3_968_SERIES(s3ChipId)) {
      s3Bt485PixMux = TRUE;
      /* XXXX Are the defaults for the other parameters correct? */
      pixMuxPossible = TRUE;
      allowPixMuxInterlace = FALSE;	/* It doesn't work right (yet) */
      allowPixMuxSwitching = FALSE;	/* XXXX Is this right? */
      if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
          S3_928_ONLY(s3ChipId)) {
	 nonMuxMaxClock = 67500;	/* Doubling only works in mux mode */
	 nonMuxMaxMemory = 1024;	/* Can't access more without mux */
	 allowPixMuxSwitching = FALSE;
	 pixMuxLimitedWidths = FALSE;
	 pixMuxMinWidth = 1024;
	 if (s3Bpp == 2) {
	    nonMuxMaxMemory = 0;	/* Only 2:1MUX works (yet)!     */
	    pixMuxMinWidth = 800;
	 } else if (s3Bpp==4) {
	    nonMuxMaxMemory = 0;
	    pixMuxMinWidth = 640;
	 }
      } else if (OFLG_ISSET(OPTION_NUMBER_NINE, &vga256InfoRec.options)) {
	 nonMuxMaxClock = 67500;
	 allowPixMuxSwitching = TRUE;
	 pixMuxLimitedWidths = TRUE;
	 pixMuxMinWidth = 800;
      } else if (OFLG_ISSET(OPTION_STB_PEGASUS, &vga256InfoRec.options)) {
	allowPixMuxSwitching = TRUE;
	pixMuxLimitedWidths = TRUE;
	/* For 8bpp mode, allow PIXMUX selection based on Clock and Width. */
	if (s3Bpp == 1) {
	  nonMuxMaxClock = 85000;
	  pixMuxMinWidth = 1024;
	} else {
	  /* For 16bpp and 32bpp modes, require PIXMUX. */
	  nonMuxMaxClock = 0;
	  pixMuxMinWidth = 0;
	}
      } else if (OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options)) {
	allowPixMuxSwitching = FALSE;
	pixMuxLimitedWidths = TRUE;
 	/* For 8bpp mode, allow PIXMUX selection based on Clock and Width. */
 	if (s3Bpp == 1) {
 	  nonMuxMaxClock = 85000;
 	  pixMuxMinWidth = 1024;
 	} else {
	  /* For 16bpp and 32bpp modes, require PIXMUX. */
	  nonMuxMaxClock = 0;
	  pixMuxMinWidth = 0;
 	}
      } else if (S3_964_SERIES(s3ChipId) || S3_968_SERIES(s3ChipId)) {
         nonMuxMaxClock = 0;  /* 964 can only be in pixmux mode when */
         pixMuxMinWidth = 0;  /* working in enhanced mode */  
	 pixMuxLimitedWidths = FALSE;
      } else {
	 nonMuxMaxClock = 85000;
      }

   }


    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */

   /* Diamond Stealth 64 VRAM uses an ICD2061A */
   if (!OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions) &&
       (s3BiosVendor == DIAMOND_BIOS) && S3_964_ONLY(s3ChipId)) {
      OFLG_SET(CLOCK_OPTION_ICD2061A, &vga256InfoRec.clockOptions);
      OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
      s3ClockChipProbed = XCONFIG_PROBED;
   }

   OtherClocksSetup();	/* setup clock */

    /* Make any necessary clock alterations due to multiplexing,
	clock doubling, etc...  s3Probe will do some last minute
	clock sanity checks when we return */

   if(s3RamdacType == BT485_DAC) {
      if (s3maxRawClock > 67500)
	 s3clockDoublingPossible = TRUE;
      /* These limits are based on the LCLK rating, and may be too high */
      if (s3Bt485PixMux && s3Bpp < 4)
	 vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
      else {
	 if (vga256InfoRec.dacSpeeds[0] < 150000)    /* 110 and 135 */
	    vga256InfoRec.maxClock = 90000;
	 else				      /* 150 and 170 (if they exist) */
	    vga256InfoRec.maxClock = 110000;
      }
   } else { /* ATT20C505_DAC */
      if (s3maxRawClock > 90000)
	 s3clockDoublingPossible = TRUE;
      /* These limits are based on the LCLK rating, and may be too high */
      if (s3Bt485PixMux && s3Bpp < 4)
	 vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
      else {
	 if (vga256InfoRec.dacSpeeds[0] < 110000)	  /* 85 */
	    vga256InfoRec.maxClock = 85000;
	 else if (vga256InfoRec.dacSpeeds[0] < 135000)	  /* 110 */
	    vga256InfoRec.maxClock = 90000;
	 else					  /* 135, 150, 170 */
	    vga256InfoRec.maxClock = 110000;
      }
   }

   return 1;
}

static void BT485_Restore(vgaS3Ptr restore)
{
      unsigned char tmp;

      /* Turn off parallel mode explicitly here */
      if (s3Bt485PixMux) {
         if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
             S3_928_ONLY(s3ChipId))
	 {
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x20);
	    outb(0x3C7, 0x00);
	    /* set s3 reg53 to non-parallel addressing by and'ing 0xDF     */
            outb(vgaCRIndex, 0x53);
            tmp = inb(vgaCRReg);
            outb(vgaCRReg, tmp & 0xDF);
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x00);
         }
         if (OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options) &&
             S3_928_ONLY(s3ChipId))
	 {
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x40); /* XXXXXXXXXXXXXXXXXXXXX */
	    outb(0x3C7, 0x00);
	    /* set s3 reg53 to non-parallel addressing by and'ing 0xDF     */
            outb(vgaCRIndex, 0x53);
            tmp = inb(vgaCRReg);
            outb(vgaCRReg, tmp & 0xDF);
         }
      }
	 
      s3OutBtReg(BT_COMMAND_REG_0, 0xFE, 0x01);
      s3OutBtRegCom3(0x00, restore->s3DacRegs[3]);
      if (s3Bt485PixMux) {
	 s3OutBtReg(BT_COMMAND_REG_2, 0x00, restore->s3DacRegs[2]);
	 s3OutBtReg(BT_COMMAND_REG_1, 0x00, restore->s3DacRegs[1]);
      }
      s3OutBtReg(BT_COMMAND_REG_0, 0x00, restore->s3DacRegs[0]);
 }

static void BT485_Save(vgaS3Ptr save)
{         
	 save->s3DacRegs[0] = s3InBtReg(BT_COMMAND_REG_0);
	 if (s3Bt485PixMux) {
	    save->s3DacRegs[1] = s3InBtReg(BT_COMMAND_REG_1);
	    save->s3DacRegs[2] = s3InBtReg(BT_COMMAND_REG_2);
	 }
	 save->s3DacRegs[3] = s3InBtRegCom3();
}

static int BT485_Init(DisplayModePtr mode)
{
   register unsigned char tmp, blank;

   outb(0x3C4, 1);
   blank = inb(0x3C5);
   outb(0x3C5, blank | 0x20); /* blank the screen */
   s3OutBtReg(BT_COMMAND_REG_0, 0xFE, 0x01); /* sleep mode */
   
   if (s3Bt485PixMux) {
      if (s3PixelMultiplexing) {
         /* fun timing mods for pixel-multiplexing!  

	   Pixel Multiplexing is selected for 16bpp, 32bpp, or 8bpp
	   with Width > 1024.  Pixel Multiplexing requires we also
	   Select Parallel VRAM Addressing (CR53.5), and Parallel
	   VRAM Addressing also requires a line width of 1024 or
	   2048, external SID enabled (CR55.3), and split transfers
	   disabled (CR51.6).
	 */

	 if (OFLG_ISSET(OPTION_STB_PEGASUS, &vga256InfoRec.options) ||
	     OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options)) {
	   outb(vgaCRIndex, 0x53);
	   tmp = inb(vgaCRReg);
	   outb(vgaCRReg, tmp | 0x20);
	 }

	 if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
              S3_928_ONLY(s3ChipId))	
	 {
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x20);
	    outb(0x3C7, 0x21);

            /* set s3 reg53 to parallel addressing by or'ing 0x20          */
            outb(vgaCRIndex, 0x53);
            tmp = inb(vgaCRReg);
            outb(vgaCRReg, tmp | 0x20);

	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x00);
	 }

         /* set s3 reg55 to external serial by or'ing 0x08              */
         outb(vgaCRIndex, 0x55);
         tmp = inb(vgaCRReg); /* XXXX Something should be masked here */
	 if (vga256InfoRec.bitsPerPixel == 32 &&
	     !OFLG_ISSET(OPTION_MIRO_MAGIC_S4,&vga256InfoRec.options))  
	/* 24bpp truecolor */
 	   tmp |= 0x48;
	 else
	   tmp |= 0x08;
         outb(vgaCRReg, tmp);
	 if (S3_964_SERIES(s3ChipId) || S3_968_SERIES(s3ChipId)) {
	    /* Stealth 64 and Miro Crystal 20SV */
	    outb(vgaCRIndex, 0x66);
	    tmp = inb(vgaCRReg) & 0xc0;
	    if (mode->Flags & V_DBLCLK) {
	       /* Set VCLK = DCLCK/2 */
	       /* And set up a 32 bit interleaved bus */
	       if (s3Bpp == 1)
		  tmp |= 0x11;
	       else
		  tmp |= 0x10; /* 16bpp */
            } else {
	       if (s3Bpp == 1)
		  tmp |= 0x12;
	       else if (s3Bpp == 2)
		  tmp |= 0x11;
	       else
		  tmp |= 0x10;	/* for 20SV, Stealth needs 0x10 ? */
            }
	    outb(vgaCRReg, tmp);

	    /* blank_delay = 0 (at least for Miro Crystal 20SV) */
	    outb(vgaCRIndex, 0x6d);
	    if ((mode->Flags & V_DBLCLK) || s3Bpp > 1) 
	       outb(vgaCRReg, 0);
	    else
	       outb(vgaCRReg, 1);  /* or 2; needed for 20SV with ATT 20C505 */
         }
	 outb(vgaCRIndex, 0x65);
	 tmp = inb(vgaCRReg);

         if (OFLG_ISSET(OPTION_STB_PEGASUS, &vga256InfoRec.options))
	   /*
	     Setting this register non-zero on the Pegasus causes a wrap of
	     the rightmost pixels back to the left of the display.
	   */
	   outb(vgaCRReg, 0x00);
         else if (!(OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
                    S3_928_ONLY(s3ChipId))) {
	    outb(vgaCRReg, tmp | 0x20);
 	    /* set s3 reg65 for some unknown reason                      */
	    /* Setting this for the SPEA Mercury affects clocks > 120MHz */
	  } else if (OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options)) {
	     /* do nothing */ ;
	  } else if ((s3DisplayWidth >= 1024) || (vga256InfoRec.depth == 24)
		     || (vga256InfoRec.depth == 32)) {
#ifndef PC98_PW
	    outb(vgaCRReg, tmp | 0x40);
#else
	    outb(vgaCRReg, tmp | 0x08);
#endif
	    /* remove horizontal stripes in 1600/8bpp and 1152/16bpp      */
	    /* 800/32bpp linewidth pixmux modes                           */
	    /* someone should check this for other 928 + Bt485 cards      */
	  } else outb(vgaCRReg, tmp & 0xBF);

         /*
          * set output clocking to 4:1 multiplexing
          */
	 if (vga256InfoRec.depth == 24 || vga256InfoRec.depth == 32)  /* 24bpp */
	    tmp = 0x10;
	 else if (vga256InfoRec.depth == 16)               /* 5-6-5 */
	    tmp = 0x38;
	 else if (vga256InfoRec.depth == 15)               /* 5-5-5 */
	    tmp = 0x30;
	 else
	    tmp = 0x40;                                /* 8bpp */
         s3OutBtReg(BT_COMMAND_REG_1, 0x00, tmp);

	 /* SCLK enable,pclk1,pixport	                           */
	 if (mode->Flags & V_INTERLACE)
	    s3OutBtReg(BT_COMMAND_REG_2, 0x00, 0x30 | 0x08);
	 else
	    s3OutBtReg(BT_COMMAND_REG_2, 0x00, 0x30);

      } else {

	 if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
             S3_928_ONLY(s3ChipId))
	 {
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x20);
	    outb(0x3C7, 0x00);
	 }
	 if (OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options) &&
             S3_928_ONLY(s3ChipId))
	 {
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x20);
	    outb(0x3C7, 0x00);
	 }

         /* set s3 reg53 to non-parallel addressing by and'ing 0xDF     */
         outb(vgaCRIndex, 0x53);
         tmp = inb(vgaCRReg);
	 if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) && 
	     S3_928_ONLY(s3ChipId) && (s3Bpp != 1)) {
            outb(vgaCRReg, tmp | 0x20);
	 } else {
            outb(vgaCRReg, tmp & 0xDF);
	 }

         /* set s3 reg65 for some unknown reason                        */
         outb(vgaCRIndex, 0x65);
         tmp = inb(vgaCRReg);
         outb(vgaCRReg, tmp & 0xDF);

	 if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
             S3_928_ONLY(s3ChipId))
	 {
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x00);
	 }

	 if (OFLG_ISSET(OPTION_MIRO_MAGIC_S4, &vga256InfoRec.options))
	 {
 	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, 0x00);
	    outb(vgaCRIndex, 0x55);
	    outb(vgaCRReg, 0x20);
	 } else {
	   /* set s3 reg55 to non-external serial by and'ing 0xF7         */
	   outb(vgaCRIndex, 0x55);
	   tmp = inb(vgaCRReg);
	   outb(vgaCRReg, tmp & 0xF7);
	 }
	 
	 if (vga256InfoRec.depth == 24 || vga256InfoRec.depth == 32)/* 24bpp */
	    tmp = 0x10;
	 else if (vga256InfoRec.depth == 16)		/* 5-6-5 */
	    tmp = 0x3c;					/* 1:1 MUX */
	 else if (vga256InfoRec.depth == 15)		/* 5-5-5 */
	    tmp = 0x34;					/* 1:1 MUX */
	 else
	    tmp = 0x00;
         s3OutBtReg(BT_COMMAND_REG_1, 0x00, tmp);

	 if (vga256InfoRec.bitsPerPixel > 8)
	    tmp = 0x30;
	 else
	    tmp = 0x10;

	 /* pclk1,vgaport                                               */
	 if (mode->Flags & V_INTERLACE)
	    s3OutBtReg(BT_COMMAND_REG_2, 0x00, tmp | 0x08);
	 else
	    s3OutBtReg(BT_COMMAND_REG_2, 0x00, tmp);

      }  /* end of s3PixelMultiplexing */
   }

   /* Set 6/8 bit mode and sync-on-green if required */
      s3OutBtReg(BT_COMMAND_REG_0, 0x00, 0x01 |
		 (s3DAC8Bit ? 0x02 : 0) | (s3DACSyncOnGreen ? 0x08 : 0x00));
#ifdef CLOCKDEBUG
      if (mode->Flags & V_DBLCLK) {
	 ErrorF("Setting clock doubler in s3Init(), freq = %.3f\n",
		vga256InfoRec.clock[mode->Clock] / 1000.0);
      }
#endif
      /* Use Bt485 clock doubler - Bit 3 of Command Reg 3 */
      s3OutBtRegCom3(0xF7, (mode->Flags & V_DBLCLK ? 0x08 : 0x00));
      s3OutBtReg(BT_COMMAND_REG_0, 0xFE, 0x00); /* wake up    */
      outb(0x3C4, 1);
      outb(0x3C5, blank); /* unblank the screen */
	
      return 1;
}

/************************************************************\

			ATT20C505_DAC

\************************************************************/

static Bool ATT20C505_Probe()
{
    Bool found = FALSE;
    unsigned char tmp,tmp2;

    /*quick check*/
    if (!S3_928_ONLY(s3ChipId) && !S3_964_SERIES(s3ChipId))
	return FALSE;

    tmp = inb(0x3C6);
    outb(0x3C6, 0x0F);
    tmp2 = s3InBtStatReg();
    if ((tmp2 & 0x80) && ((tmp2 & 0xF0) == 0xD0)) {
          found = TRUE;
          ErrorF("%s %s: Detected an AT&T 20C505 RAMDAC\n",
                    XCONFIG_PROBED, vga256InfoRec.name);
    }
    outb(0x3C6, tmp);
    return found;
}


/*******************************************************\
 
		TI3020_DAC  
		TI3025_DAC 

\*******************************************************/

static Bool TI3020_3025_Probe(int type)
{
    int found = 0;
    unsigned char saveCR55, saveCR45, saveCR43, saveCR5C;
    unsigned char saveTIndx,saveTIndx2,saveTIdata;

    outb(vgaCRIndex, 0x43);
    saveCR43 = inb(vgaCRReg);
    outb(vgaCRReg, saveCR43 & ~0x02);

    outb(vgaCRIndex, 0x45);
    saveCR45 = inb(vgaCRReg);
    outb(vgaCRReg, saveCR45 & ~0x20);

    outb(vgaCRIndex, 0x55);
    saveCR55 = inb(vgaCRReg);
	 
    /* toggle to upper 4 direct registers */
    outb(vgaCRReg, (saveCR55 & 0xFC) | 0x01);

    saveTIndx = inb(TI_INDEX_REG);
    outb(TI_INDEX_REG, TI_ID);
    if (inb(TI_DATA_REG) == TI_VIEWPOINT20_ID) {
	    /*
	     * Found TI ViewPoint 3020 DAC
	     */
	    found = TI3020_DAC;
	    saveCR43 &= ~0x02;
	    saveCR45 &= ~0x20;
    } else {
	    outb(vgaCRIndex, 0x5C);
	    saveCR5C = inb(vgaCRReg);
	    /* clear 0x20 (RS4) for 3020 mode */
	    outb(vgaCRReg, saveCR5C & 0xDF);
	    saveTIndx2 = inb(TI_INDEX_REG);
	    /* already twiddled CR55 above */
	    outb(TI_INDEX_REG, TI_CURS_CONTROL);
	    saveTIdata = inb(TI_DATA_REG);
	    /* clear TI_PLANAR_ACCESS bit */
	    outb(TI_DATA_REG, saveTIdata & 0x7F);

	    outb(TI_INDEX_REG, TI_ID);
	    if (inb(TI_DATA_REG) == TI_VIEWPOINT25_ID) {
	       /*
	        * Found TI ViewPoint 3025 DAC
	        */
	       found = TI3025_DAC;
	       saveCR43 &= ~0x02;
	       saveCR45 &= ~0x20;
	    }

	    /* restore this mess */
	    outb(TI_INDEX_REG, TI_CURS_CONTROL);
	    outb(TI_DATA_REG, saveTIdata);
	    outb(TI_INDEX_REG, saveTIndx2);
	    outb(vgaCRIndex, 0x5C);
	    outb(vgaCRReg, saveCR5C);
    }

    outb(TI_INDEX_REG, saveTIndx);
    outb(vgaCRIndex, 0x55);
    outb(vgaCRReg, saveCR55);

    outb(vgaCRIndex, 0x45);
    outb(vgaCRReg, saveCR45);

    outb(vgaCRIndex, 0x43);
    outb(vgaCRReg, saveCR43);

    return (found == type);
}

static Bool TI3020_Probe()
{
  if (!S3_928_ONLY(s3ChipId) && !S3_964_SERIES(s3ChipId))
	return FALSE;
  else if (TI3020_3025_Probe(TI3020_DAC)) {
	ErrorF("%s %s: Detected a TI ViewPoint 3020 RAMDAC\n",
	           XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
  } else return FALSE;	
}

static Bool TI3025_Probe()
{
  if (!S3_964_SERIES(s3ChipId))
	return FALSE;
  else if(TI3020_3025_Probe(TI3025_DAC)) {
	ErrorF("%s %s: Detected a TI ViewPoint 3025 RAMDAC\n",
	            XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
  } else return FALSE;
}

static int TI3020_3025_PreInit()
{
    /* Verify that depth is supported by ramdac */
	/* all are supported */


    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    pixMuxPossible = TRUE;
    allowPixMuxInterlace = FALSE;
    allowPixMuxSwitching = FALSE;
    nonMuxMaxClock = 70000;
    pixMuxLimitedWidths = FALSE;
    if (S3_964_SERIES(s3ChipId)) {
         nonMuxMaxClock = 0;  /* 964 can only be in pixmux mode when */
         pixMuxMinWidth = 0;  /* working in enhanced mode */  
    }

    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */

  if (DAC_IS_TI3025 && 
       !OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions)) {
      OFLG_SET(CLOCK_OPTION_TI3025, &vga256InfoRec.clockOptions);
      OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
      s3ClockChipProbed = XCONFIG_PROBED;
   }
   if (OFLG_ISSET(CLOCK_OPTION_TI3025, &vga256InfoRec.clockOptions)) {
        int mclk, m, n, p, mcc, cr5c;

        s3ClockSelectFunc = ti3025ClockSelect;
      	s3numClocks = 3;
        s3maxRawClock = vga256InfoRec.dacSpeeds[0]; 

      	outb(vgaCRIndex, 0x5c);
      	cr5c = inb(vgaCRReg);
      	outb(vgaCRReg, cr5c & 0xdf);     /* clear RS4 - use 3020 mode */

      	s3OutTiIndReg(TI_PLL_CONTROL, 0x00, 0x00);
      	n = s3InTiIndReg(TI_MCLK_PLL_DATA) & 0x7f;
      	s3OutTiIndReg(TI_PLL_CONTROL, 0x00, 0x01);
      	m = s3InTiIndReg(TI_MCLK_PLL_DATA) & 0x7f;
      	s3OutTiIndReg(TI_PLL_CONTROL, 0x00, 0x02);
      	p = s3InTiIndReg(TI_MCLK_PLL_DATA) & 0x03;
      	mcc = s3InTiIndReg(TI_MCLK_DCLK_CONTROL);
      	if (mcc & 0x08) 
	 	mcc = (mcc & 0x07) * 2 + 2;
      	else 
	 	mcc = 1;
      	mclk = ((1431818 * ((m+2) * 8)) / (n+2) / (1 << p) / mcc + 50) / 100;
      	if (xf86Verbose)
	 ErrorF("%s %s: Using TI 3025 programmable clock (MCLK %1.3f MHz)\n",
		s3ClockChipProbed, vga256InfoRec.name, mclk / 1000.0);
      	if (OFLG_ISSET(OPTION_NUMBER_NINE, &vga256InfoRec.options)) {
	   mclk = 55000;
	   if (xf86Verbose)
	    ErrorF("%s %s: Setting MCLK to %1.3f MHz for #9GXE64 Pro\n",
		   XCONFIG_PROBED, vga256InfoRec.name, mclk / 1000.0);
	   Ti3025SetClock(2 * mclk, TI_MCLK_PLL_DATA, s3ProgramTi3025Clock);
	   mcc = s3InTiIndReg(TI_MCLK_DCLK_CONTROL);
	   s3OutTiIndReg(TI_MCLK_DCLK_CONTROL,0x00, (mcc & 0xf0) | 0x08);
      	}
        if (!vga256InfoRec.s3MClk)
	 	vga256InfoRec.s3MClk = mclk;
      	outb(vgaCRIndex, 0x5c);
      	outb(vgaCRReg, cr5c);

   }	/* Ti3020 will have an external clock */
   else 
      OtherClocksSetup();	/* external clocks */

    /* Make any necessary clock alterations due to multiplexing,
	clock doubling, etc...  s3Probe will do some last minute
	clock sanity checks when we return */
   s3clockDoublingPossible = TRUE;
   vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];

   return 1;
}


static void TI3020_3025_Restore(vgaS3Ptr restore)
{
   s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, restore->s3DacRegs[TI_MUX_CONTROL_1]);
   s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, restore->s3DacRegs[TI_MUX_CONTROL_2]);
   s3OutTiIndReg(TI_INPUT_CLOCK_SELECT, 0x00,
		    restore->s3DacRegs[TI_INPUT_CLOCK_SELECT]);
   s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00,
		    restore->s3DacRegs[TI_OUTPUT_CLOCK_SELECT]);
   s3OutTiIndReg(TI_GENERAL_CONTROL, 0x00,
		    restore->s3DacRegs[TI_GENERAL_CONTROL]);
   s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0x00,
		    restore->s3DacRegs[TI_AUXILIARY_CONTROL]);
   s3OutTiIndReg(TI_GENERAL_IO_CONTROL, 0x00, 0x1f);
   s3OutTiIndReg(TI_GENERAL_IO_DATA, 0x00,
		    restore->s3DacRegs[TI_GENERAL_IO_DATA]);
   if (DAC_IS_TI3025) {
     s3OutTiIndReg(TI_PLL_CONTROL, 0x00, 0x00);
  /* N */
     s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[0 + 0x40]);
  /* M */
     s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[1 + 0x40]);
  /* P */
     s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[2 + 0x40]);

   /* N */
     s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, restore->s3DacRegs[3 + 0x40]);
  /* M */
     s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, restore->s3DacRegs[4 + 0x40]); 
 /* P */
     s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00,restore->s3DacRegs[5 + 0x40] | 0x80 );

  /* N */
     s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[6 + 0x40]);
  /* M */
     s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[7 + 0x40]);
 /* P */
     s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[8 + 0x40]); 

     s3OutTiIndReg(TI_TRUE_COLOR_CONTROL, 0x00,
                    restore->s3DacRegs[TI_TRUE_COLOR_CONTROL]);
     s3OutTiIndReg(TI_MISC_CONTROL, 0x00, restore->s3DacRegs[TI_MISC_CONTROL]);
   }
   
   s3OutTiIndReg(TI_CURS_CONTROL, 0x00, restore->s3DacRegs[TI_CURS_CONTROL]);
}


static void TI3020_3025_Save(vgaS3Ptr save)
{
      if (DAC_IS_TI3025) {
          unsigned char CR5C;

          /* switch the ramdac from bt485 to ti3020 mode clearing RS4 */
	  outb(vgaCRIndex, 0x5C);
	  CR5C = inb(vgaCRReg);
	  outb(vgaCRReg, CR5C & 0xDF);

          save->s3DacRegs[TI_CURS_CONTROL] = s3InTiIndReg(TI_CURS_CONTROL);
          /* clear TI_PLANAR_ACCESS bit */
	  s3OutTiIndReg(TI_CURS_CONTROL, 0x7F, 0x00);
      }

      save->s3DacRegs[TI_MUX_CONTROL_1] = s3InTiIndReg(TI_MUX_CONTROL_1);
      save->s3DacRegs[TI_MUX_CONTROL_2] = s3InTiIndReg(TI_MUX_CONTROL_2);
      save->s3DacRegs[TI_INPUT_CLOCK_SELECT] =
                 s3InTiIndReg(TI_INPUT_CLOCK_SELECT);
      save->s3DacRegs[TI_OUTPUT_CLOCK_SELECT] =
                 s3InTiIndReg(TI_OUTPUT_CLOCK_SELECT);
      save->s3DacRegs[TI_GENERAL_CONTROL] = s3InTiIndReg(TI_GENERAL_CONTROL);
      save->s3DacRegs[TI_AUXILIARY_CONTROL] =
		 s3InTiIndReg(TI_AUXILIARY_CONTROL);
      s3OutTiIndReg(TI_GENERAL_IO_CONTROL, 0x00, 0x1f);
      save->s3DacRegs[TI_GENERAL_IO_DATA] = s3InTiIndReg(TI_GENERAL_IO_DATA);

      if (DAC_IS_TI3025) {
          save->s3DacRegs[TI_TRUE_COLOR_CONTROL] =
                 s3InTiIndReg(TI_TRUE_COLOR_CONTROL);
          save->s3DacRegs[TI_MISC_CONTROL] = s3InTiIndReg(TI_MISC_CONTROL);

          s3OutTiIndReg(TI_PLL_CONTROL, 0x00, 0x00);
 /* N */
          save->s3DacRegs[0 + 0x40] = s3InTiIndReg(TI_PIXEL_CLOCK_PLL_DATA); 
/* M */
          s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA,0x00,save->s3DacRegs[0 + 0x40]);
          save->s3DacRegs[1 + 0x40] = s3InTiIndReg(TI_PIXEL_CLOCK_PLL_DATA);  
 /* P */
         s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA,0x00,save->s3DacRegs[1 + 0x40]);
          save->s3DacRegs[2 + 0x40] = s3InTiIndReg(TI_PIXEL_CLOCK_PLL_DATA);  
          s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA,0x00,save->s3DacRegs[2 + 0x40]);

          save->s3DacRegs[3 + 0x40] = s3InTiIndReg(TI_MCLK_PLL_DATA);  /* N */
          s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, save->s3DacRegs[3 + 0x40]);
          save->s3DacRegs[4 + 0x40] = s3InTiIndReg(TI_MCLK_PLL_DATA);  /* M */
          s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, save->s3DacRegs[4 + 0x40]);
          save->s3DacRegs[5 + 0x40] = s3InTiIndReg(TI_MCLK_PLL_DATA);  /* P */
          s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, save->s3DacRegs[5 + 0x40]);

          save->s3DacRegs[6 + 0x40] = s3InTiIndReg(TI_LOOP_CLOCK_PLL_DATA);  
/* N */
          s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA,0x00,save->s3DacRegs[6 + 0x40]);
          save->s3DacRegs[7 + 0x40] = s3InTiIndReg(TI_LOOP_CLOCK_PLL_DATA);  
/* M */
          s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA,0x00,save->s3DacRegs[7 + 0x40]);
          save->s3DacRegs[8 + 0x40] = s3InTiIndReg(TI_LOOP_CLOCK_PLL_DATA); 
 /* P */
          s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA,0x00,save->s3DacRegs[8 + 0x40]);

      }
}

static int TI3020_3025_Init(DisplayModePtr mode)
{
      unsigned char tmp, tmp1, tmp2;

      outb(0x3C4, 1);
      tmp2 = inb(0x3C5);
      outb(0x3C5, tmp2 | 0x20); /* blank the screen */

      /* change polarity on S3 to pass through control to the 3020      */
      tmp = new->MiscOutReg; /* BUG tmp = inb(0x3CC); */
      new->MiscOutReg |= 0xC0;
      tmp1 = 0x00;
      if (!(tmp & 0x80)) tmp1 |= 0x02; /* invert bits for the 3020      */
      if (!(tmp & 0x40)) tmp1 |= 0x01;
      if (s3DACSyncOnGreen) tmp1 |= 0x20;  /* add IOG sync              */
      s3OutTiIndReg(TI_GENERAL_CONTROL, 0x00, tmp1);
      s3OutTiIndReg(TI_TRUE_COLOR_CONTROL, 0x00, 0x00);

      if (DAC_IS_TI3020) {
	 /* the 3025 clock programming code sets the input clock select */
         if (mode->Flags & V_DBLCLK)
	    s3OutTiIndReg(TI_INPUT_CLOCK_SELECT, 0x00, TI_ICLK_CLK1_DOUBLE);
         else
	    s3OutTiIndReg(TI_INPUT_CLOCK_SELECT, 0x00, TI_ICLK_CLK1);
      }

      outb(vgaCRIndex, 0x65);
      if (DAC_IS_TI3025) {
	 if (OFLG_ISSET(OPTION_NUMBER_NINE,&vga256InfoRec.options)) {
	    outb(vgaCRReg, 0x82);
	 } else {
	    outb(vgaCRReg, 0);
	 }
      } else {
	 /* set s3 reg65 for some unknown reason			*/
	 if (vga256InfoRec.bitsPerPixel == 32) {
            if (OFLG_ISSET(OPTION_ELSA_W2000PRO,&vga256InfoRec.options))
               outb(vgaCRReg, 0x40);
            else
               outb(vgaCRReg, 0x80);
	 } else if (vga256InfoRec.bitsPerPixel == 16)
	    outb(vgaCRReg, 0x40);
	 else
	    outb(vgaCRReg, 0x00);
      }

      if (s3PixelMultiplexing) {
	 /* fun timing mods for pixel-multiplexing!                     */

	 if (OFLG_ISSET(OPTION_ELSA_W2000PRO,&vga256InfoRec.options)) {
	    /* set CR40 acording to Bernhard Bender */
	    outb(vgaCRIndex, 0x40);
	    outb(vgaCRReg, 0xd1);
	 } else if (DAC_IS_TI3025) {
	    outb(vgaCRIndex, 0x40);
	    outb(vgaCRReg, 0x11);
	    outb(vgaCRIndex, 0x55);
	    outb(vgaCRReg, 0x00);
	 } else {
            /* set s3 reg53 to parallel addressing by or'ing 0x20          */
            outb(vgaCRIndex, 0x53);
            tmp = inb(vgaCRReg);
            outb(vgaCRReg, tmp | 0x20);

            /* set s3 reg55 to external serial by or'ing 0x08              */
            outb(vgaCRIndex, 0x55);
            tmp = inb(vgaCRReg);
            outb(vgaCRReg, tmp | 0x08);
	 }
	 /* the input clock is already set to clk1 or clk1double (s3.c) */

	 if (DAC_IS_TI3025) {
	    if (vga256InfoRec.bitsPerPixel > 8)
	       s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0, 0x00);
            else
	       s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0, TI_AUX_W_CMPL);
         } else {
	    /* set aux control to self clocked, window function complement */
	    if (vga256InfoRec.bitsPerPixel > 8)
	       s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0, TI_AUX_SELF_CLOCK);
            else
	       s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0,
		          TI_AUX_SELF_CLOCK | TI_AUX_W_CMPL);
         }
	 if (OFLG_ISSET(OPTION_ELSA_W2000PRO,&vga256InfoRec.options)) {
   	    int vclock,rclock;

	    /*
	     * The 964 needs different VCLK division depending on the 
	     * clock frequenzy used. VCLK/1 for 0-60MHz, VCLK/2 for
	     * 60-120MHz and VCLK/4 for 120-175MHz (or -200MHz, depending
	     * on the RAMDAC actually used)
	     * the RCLK output is tied to the LCLK input which is the same
	     * as SCLK but with no blanking.  SCLK is the actual pixel
	     * shift clock for the pixel bus.
	     * RCLK/8 is used because of the 8:1 pixel-multiplexing below.
	     * (964 uses always 8:1 in 256 color modes)
	     */
	    if (vga256InfoRec.clock[mode->Clock] > 120000) {
	       vclock = TI_OCLK_V4;
	    } else if (vga256InfoRec.clock[mode->Clock] > 60000){
	       vclock = TI_OCLK_V2;
            } else {
	       vclock = TI_OCLK_V1;
            }
	    if (vga256InfoRec.bitsPerPixel == 32) {           /* 24bpp */
               rclock = TI_OCLK_R2;
            } else if (vga256InfoRec.bitsPerPixel == 16) { /* 15/16bpp */
               rclock = TI_OCLK_R4;
            } else {
               rclock = TI_OCLK_R8;
            }
            s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00,
			  TI_OCLK_S | vclock | rclock);
            outb(vgaCRIndex, 0x66);
            tmp = inb(vgaCRReg);
            outb(vgaCRReg, (tmp & 0xf8) | ((rclock - (vclock >> 3)) & 7));
	 } else if (DAC_IS_TI3025) {
	    if (vga256InfoRec.bitsPerPixel == 32) {           /* 24bpp */
	       s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_S_V2_R2);
	       outb(vgaCRIndex, 0x66);
	       tmp = inb(vgaCRReg);
               if (mode->Flags & V_DBLCLK)
	          outb(vgaCRReg, (tmp & 0xf8) | 0x00);
               else
	          outb(vgaCRReg, (tmp & 0xf8) | 0x01);
	    } else if (vga256InfoRec.bitsPerPixel == 16) {      /* 5-6-5 */
	       s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_S_V2_R4);
	       outb(vgaCRIndex, 0x66);
	       tmp = inb(vgaCRReg);
               if (mode->Flags & V_DBLCLK)
	          outb(vgaCRReg, (tmp & 0xf8) | 0x01);
               else
	          outb(vgaCRReg, (tmp & 0xf8) | 0x02);
	    } else {
	       s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_S_V2_R8);
	       outb(vgaCRIndex, 0x66);
	       tmp = inb(vgaCRReg);
               if (mode->Flags & V_DBLCLK)
	          outb(vgaCRReg, (tmp & 0xf8) | 0x02);
               else
	          outb(vgaCRReg, (tmp & 0xf8) | 0x03);
	    }
	 } else {
	    /*
	     * for all other boards with Ti3020 (only #9 level 14/16 ?)
	     * set output clocking to VCLK/4, RCLK/8 like the fixed Bt485.
	     * RCLK/8 is used because of the 8:1 pixel-multiplexing below.
	     */
	    if (vga256InfoRec.bitsPerPixel == 32) {           /* 24bpp */
	       s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_S_V1_R2);
	    } else if (vga256InfoRec.bitsPerPixel == 16) {      /* 5-6-5 */
	       s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_S_V2_R4);
	    } else {
	       s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_S_V4_R8);
	    }
	 }

         /*
          * set the serial access mode 256 words control
          */
         outb(vgaCRIndex, 0x58);
         tmp = inb(vgaCRReg);
         outb(vgaCRReg, (tmp & 0xbf) | s3SAM256);

	 if (vga256InfoRec.depth == 24 || vga256InfoRec.depth == 32) {  /* 24bpp */
            if (DAC_IS_TI3025) {
               s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3025T_888);
               s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_TC_D24P64);
               s3OutTiIndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x01);
            } else {
               s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_DIRECT_888);
               s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_DC_D24P64);
               s3OutTiIndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
            }
	 } else if (vga256InfoRec.depth == 16) {                    /* 5-6-5 */
            if (DAC_IS_TI3025) {
               s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3025T_565);
               s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_TC_D16P64);
               s3OutTiIndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x01);
            } else {
               s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_DIRECT_565);
               s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_DC_D16P64);
               s3OutTiIndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
            }
	 } else if (vga256InfoRec.depth == 15) {                     /* 5-5-5 */
            if (DAC_IS_TI3025) {
               s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3025T_555);
               s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_TC_D15P64);
               s3OutTiIndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x01);
            } else {
               s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_DIRECT_555);
               s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_DC_D15P64);
               s3OutTiIndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
            }
	 } else {
            /* set mux control 1 and 2 to provide pseudocolor sub-mode 4   */
            /* this provides a 64-bit pixel bus with 8:1 multiplexing      */
            s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_PSEUDO_COLOR);
            s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_PC_D8P64);
	 }
         /* change to 8-bit DAC and re-route the data path and clocking */
         s3OutTiIndReg(TI_GENERAL_IO_CONTROL, 0x00, TI_GIC_ALL_BITS);
         if (s3DAC8Bit) {
            if (DAC_IS_TI3025) {
               s3OutTiIndReg(TI_GENERAL_IO_DATA , 0x00, TI_GID_N9_964);
               s3OutTiIndReg(TI_GENERAL_IO_CONTROL, 0x00, 0x00);
               s3OutTiIndReg(TI_MISC_CONTROL , 0xF0,
                             TI_MC_INT_6_8_CONTROL | TI_MC_8_BPP);
            } else if(OFLG_ISSET(OPTION_ELSA_W2000PRO,&vga256InfoRec.options)) 
               s3OutTiIndReg(TI_GENERAL_IO_DATA , 0x00 , TI_GID_W2000_8BIT);
            else
               s3OutTiIndReg(TI_GENERAL_IO_DATA, 0x00, TI_GID_TI_DAC_8BIT);
         } else {
            if (DAC_IS_TI3025) {
               s3OutTiIndReg(TI_GENERAL_IO_DATA , 0x00, TI_GID_N9_964);
               s3OutTiIndReg(TI_GENERAL_IO_CONTROL, 0x00, 0x00);
               s3OutTiIndReg(TI_MISC_CONTROL , 0xF0, TI_MC_INT_6_8_CONTROL);
            } else if(OFLG_ISSET(OPTION_ELSA_W2000PRO,&vga256InfoRec.options)) 
	       s3OutTiIndReg( TI_GENERAL_IO_DATA , 0x00 , TI_GID_W2000_6BIT );
            else
               s3OutTiIndReg(TI_GENERAL_IO_DATA, 0x00, TI_GID_TI_DAC_6BIT);
         }
         if (DAC_IS_TI3025) {
	    outb(vgaCRIndex, 0x6D);
	    if (s3Bpp == 1)
	       if (mode->Flags & V_DBLCLK) 
		  outb(vgaCRReg, 0x02);
	       else
		  outb(vgaCRReg, 0x03);
	    else if (s3Bpp == 2)
	       if (mode->Flags & V_DBLCLK) 
		  outb(vgaCRReg, 0x00);
	       else
		  outb(vgaCRReg, 0x01);
	    else /* (s3Bpp == 4) */
	       outb(vgaCRReg, 0x00);
	 }
      } else {
         /* set s3 reg53 to non-parallel addressing by and'ing 0xDF     */
         outb(vgaCRIndex, 0x53);
         tmp = inb(vgaCRReg);
         outb(vgaCRReg, tmp & 0xDF);

         /* set s3 reg55 to non-external serial by and'ing 0xF7         */
         outb(vgaCRIndex, 0x55);
         tmp = inb(vgaCRReg);
         outb(vgaCRReg, tmp & 0xF7);

         /* the input clock is already set to clk1 or clk1double (s3.c) */

	 if (DAC_IS_TI3025) {
	    if (vga256InfoRec.bitsPerPixel > 8)
	       s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0, 0);
	    else
	       s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0, TI_AUX_W_CMPL);
	 }
	 else {
            /* set aux control to self clocked only                        */
            s3OutTiIndReg(TI_AUXILIARY_CONTROL, 0, TI_AUX_SELF_CLOCK);
	 }
         /*
          * set output clocking to default of VGA.
          */
         s3OutTiIndReg(TI_OUTPUT_CLOCK_SELECT, 0x00, TI_OCLK_VGA);

         /* set mux control 1 and 2 to provide pseudocolor VGA          */
         s3OutTiIndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_PSEUDO_COLOR);
         s3OutTiIndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_VGA);

         /* change to 8-bit DAC and re-route the data path and clocking */
         s3OutTiIndReg(TI_GENERAL_IO_CONTROL, 0x00, TI_GIC_ALL_BITS);
         if (s3DAC8Bit)
            s3OutTiIndReg(TI_GENERAL_IO_DATA, 0x00, TI_GID_S3_DAC_8BIT);
         else
            s3OutTiIndReg(TI_GENERAL_IO_DATA, 0x00, TI_GID_S3_DAC_6BIT);
      }  /* end of s3PixelMultiplexing */

      /* for some reason the bios doesn't set this properly          */
      s3OutTiIndReg(TI_SENSE_TEST, 0x00, 0x00);

      /* True color modes need the palette initialized. */
      if (s3Bpp > 1) {
	  int i;

	  outb(0x3C6, 0xFF);
	  outb(0x3C8, 0x00);
	  for (i = 0; i < 768; i++) {
	      outb(0x3C9, i);
	      DACDelay;
	      outb(0x3C9, i);
	      DACDelay;
	      outb(0x3C9, i);
	      DACDelay;
	  }
      }

      outb(0x3C4, 1);
      outb(0x3C5, tmp2);        /* unblank the screen */

      return 1;
}

/***********************************************************\

			ATT498_DAC 
			ATT20C498_DAC			
			ATT22C498_DAC 
			ATT20C409_DAC

\***********************************************************/

static Bool ATT409_498_Probe(int type)
{
    int found = 0;
    int dir, mir, olddaccomm;

    /*quick check*/
    if (!S3_86x_SERIES(s3ChipId) && !S3_805_I_SERIES(s3ChipId))
	return FALSE;

    xf86dactopel();
    xf86dactocomm();
    (void)inb(0x3C6);
    mir = inb(0x3C6);
    dir = inb(0x3C6);
    xf86dactopel();

    if ((mir == 0x84) && (dir == 0x98)) {
	olddaccomm = xf86getdaccomm();
	xf86setdaccomm(0x0a);
	if (xf86getdaccomm() == 0) 
		found = ATT22C498_DAC;
	else
		found = ATT498_DAC;
	
	xf86setdaccomm(olddaccomm);
    } else if ((mir == 0x84) && (dir == 0x09)) {
	found = ATT20C409_DAC;
    } else if ((mir == 0x84) && (dir == 0x99)) {
	/*
	 * according to the 21C499 data sheet it is fully compatible
	 * with the 22C409. So we will only miss its new features
	 * this way, but in theory things might work.
	 */
	found = ATT20C409_DAC;
	if(type == ATT20C409_DAC) {
	    ErrorF("%s %s: Detected an ATT 21C499 RAMDAC\n",
		XCONFIG_PROBED, vga256InfoRec.name);
	    ErrorF("%s %s:    support for this RAMDAC is untested. "
		"Please report to XFree86@XFree86.Org\n",
		XCONFIG_PROBED, vga256InfoRec.name);
   	}
    }

    if (found == ATT20C409_DAC) {
	if (!OFLG_ISSET(CLOCK_OPTION_ATT409, &vga256InfoRec.clockOptions)) {
	    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	    OFLG_SET(CLOCK_OPTION_ATT409, &vga256InfoRec.clockOptions);
	    s3ClockChipProbed = XCONFIG_PROBED;
	}
    }

    return (found == type);
}

static Bool ATT498_Probe()
{
    if(ATT409_498_Probe(ATT498_DAC)) {
	ErrorF("%s %s: Detected an ATT 20C498/21C498 RAMDAC\n",
			XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
    } else return FALSE;
}

static Bool ATT22C498_Probe()
{
    if(ATT409_498_Probe(ATT22C498_DAC)) {
	ErrorF("%s %s: Detected an ATT 22C498 RAMDAC\n",
			XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
    } else return FALSE;
}

static Bool ATT20C409_Probe()
{
    if(ATT409_498_Probe(ATT20C409_DAC)) {
	ErrorF("%s %s: Detected an ATT 20C409 RAMDAC\n",
		XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
    } else return FALSE;
}


static int ATT409_498_PreInit()
{
    /* Verify that depth is supported by ramdac */
	/* all are supported */
	
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    if((xf86bpp <= 8) && 
	(S3_x64_SERIES(s3ChipId) || S3_805_I_SERIES(s3ChipId))) {
    	s3ATT498PixMux = TRUE;
       	pixMuxPossible = TRUE;

        if (DAC_IS_ATT20C498) {  
	  if (S3_866_SERIES(s3ChipId) || S3_868_SERIES(s3ChipId)) {
	    nonMuxMaxClock = 100000; /* 866/868 DCLK limit */
	    pixMuxMinClock =  67500;
	  }
	  else if (S3_864_SERIES(s3ChipId)) {
	    nonMuxMaxClock =  95000; /* 864 DCLK limit */
	    pixMuxMinClock =  67500;
	  }
	  else if (S3_805_I_SERIES(s3ChipId)) {
	    nonMuxMaxClock = 80000;  /* XXXX just a guess, who has 805i docs? */
	    pixMuxMinClock = 67500;
	  }
	  else {
	    nonMuxMaxClock = 67500;
	    pixMuxMinClock = 67500;
	  }
        }
        else {
	 nonMuxMaxClock = 67500;
	 pixMuxMinClock = 67500;
        }
        allowPixMuxInterlace = TRUE;
        allowPixMuxSwitching = TRUE;
        pixMuxLimitedWidths = FALSE;
        pixMuxMinWidth = 0;

    }		

    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */
   if (DAC_IS_ATT20C409 && 
       !OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions)) {
      OFLG_SET(CLOCK_OPTION_ATT409, &vga256InfoRec.clockOptions);
      OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
      s3ClockChipProbed = XCONFIG_PROBED;
   }
   if (OFLG_ISSET(CLOCK_OPTION_ATT409, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = att409ClockSelect;
      s3numClocks = 3;
      s3maxRawClock = vga256InfoRec.dacSpeeds[0]; /* Is this right?? */
      if (xf86Verbose)
	 ErrorF("%s %s: Using ATT20C409/ATT20C499 programmable clock\n",
		s3ClockChipProbed, vga256InfoRec.name);
   }
   else 
       OtherClocksSetup();   


   if (s3ATT498PixMux) {
	 vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
	 if (s3Bpp == 1)	/* XXXX is this right?? */
	    s3clockDoublingPossible = TRUE;
   } else {
	 if (vga256InfoRec.dacSpeeds[0] >= 135000) /* 20C498 -13, -15, -17 */
	    vga256InfoRec.maxClock = 110000;
	 else				   /* 20C498 -11 */
	    vga256InfoRec.maxClock = 80000;
	 /* Halve it for 32bpp */
	 if (s3Bpp == 4) {
	    vga256InfoRec.maxClock /= 2;
	    s3maxRawClock /= 2;
	 }
   }

   return 1;
}

static void ATT409_498_Restore(vgaS3Ptr restore)
{
   xf86setdaccomm(restore->s3DacRegs[0]);
}

static void ATT409_498_Save(vgaS3Ptr save)
{
   save->s3DacRegs[0] = xf86getdaccomm();
}

static int ATT409_498_Init(DisplayModePtr mode)
{
      unsigned char tmp, blank;

      if (s3DAC8Bit) 
         xf86setdaccommbit(0x02);
      else 
	 xf86clrdaccommbit(0x02);
     

      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      if (s3PixelMultiplexing) { /* x64:pixmux */
	 /* pixmux with 16/32 bpp not possible for 864 ==> only 8bit mode  */
	 int daccomm;
	 tmp = xf86getdaccomm();
	 
	 if (DAC_IS_ATT22C498) {
	    if (vga256InfoRec.clock[mode->Clock]/2 < 22500) daccomm = 0x20;
	    else if (vga256InfoRec.clock[mode->Clock]/2 < 45000) daccomm = 0x21;
	    else daccomm = 0x24;
#if 0
	    /* using digital clock doubler; 20C498 compatible */
	    daccomm = 0x25;
#endif
	 } else daccomm = 0x20;

#ifdef EXTENDED_DEBUG
	 ErrorF("Putting AT&T 2xC4[09][89] RAMDAC into pixmux\n");
#endif
	 xf86setdaccomm( (tmp&0x02) | daccomm );  /* set mode 2,
						  pixel multiplexing on */

	 if ( ! DAC_IS_ATT20C409 ) {
	    outb(vgaCRIndex, 0x33);	/* set VCLK = -DCLK */
	    tmp = inb(vgaCRReg) | 0x08;
	    outb(vgaCRReg, tmp);
	 }
	 
	 if (S3_x64_SERIES(s3ChipId) || S3_805_I_SERIES(s3ChipId)) {
	    outb(vgaCRIndex, 0x67);	/* set Mode 8: Two 8-bit color
	    				   1 VCLK / 2 pixels */
	    if (    OFLG_ISSET(OPTION_NUMBER_NINE, &vga256InfoRec.options)
	         || DAC_IS_ATT20C409 )
	       outb(vgaCRReg, 0x10 );	/* VCLK is out of phase with DCLK */
	    else
	       outb(vgaCRReg, 0x11 );	/* VCLK is in phase with DCLK */

	    outb(vgaCRIndex, 0x6d);
	    outb(vgaCRReg, 2 );     /* delay -BLANK pulse by 2 DCLKs */
	 }
	 else {
	    /* don't know */
	 }
      } else { /* !s3PixelMultiplexing */
	 outb(vgaCRIndex, 0x33);
	 tmp = inb(vgaCRReg) & ~0x08;
	 outb(vgaCRReg, tmp);

	 tmp = xf86getdaccomm() & 0x0f;

	 if (S3_x64_SERIES(s3ChipId) || S3_805_I_SERIES(s3ChipId)) {
	    int invert_vclk = 0;
	    int delay_blank = 0;
	    outb(vgaCRIndex, 0x67);

	    switch (vga256InfoRec.bitsPerPixel) {
	    case 8: /* set Mode  0:  8-bit color, 1 VCLK/pixel */
	       outb(vgaCRReg, 0x00 | invert_vclk); 
	       xf86setdaccomm(tmp | 0x00);  /* set mode 0 */
	       break;
	    case 16: 
	       if (xf86weight.green == 5) {
		  outb(vgaCRReg, 0x30 | invert_vclk); /* set Mode 9:
						15-bit color, 1 VCLK/pixel */
		  xf86setdaccomm(tmp | 0x10);  /* set mode 1 */
	       }
	       else {
		  outb(vgaCRReg, 0x50 | invert_vclk); /* set Mode 10:
						16-bit color, 1 VCLK/pixel */
		  xf86setdaccomm(tmp | 0x30);  /* set mode 3 */
	       }
	       delay_blank = 2;
	       break;
	    case 32: /* set Mode 11: 24/32-bit color, 2 VCLK/pixel */
	       outb(vgaCRReg, 0x70 | invert_vclk);  
	       xf86setdaccomm(tmp | 0x50);  /* set mode 5 */
	       if (mode->HDisplay > 640)   /* why not for 640 ?  tsk */
		  delay_blank = 2;
	       break;
	    default:
	       ;
	    }
	    outb(vgaCRIndex, 0x6d);
	    outb(vgaCRReg, delay_blank);
	 }
	 else {
	    /* don't know */
	 }
      }  /* end of s3PixelMultiplexing */

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */


     return 1;
}

/********************************************************\

		 SC15025_DAC 

\********************************************************/

static Bool SC15025_Probe()
{
    /* What chipsets use this? so we can do a quick check */

    Bool found = FALSE;
    int i;
    unsigned char c,id[4];

    c = xf86getdaccomm();
    xf86setdaccomm(c | 0x10);
    for (i=0; i<4; i++) {
	    outb(0x3C7, 0x9+i); 
	    id[i] = inb(0x3C8);
    }
    xf86setdaccomm(c);
    xf86dactopel();
    if (id[0] == 'S' && ((id[1]<<8)|id[2]) == 15025) {  
        /* unique for the SC 15025/26 */
	if (id[3] != 'A') {           /* version number */
	   ErrorF(
 	     "%s %s: ==> New Sierra SC 15025/26 version (0x%x) found,\n",
		XCONFIG_PROBED, vga256InfoRec.name, id[3]);
	   ErrorF("\tplease report!\n");
	}
	ErrorF("%s %s: Detected a Sierra SC 15025/26 RAMDAC\n",
	           XCONFIG_PROBED, vga256InfoRec.name);
	found = TRUE;
    }
    return found;    
}

static int SC15025_PreInit()
{
    int doubleEdgeLimit;
    /* Verify that depth is supported by ramdac */
	/* all are supported */

    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
	/* no PixMux */

    /* let OtherClocksSetup() take care of the clock options */
    OtherClocksSetup();
     
    /* Make any necessary clock alterations due to multiplexing,
	clock doubling, etc...  s3Probe will do some last minute
	clock sanity checks when we return */

    if (vga256InfoRec.dacSpeeds[0] >= 125000)	/* -125 */
	  doubleEdgeLimit = 85000;
    else if (vga256InfoRec.dacSpeeds[0] >= 110000)	/* -110 */
	  doubleEdgeLimit = 65000;
    else					/* -80, -66 */
	  doubleEdgeLimit = 50000;
    switch (s3Bpp) {
	  case 1:
	    vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[0];
	    break;
	  case 2:
	    vga256InfoRec.maxClock = doubleEdgeLimit;
	    s3maxRawClock /= 2;
	    break;
	  case 4:
	    vga256InfoRec.maxClock = doubleEdgeLimit / 2;
	    s3maxRawClock /= 4;
	    break;
    }

    return 1;
}

static void SC15025_Restore(vgaS3Ptr restore)
{
      unsigned char c;

      c=xf86getdaccomm();
      xf86setdaccomm( c | 0x10 );  /* set internal register access */
      (void)xf86dactocomm();
      outb(0x3c7, 0x8);   /* Auxiliary Control Register */
      outb(0x3c8, restore->s3DacRegs[1]);
      outb(0x3c7, 0x10);  /* Pixel Repack Register */
      outb(0x3c8, restore->s3DacRegs[2]);
      xf86setdaccomm( c );
      xf86setdaccomm(restore->s3DacRegs[0]);
}

static void SC15025_Save(vgaS3Ptr save)
{
      LOCK_SYS_REGS;
         save->s3DacRegs[0] = xf86getdaccomm();
	 xf86setdaccomm((save->s3DacRegs[0] | 0x10));
         (void)xf86dactocomm();
	 outb(0x3c7,0x8);   /* Auxiliary Control Register */
	 save->s3DacRegs[1] = inb(0x3c8);
	 outb(0x3c7,0x10);  /* Pixel Repack Register */
	 save->s3DacRegs[2] = inb(0x3c8);
	 xf86setdaccomm(save->s3DacRegs[0]);
      UNLOCK_SYS_REGS;
}

static int SC15025_Init(DisplayModePtr mode)
{
      unsigned char aux=0, comm=0, prr=0;

      if (s3DAC8Bit || vga256InfoRec.bitsPerPixel > 8) aux=1;
      switch (vga256InfoRec.bitsPerPixel) {
      case 8: 
	 comm = 0;  /* repack mode 0, color mode 0 */
	 break;
      case 16:
	 if (xf86weight.green == 5) {
	    comm = 0x80;  /* repack mode 1a using both clock edges */
	 }
	 else {  /* RGB16_565 */
	    comm = 0xc0;  /* repack mode 1a using both clock edges */
	 }
	 break;
      case 32:
	 comm = 0x40;  /* repack mode 3a using both clock edges */
	 prr = 1;
	 break;
      default:
	 return 0;
      }
      
      comm |= 0x08;  /* enable LUT for gamma correction */

      xf86setdaccomm(comm | 0x10);
      outb(0x3c7,0x8);
      outb(0x3c8,aux);
      outb(0x3c7,0x10);
      outb(0x3c8,prr);
      xf86setdaccomm(comm);
	
      return 1;
}

/*********************************************************\

 			STG1700_DAC  
			STG1703_DAC 

\*********************************************************/

static Bool STG17xx_Probe(int type)
{
    int found = 0;
    int cid, did, daccomm, readmask;

    if (!S3_86x_SERIES(s3ChipId) && !S3_805_I_SERIES(s3ChipId))
	return FALSE;

    readmask = inb(0x3c6);
    xf86dactopel();
    daccomm = xf86getdaccomm();
    xf86setdaccommbit(0x10);
    xf86dactocomm();
    inb(0x3c6);
    outb(0x3c6, 0x00);
    outb(0x3c6, 0x00);
    cid = inb(0x3c6);     /* company ID */
    did = inb(0x3c6);     /* device ID */
    xf86dactopel(); 
    outb(0x3c6,readmask);

    if ((cid == 0x44) && (did == 0x00))
    {
	found = STG1700_DAC;
    }  else if ((cid == 0x44) && (did == 0x03)) {
	found = STG1703_DAC;
	if (!OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE,&vga256InfoRec.clockOptions)) {
	       OFLG_SET(CLOCK_OPTION_STG1703,    &vga256InfoRec.clockOptions);
	       OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	       s3ClockChipProbed = XCONFIG_PROBED;
	}
    }

    xf86setdaccomm(daccomm);

    return (found == type);
}

static Bool STG1700_Probe()
{
    if(STG17xx_Probe(STG1700_DAC)) {
	ErrorF("%s %s: Detected an STG1700 RAMDAC\n",
	           XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
    } else return FALSE;
}

static Bool STG1703_Probe()
{
    if(STG17xx_Probe(STG1703_DAC)) {
	ErrorF("%s %s: Detected an STG1703 RAMDAC\n",
	           XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
    } else return FALSE;
}



static int STG17xx_PreInit()
{
    /* Verify that depth is supported by ramdac */
	/* all are supported */
   
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    if((xf86bpp <= 8) &&  (S3_x64_SERIES(s3ChipId) || 
			S3_805_I_SERIES(s3ChipId))) {
	s3ATT498PixMux = TRUE;
        pixMuxPossible = TRUE;
	nonMuxMaxClock = 67500;
	pixMuxMinClock = 67500;
        allowPixMuxInterlace = TRUE;
        allowPixMuxSwitching = TRUE;
        pixMuxLimitedWidths = FALSE;
        pixMuxMinWidth = 0;

    }   

    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */
    if (OFLG_ISSET(CLOCK_OPTION_STG1703, &vga256InfoRec.clockOptions)) {
      unsigned char mi, ml, mh, tmp;
      int mclk;
      
      outb(vgaCRIndex, 0x43);
      tmp = inb(vgaCRReg);
      outb(vgaCRReg, tmp & ~0x02);
      
      outb(vgaCRIndex, 0x55);
      tmp = inb(vgaCRReg) & ~0x03;
      outb(vgaCRReg, tmp | 1);  /* set RS2 */
      
      outb(0x3c7, 0x00);  /* index high */
      outb(0x3c8, 0x48);  /* index low  */
      mi = (inb(0x3c9) >> 4) & 0x03;
      
      outb(0x3c8, 0x40 + 2*mi);  /* index low  */
      ml = inb(0x3c9);
      mh = inb(0x3c9);
      
      outb(vgaCRReg, tmp);  /* reset RS2 */
      
      mclk = ((((1431818 * ((ml&0x7f) + 2)) / ((mh&0x1f) + 2)) 
	       >> ((mh>>5)&0x03)) + 50) / 100;
      
      s3ClockSelectFunc = STG1703ClockSelect;
      s3numClocks = 3;
      s3maxRawClock = 135000;

      if (xf86Verbose)
	 ErrorF("%s %s: Using STG1703 programmable clock(MCLK%d %02x %02x "
	   "%1.3f MHz)\n",XCONFIG_GIVEN, vga256InfoRec.name, mi, ml,mh, mclk/1e3);
      if (vga256InfoRec.s3MClk > 0) {
	 if (xf86Verbose)
	    ErrorF("%s %s: using specified MCLK value of %1.3f MHz for DRAM "
 	"timings\n",XCONFIG_GIVEN, vga256InfoRec.name, vga256InfoRec.s3MClk / 1000.0);
      }
      else
	 vga256InfoRec.s3MClk = mclk;
      
   }
   else
       OtherClocksSetup();

    /* Make any necessary clock alterations due to multiplexing,
	clock doubling, etc...  s3Probe will do some last minute
	clock sanity checks when we return */
   if (s3ATT498PixMux) {
	 vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
	 if (s3Bpp == 1)	/* XXXX is this right?? */
	    s3clockDoublingPossible = TRUE;
   }
   else {
	 if (vga256InfoRec.dacSpeeds[0] >= 135000) /* 20C498 -13, -15, -17 */
	    vga256InfoRec.maxClock = 110000;
	 else				   /* 20C498 -11 */
	    vga256InfoRec.maxClock = 80000;
	 /* Halve it for 32bpp */
	 if (s3Bpp == 4) {
	    vga256InfoRec.maxClock /= 2;
	    s3maxRawClock /= 2;
	 }
   }

   return 1;
}

static void STG17xx_Restore(vgaS3Ptr restore)
{
      xf86dactopel();
      xf86setdaccommbit(0x10);   /* enable extended registers */
      xf86dactocomm();
      inb(0x3c6);                /* command reg */

      outb(0x3c6, 0x03);         /* index low */
      outb(0x3c6, 0x00);         /* index high */
      
      outb(0x3c6, restore->s3DacRegs[1]);  /* primary pixel mode */
      outb(0x3c6, restore->s3DacRegs[2]);  /* secondary pixel mode */
      outb(0x3c6, restore->s3DacRegs[3]);  /* PLL control */
      usleep(500);		           /* PLL settling time */

      xf86dactopel();
      xf86setdaccomm(restore->s3DacRegs[0]);
}


static void STG17xx_Save(vgaS3Ptr save)
{
         xf86dactopel();
         save->s3DacRegs[0] = xf86getdaccomm();

         xf86setdaccommbit(0x10);   /* enable extended registers */
         xf86dactocomm();
         inb(0x3c6);                /* command reg */

         outb(0x3c6, 0x03);         /* index low */
         outb(0x3c6, 0x00);         /* index high */

         save->s3DacRegs[1] = inb(0x3c6);  /* primary pixel mode */
         save->s3DacRegs[2] = inb(0x3c6);  /* secondary pixel mode */
         save->s3DacRegs[3] = inb(0x3c6);  /* PLL control */

         xf86dactopel();
}

static int STG17xx_Init(DisplayModePtr mode)
{
      unsigned char blank, tmp;
      int daccomm;

      if (s3DAC8Bit) 
         xf86setdaccommbit(0x02);
      else 
	 xf86clrdaccommbit(0x02);

      daccomm = (xf86getdaccomm() & 0x06) | 0x10;

      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      if (s3PixelMultiplexing) {
	 /* x64:pixmux */
	 /* pixmux with 16/32 bpp not possible for 864 ==> only 8bit mode  */
         daccomm |= 0x08;           /* enable extended pixel modes */
	 xf86setdaccomm(daccomm);   /* pixel multiplexing on */

         xf86dactocomm();
         inb(0x3c6);                /* command reg */

         outb(0x3c6, 0x03);         /* index low */
         outb(0x3c6, 0x00);         /* index high */
      
         outb(0x3c6, 0x05);         /* primary pixel mode */
         outb(0x3c6, 0x05);         /* secondary pixel mode */
         outb(0x3c6, 0x02);         /* PLL control for 64-135 MHz pixclk */
         usleep(500);		    /* PLL settling time before LUT access */

         xf86dactopel();

	 outb(vgaCRIndex, 0x33);
	 tmp = inb(vgaCRReg) | 0x08;
	 outb(vgaCRReg, tmp);
	 
	 if (S3_x64_SERIES(s3ChipId) || S3_805_I_SERIES(s3ChipId)) {
	    outb(vgaCRIndex, 0x67);
	    outb(vgaCRReg, 0x11 );   /* set Mode 5: double 8-bit indexed color,
					  1 VCLK/2 pixels */
	    outb(vgaCRIndex, 0x6d);
	    outb(vgaCRReg, 2 );     /* delay -BLANK pulse by 2 DCLKs */
	 }
	 else {
	    /* don't know */
	 }
      }
      else 
      { /* !s3PixelMultiplexing */
	 outb(vgaCRIndex, 0x33);
	 tmp = inb(vgaCRReg) & ~0x08;
	 outb(vgaCRReg, tmp);

	 if (S3_x64_SERIES(s3ChipId) || S3_805_I_SERIES(s3ChipId)) {
	    int invert_vclk = 0;
	    int delay_blank = 0;
            int pixmode     = 0;
            int s3mux       = 0;

	    outb(vgaCRIndex, 0x67);

	    switch (vga256InfoRec.bitsPerPixel) 
            {
	       case 8: /* set Mode  0:  8-bit indexed color, 1 VCLK/pixel */
	          daccomm |= 0x00;
                  s3mux    = 0x00 | invert_vclk; 
	          break;

       	       case 16: 
	          if (xf86weight.green == 5) 
                  {  /* set Mode 2: 15-bit direct color */
	             daccomm |= 0xa8;
                     pixmode  = 0x02;
                     s3mux    = 0x30 | invert_vclk; 
	          }
	          else 
                  {  /* set Mode 3: 16-bit (565) direct color */
	             daccomm |= 0xc8;
                     pixmode  = 0x03;
                     s3mux    = 0x50 | invert_vclk; 
	          }
	          delay_blank = 2;
	          break;

	       case 32: /* set Mode 4: 24-bit direct color, 2 VCLK/pixel */
	          daccomm |= 0xe8;
                  pixmode  = 0x04;
                  s3mux    = 0x70 | invert_vclk; 
	          delay_blank = 2;
	          break;

	       default:
	          ErrorF("default switch 2\n");
	    }
	    outb(vgaCRReg, s3mux);  
	    xf86setdaccomm(daccomm);

            xf86dactocomm();
            inb(0x3c6);                /* command reg */

            outb(0x3c6, 0x03);         /* index low */
            outb(0x3c6, 0x00);         /* index high */
      
            outb(0x3c6, pixmode);      /* primary pixel mode */
            outb(0x3c6, pixmode);      /* secondary pixel mode */

            xf86dactopel();

	    outb(vgaCRIndex, 0x6d);
	    outb(vgaCRReg, delay_blank);
	 }
	 else {
	    /* don't know */
	 }
      }  /* end of s3PixelMultiplexing */

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */
   
      return 1;
}

/*********************************************************\

 			S3_SDAC_DAC  
			S3_GENDAC_DAC 

\*********************************************************/

static Bool S3_SDAC_GENDAC_Probe(int type)
{
   /* probe for S3 GENDAC or SDAC */
   /*
    * S3 GENDAC and SDAC have two fixed read only PLL clocks
    *     CLK0 f0: 25.255MHz   M-byte 0x28  N-byte 0x61
    *     CLK0 f1: 28.311MHz   M-byte 0x3d  N-byte 0x62
    * which can be used to detect GENDAC and SDAC since there is no chip-id
    * for the GENDAC.
    *
    * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values
    * for CLK0 f0 and f1 always returns 0x7f (but is documented "read only")
    */
      
   unsigned char saveCR55, saveCR45, saveCR43, savelut[6];
   unsigned int i;		/* don't use signed int, UW2.0 compiler bug */
   long clock01, clock23;
   int found = 0;

   outb(vgaCRIndex, 0x43);
   saveCR43 = inb(vgaCRReg);
   outb(vgaCRReg, saveCR43 & ~0x02);

   outb(vgaCRIndex, 0x45);
   saveCR45 = inb(vgaCRReg);
   outb(vgaCRReg, saveCR45 & ~0x20);

   outb(vgaCRIndex, 0x55);
   saveCR55 = inb(vgaCRReg);
   outb(vgaCRReg, saveCR55 & ~1);

   outb(0x3c7,0);
   for(i=0; i<2*3; i++)		/* save first two LUT entries */
      savelut[i] = inb(0x3c9);
   outb(0x3c8,0);
   for(i=0; i<2*3; i++)		/* set first two LUT entries to zero */
      outb(0x3c9,0);

   outb(vgaCRIndex, 0x55);
   outb(vgaCRReg, saveCR55 | 1);

   outb(0x3c7,0);
   for(i=clock01=0; i<4; i++)
      clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff);
   for(i=clock23=0; i<4; i++)
      clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff);

   outb(vgaCRIndex, 0x55);
   outb(vgaCRReg, saveCR55 & ~1);

   outb(0x3c8,0);
   for(i=0; i<2*3; i++)		/* restore first two LUT entries */
      outb(0x3c9,savelut[i]);

   outb(vgaCRIndex, 0x55);
   outb(vgaCRReg, saveCR55);

   if ( clock01 == 0x28613d62 ||
       (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) {

	
	 xf86dactopel();
	 inb(0x3c6);
	 inb(0x3c6);
	 inb(0x3c6);

	 /* the fourth read will show the SDAC chip ID and revision */
	 if (((i=inb(0x3c6)) & 0xf0) == 0x70) {
	    found = S3_SDAC_DAC;
	    saveCR43 &= ~0x02;
	    saveCR45 &= ~0x20;
	 }
	 else {
	    found = S3_GENDAC_DAC;
	    saveCR43 &= ~0x02;
	    saveCR45 &= ~0x20;
	 }
         xf86dactopel();
   }

   outb(vgaCRIndex, 0x45);
   outb(vgaCRReg, saveCR45);

   outb(vgaCRIndex, 0x43);
   outb(vgaCRReg, saveCR43);

   if((found==type) && !OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, 
					&vga256InfoRec.clockOptions)) {
	OFLG_SET(CLOCK_OPTION_S3GENDAC,    &vga256InfoRec.clockOptions);
	OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	s3ClockChipProbed = XCONFIG_PROBED;
   }


   return (found == type);
}

static Bool S3_SDAC_Probe()
{
   if (!S3_86x_SERIES(s3ChipId) && !S3_805_I_SERIES(s3ChipId))
	return FALSE;
   else if(S3_SDAC_GENDAC_Probe(S3_SDAC_DAC)) {
   ErrorF("%s %s: Detected an S3 SDAC 86C716 RAMDAC and programmable clock\n", 
		XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
   } else return FALSE;
}

static Bool S3_GENDAC_Probe()
{
   if (!S3_801_SERIES(s3ChipId))
	return FALSE;
   else if (S3_SDAC_GENDAC_Probe(S3_GENDAC_DAC)) {
  ErrorF("%s %s: Detected an S3 GENDAC 86C708 RAMDAC and programmable clock\n", 
			XCONFIG_PROBED, vga256InfoRec.name);
	return TRUE;
   } else return FALSE;
}

static int S3_SDAC_GENDAC_PreInit()
{
    /* Verify that depth is supported by ramdac */
	/* all are supported */
   
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    if(DAC_IS_SDAC && (xf86bpp <= 8) && 
	(S3_x64_SERIES(s3ChipId) || S3_805_I_SERIES(s3ChipId))) {
	s3ATT498PixMux = TRUE;
        pixMuxPossible = TRUE;
	nonMuxMaxClock = 67500;
	pixMuxMinClock = 67500;
        allowPixMuxInterlace = TRUE;
        allowPixMuxSwitching = TRUE;
        pixMuxLimitedWidths = FALSE;
        pixMuxMinWidth = 0;
    }

    OtherClocksSetup();   /* S3_GENDAC clock is in OtherClocksSetup()
			     since it is compatible with the ICS5342 */

    /* Make any necessary clock alterations due to multiplexing,
	clock doubling, etc...  s3Probe will do some last minute
	clock sanity checks when we return */
    if(DAC_IS_SDAC) {
    	if (s3ATT498PixMux) {
	  vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
	  if (s3Bpp == 1)	/* XXXX is this right?? */
	    s3clockDoublingPossible = TRUE;
        } else {
	  if (vga256InfoRec.dacSpeeds[0] >= 135000) /* 20C498 -13, -15, -17 */
	    vga256InfoRec.maxClock = 110000;
	  else				   /* 20C498 -11 */
	    vga256InfoRec.maxClock = 80000;
	 /* Halve it for 32bpp */
	  if (s3Bpp == 4) {
	    vga256InfoRec.maxClock /= 2;
	    s3maxRawClock /= 2;
	  }
        }
     } else  /* DAC_IS_GENDAC */
        vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];

      return 1;
}


static void S3_SDAC_GENDAC_Restore(vgaS3Ptr restore)
{
#if !defined(PC98_PW) && !defined(PC98_PWLB)
      unsigned char tmp;

      outb(vgaCRIndex, 0x55);
      tmp = inb(vgaCRReg);
      outb(vgaCRReg, tmp | 1);

      outb(0x3c6, restore->s3DacRegs[0]);      /* Enhanced command register */
      outb(0x3c8, 2);                   /* index to f2 reg */
      outb(0x3c9, restore->s3DacRegs[3]);      /* f2 PLL M divider */
      outb(0x3c9, restore->s3DacRegs[4]);      /* f2 PLL N1/N2 divider */
      outb(0x3c8, 0x0e);                /* index to PLL control */
      outb(0x3c9, restore->s3DacRegs[5]);      /* PLL control */
      outb(0x3c8, restore->s3DacRegs[2]);      /* PLL write index */
      outb(0x3c7, restore->s3DacRegs[1]);      /* PLL read index */

      outb(vgaCRReg, tmp & ~1);
#endif
}

static void S3_SDAC_GENDAC_Save(vgaS3Ptr save)
{
#if !defined(PC98_PW) && !defined(PC98_PWLB)
         unsigned char tmp;

         outb(vgaCRIndex, 0x55);
	 tmp = inb(vgaCRReg);
         outb(vgaCRReg, tmp | 1);

         save->s3DacRegs[0] = inb(0x3c6);      /* Enhanced command register */
         save->s3DacRegs[2] = inb(0x3c8);      /* PLL write index */
         save->s3DacRegs[1] = inb(0x3c7);      /* PLL read index */
         outb(0x3c7, 2);                   /* index to f2 reg */
         save->s3DacRegs[3] = inb(0x3c9);      /* f2 PLL M divider */
         save->s3DacRegs[4] = inb(0x3c9);      /* f2 PLL N1/N2 divider */
         outb(0x3c7, 0x0e);                /* index to PLL control */
         save->s3DacRegs[5] = inb(0x3c9);      /* PLL control */

         outb(vgaCRReg, tmp & ~1);
#endif
}

static int S3_SDAC_Init(DisplayModePtr mode)
{
      int pixmux = 0;           /* SDAC command and CR67 */
      int blank_delay = 0;      /* CR6D */
      int invert_vclk = 0;      /* CR66 bit 0 */
      unsigned char blank, tmp;

      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      if (s3PixelMultiplexing)
      {
	 /* x64:pixmux */
	 /* pixmux with 16/32 bpp not possible for 864 ==> only 8bit mode  */
         pixmux = 0x10;         /* two 8bit pixels per clock */
	 if (!S3_866_SERIES(s3ChipId) && !S3_868_SERIES(s3ChipId))
	    invert_vclk = 1;
         blank_delay = 2;
      }
      else
      {
         switch (vga256InfoRec.bitsPerPixel) 
         {
            case 8:  /* 8-bit color, 1 VCLK/pixel */
               break;

            case 16: /* 15/16-bit color, 1VCLK/pixel */
               if (xf86weight.green == 5)
                  pixmux = 0x30;
               else
                  pixmux = 0x50;
               blank_delay = 2;
               break;

            case 32: /* 32-bit color, 2VCLK/pixel */
               pixmux = 0x70;
               blank_delay = 2;
         }
      }

      outb(vgaCRIndex, 0x55);
      tmp = inb(vgaCRReg) | 1;
      outb(vgaCRReg, tmp);

      outb(vgaCRIndex, 0x67);
      outb(vgaCRReg, pixmux | invert_vclk);    /* set S3 mux mode */
      outb(0x3c6, pixmux);                     /* set SDAC mux mode */

      outb(vgaCRIndex, 0x6D);
      outb(vgaCRReg, blank_delay);             /* set blank delay */

      outb(vgaCRIndex, 0x55);
      tmp = inb(vgaCRReg) & ~1;
      outb(vgaCRReg, tmp);

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */

      return 1;
}

static int S3_GENDAC_Init(DisplayModePtr mode)
{
      int daccomm = 0;           /* GENDAC command */
      unsigned char blank, tmp;

      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      switch (vga256InfoRec.bitsPerPixel) 
      {
         case 8:  /* 8-bit color, 1 VCLK/pixel */
            break;

         case 16: /* 15/16-bit color, 2VCLK/pixel */
            if (xf86weight.green == 5)
               daccomm = 0x20;
            else
               daccomm = 0x60;
            break;

         case 32: /* 32-bit color, 3VCLK/pixel */
            daccomm = 0x40;
      }

      outb(vgaCRIndex, 0x55);
      tmp = inb(vgaCRReg) | 1;
      outb(vgaCRReg, tmp);

      outb(0x3c6, daccomm);                     /* set GENDAC mux mode */

      outb(vgaCRIndex, 0x55);
      tmp = inb(vgaCRReg) & ~1;
      outb(vgaCRReg, tmp);

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */

      return 1;
}

/***********************************************************\

 			S3_TRIO32_DAC  
			S3_TRIO64_DAC 

\***********************************************************/


static Bool S3_TRIO_Probe(int type) 
{
   int found = 0;

   if (S3_TRIOxx_SERIES(s3ChipId)) {
	 if (S3_TRIO32_SERIES(s3ChipId))
		found = S3_TRIO32_DAC;
	 else if (S3_TRIO64V_SERIES(s3ChipId))
		found = S3_TRIO64V_DAC;
	 else if (S3_TRIO64V2_SERIES(s3ChipId))
		found = S3_TRIO64V2_DAC;
	 else 
		found = S3_TRIO64_DAC;
   }

   if(found == type)
   {
      if (S3_AURORA64VP_SERIES(s3ChipId)) {
	 if ( OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions)) {
	    ErrorF("%s %s: for Aurora64 chips you shouldn't specify any Clockchip\n"
		   "\t other than \"s3_aurora64\" or maybe \"s3_trio64\"\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	    /* Clear the other clock options */
	    OFLG_ZERO(&vga256InfoRec.clockOptions);
	 }
	 if (S3_AURORA64VP_SERIES(s3ChipId) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions)) {
	    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	    OFLG_SET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions);
	    s3ClockChipProbed = XCONFIG_PROBED;
	 }
      }
      else if (S3_TRIO64V2_SERIES(s3ChipId)) {
	 if ( OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3TRIO64V2, &vga256InfoRec.clockOptions)) {
	    ErrorF("%s %s: for Trio64V2 chips you shouldn't specify any Clockchip\n"
		   "\t other than \"s3_trio64v2\" or maybe \"s3_trio64\"\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	    /* Clear the other clock options */
	    OFLG_ZERO(&vga256InfoRec.clockOptions);
	 }
	 if (S3_TRIO64V2_SERIES(s3ChipId) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions)) {
	    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	    OFLG_SET(CLOCK_OPTION_S3TRIO64V2, &vga256InfoRec.clockOptions);
	    s3ClockChipProbed = XCONFIG_PROBED;
	 }
      }
      else {
	 if ( OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions) &&
	     !OFLG_ISSET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions)) {
	    ErrorF("%s %s: for Trio32/64 chips you shouldn't specify a Clockchip\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	    /* Clear the other clock options */
	    OFLG_ZERO(&vga256InfoRec.clockOptions);
	 }
	 if (!OFLG_ISSET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions)) {
	    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	    OFLG_SET(CLOCK_OPTION_S3TRIO, &vga256InfoRec.clockOptions);
	    s3ClockChipProbed = XCONFIG_PROBED;
	 }
      }
   }
  
   return (found == type);
}

static Bool S3_TRIO64V2_Probe()
{
    return S3_TRIO_Probe(S3_TRIO64V2_DAC);
}

static Bool S3_TRIO64V_Probe()
{
    return S3_TRIO_Probe(S3_TRIO64V_DAC);
}

static Bool S3_TRIO64_Probe()
{
    return S3_TRIO_Probe(S3_TRIO64_DAC);
}

static Bool S3_TRIO32_Probe()
{
    return S3_TRIO_Probe(S3_TRIO32_DAC);
}

static int S3_TRIO_PreInit()
{
    unsigned char sr8, sr27, sr28;
    int m,n,n1,n2, mclk;

    /* Verify that depth is supported by ramdac */
	/* all are supported */

    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    if(xf86bpp <= 8) {
	s3ATT498PixMux = TRUE;
       	pixMuxPossible = TRUE;
    	if (OFLG_ISSET(OPTION_TRIO64VP_BUG3, &vga256InfoRec.options)) {
	    nonMuxMaxClock = 0;   /* need CR67=10 and DCLK/2 all the time */
	    pixMuxMinClock = 0;   /* to avoid problems with blank signal */
    	} else {
	    nonMuxMaxClock = 80000;
	    pixMuxMinClock = 80000;
    	}
    	allowPixMuxInterlace = TRUE;
    	allowPixMuxSwitching = TRUE;
    	pixMuxLimitedWidths = FALSE;
    	pixMuxMinWidth = 0;
    }
 	
    vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];


    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */
    s3ClockSelectFunc = s3GendacClockSelect;
    s3numClocks = 3;
    if (OFLG_ISSET(CLOCK_OPTION_S3TRIO64V2, &vga256InfoRec.clockOptions))
       s3maxRawClock = 170000;
    else
       s3maxRawClock = 135000;
      
    outb(0x3c4, 0x08);
    sr8 = inb(0x3c5);
    outb(0x3c5, 0x06);
      
    outb(0x3c4, 0x11);
    m = inb(0x3c5);
    outb(0x3c4, 0x10);
    n = inb(0x3c5);
      
    m &= 0x7f;
    n1 = n & 0x1f;
    n2 = (n>>5) & 0x03;
    mclk = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
    if (OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions)) {       
       outb(0x3c4, 0x27);
       sr27 = inb(0x3c5);
       outb(0x3c4, 0x28);
       sr28 = inb(0x3c5);
       mclk /= ((sr27 >> 2) & 0x03) + 1;
    }

    outb(0x3c4, 0x08);
    outb(0x3c5, sr8);

    if (xf86Verbose)
       if (OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions))
	  ErrorF("%s %s: Using Aurora64 programmable clock (MCLK %1.3f MHz, SR27=%02x, SR28=%02x)\n"
		 ,s3ClockChipProbed, vga256InfoRec.name
		 ,mclk / 1000.0, sr27, sr28);
       else if (OFLG_ISSET(CLOCK_OPTION_S3TRIO64V2, &vga256InfoRec.clockOptions))
	  ErrorF("%s %s: Using Trio64V2 programmable clock (MCLK %1.3f MHz)\n"
		 ,s3ClockChipProbed, vga256InfoRec.name
		 ,mclk / 1000.0);
       else
	 ErrorF("%s %s: Using Trio32/64 programmable clock (MCLK %1.3f MHz)\n"
		,s3ClockChipProbed, vga256InfoRec.name
		,mclk / 1000.0);
    if (vga256InfoRec.s3MClk > 0) {
      if (xf86Verbose)
	ErrorF("%s %s: using specified MCLK value of %1.3f MHz for DRAM "
	 "timings\n",XCONFIG_GIVEN, vga256InfoRec.name, vga256InfoRec.s3MClk/1000.0);
    } else {
	 vga256InfoRec.s3MClk = mclk;
    }

 
    return 1;  
}

static void S3_TRIO_Restore(vgaS3Ptr restore)
{
      unsigned char tmp;

      outb(0x3c2, restore->s3DacRegs[0]);
      outb(0x3c4, 0x08); outb(0x3c5, 0x06);

      outb(0x3c4, 0x09); outb(0x3c5, restore->s3DacRegs[2]);
      outb(0x3c4, 0x0a); outb(0x3c5, restore->s3DacRegs[3]);
      outb(0x3c4, 0x0b); outb(0x3c5, restore->s3DacRegs[4]);
      outb(0x3c4, 0x0d); outb(0x3c5, restore->s3DacRegs[5]);

      outb(0x3c4, 0x10); outb(0x3c5, restore->s3DacRegs[8]); 
      outb(0x3c4, 0x11); outb(0x3c5, restore->s3DacRegs[9]); 
      outb(0x3c4, 0x12); outb(0x3c5, restore->s3DacRegs[10]); 
      outb(0x3c4, 0x13); outb(0x3c5, restore->s3DacRegs[11]); 
      outb(0x3c4, 0x1a); outb(0x3c5, restore->s3DacRegs[12]); 
      outb(0x3c4, 0x1b); outb(0x3c5, restore->s3DacRegs[13]); 
      outb(0x3c4, 0x15);
      tmp = inb(0x3c5);
      outb(0x3c4, tmp & ~0x20);
      outb(0x3c4, tmp |  0x20);
      outb(0x3c4, tmp & ~0x20);

      outb(0x3c4, 0x15); outb(0x3c5, restore->s3DacRegs[6]); 
      outb(0x3c4, 0x18); outb(0x3c5, restore->s3DacRegs[7]);

      /* if (S3_AURORA64VP_SERIES(s3ChipId)) { */
      if (OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions)) {
	 int i;
	 for (i=0x1a; i<= 0x6f; i++) {
	    outb(0x3c4, i); outb(0x3c5, restore->s3DacRegs[i]);
	 }
      }

      outb(0x3c4, 0x08); outb(0x3c5, restore->s3DacRegs[1]);

} 

static void S3_TRIO_Save(vgaS3Ptr save)
{
	 save->s3DacRegs[0] = inb(0x3cc);

	 outb(0x3c4, 0x08); save->s3DacRegs[1] = inb(0x3c5);
	 outb(0x3c5, 0x06);

	 outb(0x3c4, 0x09); save->s3DacRegs[2]  = inb(0x3c5);
	 outb(0x3c4, 0x0a); save->s3DacRegs[3]  = inb(0x3c5);
	 outb(0x3c4, 0x0b); save->s3DacRegs[4]  = inb(0x3c5);
	 outb(0x3c4, 0x0d); save->s3DacRegs[5]  = inb(0x3c5);
	 outb(0x3c4, 0x15); save->s3DacRegs[6]  = inb(0x3c5) & 0xfe; 
	 outb(0x3c5, save->s3DacRegs[6]);
	 outb(0x3c4, 0x18); save->s3DacRegs[7]  = inb(0x3c5);

	 outb(0x3c4, 0x10); save->s3DacRegs[8]  = inb(0x3c5);
	 outb(0x3c4, 0x11); save->s3DacRegs[9]  = inb(0x3c5);
	 outb(0x3c4, 0x12); save->s3DacRegs[10] = inb(0x3c5);
	 outb(0x3c4, 0x13); save->s3DacRegs[11] = inb(0x3c5);
	 outb(0x3c4, 0x1a); save->s3DacRegs[12] = inb(0x3c5);
	 outb(0x3c4, 0x1b); save->s3DacRegs[13] = inb(0x3c5);

      /* if (S3_AURORA64VP_SERIES(s3ChipId)) { */
	 if (OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions)) {
	    int i;
	    for (i=0x1a; i<= 0x6f; i++) {
	       outb(0x3c4, i); save->s3DacRegs[i] = inb(0x3c5);
	    }
	 }

	 outb(0x3c4, 8);
	 outb(0x3c5, 0x00);
}

static int S3_TRIO_Init(DisplayModePtr mode)
{
      int pixmux = 0;           /* SDAC command and CR67 */
      int invert_vclk = 0;      /* CR66 bit 0 */
      int sr8, sr15, sr18, cr33;
      unsigned char blank, tmp;
      
      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      outb(0x3c4, 0x08);
      sr8 = inb(0x3c5);
      outb(0x3c5, 0x06);

      outb(0x3c4, 0x0d);        /* fix for VideoLogic GrafixStar cards: */
      tmp = inb(0x3c5) & ~1;
      outb(0x3c5, tmp);

      outb(0x3c4, 0x15);
      sr15 = inb(0x3c5) & ~0x10;  /* XXXX ~0x40 and SynthClock /= 2 in s3.c 
						might be better... */
      outb(0x3c4, 0x18);
      sr18 = inb(0x3c5) & ~0x80;
      outb(vgaCRIndex, 0x33);
      cr33 = inb(vgaCRReg) & ~0x28;

      /* for Trio64+ we need corrected blank signal timing */
      if (!(S3_TRIO64V_SERIES(s3ChipId) && (s3ChipRev <= 0x53)
	    || (S3_TRIO64V2_SERIES(s3ChipId))) ^ 
	  !!OFLG_ISSET(OPTION_TRIO64VP_BUG1, &vga256InfoRec.options)) {
	 cr33 |= 0x20;
      }

      if (s3PixelMultiplexing)
      {
	 /* x64:pixmux */
	 /* pixmux with 16/32 bpp not possible for 864 ==> only 8bit mode  */
         pixmux = 0x10;         /* two 8bit pixels per clock */
         invert_vclk = 2;       /* XXXX strange: reserved bit which helps! */
	 sr15 |= 0x10;  /* XXXX 0x40? see above! */
	 sr18 |= 0x80;
      }
      else
      {
         switch (vga256InfoRec.bitsPerPixel) 
         {
            case 8:  /* 8-bit color, 1 VCLK/pixel */
               break;

            case 16: /* 15/16-bit color, 1VCLK/pixel */
	       cr33 |= 0x08;
               if (xf86weight.green == 5)
                  pixmux = 0x30;
               else
                  pixmux = 0x50;
               break;

            case 32: /* 32-bit color, 2VCLK/pixel */
               pixmux = 0xd0;
         }
      }

      outb(vgaCRReg, cr33);

      outb(vgaCRIndex, 0x67);
      outb(vgaCRReg, pixmux | invert_vclk);    /* set S3 mux mode */

      outb(0x3c4, 0x15);
      outb(0x3c5, sr15);
      outb(0x3c4, 0x18);
      outb(0x3c5, sr18);

      if (OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions)) {
	 outb(0x3c4, 0x28);
	 outb(0x3c5, 0);
      }

      outb(0x3c4, 0x08);
      outb(0x3c5, sr8);

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */

      return 1;
}

/******************************************************\

  			TI3030_DAC  
			TI3026_DAC 

\******************************************************/

static Bool TI3030_3026_Probe(int type)
{ 
    int found = 0;
    unsigned char saveCR55, saveCR45, saveCR43, saveID, saveTIndx;

    if (!S3_964_SERIES(s3ChipId) && !S3_968_SERIES(s3ChipId))
	return FALSE;

    outb(vgaCRIndex, 0x43);
    saveCR43 = inb(vgaCRReg);
    outb(vgaCRReg, saveCR43 & ~0x02);

    outb(vgaCRIndex, 0x45);
    saveCR45 = inb(vgaCRReg);
    outb(vgaCRReg, saveCR45 & ~0x20);

    outb(vgaCRIndex, 0x55);
    saveCR55 = inb(vgaCRReg);

    outb(vgaCRReg, (saveCR55 & 0xFC) | 0x00);
    saveTIndx = inb(0x3c8);
    outb(0x3c8, 0x3f);

    outb(vgaCRReg, (saveCR55 & 0xFC) | 0x02);
    saveID = inb(0x3c6);
    if (saveID == 0x26 || saveID == 0x30) {
	 outb(0x3c6, ~saveID);    /* check if ID reg. is read-only */
	 if (inb(0x3c6) != saveID) {
	       outb(0x3c6, saveID);
	 }
	 else {
	     /*
	      * Found TI ViewPoint 3026/3030 DAC
	      */
	      int rev;
	      outb(vgaCRReg, (saveCR55 & 0xFC) | 0x00);
	      saveTIndx = inb(0x3c8);
	      outb(0x3c8, 0x01);
	      outb(vgaCRReg, (saveCR55 & 0xFC) | 0x02);
	      rev = inb(0x3c6);
	       
	      if (saveID == 0x26)
		  found = TI3026_DAC;
	      else  /* saveID == 0x30 */
		  found = TI3030_DAC;
	      
 	      if(found == type)
	       ErrorF("%s %s: Detected a TI ViewPoint 30%02x RAMDAC rev. %x\n",
		      XCONFIG_PROBED, vga256InfoRec.name, saveID, rev);
	      saveCR43 &= ~0x02;
	      saveCR45 &= ~0x20;
	 }
    }

    /* restore this mess */
    outb(vgaCRReg, (saveCR55 & 0xFC) | 0x00);
    outb(0x3c8, saveTIndx);

    outb(vgaCRIndex, 0x55);
    outb(vgaCRReg, saveCR55);

    outb(vgaCRIndex, 0x45);
    outb(vgaCRReg, saveCR45);

    outb(vgaCRIndex, 0x43);
    outb(vgaCRReg, saveCR43);

    if (found == type)
       Probe_ELSA();

    return (found == type);
}

static Bool TI3030_Probe()
{
    return TI3030_3026_Probe(TI3030_DAC);
}

static Bool TI3026_Probe()
{
    return TI3030_3026_Probe(TI3026_DAC);
}

static int TI3030_3026_PreInit()
{
    int mclk, m, n, p;

    /* Verify that depth is supported by ramdac */
	/* all are supported */
    
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    pixMuxPossible = TRUE;
    allowPixMuxInterlace = TRUE;
    allowPixMuxSwitching = FALSE;
    nonMuxMaxClock = 70000;
    pixMuxLimitedWidths = FALSE;
    if (S3_964_SERIES(s3ChipId)) {
         nonMuxMaxClock = 0;  /* 964 can only be in pixmux mode when */
         pixMuxMinWidth = 0;  /* working in enhanced mode */  
    }

    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */
   
   if(!OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions)) {
   	OFLG_SET(CLOCK_OPTION_TI3026, &vga256InfoRec.clockOptions);
   	OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
   	s3ClockChipProbed = XCONFIG_PROBED;
   }  /* there are cards with Ti3026 and EXTERNAL clock! */

   s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);
   n = s3InTi3026IndReg(TI_MCLK_PLL_DATA) & 0x3f;
   s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x04);
   m = s3InTi3026IndReg(TI_MCLK_PLL_DATA) & 0x3f;
   s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x08);
   p = s3InTi3026IndReg(TI_MCLK_PLL_DATA) & 0x03;
   mclk = ((1431818 * ((65-m) * 8)) / (65-n) / (1 << p) + 50) / 100;
   if (xf86Verbose)
	ErrorF("%s %s: Using Ti3026/3030 programmable MCLK (MCLK %1.3f MHz)\n",
		s3ClockChipProbed, vga256InfoRec.name, mclk / 1000.0);
   s3numClocks = 3;
   if (!vga256InfoRec.s3MClk)
	vga256InfoRec.s3MClk = mclk;

   if (OFLG_ISSET(CLOCK_OPTION_TI3026, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = ti3026ClockSelect;
      s3maxRawClock = vga256InfoRec.dacSpeeds[0]; /* Is this right?? */
      OFLG_SET(CLOCK_OPTION_TI3026, &vga256InfoRec.clockOptions);
      OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
      if (xf86Verbose)
	 ErrorF("%s %s: Using Ti3026/3030 programmable DCLK\n",
		s3ClockChipProbed, vga256InfoRec.name);
   }  /* there are cards with Ti3026 and EXTERNAL clock! */
   else
      OtherClocksSetup();

   s3clockDoublingPossible = TRUE;
   vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
   dacOutTi3026IndReg = s3OutTi3026IndReg;
   dacInTi3026IndReg = s3InTi3026IndReg;

   return 1;
}

static void TI3030_3026_Restore(vgaS3Ptr restore)
{
      int i;

      /* select pixel clock PLL as dot clock soure */
      s3OutTi3026IndReg(TI_INPUT_CLOCK_SELECT, 0x00, TI_ICLK_PLL);

      /* programm dot clock PLL to new MCLK frequency */
      s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);
      s3OutTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[3 + 0x40]);
      s3OutTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[4 + 0x40]);
      s3OutTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, restore->s3DacRegs[5 + 0x40]);

      /* wait until PLL is locked */
      for (i=0; i<10000; i++)
	 if (s3InTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA) & 0x40) 
	    break;

      /* switch to output dot clock on the MCLK terminal */
      s3OutTi3026IndReg(0x39, 0xe7, 0x00);
      s3OutTi3026IndReg(0x39, 0xe7, 0x08);
      
      /* Set MCLK */
      s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);
      s3OutTi3026IndReg(TI_MCLK_PLL_DATA, 0x00, restore->s3DacRegs[3 + 0x40]);
      s3OutTi3026IndReg(TI_MCLK_PLL_DATA, 0x00, restore->s3DacRegs[4 + 0x40]);
      s3OutTi3026IndReg(TI_MCLK_PLL_DATA, 0x00, restore->s3DacRegs[5 + 0x40]);

      /* wait until PLL is locked */
      for (i=0; i<10000; i++) 
	 if (s3InTi3026IndReg(TI_MCLK_PLL_DATA) & 0x40) 
	    break;

      /* switch to output MCLK on the MCLK terminal */
      s3OutTi3026IndReg(0x39, 0xe7, 0x10);
      s3OutTi3026IndReg(0x39, 0xe7, 0x18);


      /* restore dot clock PLL */

      s3OutTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00,
				restore->s3DacRegs[0 + 0x40]);
      s3OutTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00,
				restore->s3DacRegs[1 + 0x40]);
      s3OutTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, 
				restore->s3DacRegs[2 + 0x40]);

      s3OutTi3026IndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, 
				restore->s3DacRegs[6 + 0x40]);
      s3OutTi3026IndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, 
				restore->s3DacRegs[7 + 0x40]);
      s3OutTi3026IndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, 
				restore->s3DacRegs[8 + 0x40]);

      s3OutTi3026IndReg(TI_CURS_CONTROL, 0x00, 
			restore->s3DacRegs[TI_CURS_CONTROL]);
      s3OutTi3026IndReg(TI_INPUT_CLOCK_SELECT, 0x00,
			restore->s3DacRegs[TI_INPUT_CLOCK_SELECT]);
      s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00,
			restore->s3DacRegs[TI_MUX_CONTROL_1]);
      s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00,
			restore->s3DacRegs[TI_MUX_CONTROL_2]);
      s3OutTi3026IndReg(TI_COLOR_KEY_CONTROL, 0x00, 
			restore->s3DacRegs[TI_COLOR_KEY_CONTROL]);
      s3OutTi3026IndReg(TI_GENERAL_CONTROL, 0x00, 
			restore->s3DacRegs[TI_GENERAL_CONTROL]);
      s3OutTi3026IndReg(TI_MISC_CONTROL, 0x00, 
			restore->s3DacRegs[TI_MISC_CONTROL]);
      s3OutTi3026IndReg(TI_MCLK_LCLK_CONTROL, 0x00, 
			restore->s3DacRegs[TI_MCLK_LCLK_CONTROL]);
      s3OutTi3026IndReg(TI_GENERAL_IO_CONTROL, 0x00, 
			restore->s3DacRegs[TI_GENERAL_IO_CONTROL]);
      s3OutTi3026IndReg(TI_GENERAL_IO_DATA, 0x00, 
			restore->s3DacRegs[TI_GENERAL_IO_DATA]);
}

static void TI3030_3026_Save(vgaS3Ptr save)
{
	int i;

	  for (i=0; i<0x40; i++) 
	     save->s3DacRegs[i] = s3InTi3026IndReg(i);

          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);
          save->s3DacRegs[0 + 0x40] = s3InTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x01);
          save->s3DacRegs[1 + 0x40] = s3InTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x02);
          save->s3DacRegs[2 + 0x40] = s3InTi3026IndReg(TI_PIXEL_CLOCK_PLL_DATA);

          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);
          save->s3DacRegs[3 + 0x40] = s3InTi3026IndReg(TI_MCLK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x04);
          save->s3DacRegs[4 + 0x40] = s3InTi3026IndReg(TI_MCLK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x08);
          save->s3DacRegs[5 + 0x40] = s3InTi3026IndReg(TI_MCLK_PLL_DATA);

          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);
          save->s3DacRegs[6 + 0x40] = s3InTi3026IndReg(TI_LOOP_CLOCK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x10);
          save->s3DacRegs[7 + 0x40] = s3InTi3026IndReg(TI_LOOP_CLOCK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x20);
          save->s3DacRegs[8 + 0x40] = s3InTi3026IndReg(TI_LOOP_CLOCK_PLL_DATA);
          s3OutTi3026IndReg(TI_PLL_CONTROL, 0x00, 0x00);

 }

static int TI3030_3026_Init(DisplayModePtr mode)
{
      unsigned char tmp, tmp1, blank;

      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      /* change polarity on S3 to pass through control to the 3020      */
      tmp = new->MiscOutReg;
      new->MiscOutReg |= 0xC0;
      tmp1 = 0x00;
      if (!(tmp & 0x80)) tmp1 |= 0x02; /* invert bits for the 3020      */
      if (!(tmp & 0x40)) tmp1 |= 0x01;
      if (s3DACSyncOnGreen) tmp1 |= 0x30;  /* add IOG sync  & 7.5 IRE   */
      s3OutTi3026IndReg(TI_GENERAL_CONTROL, 0x00, tmp1);

      if (s3DACSyncOnGreen) {  /* needed for ELSA Winner 2000PRO/X */
	 s3OutTi3026IndReg(TI_GENERAL_IO_CONTROL, 0x00, TI_GIC_ALL_BITS);
         s3OutTi3026IndReg(TI_GENERAL_IO_DATA, ~TI_GID_ELSA_SOG, 0);
      }

      outb(vgaCRIndex, 0x65);
      if (DAC_IS_TI3030)
	 outb(vgaCRReg, 0x80);
      else
	 outb(vgaCRReg, 0);

      if (s3PixelMultiplexing) {
	 int vclock,rclock;
	 /* fun timing mods for pixel-multiplexing!                     */

	 outb(vgaCRIndex, 0x40);
	 outb(vgaCRReg, 0x11);
	 outb(vgaCRIndex, 0x55);
	 outb(vgaCRReg, 0x00);

	 if (vga256InfoRec.clock[mode->Clock] > 120000) {
	    vclock = TI_OCLK_V4;
	 } else if (vga256InfoRec.clock[mode->Clock] > 60000){
	    vclock = TI_OCLK_V2;
	 } else {
	    vclock = TI_OCLK_V1;
	 }
	 if (vga256InfoRec.bitsPerPixel >= 24) {   /* 32bpp or packed 24bpp */
	    rclock = TI_OCLK_R2;
	 } else if (vga256InfoRec.bitsPerPixel == 16) {  /* 15/16bpp */
	    rclock = TI_OCLK_R4;
	 } else {
	    rclock = TI_OCLK_R8;
	 }

	 outb(vgaCRIndex, 0x66);
	 tmp = inb(vgaCRReg);
	 if (DAC_IS_TI3030)
	    tmp |= 0x60;
	 if ((mode->Flags & V_DBLCLK)
	     && OFLG_ISSET(CLOCK_OPTION_ICD2061A, &vga256InfoRec.clockOptions))
	    if (s3Bpp <= 2)
	       outb(vgaCRReg, (tmp & 0xf8) | (rclock-2));
	    else
	       outb(vgaCRReg, (tmp & 0xf8) | (rclock-1));
	 else if ((mode->Flags & V_DBLCLK) && DAC_IS_TI3030)
	    outb(vgaCRReg, (tmp & 0xf8) | (rclock-1));
	 else 
		    outb(vgaCRReg, (tmp & 0xf8) | (rclock-0));

         /*
          * set the serial access mode 256 words control
          */
         outb(vgaCRIndex, 0x58);
	 tmp = (inb(vgaCRReg) & 0xbf) | s3SAM256;
         outb(vgaCRReg, tmp);

	 if (vga256InfoRec.bitsPerPixel == 24) {            /* packed 24bpp */
	    s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3026D_888_P8);
	    if (DAC_IS_TI3030)
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
			TI_MUX2_BUS_3030TC_D24P128);
	    else
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
			TI_MUX2_BUS_3026DC_D24P64);
	    s3OutTi3026IndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
	 } else if (vga256InfoRec.bitsPerPixel == 32) {              /* 32bpp */
	    s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3026D_888);
	    if (DAC_IS_TI3030)
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00,
			 TI_MUX2_BUS_3030TC_D24P128);
	    else
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
				TI_MUX2_BUS_3026DC_D24P64);
	    s3OutTi3026IndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
	 } else if (vga256InfoRec.depth == 16) {                    /* 5-6-5 */
	    s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3026D_565);
	    if (DAC_IS_TI3030)
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
				TI_MUX2_BUS_3030TC_D16P128);
	    else
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
				TI_MUX2_BUS_3026DC_D16P64);
	    s3OutTi3026IndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
	 } else if (vga256InfoRec.depth == 15) {                     /* 5-5-5 */
	    s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_3026D_555);
	    if (DAC_IS_TI3030)
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
				TI_MUX2_BUS_3030TC_D15P128);
	    else
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
				TI_MUX2_BUS_3026DC_D15P64);
	    s3OutTi3026IndReg(TI_COLOR_KEY_CONTROL, 0x00, 0x00);
	 } else {
            /* set mux control 1 and 2 to provide pseudocolor sub-mode 4   */
            /* this provides a 64-bit pixel bus with 8:1 multiplexing      */
            s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_PSEUDO_COLOR);
	    if (DAC_IS_TI3030)
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
			TI_MUX2_BUS_3030PC_D8P128);
	    else
	       s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, 
				TI_MUX2_BUS_3026PC_D8P64);
	 }

         /* change to 8-bit DAC and re-route the data path and clocking */
         s3OutTi3026IndReg(TI_GENERAL_IO_CONTROL, 0x00, TI_GIC_ALL_BITS);
         if (s3DAC8Bit) {
	   if (vga256InfoRec.bitsPerPixel>8)
	     s3OutTi3026IndReg(TI_MISC_CONTROL , 0xF0,
			       TI_MC_INT_6_8_CONTROL | TI_MC_8_BPP
			       | TI_MC_PSEL_POLARITY);
	   else
	     s3OutTi3026IndReg(TI_MISC_CONTROL , 0xF0,
			       TI_MC_INT_6_8_CONTROL | TI_MC_8_BPP);
         } else {
	   if (vga256InfoRec.bitsPerPixel>8)
	     s3OutTi3026IndReg(TI_MISC_CONTROL , 0xF0, TI_MC_INT_6_8_CONTROL
			       | TI_MC_PSEL_POLARITY);
	   else
	     s3OutTi3026IndReg(TI_MISC_CONTROL , 0xF0, TI_MC_INT_6_8_CONTROL);
         }
	 if (OFLG_ISSET(OPTION_DIAMOND, &vga256InfoRec.options)) {
	    outb(vgaCRIndex, 0x67);
	    outb(vgaCRReg, 0x01);
	    outb(vgaCRIndex, 0x6D);
	    if (s3Bpp == 1)
	       outb(vgaCRReg, 0x72);
	    else if (s3Bpp == 2)
	       outb(vgaCRReg, 0x73);
	    else /* if (s3Bpp == 4) */
	       outb(vgaCRReg, 0x75);
	 }
	 else {
	    outb(vgaCRIndex, 0x67);
	    outb(vgaCRReg, 0x00);
	    outb(vgaCRIndex, 0x6d);
	    if (s3Bpp == 1)
	       outb(vgaCRReg, 0x00);
	    else if (s3Bpp == 2)
	       outb(vgaCRReg, 0x01);
	    else /* if (s3Bpp == 4) */
	       outb(vgaCRReg, 0x00);
	 }
	 if (DAC_IS_TI3030) {
	    /* set s3 reg53 to parallel addressing by or'ing 0x20     */
	    outb(vgaCRIndex, 0x53);
	    tmp = inb(vgaCRReg) | 0x20;
	    outb(vgaCRReg, tmp);
	 }
      } else {
         outb(vgaCRIndex, 0x53);
         tmp = inb(vgaCRReg);
	 if (DAC_IS_TI3030)
	    /* set s3 reg53 to parallel addressing by or'ing 0x20     */
	    outb(vgaCRReg, tmp | 0x20);
	 else
	    /* set s3 reg53 to non-parallel addressing by and'ing 0xDF     */
	    outb(vgaCRReg, tmp & ~0x20);
         /* set s3 reg55 to non-external serial by and'ing 0xF7         */
         outb(vgaCRIndex, 0x55);
	 tmp = inb(vgaCRReg) & 0xF7;
         outb(vgaCRReg, tmp);

         /* set mux control 1 and 2 to provide pseudocolor VGA          */
         s3OutTi3026IndReg(TI_MUX_CONTROL_1, 0x00, TI_MUX1_PSEUDO_COLOR);
         s3OutTi3026IndReg(TI_MUX_CONTROL_2, 0x00, TI_MUX2_BUS_VGA);
      }  /* end of s3PixelMultiplexing */

      /* for some reason the bios doesn't set this properly          */
      s3OutTi3026IndReg(TI_SENSE_TEST, 0x00, 0x00);

      if (OFLG_ISSET(OPTION_TI3026_CURS, &vga256InfoRec.options)) {
	 /* enable interlaced cursor;
	    not very useful without CR45 bit 5 set, but anyway */
	 if (mode->Flags & V_INTERLACE) {
	    static int already = 0;
	    if (!already) {
	       already++;
	       ErrorF("%s %s: Ti3026 hardware cursor in interlaced modes "
		      "doesn't work correctly,\n"
		      "\tplease use Option \"sw_cursor\" when using "
		      "interlaced modes!\n"
		      ,XCONFIG_PROBED, vga256InfoRec.name);
	    }
	    s3OutTi3026IndReg(TI_CURS_CONTROL, ~0x60, 0x60);
	 }
	 else
	    s3OutTi3026IndReg(TI_CURS_CONTROL, ~0x60, 0x00);
      }

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */

      return 1;

}

/*********************************************************\

 			IBMRGB524_DAC 
			IBMRGB525_DAC 
			IBMRGB528_DAC 

\*********************************************************/


static Bool IBMRGB52x_Probe(int type)
{
    int found = 0;
    int ibm_id;
	 
    if (!S3_964_SERIES(s3ChipId) && !S3_968_SERIES(s3ChipId))
	return FALSE;

    if ((ibm_id = s3IBMRGB_Probe())) {
	s3IBMRGB_Init();
	switch(ibm_id >> 8) {
	    case 1:
	       if(type == IBMRGB525_DAC) {
	         ErrorF("%s %s: Detected an IBM RGB525 ramdac rev. %x\n",
		      XCONFIG_PROBED, vga256InfoRec.name, ibm_id&0xff);
	         found = IBMRGB525_DAC;
               }
	       break;
	    case 2:
	       if(type == IBMRGB524_DAC) {
		  ErrorF("%s %s: Detected an IBM RGB524 ramdac rev. %x\n",
			 XCONFIG_PROBED, vga256InfoRec.name, ibm_id&0xff);
		  found = IBMRGB524_DAC;
	       }
	       break;
	    case 0x102:
	       if(type == IBMRGB528_DAC) {
	          ErrorF("%s %s: Detected an IBM RGB528 ramdac rev. %x\n",
		      XCONFIG_PROBED, vga256InfoRec.name, ibm_id&0xff);
		  found = IBMRGB528_DAC;
               }
	       break;
	    default:
	       break;
	}
    }

    if (found == type)
       Probe_ELSA();

    return (found == type);
}

static Bool IBMRGB524_Probe()
{
    return IBMRGB52x_Probe(IBMRGB524_DAC);
}

static Bool IBMRGB525_Probe()
{
    return IBMRGB52x_Probe(IBMRGB525_DAC);
}

static Bool IBMRGB528_Probe()
{
    return IBMRGB52x_Probe(IBMRGB528_DAC);
}

static int IBMRGB52x_PreInit()
{
    /* Verify that depth is supported by ramdac */
	/* all are supported */
  
    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
    pixMuxPossible = TRUE;
    allowPixMuxInterlace = TRUE;
    allowPixMuxSwitching = TRUE;
    nonMuxMaxClock = 70000;
    pixMuxLimitedWidths = FALSE;
    if (S3_964_SERIES(s3ChipId)) {
         nonMuxMaxClock = 0;  /* 964 can only be in pixmux mode when */
         pixMuxMinWidth = 0;  /* working in enhanced mode */  
    }

    /* Set Clock options, s3ClockSelectFunc, & s3maxRawClock 
	for internal clocks. Pass the job to the OtherClocksSetup()
	function for external clocks*/
   if (DAC_IS_IBMRGB && 
       !OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions)) {
      OFLG_SET(CLOCK_OPTION_IBMRGB, &vga256InfoRec.clockOptions);
      OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
      s3ClockChipProbed = XCONFIG_PROBED;
   }

   if (OFLG_ISSET(CLOCK_OPTION_IBMRGB, &vga256InfoRec.clockOptions)) {
      char *refclock_probed;
      int mclk=0, m, n, df;
      int m0,m1,n0,n1;
      double f0,f1,f,fdiff;
	 
      s3maxRawClock = vga256InfoRec.dacSpeeds[s3Bpp - 1]; /* Is this right?? */
      s3ClockSelectFunc = IBMRGBClockSelect;
      s3numClocks = 3;

      OFLG_SET(CLOCK_OPTION_IBMRGB, &vga256InfoRec.clockOptions);
      OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);

      if (vga256InfoRec.s3RefClk) 
	 refclock_probed = XCONFIG_GIVEN;
      else 
	 refclock_probed = XCONFIG_PROBED;

      if (s3InIBMRGBIndReg(IBMRGB_pll_ctrl1) & 1) {
	 m0 = s3InIBMRGBIndReg(IBMRGB_m0+0*2);
	 n0 = s3InIBMRGBIndReg(IBMRGB_n0+0*2) & 0x1f;
	 m1 = s3InIBMRGBIndReg(IBMRGB_m0+1*2);
	 n1 = s3InIBMRGBIndReg(IBMRGB_n0+1*2) & 0x1f;
      }
      else {
	 m0 = s3InIBMRGBIndReg(IBMRGB_f0+0);
	 m1 = s3InIBMRGBIndReg(IBMRGB_f0+1);
	 n0 = s3InIBMRGBIndReg(IBMRGB_pll_ref_div_fix) & 0x1f;
	 n1 = n0;
      }
      f0 = 25.175 / ((m0&0x3f)+65.0) * n0 * (8 >> (m0>>6));
      f1 = 28.322 / ((m1&0x3f)+65.0) * n1 * (8 >> (m1>>6));      
      if (f1>f0) fdiff = f1-f0;
      else       fdiff = f0-f1;


      /* refclock defaults should not depend on vendor options
       * but only on probed BIOS tags; it's too dangerous
       * when users play with options */

      if (find_bios_string(vga256InfoRec.BIOSbase,"VideoBlitz III AV",
			   "Genoa Systems Corporation") != NULL) {
         if (s3BiosVendor == UNKNOWN_BIOS) 
	    s3BiosVendor = GENOA_BIOS;
	 if (xf86Verbose)
	    ErrorF("%s %s: Genoa VideoBlitz III AV BIOS found\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	 if (!vga256InfoRec.s3RefClk) 
	    vga256InfoRec.s3RefClk = 50000;
      }
      else if (find_bios_string(vga256InfoRec.BIOSbase,"STB Systems, Inc.", NULL) 
	       != NULL) {
	 if (s3BiosVendor == UNKNOWN_BIOS) 
	    s3BiosVendor = STB_BIOS;
	 if (xf86Verbose)
	    ErrorF("%s %s: STB Velocity 64 BIOS found\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	 if (!vga256InfoRec.s3RefClk)
	    vga256InfoRec.s3RefClk = 24000;
      }
      else if (find_bios_string(vga256InfoRec.BIOSbase,
				"Number Nine Visual Technology","Motion 771")
	       != NULL) {
	 if (s3BiosVendor == UNKNOWN_BIOS) 
	    s3BiosVendor = NUMBER_NINE_BIOS;
	 if (xf86Verbose)
	    ErrorF("%s %s: #9 Motion 771 BIOS found\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	 if (!vga256InfoRec.s3RefClk)
	    vga256InfoRec.s3RefClk = 16000;
      }
      else if (OFLG_ISSET(OPTION_ELSA_W2000PRO_X8,  &vga256InfoRec.options)) {
	 if (!vga256InfoRec.s3RefClk)
	    vga256InfoRec.s3RefClk = 28322;
      }
      else if (find_bios_string(vga256InfoRec.BIOSbase,
				"Hercules Graphite Terminator",NULL) != NULL) {
	 if (s3BiosVendor == UNKNOWN_BIOS) 
	    s3BiosVendor = HERCULES_BIOS;
	 if (xf86Verbose)
	    ErrorF("%s %s: Hercules Graphite Terminator BIOS found\n",
		   XCONFIG_PROBED, vga256InfoRec.name);
	 if (!vga256InfoRec.s3RefClk)
	    if (S3_968_SERIES(s3ChipId))
	       /* Hercules Graphite Terminator Pro(tm) BIOS. */
	       vga256InfoRec.s3RefClk = 16000;
	    else		/* S3 964 */
	       /* Hercules Graphite Terminator 64(tm) BIOS. */
	       vga256InfoRec.s3RefClk = 50000;
      }
      else if (!vga256InfoRec.s3RefClk) {
	 if (fdiff < f0*0.02) {
	    int i;
	    /* f = (f0+f1)/2; */ 
	    f = f1;
	    i = (int)(f*1e3/200+0.5);
            /* 28.322 MHz clock seems to be more acurate then 25.175 */
	    /* try to match some known reclock values */
	    if      (i == (14318+100)/200)
	       vga256InfoRec.s3RefClk = 14318;
	    else if (i == (16000+100)/200)
	       vga256InfoRec.s3RefClk = 16000;
	    else if (i == (24000+100)/200)
	       vga256InfoRec.s3RefClk = 24000;
	    else if (i == (28322+100)/200)
	       vga256InfoRec.s3RefClk = 28322;
	    else if (i == (50000+100)/200)
	       vga256InfoRec.s3RefClk = 50000;
	    else 
	       vga256InfoRec.s3RefClk = (int)(f*2+0.5)*500;
	 }
      }
      else if (!vga256InfoRec.s3RefClk) {
	 vga256InfoRec.s3RefClk = 16000; /* default */
      }
      
      if (!DAC_IS_IBMRGB525) {
	 m = s3InIBMRGBIndReg(IBMRGB_sysclk_vco_div);
	 n = s3InIBMRGBIndReg(IBMRGB_sysclk_ref_div) & 0x1f;
	 df = m>>6;
	 m &= 0x3f;
	 if (!n) { m=0; n=1; }
	 mclk = ((vga256InfoRec.s3RefClk*100 * (m+65)) / n / (8 >> df) + 50) / 100;
      }
      if (xf86Verbose) {
	 ErrorF("%s %s: Using IBM RGB52x programmable clock",
		s3ClockChipProbed, vga256InfoRec.name);
	 if (mclk)
	    ErrorF(" (MCLK %1.3f MHz)", mclk / 1000.0);
	 ErrorF("\n");	
	 ErrorF("%s %s: with refclock %1.3f MHz (probed %1.3f & %1.3f)\n",
		refclock_probed,vga256InfoRec.name,vga256InfoRec.s3RefClk/1e3,f0,f1);
      }
      if (!vga256InfoRec.s3MClk)
	 vga256InfoRec.s3MClk = mclk;
   } 
   else 
       OtherClocksSetup();

   s3clockDoublingPossible = FALSE;
      /* LCLK & SCLK limit is 100 MHz */
   if ((vga256InfoRec.dacSpeeds[0] * s3Bpp) / 8 > 100000)  
	 vga256InfoRec.maxClock = (100000 * 8) / s3Bpp; 
   else
	 vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];

   return 1;
}

static void IBMRGB52x_Restore(vgaS3Ptr restore)
{
      int i;

      if (DAC_IS_IBMRGB525) {
	 s3OutIBMRGBIndReg(IBMRGB_vram_mask_0, 0, 3);
	 s3OutIBMRGBIndReg(IBMRGB_misc1, ~0x40, 0x40);
	 usleep (1000);
	 s3OutIBMRGBIndReg(IBMRGB_misc2, ~1, 0);
      }
      for (i=0; i<0x100; i++)
	 s3OutIBMRGBIndReg(i, 0, restore->s3DacRegs[i]);
      outb(vgaCRIndex, 0x22);
      outb(vgaCRReg, restore->s3DacRegs[0x100]);   
}

static void IBMRGB52x_Save(vgaS3Ptr save)
{
	int i;

	for (i=0; i<0x100; i++) {
	    save->s3DacRegs[i] = s3InIBMRGBIndReg(i);
        }
	outb(vgaCRIndex, 0x22);
	save->s3DacRegs[0x100] = inb(vgaCRReg);
}

static int IBMRGB52x_Init(DisplayModePtr mode)
{
      unsigned char tmp, blank;

      outb(0x3C4, 1);
      blank = inb(0x3C5);
      outb(0x3C5, blank | 0x20); /* blank the screen */

      if (DAC_IS_IBMRGB528) {
	 if (vga256InfoRec.clock[mode->Clock] <=  110000)
	    s3OutIBMRGBIndReg(IBMRGB_misc_clock, 0xf0, 0x01);
	 else if (vga256InfoRec.clock[mode->Clock] <=  220000)
	    s3OutIBMRGBIndReg(IBMRGB_misc_clock, 0xf0, 0x03);
	 else
	    s3OutIBMRGBIndReg(IBMRGB_misc_clock, 0xf0, 0x05);
      }
      else if (mode->Flags & V_DBLCLK)
	 s3OutIBMRGBIndReg(IBMRGB_misc_clock, 0xf0, 0x03);
      else
	 s3OutIBMRGBIndReg(IBMRGB_misc_clock, 0xf0, 0x01);

      s3OutIBMRGBIndReg(IBMRGB_sync, 0, 0);
      if ((mode->Private[0] & (1 << S3_BLANK_DELAY))
	  && S3_968_SERIES(s3ChipId)) {
	 int pixels = (mode->Private[S3_BLANK_DELAY] & 0x07) * 8 / s3Bpp;
	 if (pixels > 15) pixels = 15;
	 s3OutIBMRGBIndReg(IBMRGB_hsync_pos, 0, pixels);
      }
      else if (s3Bpp < 3)
	 s3OutIBMRGBIndReg(IBMRGB_hsync_pos, 0, 0);
      else
	 s3OutIBMRGBIndReg(IBMRGB_hsync_pos, 0, 2);
      s3OutIBMRGBIndReg(IBMRGB_pwr_mgmt, 0, 0);
      s3OutIBMRGBIndReg(IBMRGB_dac_op, ~8, s3DACSyncOnGreen ? 8 : 0);
      s3OutIBMRGBIndReg(IBMRGB_dac_op, ~2, 1 /* fast slew */ ? 2 : 0);
      s3OutIBMRGBIndReg(IBMRGB_pal_ctrl, 0, 0);

      /* set VRAM size to 128/64 bit and disable VRAM mask */
      if (DAC_IS_IBMRGB528)
	 s3OutIBMRGBIndReg(IBMRGB_misc1, ~0x43, 3);
      else
	 s3OutIBMRGBIndReg(IBMRGB_misc1, ~0x43, 1);
      if (s3DAC8Bit)
	 s3OutIBMRGBIndReg(IBMRGB_misc2, 0, 0x47);
      else
	 s3OutIBMRGBIndReg(IBMRGB_misc2, 0, 0x43);

#if 0  /* this will lock up the S3 chip & PC sometimes */
      outb(vgaCRIndex, 0x22); /* don't know why it's important    */
      outb(vgaCRReg, 0xff);   /* to set a "read only" register ?? */
#else
      outb(vgaCRIndex, 0x22);
      tmp = inb(vgaCRReg);
      if (s3Bpp == 1 && S3_968_SERIES(s3ChipId) && !DAC_IS_IBMRGB528)
	 outb(vgaCRReg, tmp | 8);
      else 
	 outb(vgaCRReg, tmp & ~8);
#endif

      outb(vgaCRIndex, 0x65);
      if (DAC_IS_IBMRGB528)
	 outb(vgaCRReg, 0x80);
      else
	 outb(vgaCRReg, 0);

      if (s3PixelMultiplexing) {
	 outb(vgaCRIndex, 0x40);
	 outb(vgaCRReg, 0x11);
	 outb(vgaCRIndex, 0x55);
	 outb(vgaCRReg, 0x00);

	 if (vga256InfoRec.bitsPerPixel == 32) { 	/* 32 bpp */
            if(0){   
                /* turn on color keying with key 0x00 */
                s3OutIBMRGBIndReg(0x68, 0, 0x00); 
                s3OutIBMRGBIndReg(0x6c, 0, 0xFF);
                s3OutIBMRGBIndReg(0x78, 0xFE, 0x01); 
            }
	    s3OutIBMRGBIndReg(IBMRGB_pix_fmt, 0xf8, 6);
	    s3OutIBMRGBIndReg(IBMRGB_32bpp, 0, 0x03);
	 } else if (vga256InfoRec.bitsPerPixel == 24) {	/* 24 bpp */
	    s3OutIBMRGBIndReg(IBMRGB_pix_fmt, 0xf8, 5);
	    s3OutIBMRGBIndReg(IBMRGB_24bpp, 0, 0x01);
	 } else if (vga256InfoRec.bitsPerPixel == 16) { 
	    if(xf86weight.green == 5) {			/* 15 bpp */
	    	s3OutIBMRGBIndReg(IBMRGB_pix_fmt, 0xf8, 4);
	    	s3OutIBMRGBIndReg(IBMRGB_16bpp, 0, 0xC0);
	    } else {					/* 16 bpp */
	    	s3OutIBMRGBIndReg(IBMRGB_pix_fmt, 0xf8, 4);
	    	s3OutIBMRGBIndReg(IBMRGB_16bpp, 0, 0xC2);
	    }
	 } else {					/*  8 bpp */
	    s3OutIBMRGBIndReg(IBMRGB_pix_fmt, 0xf8, 3);
	    s3OutIBMRGBIndReg(IBMRGB_8bpp, 0, 0);
	 }
	 /* if (DAC_IS_RGB528) tmp++; */

	 outb(vgaCRIndex, 0x66);
	 tmp = inb(vgaCRReg) & 0xf8;
	 if (DAC_IS_IBMRGB528) {
	    int tmp2;
	    tmp = tmp & 0x80 | 0x60;   /* 128 bit SID mode */
	    if (vga256InfoRec.clock[mode->Clock] <= 110000) tmp2 = 3;
	    else if (vga256InfoRec.clock[mode->Clock] <= 220000) tmp2 = 2;
	    else tmp2 = 1;
	    if (s3Bpp == 2) tmp2--;
	    else if (s3Bpp == 4) {
	       if (tmp2 >= 2) tmp2 -= 2;
	       else tmp2 = 0;
	    }
	    tmp |= tmp2;
	 }
	 else if (!S3_968_SERIES(s3ChipId)) {
	   if (s3Bpp == 1) tmp |= 3;
	   else if (s3Bpp == 2) tmp |= 2;
	   else /* if (s3Bpp == 4) */ tmp |= 1;
	   if (mode->Flags & V_DBLCLK) tmp--;
	 }
	 outb(vgaCRReg, tmp);

         /*
          * set the serial access mode 256 words control
          */
         outb(vgaCRIndex, 0x58);
	 tmp = (inb(vgaCRReg) & 0xbf) | s3SAM256;
         outb(vgaCRReg, tmp);

	 outb(vgaCRIndex, 0x67);
	 if (DAC_IS_IBMRGB528)
	    if (s3Bpp == 1)
	       outb(vgaCRReg, 0x01);
	    else
	       outb(vgaCRReg, 0x00);
	 else if (s3Bpp == 4)
	    if (DAC_IS_IBMRGB && S3_968_SERIES(s3ChipId))
	       outb(vgaCRReg, 0x10);
	    else
	       outb(vgaCRReg, 0x00);
	 else
	    if (S3_968_SERIES(s3ChipId))
	       outb(vgaCRReg, 0x11);
	    else
	       outb(vgaCRReg, 0x01);

	 outb(vgaCRIndex, 0x6d);
	 if (s3Bpp == 1)
	    outb(vgaCRReg, 0x21);
	 else if (s3Bpp == 2)
	    outb(vgaCRReg, 0x10);
	 else /* if (s3Bpp == 4) */
	    outb(vgaCRReg, 0x00);

      } else {
         /* set s3 reg53 to non-parallel addressing by and'ing 0xDF     */
         outb(vgaCRIndex, 0x53);
	 tmp = inb(vgaCRReg) & 0xDF;
         outb(vgaCRReg, tmp);

         /* set s3 reg55 to non-external serial by and'ing 0xF7         */
         outb(vgaCRIndex, 0x55);
	 tmp = inb(vgaCRReg) & 0xF7;
         outb(vgaCRReg, tmp);

         /* provide pseudocolor VGA          */
         s3OutIBMRGBIndReg(IBMRGB_misc2, 0, 0);
      }  /* end of s3PixelMultiplexing */

      if (DAC_IS_IBMRGB528) {
         /* set s3 reg53 to parallel addressing   */
         outb(vgaCRIndex, 0x53);
         tmp = inb(vgaCRReg);
         outb(vgaCRReg, tmp | 0x20);
      }

#if 0
      if (OFLG_ISSET(OPTION_IBMRGB_CURS, &vga256InfoRec.options)) {
	 /* enable interlaced cursor;
	    not very useful without CR45 bit 5 set, but anyway */
	 if (mode->Flags & V_INTERLACE) {
	    static int already = 0;
	    if (!already) {
	       already++;
	       ErrorF("%s %s: IBMRGB hardware cursor in interlaced modes "
		      "doesn't work correctly,\n"
		      "\tplease use Option \"sw_cursor\" when using "
		      "interlaced modes!\n"
		      ,XCONFIG_PROBED, vga256InfoRec.name);
	    }
	    s3OutIBMRGBIndReg(TI_CURS_CONTROL, ~0x60, 0x60);
	 }
	 else
	    s3OutIBMRGBIndReg(TI_CURS_CONTROL, ~0x60, 0x00);
      }
#endif

      outb(0x3C4, 1);
      outb(0x3C5, blank);        /* unblank the screen */

      return 1;
}

/***********************************************************\

  			ATT20C490_DAC  
			SS2410_DAC  
			SC1148x_DAC  

\***********************************************************/

#define Setcomm(v)        (xf86dactocomm(),outb(0x3C6,v),\
                        xf86dactocomm(),inb(0x3C6))

static Bool MISC_HI_COLOR_Probe(int type)
{
    int found = 0;
    unsigned char tmp, olddacpel, olddaccomm, notdaccomm;

    /*quick check*/
    if(!S3_8XX_9XX_SERIES(s3ChipId)) 
	return FALSE;
	
    (void) xf86dactocomm();
    olddaccomm = inb(0x3C6);
    xf86dactopel();
    olddacpel = inb(0x3C6);

    notdaccomm = ~olddaccomm;
    outb(0x3C6, notdaccomm);
    (void) xf86dactocomm();

    if (inb(0x3C6) != notdaccomm) {
            /* Looks like a HiColor RAMDAC */
            if ((Setcomm(0xE0) & 0xE0) == 0xE0) {
               if ((Setcomm(0x60) & 0xE0) == 0x00) {
                  if ((Setcomm(0x02) & 0x02) != 0x00) {
                     found = ATT20C490_DAC;
                  }
               } else {
                  tmp = Setcomm(olddaccomm);
                  if (inb(0x3C6) != notdaccomm) {
                     (void) inb(0x3C6);
                     (void) inb(0x3C6);
                     (void) inb(0x3C6);
                     if (inb(0x3C6) != notdaccomm) {
                        xf86dactopel();
                        outb(0x3C6, 0xFF);
                        (void) inb(0x3C6);
                        (void) inb(0x3C6);
                        (void) inb(0x3C6);
                        switch (inb(0x3C6)) {
                        case 0x44:
                        case 0x82:
			    break;
                        case 0x8E:
			    found = SS2410_DAC;		
			    break;
                        default:
                            xf86dactopel();
                            outb(0x3C6, olddacpel & 0xFB);
                            xf86dactocomm();
                            outb(0x3C6, olddaccomm & 0x04);
                            tmp = inb(0x3C6);
                            outb(0x3C6, tmp & 0xFB);
                            if ((tmp & 0x04) == 0) {
                               found = SC1148x_DAC;
                            }
                        }
                     }
                  }
               }
            }
    }
    (void) xf86dactocomm();
    outb(0x3C6, olddaccomm);
    xf86dactopel();
    outb(0x3C6, olddacpel);

    return (found == type);
}
#undef Setcomm

static Bool SC1148x_Probe()
{
    return MISC_HI_COLOR_Probe(SC1148x_DAC);
}

static Bool SS2410_Probe()
{
    return MISC_HI_COLOR_Probe(SS2410_DAC);
}

static Bool ATT20C490_Probe()
{
    return MISC_HI_COLOR_Probe(ATT20C490_DAC);
}

static int MISC_HI_COLOR_PreInit()
{
    /* Verify that depth is supported by ramdac */
    switch(s3RamdacType) {
	case SC1148x_DAC:
    	    if ( vga256InfoRec.depth > 16 ) {
		ErrorF("Depths greater than 16bpp are not supported for "
		   	"the Sierra 1148x RAMDAC.\n");
	        return 0;
    	    }
	    break;
	case SS2410_DAC:
    	    if ( vga256InfoRec.depth > 24 ) {
		ErrorF("Depths greater than 24bpp are not supported for "
		    "the Diamond SS2410 RAMDAC.\n");
		return 0;
    	     }
	    break;
	case ATT20C490_DAC:
    	    if (s3Bpp > 2) {
		ErrorF("Depths greater than 16bpp are not supported for "
		    "the ATT20C490 RAMDAC.\n");
		return 0;
    	     }
	    break;
	default:
	    ErrorF("Internal error in MISC_HI_COLOR_PreInit().\n");
		return 0;
     }	


    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
	/* No PixMux */

    /* Set Clock options, s3ClockSelectFunc, & s3maxRawClock 
	for internal clocks. Pass the job to the OtherClocksSetup()
	function for external clocks*/
    OtherClocksSetup();

    vga256InfoRec.maxClock = vga256InfoRec.dacSpeeds[s3Bpp - 1];
    /* Halve it for 16bpp (32bpp not supported) */
    if (s3Bpp > 1) {
	 vga256InfoRec.maxClock /= 2;
	 s3maxRawClock /= 2;
    }

    return 1;
}


static void SC1148x_Restore(vgaS3Ptr restore)
{
   xf86setdaccomm(restore->s3DacRegs[0]);
}

static void ATT20C490_Restore(vgaS3Ptr restore)
{
    xf86setdaccomm(restore->s3DacRegs[0]);
}

static void SS2410_Restore(vgaS3Ptr restore)
{
    unsigned char tmp;

    outb( vgaCRIndex, 0x55 );
    tmp = inb( vgaCRReg );
    outb( vgaCRReg, tmp | 1 ); 
    xf86setdaccomm(restore->s3DacRegs[0]);
    outb( vgaCRReg, tmp );
}

static void SC1148x_Save(vgaS3Ptr save)
{
   save->s3DacRegs[0] = xf86getdaccomm();
}

static void ATT20C490_Save(vgaS3Ptr save)
{
   save->s3DacRegs[0] = xf86getdaccomm();
}

static void SS2410_Save(vgaS3Ptr save)
{
	unsigned char tmp;

 	outb( vgaCRIndex, 0x55 );
	tmp = inb( vgaCRReg );
	outb( vgaCRReg, tmp | 1 ); 
	save->s3DacRegs[0] = xf86getdaccomm( );
	outb( vgaCRReg, tmp );
}

static int SC1148x_Init(DisplayModePtr mode)
{
   if (vga256InfoRec.bitsPerPixel == 8) {
      xf86clrdaccommbit(0xe0);
   } else {
      if (vga256InfoRec.depth == 16) {
	xf86setdaccommbit(0xe0);
      } else if (vga256InfoRec.depth == 15) {
        xf86setdaccommbit(0xa0);
      } else {
	ErrorF("Depths greater than 16bpp are not supported by the SC1148x!\n");
	return 0;
      }
   }
   return 1;
}

static int ATT20C490_Init(DisplayModePtr mode)
{
      if (vga256InfoRec.bitsPerPixel == 8) {
	if (s3DAC8Bit) {
         xf86setdaccomm(0x02);
	} else {
	 xf86setdaccomm(0x00);
	}
      } else {
	switch (vga256InfoRec.depth) {
	  case 15:
		xf86setdaccomm(0xa0);
                break;
          case 16:
		xf86setdaccomm(0xc0);
                break;
          case 24:
          case 32:
		xf86setdaccomm(0xe0);  /* XXXX just a guess, check !!! */
                break;
          default:
		ErrorF("Bad depth %i!\n",vga256InfoRec.depth);
		return 0;
	}
      }  
      return 1;  
}

static int SS2410_Init(DisplayModePtr mode)
{
    register unsigned char tmp;

	if ( vga256InfoRec.bitsPerPixel == 8 ) {
 		outb( vgaCRIndex, 0x55 );
		tmp = inb( vgaCRReg );
		outb( vgaCRReg, tmp | 1 ); 
		xf86setdaccomm( 0 );
		outb( vgaCRReg, tmp );
	} else {
	     switch ( vga256InfoRec.depth ) { 
		   case 15:
 			outb( vgaCRIndex, 0x55 );
			tmp = inb( vgaCRReg );
			outb( vgaCRReg, tmp | 1 ); 
			xf86setdaccomm( 0xA0 );
			outb( vgaCRReg, tmp );
			break;
		   case 16:
 			outb( vgaCRIndex, 0x55 );
			tmp = inb( vgaCRReg );
			outb( vgaCRReg, tmp | 1 ); 
			xf86setdaccomm( 0xA6 );
			outb( vgaCRReg, tmp );
			break;
		   case 24:
 			outb( vgaCRIndex, 0x55 );
			tmp = inb( vgaCRReg );
			outb( vgaCRReg, tmp | 1 ); 
			xf86setdaccomm( 0x9E );
			outb( vgaCRReg, tmp );
			break;
		   default:
			ErrorF("Bad depth %i!\n",vga256InfoRec.depth);
			return 0;
		
	     }
     	}

	return 1;
}

/*****************************************************\

  			NORMAL_DAC 

\*****************************************************/

static Bool NORMAL_Probe()
{
    return TRUE;
}

static int NORMAL_PreInit()
{
    /* Verify that depth is supported by ramdac */
    if(s3Bpp > 1) {
	ErrorF("Depths greater than 8bpp are not supported for a "
	"\"normal\" RAMDAC.\n");
	return 0;	/* BAD_DEPTH */
    }

    /* Check if PixMux is supported and set the PixMux
	related flags and variables */
	/* of course not */

    /* If there is an internal clock, set s3ClockSelectFunc, s3maxRawClock
	s3numClocks and whatever options need to be set. For external
	clocks, pass the job to OtherClocksSetup() */
    OtherClocksSetup();
    
    /* anything else */
    vga256InfoRec.maxClock = 100000; /* ???? MArk */ 

    return 1;
}



/**********************************************************\

	THE CLOCKS

\**********************************************************/


void OtherClocksSetup()
{
	/*******************************************\
	|  Clocks that aren't built into the ramdac |
	\*******************************************/

   if (OFLG_ISSET(OPTION_LEGEND, &vga256InfoRec.options)) {
      s3ClockSelectFunc = LegendClockSelect;
      s3numClocks = 32;
/*      s3maxRawClock = ;		(MArk) what to do here? */
      if (xf86Verbose)
         ErrorF("%s %s: Using Sigma Legend ICS 1394-046 clock generator\n",
		s3ClockChipProbed, vga256InfoRec.name);
   } else if (OFLG_ISSET(CLOCK_OPTION_ICD2061A, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = icd2061ClockSelect;
      s3numClocks = 3;
      s3maxRawClock = 120000;
      if (xf86Verbose)
         ErrorF("%s %s: Using ICD2061A programmable clock\n",
		s3ClockChipProbed, vga256InfoRec.name);
   } else if (OFLG_ISSET(CLOCK_OPTION_GLORIA8, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = Gloria8ClockSelect;
      s3numClocks = 3;
/*      s3maxRawClock = ;		(MArk) what to do here? */
      if (xf86Verbose)
         ErrorF("%s %s: Using ELSA Gloria-8 ICS9161/TVP3030 programmable " 	
		"clock\n",s3ClockChipProbed, vga256InfoRec.name);
   } else if (OFLG_ISSET(CLOCK_OPTION_SC11412, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = icd2061ClockSelect;
      s3numClocks = 3;
      if (OFLG_ISSET(CLOCK_OPTION_SC11412, &vga256InfoRec.clockOptions)) {
	 if (!OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options)) {
	    switch (s3RamdacType) {
	    case BT485_DAC:
	       s3maxRawClock = 67500;
	       break;
	    case ATT20C505_DAC:
	       s3maxRawClock = 90000;
	       break;
	    default:
	       s3maxRawClock = 100000;
	       break;
	    }
	 } else {
	    s3maxRawClock = 100000;
	 }
      }
      if (xf86Verbose)
	 ErrorF("%s %s: Using Sierra SC11412 programmable clock\n",
		s3ClockChipProbed, vga256InfoRec.name);
   } else if (OFLG_ISSET(CLOCK_OPTION_ICS2595, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = icd2061ClockSelect;
      s3numClocks = 3;
      s3maxRawClock = 145000; /* This is what is in common_hw/ICS2595.h */
      if (xf86Verbose)
	 ErrorF("%s %s: Using ICS2595 programmable clock\n",
		XCONFIG_GIVEN, vga256InfoRec.name);
   } else if (OFLG_ISSET(CLOCK_OPTION_CH8391, &vga256InfoRec.clockOptions)) {
      s3ClockSelectFunc = ch8391ClockSelect;
      s3numClocks = 3;
      s3maxRawClock = 135000;
      if (xf86Verbose)
	 ErrorF("%s %s: Using Chrontel 8391 programmable clock\n",
		XCONFIG_GIVEN, vga256InfoRec.name);
   } else if (OFLG_ISSET(CLOCK_OPTION_S3GENDAC, &vga256InfoRec.clockOptions) ||
	      OFLG_ISSET(CLOCK_OPTION_ICS5342,  &vga256InfoRec.clockOptions)) {
      unsigned char saveCR55;
      int m,n,n1,n2, mclk;
      
      s3ClockSelectFunc = s3GendacClockSelect;
      s3numClocks = 3;
      s3maxRawClock = 110000;
      
      outb(vgaCRIndex, 0x55);
      saveCR55 = inb(vgaCRReg);
      outb(vgaCRReg, saveCR55 | 1);
      
      outb(0x3C7, 10); /* read MCLK */
      m = inb(0x3C9);
      n = inb(0x3C9);
      
      outb(vgaCRIndex, 0x55);
      outb(vgaCRReg, saveCR55);	 
      
      m &= 0x7f;
      n1 = n & 0x1f;
      n2 = (n>>5) & 0x03;
      mclk = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;

      if (xf86Verbose)
	 ErrorF("%s %s: Using %s programmable clock (MCLK %1.3f MHz)\n"
		,s3ClockChipProbed, vga256InfoRec.name
		,OFLG_ISSET(CLOCK_OPTION_ICS5342, &vga256InfoRec.clockOptions)
		? "ICS5342" : "S3 Gendac/SDAC"
		,mclk / 1000.0);
      if (vga256InfoRec.s3MClk > 0) {
       if (xf86Verbose)
	 ErrorF("%s %s: using specified MCLK value of %1.3f MHz for DRAM "
 	"timings\n",XCONFIG_GIVEN, vga256InfoRec.name, vga256InfoRec.s3MClk/1000.0);
      } else {
	 vga256InfoRec.s3MClk = mclk;
      }
   } else {
      s3ClockSelectFunc = s3ClockSelect;
      s3numClocks = 16;
      if (!vga256InfoRec.clocks) 
         vgaGetClocks(s3numClocks, s3ClockSelectFunc);
   }

}



static Bool
s3ClockSelect(no)
     int   no;

{
   unsigned char temp;
   static unsigned char save1, save2;
 
   UNLOCK_SYS_REGS;
   
   switch(no)
   {
   case CLK_REG_SAVE:
      save1 = inb(0x3CC);
      outb(vgaCRIndex, 0x42);
      save2 = inb(vgaCRReg);
      break;
   case CLK_REG_RESTORE:
      outb(0x3C2, save1);
      outb(vgaCRIndex, 0x42);
      outb(vgaCRReg, save2);
      break;
   default:
      no &= 0xF;
      if (no == 0x03)
      {
	 /*
	  * Clock index 3 is a 0Hz clock on all the S3-recommended 
	  * synthesizers (except the Chrontel).  A 0Hz clock will lock up 
	  * the chip but good (requiring power to be cycled).  Nuke that.
	  */
         LOCK_SYS_REGS;
	 return(FALSE);
      }
      temp = inb(0x3CC);
      outb(0x3C2, temp | 0x0C);
      outb(vgaCRIndex, 0x42);
      temp = inb(vgaCRReg) & 0xf0;
      outb(vgaCRReg, temp | no);
      usleep(150000);
   }
   LOCK_SYS_REGS;
   return(TRUE);
}



static Bool
LegendClockSelect(no)
     int   no;
{

 /*
  * Sigma Legend special handling
  * 
  * The Legend uses an ICS 1394-046 clock generator.  This can generate 32
  * different frequencies.  The Legend can use all 32.  Here's how:
  * 
  * There are two flip/flops used to latch two inputs into the ICS clock
  * generator.  The five inputs to the ICS are then
  * 
  * ICS     ET-4000 ---     --- FS0     CS0 FS1     CS1 FS2     ff0 flip/flop 0
  * outbut FS3     CS2 FS4     ff1     flip/flop 1 outbut
  * 
  * The flip/flops are loaded from CS0 and CS1.  The flip/flops are latched by
  * CS2, on the rising edge. After CS2 is set low, and then high, it is then
  * set to its final value.
  * 
  */
   static unsigned char save1, save2;
   unsigned char temp = inb(0x3CC);

   switch(no)
   {
   case CLK_REG_SAVE:
      save1 = inb(0x3CC);
      outb(vgaCRIndex, 0x34);
      save2 = inb(vgaCRReg);
      break;
   case CLK_REG_RESTORE:
      outb(0x3C2, save1);
      outw(vgaCRIndex, 0x34 | (save2 << 8));
      break;
   default:
      outb(0x3C2, (temp & 0xF3) | ((no & 0x10) >> 1) | (no & 0x04));
      outw(vgaCRIndex, 0x0034);
      outw(vgaCRIndex, 0x0234);
      outw(vgaCRIndex, ((no & 0x08) << 6) | 0x34);
      outb(0x3C2, (temp & 0xF3) | ((no << 2) & 0x0C));
   }
   return(TRUE);
}




static Bool
icd2061ClockSelect(freq)
     int   freq;
{
   Bool result = TRUE;

   UNLOCK_SYS_REGS;
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Convert freq to Hz */
	 freq *= 1000;
	 /* Use the "Alt" version always since it is more reliable */
	 if (OFLG_ISSET(CLOCK_OPTION_ICD2061A, &vga256InfoRec.clockOptions)) {
	    /* setting exactly 120 MHz doesn't work all the time */
	    if (freq > 119900000) freq = 119900000;
#if defined(PC98_PW) || defined(PC98_PWLB)
	    AltICD2061SetClock(freq, 0);
#else
	    AltICD2061SetClock(freq, 2);
	    AltICD2061SetClock(freq, 2);
	    AltICD2061SetClock(freq, 2);
#endif
	    if (DAC_IS_TI3026 || DAC_IS_TI3030) {
	       /* 
	        * then we need to setup the loop clock
	        */
	       Ti3026SetClock(freq/1000, 2, s3Bpp, TI_LOOP_CLOCK);
	       s3OutTi3026IndReg(TI_MCLK_LCLK_CONTROL, ~0x20, 0x20);
            }
	 } else if (OFLG_ISSET(CLOCK_OPTION_SC11412, &vga256InfoRec.clockOptions)) {
	    result = SC11412SetClock((long)freq/1000);
	 } else if (OFLG_ISSET(CLOCK_OPTION_ICS2595, &vga256InfoRec.clockOptions)) {
	    result = ICS2595SetClock((long)freq/1000);
	    result = ICS2595SetClock((long)freq/1000);
	 } else { /* Should never get here */
	    result = FALSE;
	    break;
	 }

	 if (!OFLG_ISSET(CLOCK_OPTION_ICS2595, &vga256InfoRec.clockOptions)) {
	    unsigned char tmp;
	    outb(vgaCRIndex, 0x42);/* select the clock */
	    tmp = inb(vgaCRReg) & 0xf0;
	    if (OFLG_ISSET(OPTION_SPEA_MERCURY, &vga256InfoRec.options) &&
                S3_964_SERIES(s3ChipId)) /* SPEA Mercury P64 uses bit2/3  */
                 outb(vgaCRReg, tmp | 0x06);   /* for synchronizing reasons (?) */
            else outb(vgaCRReg, tmp | 0x02); 
            usleep(150000);
	 }
	 /* Do the clock doubler selection in s3Init() */
      }
   }
   LOCK_SYS_REGS;
   return(result);
}




/* the ELSA Gloria-8 uses a TVP3030 with ICS9161 as refclock */

static Bool
Gloria8ClockSelect(freq)
     int   freq;
{
   Bool result = TRUE;
   unsigned char tmp;
   int p;

   UNLOCK_SYS_REGS;
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      for(p=0; p<4; p++)
	 if ((freq << p) >= 120000) break;

      AltICD2061SetClock((freq * 1000) >> (3-p), 2);

      Ti3030SetClock(14318 << (3-p), 2, s3Bpp, TI_BOTH_CLOCKS);
      Ti3030SetClock(freq, 2, s3Bpp, TI_LOOP_CLOCK);
      s3OutTi3026IndReg(TI_MCLK_LCLK_CONTROL, ~0x38, 0x30);
      s3OutTi3026IndReg(TI_MCLK_LCLK_CONTROL, ~0x38, 0x38);

      outb(vgaCRIndex, 0x42);/* select the clock */
      tmp = inb(vgaCRReg) & 0xf0;
      outb(vgaCRReg, tmp | 0x06);
   }
   LOCK_SYS_REGS;
   return(result);
}






/* The GENDAC code also works for the SDAC, used for Trio32/Trio64 too */

static Bool
s3GendacClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
   unsigned char tmp;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
#if defined(PC98_PW)
	(void) S3gendacSetClock(freq, 7);  /* PW805i can't use reg 2 */
#else

	 if (S3_TRIOxx_SERIES(s3ChipId)) {
	    if (OFLG_ISSET(CLOCK_OPTION_S3AURORA, &vga256InfoRec.clockOptions))
	       (void) S3AuroraSetClock(freq, 2); /* can't fail */
	    else if (OFLG_ISSET(CLOCK_OPTION_S3TRIO64V2, &vga256InfoRec.clockOptions))
	       (void) S3Trio64V2SetClock(freq, 2); /* can't fail */
	    else 
	       (void) S3TrioSetClock(freq, 2); /* can't fail */
	 }
	 else {
	    if (OFLG_ISSET(CLOCK_OPTION_ICS5342, &vga256InfoRec.clockOptions))
	       (void) ICS5342SetClock(freq, 2); /* can't fail */
	    else
	       (void) S3gendacSetClock(freq, 2); /* can't fail */
#endif
	    outb(vgaCRIndex, 0x42);/* select the clock */
#if defined(PC98_PW)
	    tmp = inb(vgaCRReg) & 0xf0;
	    outb(vgaCRReg, tmp | 0x07);
#else
	    tmp = inb(vgaCRReg) & 0xf0;
	    outb(vgaCRReg, tmp | 0x02);
#endif
	    usleep(150000);
#if !defined(PC98_PW)
	 }
#endif
      }
   }
   LOCK_SYS_REGS;
   return(result);
}



static void
#if NeedFunctionPrototypes
s3ProgramTi3025Clock(
int clk,
unsigned char n,
unsigned char m,
unsigned char p)
#else
s3ProgramTi3025Clock(clk, n, m, p)
int clk;
unsigned char n;
unsigned char m;
unsigned char p;
#endif
{
   /*
    * Reset the clock data index
    */
   s3OutTiIndReg(TI_PLL_CONTROL, 0x00, 0x00);

   if (clk != TI_MCLK_PLL_DATA) {
      /*
       * Now output the clock frequency
       */
      s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, n);
      s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, m);
      s3OutTiIndReg(TI_PIXEL_CLOCK_PLL_DATA, 0x00, p | TI_PLL_ENABLE);

      /*
       * And now set up the loop clock for RCLK
       */
      s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, 0x01);
      s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, 0x01);
      s3OutTiIndReg(TI_LOOP_CLOCK_PLL_DATA, 0x00, p>0 ? p : 1);
      s3OutTiIndReg(TI_MISC_CONTROL, 0x00,
		    TI_MC_LOOP_PLL_RCLK | TI_MC_LCLK_LATCH | TI_MC_INT_6_8_CONTROL);

      /*
       * And finally enable the clock
       */
      s3OutTiIndReg(TI_INPUT_CLOCK_SELECT, 0x00, TI_ICLK_PLL);
   } else {
      /*
       * Set MCLK
       */
      s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, n);
      s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, m);
      s3OutTiIndReg(TI_MCLK_PLL_DATA, 0x00, p | 0x80);
   }
}




static Bool
ti3025ClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
   unsigned char tmp;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Check if clock frequency is within range */
	 /* XXXX Check this elsewhere */
	 if (freq < 20000) {
	    ErrorF("%s %s: Specified dot clock (%.3f) too low for TI 3025",
		   XCONFIG_PROBED, vga256InfoRec.name, freq / 1000.0);
	    result = FALSE;
	    break;
	 }
	 (void) Ti3025SetClock(freq, 2, s3ProgramTi3025Clock); /* can't fail */
	 outb(vgaCRIndex, 0x42);/* select the clock */
	 tmp = inb(vgaCRReg) & 0xf0;
	 outb(vgaCRReg, tmp | 0x02);
	 usleep(150000);
      }
   }
   LOCK_SYS_REGS;
   return(result);
}




static Bool
ti3026ClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
   unsigned char tmp;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Check if clock frequency is within range */
	 /* XXXX Check this elsewhere */
	 if (freq < 13750) {
	    ErrorF("%s %s: Specified dot clock (%.3f) too low for Ti3026/3030",
		   XCONFIG_PROBED, vga256InfoRec.name, freq / 1000.0);
	    result = FALSE;
	    break;
	 }
	 (void) Ti3026SetClock(freq, 2, s3Bpp, TI_BOTH_CLOCKS); /* can't fail */
	 outb(vgaCRIndex, 0x42);/* select the clock */
	 tmp = inb(vgaCRReg) & 0xf0;
	 outb(vgaCRReg, tmp | 0x02);
      }
   }
   LOCK_SYS_REGS;
   return(result);
}




static Bool
IBMRGBClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Check if clock frequency is within range */
	 /* XXXX Check this elsewhere */
	 if (freq < 16250) {
	    ErrorF("%s %s: Specified dot clock (%.3f) too low for IBM RGB52x\n",
		   XCONFIG_PROBED, vga256InfoRec.name, freq / 1000.0);
	    result = FALSE;
	    break;
	 } 
	 (void)IBMRGBSetClock(freq, 2, vga256InfoRec.maxClock,
					vga256InfoRec.s3RefClk);
      }
   }
   LOCK_SYS_REGS;
   return(result);
}




static Bool
ch8391ClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
   unsigned char tmp;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Check if clock frequency is within range */
	 /* XXXX Check this elsewhere */
	 if (freq < 8500 || freq > 135000) {
	    ErrorF("%s %s: Specified dot clock (%.3f) out of range for Chrontel 8391",
		   XCONFIG_PROBED, vga256InfoRec.name, freq / 1000.0);
	    result = FALSE;
	    break;
	 }
	 (void) Chrontel8391SetClock(freq, 2); /* can't fail */
	 outb(vgaCRIndex, 0x42);/* select the clock */
	 tmp = inb(vgaCRReg) & 0xf0;
	 outb(vgaCRReg, tmp | 0x02);
	 usleep(150000);
      }
   }
   LOCK_SYS_REGS;
   return(result);
}




static Bool
att409ClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
   unsigned char tmp;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Check if clock frequency is within range */
	 /* XXXX Check this elsewhere */
	 if (freq < 15000 || freq > 240000) {
	    ErrorF("%s %s: Specified dot clock (%.3f) out of range for ATT20C409",
		   XCONFIG_PROBED, vga256InfoRec.name, freq / 1000.0);
	    result = FALSE;
	    break;
	 }
	 (void) Att409SetClock(freq, 2); /* can't fail */
	 outb(vgaCRIndex, 0x42);/* select the clock */
	 tmp = inb(vgaCRReg) & 0xf0;
	 outb(vgaCRReg, tmp | 0x02);
	 usleep(150000);
      }
   }
   LOCK_SYS_REGS;
   return(result);
}




static Bool
STG1703ClockSelect(freq)
     int   freq;

{
   Bool result = TRUE;
   unsigned char tmp;
 
   UNLOCK_SYS_REGS;
   
   switch(freq)
   {
   case CLK_REG_SAVE:
   case CLK_REG_RESTORE:
      result = s3ClockSelect(freq);
      break;
   default:
      {
	 /* Check if clock frequency is within range */
	 /* XXXX Check this elsewhere */
	 if (freq < 8500 || freq > 135000) {
	    ErrorF("%s %s: Specified dot clock (%.3f) out of range for STG1703",
		   XCONFIG_PROBED, vga256InfoRec.name, freq / 1000.0);
	    result = FALSE;
	    break;
	 }
	 (void) STG1703SetClock(freq, 2); /* can't fail */
	 outb(vgaCRIndex, 0x42);/* select the clock */
	 tmp = inb(vgaCRReg) & 0xf0;
	 outb(vgaCRReg, tmp | 0x02);
	 usleep(150000);
      }
   }
   LOCK_SYS_REGS;
   return(result);
}

static unsigned char *find_bios_string(int BIOSbase, char *match1, char *match2)
{
#define BIOS_BSIZE 1024
#define BIOS_BASE  0xc0000

   static unsigned char bios[BIOS_BSIZE];
   static int init=0;
   int i,j,l1,l2;

   if (!init) {
      init = 1;
      if (xf86ReadBIOS(BIOSbase, 0, bios, BIOS_BSIZE) != BIOS_BSIZE)
	 return NULL;
      if ((bios[0] != 0x55) || (bios[1] != 0xaa))
	 return NULL;
   }
   if (match1 == NULL)
      return NULL;

   l1 = strlen(match1);
   if (match2 != NULL) 
      l2 = strlen(match2);
   else	/* for compiler-warnings */
      l2 = 0;

   for (i=0; i<BIOS_BSIZE-l1; i++)
      if (bios[i] == match1[0] && !memcmp(&bios[i],match1,l1))
	 if (match2 == NULL) 
	    return &bios[i+l1];
	 else
	    for(j=i+l1; (j<BIOS_BSIZE-l2) && bios[j]; j++) 
	       if (bios[j] == match2[0] && !memcmp(&bios[j],match2,l2))
		  return &bios[j+l2];
   return NULL;
}



static int s3DetectMIRO_20SV_Rev(int BIOSbase)
{
   char *match1 = "miroCRYSTAL\37720SV", *match2 = "Rev.";
   unsigned char *p;

   if ((p = find_bios_string(BIOSbase,match1,match2)) != NULL) {
      if (s3BiosVendor == UNKNOWN_BIOS) 
	 s3BiosVendor = MIRO_BIOS;
      if (*p >= '0' && *p <= '9')
	 return *p - '0';
   }
   return -1;
}


static void Probe_ELSA()
{
   char *card, *serno, *elsa_modes;
   int card_id, max_pix_clock, max_mem_clock, hwconf;
   static int only_once=0;

   if (only_once) 
      return;
   only_once = 1;

   card_id = s3DetectELSA(vga256InfoRec.BIOSbase, &card, &serno, &max_pix_clock,
			  &max_mem_clock, &hwconf, &elsa_modes);
   if (card_id > 0) {
      if (s3BiosVendor == UNKNOWN_BIOS) 
	 s3BiosVendor = ELSA_BIOS;
      if (xf86Verbose) {
         ErrorF("%s %s: card: %s, Ser.No. %s\n",
	        XCONFIG_PROBED, vga256InfoRec.name, card, serno);
	 if (elsa_modes && *elsa_modes)
	    ErrorF("%s %s: video modes stores in ELSA EEPROM:\n%s",
		   XCONFIG_PROBED, vga256InfoRec.name, elsa_modes);
      }
      xfree(card);
      xfree(serno);
      xfree(elsa_modes);
      
      if (vga256InfoRec.dacSpeeds[0] <= 0)
	 vga256InfoRec.dacSpeeds[0] = max_pix_clock;
      
      do {
	 switch (card_id) {
	 case ELSA_WINNER_1000AVI:
	 case ELSA_WINNER_1000PRO:
	    /* This option isn't required at the moment */
	    OFLG_SET(OPTION_ELSA_W1000PRO,  &vga256InfoRec.options);
	    /* fallthrough */
	 case ELSA_WINNER_1000:
	 case ELSA_WINNER_1000VL:
	 case ELSA_WINNER_1000PCI:
	 case ELSA_WINNER_1000ISA:
	    if ((s3Ramdacs[S3_SDAC_DAC].DacProbe)()) {
	       s3RamdacType = S3_SDAC_DAC;
	       continue;	/* SDAC detected, don't set ICD2061A clock */
	    }
            if((s3Ramdacs[ATT20C409_DAC].DacProbe)())
	       s3RamdacType = ATT20C409_DAC;
	    /* if ATT20C409 is detected, the clockchip is 
	     * already set apropriately 
	     */
	    /* otherwise it's a STG1700,20C498, or SC15025 and will get 
	       detected later.  The clockchip ICD2061A will get set at
	       the at the end of this loop */
	    break;
	 case ELSA_WINNER_2000PRO:
	    OFLG_SET(OPTION_ELSA_W2000PRO,  &vga256InfoRec.options);
	    break;
	 case ELSA_WINNER_2000PRO_X8:
	    OFLG_SET(OPTION_ELSA_W2000PRO_X8,  &vga256InfoRec.options);
	    continue;		/* use IBM RGB528 clock, don't set ICD2061A flags */
	 case ELSA_WINNER_2000:
	 case ELSA_WINNER_2000VL:
	 case ELSA_WINNER_2000PCI:
	    break;		/* set ICD2061A clock chip */
	 case ELSA_GLORIA_8:
	    if (OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions)) {
	       FatalError("%s %s: for the ELSA Gloria-8 card you should not "
			  "specify a clock chip!\n",XCONFIG_GIVEN, vga256InfoRec.name);
	    }
	    OFLG_SET(CLOCK_OPTION_GLORIA8, &vga256InfoRec.clockOptions);
	    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	    s3ClockChipProbed = XCONFIG_PROBED;
	    /* fall through ... */

	 case ELSA_WINNER_2000AVI:
	 case ELSA_WINNER_2000PRO_X:
	 case ELSA_GLORIA_4:
	    if (OFLG_ISSET(OPTION_ELSA_W2000PRO,&vga256InfoRec.options)) {
	       ErrorF("%s %s: for Ti3026/3030 RAMDACs you must not specify "
		      "Option \"elsa_w2000pro\"\n",XCONFIG_PROBED, vga256InfoRec.name);
	       OFLG_CLR(OPTION_ELSA_W2000PRO, &vga256InfoRec.options);
	    }
	    if ((card_id==ELSA_WINNER_2000PRO_X) && (hwconf & 2)) {
	       /*
	        * this version of the Winner 2000PRO/X has an external ICS9161A
	        * clockchip, so set ICD2061A flag
	        */
               if (xf86Verbose)
	          ErrorF("%s %s: Rev. G Winner 2000PRO/X with external " 
			 "clockchip detected\n",XCONFIG_PROBED, vga256InfoRec.name);
	       break;
	    } else {
	       continue;
            }
	 case ELSA_WINNER_1000PRO_TRIO32:
	 case ELSA_WINNER_1000PRO_TRIO64:
	 case ELSA_WINNER_1000PRO_X:
	 default: 
	    continue;		/* unknown card_id, don't set ICD2061A flags */
	 }

	 /* a known ELSA card_id was returned, set ICD 2061A clock support 
	    if there is no ClockChip specified in XF86Config */

	 if (!OFLG_ISSET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions)) {
	    OFLG_SET(CLOCK_OPTION_ICD2061A, &vga256InfoRec.clockOptions);
	    OFLG_SET(CLOCK_OPTION_PROGRAMABLE, &vga256InfoRec.clockOptions);
	    s3ClockChipProbed = XCONFIG_PROBED;
	 }
      } while (0);
   }
}
