/*
 * (c) Copyright 1993,1994 by David Wexelblat <dwex@xfree86.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
 * DAVID WEXELBLAT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of David Wexelblat shall not be
 * used in advertising or otherwise to promote the sale, use or other dealings
 * in this Software without prior written authorization from David Wexelblat.
 *
 * Contributing Authors:
 *	Robin Cutshaw <robin@xfree86.org>
 *	Harald Koenig <koenig@tat.physik.uni-tuebingen.de>
 * 
 */

/* $XConsortium: RamDac.c,v 1.4 95/01/12 19:19:44 kaleb Exp $ */
/* $XFree86: xc/programs/Xserver/hw/xfree86/SuperProbe/RamDac.c,v 3.6 1994/11/30 20:36:17 dawes Exp $ */

#include "Probe.h"

static Word Ports[] = {0x000, 0x000, 0x3C6, 0x3C7, 0x3C8, 0x3C9};
#define NUMPORTS (sizeof(Ports)/sizeof(Word))

#define RED	0
#define GREEN	1
#define BLUE	2

static void ReadPelReg __STDCARGS((Byte, Byte *));
static void WritePelReg __STDCARGS((Byte, Byte *));
static Byte SetComm __STDCARGS((Byte));
static Bool Width8Check __STDCARGS((void));
static Bool TestDACBit __STDCARGS((Byte, Byte, Byte));
static Bool S3_Bt485Check __STDCARGS((int *));
static Bool S3_TVP3020Check __STDCARGS((int *));
static Bool S3_ATT498Check __STDCARGS((int *));
static Bool S3_STG1700Check __STDCARGS((int *));
static Bool S3_GENDACCheck __STDCARGS((int *));
static void CheckMach32_64 __STDCARGS((int, int *));

#ifdef __STDC__
static void ReadPelReg(Byte Index, Byte *Pixel)
#else
static void ReadPelReg(Index, Pixel)
Byte Index;
Byte *Pixel;
#endif
{
	outp(0x3C7, Index);
	Pixel[RED] = inp(0x3C9);
	Pixel[GREEN] = inp(0x3C9);
	Pixel[BLUE] = inp(0x3C9);

	return;
}

#ifdef __STDC__
static void WritePelReg(Byte Index, Byte *Pixel)
#else
static void WritePelReg(Index, Pixel)
Byte Index;
Byte *Pixel;
#endif
{
	outp(0x3C8, Index);
	outp(0x3C9, Pixel[RED]);
	outp(0x3C9, Pixel[GREEN]);
	outp(0x3C9, Pixel[BLUE]);
	return;
}

#ifdef __STDC__
static Byte SetComm(Byte Comm)
#else
static Byte SetComm(Comm)
Byte Comm;
#endif
{
	(void) dactocomm();
	outp(0x3C6, Comm);
	(void) dactocomm();
	return(inp(0x3C6));
}

static Bool Width8Check()
{
	Byte old, x, v;
	Byte pix[3];

	old = inp(0x3C8);
	ReadPelReg(0xFF, pix);
	v = pix[RED];
	pix[RED]= 0xFF;
	WritePelReg(0xFF, pix);
	ReadPelReg(0xFF, pix);
	x = pix[RED];
	pix[RED] = v;
	WritePelReg(0xFF, pix);
	outp(0x3C8, old);
	return(x == 0xFF);
}

#ifdef __STDC__
static Bool TestDACBit(Byte Bit, Byte OldComm, Byte OldPel)
#else
static Bool TestDACBit(Bit, OldComm, OldPel)
Byte Bit;
Byte OldComm;
Byte OldPel;
#endif
{
	Byte tmp;

	dactopel();
	outp(0x3C6, OldPel & (Bit ^ 0xFF));
	(void) dactocomm();
	outp(0x3C6, OldComm | Bit);
	tmp = inp(0x3C6);
	outp(0x3C6, tmp & (Bit ^ 0xFF));
	return((tmp & Bit) != 0);
}

static Bool S3_Bt485Check(RamDac)
int *RamDac;
{
	Byte old1, old2, old3, old4;
	Byte lock1, lock2;
	Bool Found = FALSE;
	Bool DoChecks = FALSE;

	lock1 = rdinx(CRTC_IDX, 0x38);
	lock2 = rdinx(CRTC_IDX, 0x39);
	wrinx(CRTC_IDX, 0x38, 0x48);
	wrinx(CRTC_IDX, 0x39, 0xA5);

	old1 = inp(0x3C6);
	old2 = rdinx(CRTC_IDX, 0x55);
	outp(0x3C6, 0x0F);
	wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x02);
	old3 = inp(0x3C6);
	if ((old3 & 0x80) == 0x80)
	{
		Found = TRUE;
		if ((old3 & 0xC0) == 0x80)
		{
			*RamDac = DAC_BT485;
			DoChecks = TRUE;
		}
		else if ((old3 & 0xF0) == 0xD0)
		{
			DoChecks = TRUE;
			*RamDac = DAC_ATT505;
		}
		else
		{
			*RamDac = DAC_UNKNOWN;
		}
	}
	else if ((old3 & 0xF0) == 0x40)
	{
		Found = TRUE;
		*RamDac = DAC_ATT504;
		DoChecks = TRUE;
	}
	else
	{
		/* Perhaps status reg is hidden behind CR3 */
		wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x01);
		old3 = inp(0x3C6);
		if ((old3 & 0x80) == 0x80)
		{
			/* OK.  CR3 is active... */
			wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x00);
			old3 = inp(0x3C8);
			outp(0x3C8, 0x00);
			wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x02);
			old4 = inp(0x3C6);
			if ((old4 & 0x80) == 0x80)
			{
				Found = TRUE;
				if ((old4 & 0xC0) == 0x80)
				{
					*RamDac = DAC_BT485;
				}
				else if ((old4 & 0xF0) == 0xD0)
				{
					*RamDac = DAC_ATT505;
				}
				else
				{
					*RamDac = DAC_UNKNOWN;
				}
			}
			else if ((old4 & 0xF0) == 0x40)
			{
				Found = TRUE;
				*RamDac = DAC_ATT504;
			}
			if ((Found) && (*RamDac != DAC_UNKNOWN))
			{
				*RamDac |= DAC_6_8_PROGRAM;
				if (Width8Check())
				{
					*RamDac |= DAC_8BIT;
				}
			}
			wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x00);
			outp(0x3C8, old3);
		}
	}
	if (DoChecks)
	{
		*RamDac |= DAC_6_8_PROGRAM;
		if (Width8Check())
		{
			*RamDac |= DAC_8BIT;
		}
	}
	wrinx(CRTC_IDX, 0x55, old2);
	outp(0x3C6, old1);

	wrinx(CRTC_IDX, 0x39, lock2);
	wrinx(CRTC_IDX, 0x38, lock1);

	return(Found);
}

static Bool S3_TVP3020Check(RamDac)
int *RamDac;
{
	Byte old1, old2, old3, old4;
	Byte lock1, lock2;
	Bool Found = FALSE;

	/*
	 * TI ViewPoint TVP3020 support - Robin Cutshaw
	 *
	 * The 3020 has 8 direct registers accessed through standard
	 * VGA registers 0x3C8, 0x3C9, 0x3C6, and 0x3C7.  Bit 0 of
	 * CR55 is used to map these four register to the low four
	 * or high four direct 3020 registers.  The high register set
	 * includes index and data registers which are used to address
	 * indirect registers 0x00-0x3F and 0xFF.  Indirect register
	 * 0x3F is the chip ID register which will always return 0x20.
	 */

	lock1 = rdinx(CRTC_IDX, 0x38);
	lock2 = rdinx(CRTC_IDX, 0x39);
	wrinx(CRTC_IDX, 0x38, 0x48);
	wrinx(CRTC_IDX, 0x39, 0xA5);

	old1 = inp(0x3C6);
	old2 = rdinx(CRTC_IDX, 0x55);

	wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x01); /* high four registers */
	old3 = inp(0x3C6);             /* read current index register value */

	outp(0x3C6, 0x3F);     /* write ID register index to index register */
	old4 = inp(0x3C7);     /* read ID register from data register       */
	if (old4 == 0x20) {
		Found = TRUE;
		*RamDac = DAC_TVP3020 | DAC_6_8_PROGRAM;
		wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x00); /* regular VGA */
		if (Width8Check())
		{
			*RamDac |= DAC_8BIT;
		}
	} else {
	    Byte old5, old6;

	    /* check for Ti3025 hiding behind Bt485 mode */
	    old5 = rdinx(CRTC_IDX, 0x5C);

	    /* clear 0x20 (RS4) for 3020 mode */
	    wrinx(CRTC_IDX, 0x5C, old5 & 0xDF);
	    /* already twiddled CR55 above */
	    old6 = inp(0x3C6);          /* read current index register value */
	    outp(0x3C6, 0x3F);  /* write ID register index to index register */
	    old4 = inp(0x3C7);  /* read ID register from data register       */
	    if (old4 == 0x25) {
		Found = TRUE;
	        *RamDac = DAC_TVP3025 | DAC_6_8_PROGRAM;
		wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x00); /* regular VGA */
		if (Width8Check())
		{
			*RamDac |= DAC_8BIT;
		}
	        wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x01);
	    }

	    outp(0x3C6, old6);              /* restore index register value */
	    wrinx(CRTC_IDX, 0x5C, old5);    /* restore 5C                   */
        }

	wrinx(CRTC_IDX, 0x55, (old2 & 0xFC) | 0x01); /* high four registers */
	outp(0x3C6, old3);                  /* restore index register value */

	wrinx(CRTC_IDX, 0x55, old2);
	outp(0x3C6, old1);

	wrinx(CRTC_IDX, 0x39, lock2);
	wrinx(CRTC_IDX, 0x38, lock1);

	return(Found);
}

static Bool S3_ATT498Check(RamDac)
int *RamDac;
{
	Byte mir, dir, daccomm;
	int i;
	Bool Found = FALSE;

	/*
	 * ATT20C498 support - Harald Koenig
	 *
	 * The ATT498 has 4 direct registers accessed through standard
	 * VGA registers 0x3C8, 0x3C9, 0x3C6, and 0x3C7 and 6 indirect
	 * registers accessed through a back door by successive reads 
	 * on RMR (Pixel read mask register 0x3C6).
	 */

	dactocomm();
	inp(0x3C6);		/* reading CR0 */
	mir = inp(0x3C6);
	dir = inp(0x3C6);
	dactopel();

	if ((mir == 0x84) && (dir == 0x98)) {
	   daccomm = getdaccomm();
	   SetComm(0);
	   SetComm(0x0a);
	   if (getdaccomm() == 0)
	      *RamDac = DAC_ATT22C498;
	   else
	      *RamDac = DAC_ATT498;
	   SetComm(daccomm);
	   Found = TRUE;
	   *RamDac |= DAC_6_8_PROGRAM;
	}

	return(Found);
}

static Bool S3_STG1700Check(RamDac)
int *RamDac;
{
	Byte cid, did, daccomm, readmask;
	int i;
	Bool Found = FALSE;

	readmask = inp(0x3c6);
	dactopel();
	daccomm = getdaccomm();
	SetComm(daccomm | 0x10);
	dactocomm();
	inp(0x3C6);
	outp(0x3c6, 0x00);
	outp(0x3c6, 0x00);
	cid = inp(0x3c6);     /* company ID */
	did = inp(0x3c6);     /* device ID */
	dactopel();
	outp(0x3c6,readmask);
	SetComm(daccomm);

	if ((cid == 0x44) && (did == 0x00)) {
	   Found = TRUE;
	   *RamDac = DAC_STG1700;
	   *RamDac |= DAC_6_8_PROGRAM;
	}

	return(Found);
}

static Bool S3_GENDACCheck(RamDac)
int *RamDac;
{
   Byte daccomm;
   int i;
   Bool Found = FALSE;
   Byte lock1, lock2;

   Byte saveCR55, savelut[6];
   long clock01, clock23;

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


   lock1 = rdinx(CRTC_IDX, 0x38);
   lock2 = rdinx(CRTC_IDX, 0x39);
   wrinx(CRTC_IDX, 0x38, 0x48);
   wrinx(CRTC_IDX, 0x39, 0xA5);
	 
   saveCR55 = rdinx(CRTC_IDX, 0x55);
   wrinx(CRTC_IDX, 0x55, saveCR55 & ~1);
   
   outp(0x3c7,0);
   for(i=0; i<2*3; i++)		/* save first two LUT entries */
      savelut[i] = inp(0x3c9);
   outp(0x3c8,0);
   for(i=0; i<2*3; i++)		/* set first two LUT entries to zero */
      outp(0x3c9,0);

   wrinx(CRTC_IDX, 0x55, saveCR55 | 1);
	 
   outp(0x3c7,0);
   for(i=clock01=0; i<4; i++)
      clock01 = (clock01 << 8) | (inp(0x3c9) & 0xff);
   for(i=clock23=0; i<4; i++)
      clock23 = (clock23 << 8) | (inp(0x3c9) & 0xff);

   wrinx(CRTC_IDX, 0x55, saveCR55 & ~1);

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

   wrinx(CRTC_IDX, 0x55, saveCR55);
   wrinx(CRTC_IDX, 0x39, lock2);
   wrinx(CRTC_IDX, 0x38, lock1);

   if ( clock01 == 0x28613d62 ||
       (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) {      
      Found = TRUE;
      
      dactopel();
      inp(0x3c6);
      inp(0x3c6);
      inp(0x3c6);
      
      /* the forth read will show the SDAC chip ID and revision */
      if (((i=inp(0x3c6)) & 0xf0) == 0x70)
	 *RamDac = DAC_S3_SDAC;
      else
	 *RamDac = DAC_S3_GENDAC;
      dactopel();
   }
      
   return(Found);
}

static Bool S3_SC15025Check(RamDac)
int *RamDac;
{
	Byte c,id[4];
	int i;
	Bool Found = FALSE;

	/*
	 * Sierra 15025 support - Harald Koenig
	 *
	 * The SC15025 has 9 indexed extended registers which can be accessed
	 * by setting bit 0x10 in the command register. 
	 * In extended registers 0x9-0xC an identification code is stored
	 * should be 53 3a b1 41 
	 */

	c = getdaccomm();
	SetComm(c | 0x10); /* enable extened data registers */
	for (i=0; i<4; i++) {
	   outp(0x3C7, 0x9+i); 
	   id[i] = inp(0x3C8);
	}
	SetComm(c); /* restore command register */
	dactopel();

	if (id[0] == 'S' &&                  /* Sierra */
	    ((id[1]<<8)|id[2]) == 15025) {   /* unique for the SC 15025/26 */
	        if (id[3] != 'A') {                     /* version number */
		   fprintf(stderr,"*** ==> New Sierra SC 15025/26 version (%x) found, please report!\n",id[3]);
		}
                Found = TRUE;
		*RamDac = DAC_SIERRA24;
		*RamDac |= DAC_6_8_PROGRAM;
        }

	return(Found);
}

static void CheckMach32_64(ChipSet, RamDac)
int ChipSet;
int *RamDac;
{
	Word Port = CONFIG_STATUS_1;

	if (ChipSet >= CHIP_ATI88800)
		Port = CONFIG_STATUS_0;

	EnableIOPorts(1, &Port);

	switch ((inpw(Port) & 0x0E00) >> 9)
	{
	case 0x00:
		*RamDac = DAC_ATI68830;
		break;
	case 0x01:
		*RamDac = DAC_SIERRA15_16;
		break;
	case 0x02:
		*RamDac = DAC_ATI68875;
		*RamDac |= DAC_6_8_PROGRAM;
		if (Width8Check())
		{
			*RamDac |= DAC_8BIT;
		}
		break;
	case 0x03:
		*RamDac = DAC_STANDARD;
		break;
	case 0x04:
		*RamDac = DAC_ATIMISC24;
		*RamDac |= DAC_6_8_PROGRAM;
		if (Width8Check())
		{
			*RamDac |= DAC_8BIT;
		}
		break;
	case 0x05:
		*RamDac = DAC_ATI68860;
		*RamDac |= DAC_6_8_PROGRAM;
		if (Width8Check())
		{
			*RamDac |= DAC_8BIT;
		}
		break;
	case 0x06:
		*RamDac = DAC_STG1700;
		*RamDac |= DAC_6_8_PROGRAM;
		break;
	case 0x07:
		*RamDac = DAC_ATT498;
		*RamDac |= DAC_6_8_PROGRAM;
		break;
	}

	DisableIOPorts(1, &Port);
	return;
}

void Probe_RamDac(Chipset, RamDac)
int Chipset;
int *RamDac;
{
	Byte x, y, z, u, v, oldcomm, oldpel, notcomm, tmp;
	Bool dac8, dac8now;

	*RamDac = DAC_STANDARD;
	Ports[0] = CRTC_IDX;
	Ports[1] = CRTC_REG;
	EnableIOPorts(NUMPORTS, Ports);

	/*
	 * Handle special cases.
	 */
	if (Chipset == CHIP_AL2101)
	{
	    *RamDac = DAC_ALG1101;
	    if (Width8Check())
	    {
		*RamDac |= DAC_8BIT;
	    }
	    DisableIOPorts(NUMPORTS, Ports);
	    return;
	}
	else if (SVGA_VENDOR(Chipset) == V_ATI)
	{
	    if (Chipset < CHIP_ATI68800_3)
	    {
		if (ReadBIOS(0x44, &x, 1) != 1)
		{
		    fprintf(stderr, "%s: Failed to read ATI BIOS data\n",
			    MyName);
		    DisableIOPorts(NUMPORTS, Ports);
		    return;
		}
		if (x & 0x80)
		{
		    *RamDac = DAC_ATI68830;
		    if (Width8Check())
		    {
			*RamDac |= DAC_8BIT;
		    }
		    DisableIOPorts(NUMPORTS, Ports);
		    return;
		}
	    }
	    else
	    {
		CheckMach32_64(Chipset, RamDac);
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	}
	else if ((SVGA_VENDOR(Chipset) == V_CIRRUS) &&
		 (Chipset >= CHIP_CL5420))
	{
	    if ((Chipset == CHIP_CL5420) || (Chipset >= CHIP_CL5410))
	    {
		*RamDac = DAC_CIRRUSA;
	    }
	    else
	    {
		*RamDac = DAC_CIRRUSB;
	    }
	    if (Width8Check())
	    {
		*RamDac |= DAC_8BIT;
	    }
	    DisableIOPorts(NUMPORTS, Ports);
	    return;
	}
	else if ((SVGA_VENDOR(Chipset) == V_S3) && (Chipset >= CHIP_S3_928D))
	{
	    if (S3_TVP3020Check(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	    if (S3_Bt485Check(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	    if (S3_ATT498Check(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	    if (S3_STG1700Check(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	    if (S3_GENDACCheck(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	    if (S3_SC15025Check(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	}
	else if ((SVGA_VENDOR(Chipset) == V_S3) && (Chipset >= CHIP_S3_801B))
	{
	    if (S3_GENDACCheck(RamDac))
	    {
		DisableIOPorts(NUMPORTS, Ports);
		return;
	    }
	}

	/*
	 * Save current state.
	 */
	(void) dactocomm();
	oldcomm = inp(0x3C6);
	dactopel();
	oldpel = inp(0x3C6);

	/*
	 * Do 8-bit-wide check.
	 */
	(void) dactocomm();
	outp(0x3C6, 0x00);
	dac8 = Width8Check();
	dactopel();

	/*
	 * Check whether this DAC has a HiColor-style command register.
	 */
	notcomm = ~oldcomm;
	outp(0x3C6, notcomm);
	(void) dactocomm();
	v = inp(0x3C6);
	if (v != notcomm)
	{
	    /*
	     * Check for early-generation HiColor-style DAC.
	     */
	    if ((SetComm(0xE0) & 0xE0) != 0xE0)
	    {
		dactopel();
		x = inp(0x3C6);
		do
		{
		    /*
		     * Wait for same value twice.
		     */
		    y = x;
		    x = inp(0x3C6);
		}
		while (x != y);
		z = x;
		u = dactocomm();
		if (u != 0x8E)
		{
		    /*
		     * If command register is 0x8E, we've got an SS24;
		     */
		    y = 8;
		    do
		    {
			x = inp(0x3C6);
			y--;
		    }
		    while ((x != 0x8E) && (y != 0));
		}
		else
		{
		    x = u;
		}
		if (x == 0x8E)
		{
		    *RamDac = DAC_SS24;
		}
		else
		{
		    /*
		     * Sierra SC11486
		     */
		    *RamDac = DAC_SIERRA15;
		}
		dactopel();
	    }
	    else
	    {
		/*
		 * New generation of advanced DACs
		 */
		if ((SetComm(0x60) & 0xE0) == 0x00)
		{
		    /*
		     * AT&T 20C490/20C493
		     */
		    if ((SetComm(0x02) & 0x02) != 0x00)
		    {
			*RamDac = DAC_ATT490;
			*RamDac |= DAC_6_8_PROGRAM;
		    }
		    else
		    {
			*RamDac = DAC_ATT493;
		    }
		}
		else
		{
		    x = SetComm(oldcomm);
		    if (inp(0x3C6) == notcomm)
		    {
			if (SetComm(0xFF) != 0xFF)
			{
			    *RamDac = DAC_ACUMOS;
			}
			else
			{
			    (void) Width8Check();
			    dactocomm();
			    tmp = inp(0x3C6);
			    dactocomm();
			    outp(0x3C6, (tmp | 0x02) & 0xFE);
			    dac8now = Width8Check();
			    if (dac8now)
			    {
				if (Width8Check())
				{
					*RamDac = DAC_ATT491;
					*RamDac |= DAC_6_8_PROGRAM;
				}
				else
				{
					*RamDac = DAC_CIRRUS24;
				}
			    }
			    else
			    {
				*RamDac = DAC_ATT492;
			    }
			}
		    }
		    else
		    {
			if (trigdac() == notcomm)
			{
				*RamDac = DAC_CIRRUS24;
			}
			else
			{
			    dactopel();
			    outp(0x3C6, 0xFF);
			    switch (trigdac())
			    {
			    case 0x44:
				*RamDac = DAC_MUSIC4870;
				break;
			    case 0x82:
				*RamDac = DAC_MUSIC4910;
				break;
			    case 0x8E:
				*RamDac = DAC_SS24;
				break;
			    default:
				if (TestDACBit(0x10,oldcomm,oldpel))
				{
					*RamDac = DAC_SIERRA24;
					*RamDac |= DAC_6_8_PROGRAM;
				}
				else if (TestDACBit(0x04,oldcomm,oldpel))
				{
					*RamDac = DAC_UNKNOWN;
				}
				else
				{
					*RamDac = DAC_SIERRA15_16;
				}
				break;
			    }
			}
		    }
		}
	    }
	    dactocomm();
	    outp(0x3C6, oldcomm);
	}
	dactopel();
	outp(0x3C6, oldpel);

	/*
	 * If no special DAC found, check for the EDSUN DAC.
	 */
	if (*RamDac == DAC_STANDARD)
	{
	    /*
	     * Write "CEGEDSUN" + mode to DAC index 0xDE (222)
	     */
	    waitforretrace();
	    outp(0x3C8, 0xDE);
	    outp(0x3C9, (Byte)'C');
	    outp(0x3C9, (Byte)'E');
	    outp(0x3C9, (Byte)'G');
	    outp(0x3C8, 0xDE);
	    outp(0x3C9, (Byte)'E');
	    outp(0x3C9, (Byte)'D');
	    outp(0x3C9, (Byte)'S');
	    outp(0x3C8, 0xDE);
	    outp(0x3C9, (Byte)'U');
	    outp(0x3C9, (Byte)'N');
	    outp(0x3C9, (Byte)'\n');
	    /*
	     * Should be in CEG mode now.
	     */
	    outp(0x3C6, 0xFF);
	    x = (inp(0x3C6) >> 4) & 0x07;
	    if (x < 0x07)
	    {
		*RamDac = DAC_EDSUN;
		waitforretrace();
		outp(0x3C8, 0xDF);
		outp(0x3C9, 0x00);
	    }
	}

	/*
	 * Remember if DAC was in 8-bit mode.
	 */
	if (dac8)
	{
		*RamDac |= DAC_8BIT;
	}

	DisableIOPorts(NUMPORTS, Ports);
	return;
}
