/* $XConsortium: mach32.c,v 1.5 95/01/16 13:16:29 kaleb Exp $ */
/* $XFree86: xc/programs/Xserver/hw/xfree86/accel/mach32/mach32.c,v 3.30 1995/01/28 16:58:23 dawes Exp $ */
/*
 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
 * Copyright 1993 by Kevin E. Martin, Chapel Hill, North Carolina.
 *
 * 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, KEVIN E. MARTIN, RICKARD E. FAITH, AND CRAIG E. GROESCHEL
 * DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ANY OR ALL OF
 * THE AUTHORS 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)
 * Modified for the Mach-8 by Rickard E. Faith (faith@cs.unc.edu)
 * Rewritten for the Mach32 by Kevin E. Martin (martin@cs.unc.edu)
 * Modified for 16 bpp and VTSema-independence by Craig E. Groeschel
 *
 * Header: /proj/X11/mit/server/ddx/xf86/vga/RCS/vga.c,v 1.2 1991/06/27 00:02:49 root Exp
 */


#include "X.h"
#include "Xmd.h"
#include "input.h"
#include "scrnintstr.h"
#include "pixmapstr.h"
#include "mipointer.h"
#include "cursorstr.h"
#include "mi.h"
#include "cfb.h"
#include "cfb16.h"
#include "gc.h"
#include "windowstr.h"

#include "compiler.h"

#include "xf86.h"
#include "xf86Priv.h"
#include "xf86Procs.h"
#include "xf86_OSlib.h"
#include "xf86_HWlib.h"
#include "mach32.h"
#include "regmach32.h"

#define XCONFIG_FLAGS_ONLY
#include "xf86_Config.h"

extern int defaultColorVisualClass;
extern int mach32MaxClock;
extern int mach32MaxTlc34075Clock;
extern int mach32Max16bppClock;
extern Bool xf86Verbose, xf86Resetting, xf86Exiting, xf86ProbeFailed;
unsigned short mach32MemorySize = 0;
extern char *xf86VisualNames[];

static Bool mach32ValidMode(
#if NeedFunctionPrototypes 
    DisplayModePtr 
#endif
);

ScrnInfoRec mach32InfoRec = {
    FALSE,		/* Bool configured */
    -1,			/* int tmpIndex */
    -1,			/* int scrnIndex */
    mach32Probe,      	/* Bool (* Probe)() */
    mach32Initialize,	/* Bool (* Init)() */
    mach32ValidMode,	/* Bool (* ValidMode)() */
    mach32EnterLeaveVT,/* void (* EnterLeaveVT)() */
    (void (*)())NoopDDA,		/* void (* EnterLeaveMonitor)() */
    (void (*)())NoopDDA,		/* void (* EnterLeaveCursor)() */
    mach32AdjustFrame,	/* void (* AdjustFrame)() */
    mach32SwitchMode,	/* Bool (* SwitchMode)() */
    mach32PrintIdent,	/* void (* PrintIdent)() */
    8,			/* int depth */
    {5, 6, 5},          /* xrgb weight */
    8,			/* int bitsPerPixel */
    PseudoColor,       	/* int defaultVisual */
    -1, -1,		/* int virtualX,virtualY */
    -1,                 /* int displayWidth */
    -1, -1, -1, -1,	/* int frameX0, frameY0, frameX1, frameY1 */
    {0, },	       	/* OFlagSet options */
    {0, },	       	/* OFlagSet clockOptions */
    {0, },	       	/* OFlagSet xconfigFlag */
    NULL,	       	/* char *chipset */
    NULL,	       	/* char *ramdac */
    0,			/* int dacSpeed */
    0,			/* int clocks */
    {0, },		/* int clock[MAXCLOCKS] */
    0,			/* int maxClock */
    0,			/* int videoRam */
    0xC0000,            /* int BIOSbase */   
    0,			/* unsigned long MemBase */
    240, 180,		/* int width, height */
    0,                  /* unsigned long  speedup */
    NULL,	       	/* DisplayModePtr modes */
    NULL,	       	/* DisplayModePtr pModes */
    NULL,               /* char *clockprog */
    -1,                 /* int textclock */   
    FALSE,              /* Bool bankedMono */
    "Mach32",           /* char *name */
    {0, },		/* xrgb blackColour */
    {0, },		/* xrgb whiteColour */
    mach32ValidTokens,	/* int *validTokens */
    MACH32_PATCHLEVEL,	/* char *patchlevel */
    0,			/* int IObase */
    0,			/* int PALbase */
    0,			/* int COPbase */
    0,			/* int POSbase */
    0,			/* int instance */
    0,			/* int s3Madjust */
    0,			/* int s3Nadjust */
    0,			/* int s3MClk */
};

short mach32alu[16] = {
    MIX_0,
    MIX_AND,
    MIX_SRC_AND_NOT_DST,
    MIX_SRC,
    MIX_NOT_SRC_AND_DST,
    MIX_DST,
    MIX_XOR,
    MIX_OR,
    MIX_NOR,
    MIX_XNOR,
    MIX_NOT_DST,
    MIX_SRC_OR_NOT_DST,
    MIX_NOT_SRC,
    MIX_NOT_SRC_OR_DST,
    MIX_NAND,
    MIX_1
};

extern miPointerScreenFuncRec xf86PointerScreenFuncs;

LUTENTRY mach32savedLUT[256];
static Bool LUTissaved = FALSE;

short mach32MaxX, mach32MaxY;
short mach32VirtX, mach32VirtY;
short mach32DisplayWidth;

Bool mach32Use4MbAperture = FALSE;
Bool mach32DAC8Bit = FALSE;

Bool mach32clkprobedif4fix = FALSE;

static unsigned Mach32_IOPorts[] = {
	/* VGA Registers */
        0x3B4, 0x3B5, 0x3BA, 0x3C0, 0x3C1, 0x3C2, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 
	0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CE, 0x3CF, 0x3D4, 0x3D5, 0x3DA,
	/* ATI VGA Registers */
	ATIEXT, ATIEXT+1,
	/* 8514 Registers */
        DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA, DISP_STAT,
	H_TOTAL, H_DISP, H_SYNC_STRT, H_SYNC_WID,
	V_TOTAL, V_DISP, V_SYNC_STRT, V_SYNC_WID,
	DISP_CNTL, ADVFUNC_CNTL, SUBSYS_CNTL, ROM_PAGE_SEL,
	CUR_Y, CUR_X, DESTY_AXSTP, DESTX_DIASTP, 
	ERR_TERM, MAJ_AXIS_PCNT, GP_STAT, CMD, SHORT_STROKE,
	BKGD_COLOR, FRGD_COLOR, WRT_MASK, RD_MASK,
	COLOR_CMP, BKGD_MIX, FRGD_MIX, MULTIFUNC_CNTL, PIX_TRANS,
	/* Mach32 Registers */
	GE_OFFSET_LO, GE_OFFSET_HI, CRT_OFFSET_LO, CRT_OFFSET_HI,
	CLOCK_SEL, SHADOW_SET, SHADOW_CTL, CRT_PITCH, GE_PITCH,
	EXT_SCISSOR_L, EXT_SCISSOR_R, EXT_SCISSOR_T, EXT_SCISSOR_B,
	MISC_OPTIONS, CONFIG_STATUS_1, CONFIG_STATUS_2, 
	MEM_CFG, MEM_BNDRY, ROM_ADDR_1, READ_SRC_X, CHIP_ID, EXT_FIFO_STATUS,
	R_EXT_GE_CONFIG, EXT_GE_CONFIG, DP_CONFIG, DEST_X_START, DEST_X_END,
	DEST_Y_END, ALU_FG_FN, MISC_CNTL, R_MISC_CNTL, HORZ_OVERSCAN,
	VERT_OVERSCAN, EXT_GE_STATUS, LINEDRAW, LINEDRAW_OPT, LINEDRAW_INDEX,
	EXT_CURSOR_COLOR_0, EXT_CURSOR_COLOR_1,
};
static int Num_Mach32_IOPorts = (sizeof(Mach32_IOPorts)/
				 sizeof(Mach32_IOPorts[0]));

/* mach32WeightMasks must match mach32weights[], below */
static short mach32WeightMasks[] = { RGB16_565, RGB16_555,
					RGB16_655, RGB16_664 };
short mach32WeightMask;

static mach32CRTCRegRec mach32CRTCRegs;
static ScreenPtr savepScreen = NULL;
static PixmapPtr ppix = NULL;

/*
 * ATI Hardware Probe
 *
 */

typedef struct ATIInformationBlock {
   char board_identifier[2];
   char equipment_flags[2];
   char asic_identifier;
   int  bios_major;
   int  bios_minor;
   char bios_date[21];
   int  VGA_Wonder_Present;
   int  Graphics_Ultra_Present;
   int  ATI_Accelerator_Present;
   int  Mach8_Present;
   int  Mach32_Present;
   int  Bus_8bit_Only;
   int  Bus_Type;
   int  Mem_Size;
   int  Mem_Type;
   int  DAC_Type;
} ATIInformationBlock;

int	mach32Ramdac;
char	*mach32ramdac_names[] = {
	"ATI-68830",
	"IMS-G173/SC1148[368]",
	"ATI68875/TLC34075/Bt885",
	"Bt47[68]/INMOS17[68]",
	"AT&T20C49[01]/Bt48[12]/IMS-G174/MU9C{1880,4910}/SC1502[56]",
	"ATI-68860",
	"unknown",
	"unknown",
};


int	mach32BusType;

static ATIInformationBlock *GetATIInformationBlock()
{
#define BIOS_DATA_SIZE 0x70
   char                       signature[]    = " 761295520";
   char                       bios_data[BIOS_DATA_SIZE];
   char                       bios_signature[10];
   int                        tmp;
   static ATIInformationBlock info = { 0, };
	 
   if (xf86ReadBIOS(mach32InfoRec.BIOSbase, 0x30,
		    (unsigned char *)bios_signature, 10) != 10) {
      return NULL;
   }
   if (strncmp( signature, bios_signature, 10 ))
	 return NULL;

   if (xf86ReadBIOS(mach32InfoRec.BIOSbase, 0x00,
		    (unsigned char *)bios_data, BIOS_DATA_SIZE)
       != BIOS_DATA_SIZE) {
      return NULL;
   }

   info.board_identifier[0]    = bios_data[ 0x40 ];
   info.board_identifier[1]    = bios_data[ 0x41 ];
   info.equipment_flags[0]     = bios_data[ 0x42 ];
   info.equipment_flags[1]     = bios_data[ 0x44 ];
   info.asic_identifier        = bios_data[ 0x43 ];
   info.bios_major             = bios_data[ 0x4c ];
   info.bios_minor             = bios_data[ 0x4d ];
   strncpy( info.bios_date, bios_data + 0x50, 20 );
   
   info.VGA_Wonder_Present     = bios_data[ 0x44 ] & 0x40;
   info.Graphics_Ultra_Present = !(bios_data[ 0x44 ] & 0x40);


				/* Test for graphics acceleration */
   if (!info.Graphics_Ultra_Present)
	 return &info;

				/* Test for ATI accelerator product */
   info.ATI_Accelerator_Present = 1;

   /* Reset the 8514/A, and disable all interrupts. */
   outw(SUBSYS_CNTL, GPCTRL_RESET | CHPTEST_NORMAL);
   outw(SUBSYS_CNTL, GPCTRL_ENAB | CHPTEST_NORMAL);

   tmp = inw( ROM_ADDR_1 );
   outw( ROM_ADDR_1, 0x5555 );
   ProbeWaitIdleEmpty();
   if (inw( ROM_ADDR_1 ) != 0x5555) {
      info.ATI_Accelerator_Present = 0;
   } else {
      outw( ROM_ADDR_1, 0x2a2a );
      ProbeWaitIdleEmpty();
      if (inw( ROM_ADDR_1 ) != 0x2a2a) {
	 info.ATI_Accelerator_Present = 0;
      }
   }
   outw( ROM_ADDR_1, tmp );

   if (!info.ATI_Accelerator_Present)
	 return &info;
   
				/* Determine chip family */
   outw( DESTX_DIASTP, 0xaaaa );
   WaitIdleEmpty();
   if (inw( READ_SRC_X ) != 0x02aa)
	 info.Mach8_Present = 1;
   else
	 info.Mach32_Present = 1;

   outw( DESTX_DIASTP, 0x5555 );
   WaitIdleEmpty();
   if (inw( READ_SRC_X ) != 0x0555) {
      if (!info.Mach8_Present) {
	 ErrorF( "WARNING: Chip should be Mach8, but isn't!\n" );
	 info.Mach8_Present = 0;
      }
   } else {
      if (!info.Mach32_Present) {
	 ErrorF( "WARNING: Chip should be Mach32, but isn't!\n" );
	 info.Mach32_Present = 0;
      }
   }

   if (info.Mach8_Present) {
      tmp = inw( CONFIG_STATUS_1 );

      if (!(tmp & 0x02))
	    info.Bus_8bit_Only = 1;
      
      switch( (tmp & MEM_INSTALLED) >> 5 ) {
      case 0:
	 info.Mem_Size = 512;
	 break;
      case 1:
	 info.Mem_Size = 1024;
	 break;
      }

      info.Mem_Type = (tmp & DRAM_ENA) >> 4;
   }

   if (info.Mach32_Present) {
      tmp = inw( CONFIG_STATUS_1 );
      info.Bus_Type = tmp & BUS_TYPE;
      info.Mem_Type = tmp & MEM_TYPE;
      info.DAC_Type = (tmp & DACTYPE) >> 9;

      tmp = inw( CONFIG_STATUS_2 );
      if ((info.Bus_Type == ISA_16_BIT) && !(tmp & ISA_16_ENA))
	    info.Bus_8bit_Only = 1;

      tmp = inw( MISC_OPTIONS );
      switch (tmp & MEM_SIZE_ALIAS) {
      case MEM_SIZE_512K:
	 info.Mem_Size = 512;
	 break;
      case MEM_SIZE_1M:
	 info.Mem_Size = 1024;
	 break;
      case MEM_SIZE_2M:
	 info.Mem_Size = 2*1024;
	 break;
      case MEM_SIZE_4M:
	 info.Mem_Size = 4*1024;
	 break;
      }
   }

   return &info;
}

/*
 * mach32Probe --
 *     Probe the hardware
 */
Bool
mach32Probe()
{
    int                   i, j;
    DisplayModePtr        pMode, pEnd;
    ATIInformationBlock   *info;
    int                   available_ram;
    Bool                  sw_cursor_supplied;
    OFlagSet              validOptions;
    int                   tx, ty;
    xrgb mach32weights[] = { { 5, 6, 5 }, { 5, 5, 5 }, { 6, 5, 5 },
			     { 6, 6, 4 } };


    xf86ClearIOPortList(mach32InfoRec.scrnIndex);
    xf86AddIOPorts(mach32InfoRec.scrnIndex, Num_Mach32_IOPorts, Mach32_IOPorts);

    xf86EnableIOPorts(mach32InfoRec.scrnIndex);

    info = GetATIInformationBlock();
    if (!info || !info->Mach32_Present) {
	xf86DisableIOPorts(mach32InfoRec.scrnIndex);
	return(FALSE);
    }

    if (xf86bpp < 0) {
	xf86bpp = mach32InfoRec.depth;
    }
    if (xf86weight.red == 0 || xf86weight.green == 0 || xf86weight.blue == 0) {
	xf86weight = mach32InfoRec.weight;
    }
    switch (xf86bpp) {
    case 8:
	break;
    case 16:
#if 0
	if (info->DAC_Type == DAC_BT476) {
/*
 * Hate to break the news to them, but hopefully this will forestall
 * queries of, "But you said it supports 16bpp...?"
 */
	    ErrorF("Unsupported bpp--this ramdac supports only 8 bpp\n");
	    return(FALSE);
	}
#else
	if (info->DAC_Type != DAC_TLC34075) {
	    ErrorF("Unsupported bpp.\n");
	    return(FALSE);
	}
#endif
	mach32InfoRec.depth = 16;	/* if 555, set to 15, below */
	mach32InfoRec.bitsPerPixel = 16;
	if (mach32InfoRec.defaultVisual < 0)
	    mach32InfoRec.defaultVisual = TrueColor;
	if (defaultColorVisualClass < 0)
	    defaultColorVisualClass = mach32InfoRec.defaultVisual;
	if (defaultColorVisualClass != TrueColor) {
	    ErrorF("Invalid default visual type: %d (%s)\n",
		   defaultColorVisualClass,
		   xf86VisualNames[defaultColorVisualClass]);
	    return(FALSE);
	}
	break;
    default:
#if 0
	ErrorF("Invalid bpp--valid ");
	if (info->DAC_Type == DAC_BT476)
	    ErrorF("number of bpp is 8\n");
	else
	    ErrorF("numbers of bpp are 8 and 16\n");
#else
	ErrorF("Invalid bpp.\n");
#endif
	return(FALSE);
    }


    if (xf86bpp == 16) {
	for (i = 0; i < 4; i++) {
	    if (xf86weight.red == mach32weights[i].red
		&& xf86weight.green == mach32weights[i].green
		&& xf86weight.blue == mach32weights[i].blue)
	    break;
	}
	if (i == 4) {
	    ErrorF("Invalid color weighting\n");
	    return(FALSE);
	}
	if (i == 1)
	    mach32InfoRec.depth = 15;
#if 0
	if ( (info->DAC_Type == DAC_SC11483 || info->DAC_Type == DAC_BT481)
	    && (i > 1)) {
	    ErrorF("Invalid RGB weighting--valid weights are 555 and 565.\n");
	    return(FALSE);
	}
#endif
	mach32WeightMask = mach32WeightMasks[i];
    }


    /* no pixel multiplexing at 16bpp */
    mach32InfoRec.maxClock = mach32MaxClock;
    switch (mach32InfoRec.bitsPerPixel) {
    case 8:
	switch(info->DAC_Type) {
	case DAC_TLC34075: 
	    mach32InfoRec.maxClock = mach32MaxTlc34075Clock;
	    break;
	}
	break;
    case 16:
	mach32InfoRec.maxClock = mach32Max16bppClock;
	break;
    }

    OFLG_ZERO(&validOptions);
    OFLG_SET(OPTION_SW_CURSOR, &validOptions);
    OFLG_SET(OPTION_NOLINEAR_MODE, &validOptions);
    OFLG_SET(OPTION_CSYNC, &validOptions);
    OFLG_SET(OPTION_INTEL_GX, &validOptions);
    OFLG_SET(OPTION_DAC_8_BIT, &validOptions);
    xf86VerifyOptions(&validOptions, &mach32InfoRec);
    
    mach32InfoRec.chipset = "mach32";
    xf86ProbeFailed = FALSE;

    mach32BusType = info->Bus_Type;
    if (xf86Verbose)
    {
	ErrorF("%s %s: card type: ", XCONFIG_PROBED, mach32InfoRec.name);
	switch(mach32BusType)
	{
	case EISA:
	    ErrorF("EISA\n");
	    break;
	case LOCAL_386SX:
	    ErrorF("localbus (386SX)\n");
	    break;
	case LOCAL_386DX:
	    ErrorF("localbus (386DX)\n");
	    break;
	case LOCAL_486:
	    ErrorF("localbus (486)\n");
	    break;
	case PCI:
	    ErrorF("PCI\n");
	    break;
	default:
	    ErrorF("ISA\n");
	}
    }

    if (!mach32InfoRec.clocks)
    {
        outb(DISP_CNTL, DISPEN_DISAB /*| INTERLACE*/ | MEMCFG_4 | ODDBNKENAB);
        /* 13-jun-93 TCG : set up dummy video mode */
        outw(SHADOW_SET, 1);
        outw(SHADOW_CTL, 0);
        outw(SHADOW_SET, 2);
        outw(SHADOW_CTL, 0);
        outw(SHADOW_SET, 0);
        outw(ADVFUNC_CNTL, DISABPASSTHRU);

        /* vt: 480 488 +31 528 hz: 640 656 +248 920 */
        outw(V_TOTAL, 0x420);
        outw(V_DISP, 0x3c0);
        outw(V_SYNC_STRT, 0x3d0);
        outw(V_SYNC_WID, 0x1f);
        outb(H_TOTAL, 0x72);
        outw(H_DISP, 0x4f);
        outb(H_SYNC_STRT, 0x51);
        outb(H_SYNC_WID, 0x1f);

        outb(DAC_MASK, 0x00);
        outb(DISP_CNTL, DISPEN_ENAB | MEMCFG_4 | ODDBNKENAB);

        /* 2-oct-93 TCG : detect clocks with dif4 fix */
        mach32clkprobedif4fix = TRUE;
        xf86GetClocks(16, mach32ClockSelect, (void (*)())NoopDDA,
			(void (*)())NoopDDA, DISP_STAT, 2, 7, 44900,
			&mach32InfoRec);
        outw(CLOCK_SEL, 0); /* reset pass-through */
        mach32clkprobedif4fix = FALSE;

        outw(SHADOW_SET, 1);
        outw(SHADOW_CTL, 0x3f);
        outw(SHADOW_SET, 2);
        outw(SHADOW_CTL, 0x3f);
        outw(SHADOW_SET, 0);

        outb(DAC_MASK, 0xff);

        for (j = 0; j < 16; j++)
            mach32InfoRec.clock[j + 16] = mach32InfoRec.clock[j] / 2;

        mach32InfoRec.clocks = 32;
    }


    if (xf86Verbose) {
	ErrorF("%s ",OFLG_ISSET(XCONFIG_CLOCKS,&mach32InfoRec.xconfigFlag) ?
			XCONFIG_GIVEN : XCONFIG_PROBED);
	ErrorF("%s: ", mach32InfoRec.name);
	ErrorF("Number of Clocks: %d", mach32InfoRec.clocks);

	for (i = 0; i < mach32InfoRec.clocks; i++) {
	    if (i % 8 == 0) 
               ErrorF("\n%s %s: clocks:", 
                 OFLG_ISSET(XCONFIG_CLOCKS,&mach32InfoRec.xconfigFlag) ?
                 XCONFIG_GIVEN : XCONFIG_PROBED,
                 mach32InfoRec.name);
	    ErrorF(" %6.2f", mach32InfoRec.clock[i]/1000.0);
	}
	ErrorF("\n");
    }

    tx = mach32InfoRec.virtualX;
    ty = mach32InfoRec.virtualY;
    pMode = mach32InfoRec.modes;
    if (pMode == NULL) {
	ErrorF("No modes supplied in XF86Config\n");
	xf86DisableIOPorts(mach32InfoRec.scrnIndex);
	return(FALSE);
    }
    pEnd = (DisplayModePtr)NULL;
    do {
	  DisplayModePtr pModeSv;
	  /*
	   * xf86LookupMode returns FALSE if it ran into an invalid
	   * parameter
	   */
	  if(xf86LookupMode(pMode, &mach32InfoRec) == FALSE) {
		pModeSv=pMode->next;
		xf86DeleteMode(&mach32InfoRec, pMode);
		pMode = pModeSv; 
	  } else if (((tx > 0) && (pMode->HDisplay > tx)) || 
		     ((ty > 0) && (pMode->VDisplay > ty))) {
		pModeSv=pMode->next;
		ErrorF("%s %s: Resolution %dx%d too large for virtual %dx%d\n",
		       XCONFIG_PROBED, mach32InfoRec.name,
			pMode->HDisplay, pMode->VDisplay, tx, ty);
		xf86DeleteMode(&mach32InfoRec, pMode);
		pMode = pModeSv;
	  } else {
		/*
		 * Successfully looked up this mode.  If pEnd isn't 
		 * initialized, set it to this mode.
		 */
		if(pEnd == (DisplayModePtr) NULL)
			pEnd = pMode;
		pMode = pMode->next;
		mach32InfoRec.virtualX = max(mach32InfoRec.virtualX,
					     pMode->HDisplay);
		mach32InfoRec.virtualY = max(mach32InfoRec.virtualY,
					     pMode->VDisplay);
	  }
    } while (pMode != pEnd);

    mach32VirtX = mach32InfoRec.virtualX = (mach32InfoRec.virtualX+7) & 0xfff8;
    mach32VirtY = mach32InfoRec.virtualY;

    if ((tx != mach32InfoRec.virtualX) || (ty != mach32InfoRec.virtualY))
	OFLG_CLR(XCONFIG_VIRTUAL,&mach32InfoRec.xconfigFlag);

    /*
     * Limitation of 8514 drawing commands  (taking into account 256 lines
     * required by the cache)
     */
    if (mach32VirtY > 1280) {
	ErrorF("Virtual screen height must be no greater than 1280\n");
	xf86DisableIOPorts(mach32InfoRec.scrnIndex);
	return(FALSE);
    }
    if (mach32VirtX > 1536) {
	ErrorF("Virtual screen width must be no greater than 1536\n");
	xf86DisableIOPorts(mach32InfoRec.scrnIndex);
	return(FALSE);
    }

    if (xf86Verbose) {
	ErrorF("%s %s: Virtual resolution: %dx%d\n",
	       OFLG_ISSET(XCONFIG_VIRTUAL, &mach32InfoRec.xconfigFlag) ?
		XCONFIG_GIVEN : XCONFIG_PROBED,
		mach32InfoRec.name, mach32VirtX, mach32VirtY);
    }

    if (mach32VirtX < 1024)
	mach32DisplayWidth = 1024;
    else
	mach32DisplayWidth = mach32VirtX;

    if (xf86Verbose) {
	ErrorF("%s %s: Display width: %d\n",
		XCONFIG_PROBED, mach32InfoRec.name, mach32DisplayWidth);
    }

    if (!mach32InfoRec.videoRam) {
#ifdef NEW_MEM_DETECT
	mach32InfoRec.videoRam = mach32GetMemSize();
#else
	mach32InfoRec.videoRam = info->Mem_Size;
#endif
    }

    /* Set mach32MemorySize to required MEM_SIZE value in MISC_OPTIONS */
    if (mach32InfoRec.videoRam <= 512)
	mach32MemorySize = MEM_SIZE_512K;
    else if (mach32InfoRec.videoRam <= 1024)
	mach32MemorySize = MEM_SIZE_1M;
    else if (mach32InfoRec.videoRam <= 2048)
	mach32MemorySize = MEM_SIZE_2M;
    else
	mach32MemorySize = MEM_SIZE_4M;
	
    if (xf86Verbose) {
       ErrorF("%s %s: videoram: %dk\n",
	      OFLG_ISSET(XCONFIG_VIDEORAM, &mach32InfoRec.xconfigFlag) ?
	      XCONFIG_GIVEN : XCONFIG_PROBED, mach32InfoRec.name,
	      mach32InfoRec.videoRam );
    }

    if (((mach32DisplayWidth) * (mach32VirtY) *
	 (mach32InfoRec.bitsPerPixel / 8)) >
	(mach32InfoRec.videoRam*1024)) {
	ErrorF("Not enough memory for requested display size (%dx%d)\n",
	       mach32DisplayWidth, mach32VirtY);
	xf86DisableIOPorts(mach32InfoRec.scrnIndex);
	return(FALSE);
    }

			/* Set values of mach32MaxX and mach32MaxY.
			 * This must be done here so that cache area and
			 * scissor limits are set up correctly (this setup
			 * happens before the pixmap cache is initialized).
			 */
    
    mach32MaxX = mach32DisplayWidth - 1;
    available_ram = mach32InfoRec.videoRam * 1024;

    sw_cursor_supplied = OFLG_ISSET(OPTION_SW_CURSOR, &mach32InfoRec.options);
    if (!sw_cursor_supplied)
    {
	if (available_ram - (mach32DisplayWidth * mach32VirtY) >=
	    MACH32_CURSBYTES)
	{
	    available_ram -= (MACH32_CURSBYTES + 1023) & ~1023;
	    mach32InfoRec.videoRam -= (MACH32_CURSBYTES + 1023) / 1024;
	}
	else
	{ 
	    OFLG_SET(OPTION_SW_CURSOR, &mach32InfoRec.options);
	    ErrorF("Warning: Not enough memory to use the hardware cursor.\n");
	    ErrorF("  Decreasing the virtual Y resolution by 1 will allow\n");
	    ErrorF("  you to use the hardware cursor.\n");
	}
    }
	
    ErrorF("%s %s: Using %s cursor\n", sw_cursor_supplied ? XCONFIG_GIVEN :
	   XCONFIG_PROBED, mach32InfoRec.name,
	   OFLG_ISSET(OPTION_SW_CURSOR, &mach32InfoRec.options) ?
	   "software" : "hardware");

    mach32MaxY = available_ram /
                 (mach32DisplayWidth * (mach32InfoRec.bitsPerPixel / 8)) - 1;

    if (mach32MaxY > 1535)
	  mach32MaxY = 1535;	/* Limitation of 8514 drawing commands */
	  
    mach32Use4MbAperture = !OFLG_ISSET(OPTION_NOLINEAR_MODE,
				       &mach32InfoRec.options) &&
			   (info->Bus_Type == LOCAL_386SX ||
			    info->Bus_Type == LOCAL_386DX ||
			    info->Bus_Type == LOCAL_486 ||
			    info->Bus_Type == EISA ||
                            info->Bus_Type == PCI)
			   && xf86LinearVidMem();

    mach32Ramdac = info->DAC_Type;

    if (xf86Verbose) {
	if (mach32Use4MbAperture) {
	    ErrorF("%s %s: Using 4 MB aperture\n", XCONFIG_PROBED,
		   mach32InfoRec.name);
	} else {
	    ErrorF("%s %s: Using 64 kb aperture\n",
		   OFLG_ISSET(OPTION_NOLINEAR_MODE, &mach32InfoRec.options) ?
		   XCONFIG_GIVEN : XCONFIG_PROBED, mach32InfoRec.name);
	}
	ErrorF("%s %s: Ramdac is %s\n", XCONFIG_PROBED, mach32InfoRec.name,
	       mach32ramdac_names[mach32Ramdac]);
    }

    /* The Type 2 RAMDACS can support 8 bits per RGB value, not just the
     * VGA-standard 6 bits; however, according to ATI, this is not an
     * official feature of the mach32.
     */
    mach32DAC8Bit = OFLG_ISSET(OPTION_DAC_8_BIT, &mach32InfoRec.options)
		    && info->DAC_Type == DAC_TLC34075
		    && mach32InfoRec.bitsPerPixel == 8;

    if (xf86Verbose) {
	if (mach32InfoRec.bitsPerPixel == 8)
	    ErrorF("%s %s: Using %d bits per RGB value\n",
		(info->DAC_Type == DAC_TLC34075) ?
		XCONFIG_GIVEN : XCONFIG_PROBED, mach32InfoRec.name,
		mach32DAC8Bit ?  8 : 6);
	else if (mach32InfoRec.bitsPerPixel == 16)
	    ErrorF("%s %s: Color weight: %1d%1d%1d\n", XCONFIG_GIVEN,
		mach32InfoRec.name, xf86weight.red,
		xf86weight.green, xf86weight.blue);
    }

    return(TRUE);
}


/*
 * mach32PrintIdent --
 *     Print the indentification of the video card.
 */
void
mach32PrintIdent()
{
    ErrorF("  %s: accelerated server for ATI Mach32 graphics adaptors ",
	   mach32InfoRec.name);
    ErrorF("(Patchlevel %s)\n", mach32InfoRec.patchLevel);
}


/*
 * mach32Initialize --
 *      Attempt to find and initialize a VGA framebuffer
 *      Most of the elements of the ScreenRec are filled in.  The
 *      video is enabled for the frame buffer...
 */

Bool
mach32Initialize (scr_index, pScreen, argc, argv)
    int            scr_index;    /* The index of pScreen in the ScreenInfo */
    ScreenPtr      pScreen;      /* The Screen to initialize */
    int            argc;         /* The number of the Server's arguments. */
    char           **argv;       /* The arguments themselves. Don't change! */
{
    int displayResolution = 75;  /* default to 75dpi */
    extern int monitorResolution;

    mach32InitGC();
    mach32InitDisplay(scr_index);
    mach32InitAperture(scr_index);
    mach32CalcCRTCRegs(&mach32CRTCRegs, mach32InfoRec.modes);
    mach32SetCRTCRegs(&mach32CRTCRegs);
    mach32InitEnvironment();


    /* Clear the display.
     * Need to set the color, origin, and size.  Then draw.
     */
    WaitQueue(6);
    outw(FRGD_COLOR, 1);
    outw(CUR_X, 0);
    outw(CUR_Y, 0);
    outw(MAJ_AXIS_PCNT, mach32MaxX);
    outw(MULTIFUNC_CNTL, MIN_AXIS_PCNT | mach32MaxY);
    outw(CMD, CMD_RECT | INC_Y | INC_X | DRAW | PLANAR | WRTDATA);

    xf86InitCache(mach32CacheMoveBlock);
    mach32FontCache8Init();

    mach32ImageInit();

    WaitIdleEmpty(); /* Make sure that all commands have finished */

    /*
     * Take display resolution from the -dpi flag if specified
     */

    if (monitorResolution)
	displayResolution = monitorResolution;

    if (!mach32ScreenInit(pScreen, mach32VideoMem,
			  mach32VirtX, mach32VirtY,
			  displayResolution, displayResolution,
			  mach32DisplayWidth))

		return(FALSE);

    savepScreen = pScreen;

    pScreen->CloseScreen = mach32CloseScreen;
    pScreen->SaveScreen = mach32SaveScreen;

    switch (mach32InfoRec.bitsPerPixel) {
    case 8:
	pScreen->InstallColormap = mach32InstallColormap;
	pScreen->UninstallColormap = mach32UninstallColormap;
	pScreen->ListInstalledColormaps = mach32ListInstalledColormaps;
	pScreen->StoreColors = mach32StoreColors;
	break;
    case 16:
	pScreen->InstallColormap = cfbInstallColormap;
	pScreen->UninstallColormap = cfbUninstallColormap;
	pScreen->ListInstalledColormaps = cfbListInstalledColormaps;
	pScreen->StoreColors = (void (*)())NoopDDA;
    }

    if (OFLG_ISSET(OPTION_SW_CURSOR, &mach32InfoRec.options)) {
	miDCInitialize (pScreen, &xf86PointerScreenFuncs);
    } else {
	pScreen->QueryBestSize = mach32QueryBestSize;
	xf86PointerScreenFuncs.WarpCursor = mach32WarpCursor;
	(void)mach32CursorInit(0, pScreen);
    }

    return (cfbCreateDefColormap(pScreen));
}


/*
 *	Assign a new serial number to the window.
 *	Used to force GC validation on VT switch.
 */

/*ARGSUSED*/
static int
mach32NewSerialNumber(pWin, data)
    WindowPtr pWin;
    pointer data;
{
    pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
    return WT_WALKCHILDREN;
}


/*
 * mach32EnterLeaveVT -- 
 *      grab/ungrab the current VT completely.
 */

void
mach32EnterLeaveVT(enter, screen_idx)
     Bool enter;
     int screen_idx;
{
    PixmapPtr pspix;
    ScreenPtr pScreen = savepScreen;

    if (!xf86Exiting && !xf86Resetting) {
	/* cfbGetScreenPixmap(pScreen) */
	switch (mach32InfoRec.bitsPerPixel) {
	case 8:
	    pspix = (PixmapPtr)pScreen->devPrivate;
	    break;
	case 16:
	    {
		pspix =
		  (PixmapPtr)pScreen->devPrivates[cfb16ScreenPrivateIndex].ptr;
	    }
	    break;
	}
    }

    if (pScreen)
	WalkTree(pScreen, mach32NewSerialNumber, 0);

    if (enter) {
	if (vgaBase)
	    xf86MapDisplay(screen_idx, VGA_REGION);
	if (mach32VideoMem != vgaBase)
	    xf86MapDisplay(screen_idx, LINEAR_REGION);
	if (!xf86Resetting) {
	    ScrnInfoPtr pScr = XF86SCRNINFO(pScreen);

	    mach32InitDisplay(screen_idx);
	    mach32SetCRTCRegs(&mach32CRTCRegs);
	    mach32InitEnvironment();

	    WaitQueue(6);
	    outw(FRGD_COLOR, 1);
	    outw(CUR_X, 0);
	    outw(CUR_Y, 0);
	    outw(MAJ_AXIS_PCNT, mach32MaxX);
	    outw(MULTIFUNC_CNTL, MIN_AXIS_PCNT | mach32MaxY);
	    outw(CMD, CMD_RECT | INC_Y | INC_X | DRAW | PLANAR | WRTDATA);

	    mach32FontCache8Init();
	    mach32RestoreCursor(pScreen);
	    mach32AdjustFrame(pScr->frameX0, pScr->frameY0);

	    WaitIdleEmpty(); /* Make sure that all commands have finished */

	    if (LUTissaved) {
		mach32RestoreLUT(mach32savedLUT);
		LUTissaved = FALSE;
		mach32RestoreColor0(pScreen);
	    }

	    if (pspix->devPrivate.ptr != mach32VideoMem && ppix) {
		pspix->devPrivate.ptr = mach32VideoMem;
		(mach32ImageWriteFunc)(0, 0, pScreen->width, pScreen->height,
				 ppix->devPrivate.ptr,
				 PixmapBytePad(pScreen->width,
					       pScreen->rootDepth),
				 0, 0, MIX_SRC, ~0);
	    }
	    if (pScreen) {
		pScreen->CopyWindow = mach32CopyWindow;
		pScreen->GetSpans = mach32GetSpans;
		pScreen->PaintWindowBackground = mach32PaintWindow;
		pScreen->PaintWindowBorder = mach32PaintWindow;
	    }
	}
	if (ppix) {
	    (pScreen->DestroyPixmap)(ppix);
	    ppix = NULL;
	}
    } else {
	if (vgaBase)
	    xf86MapDisplay(screen_idx, VGA_REGION);
	if (mach32VideoMem != vgaBase)
	    xf86MapDisplay(screen_idx, LINEAR_REGION);
	if (!xf86Exiting) {
	    ppix = (pScreen->CreatePixmap)(pScreen,
					   pScreen->width, pScreen->height,
					   pScreen->rootDepth);

	    if (ppix) {
		(mach32ImageReadFunc)(0, 0, pScreen->width, pScreen->height,
				ppix->devPrivate.ptr,
				PixmapBytePad(pScreen->width,
					      pScreen->rootDepth),
				0, 0, ~0);
		pspix->devPrivate.ptr = ppix->devPrivate.ptr;
	    }
	    switch (mach32InfoRec.bitsPerPixel) {
	    case 8:
		pScreen->CopyWindow = cfbCopyWindow;
		pScreen->GetSpans = cfbGetSpans;
		pScreen->PaintWindowBackground = cfbPaintWindow;
		pScreen->PaintWindowBorder = cfbPaintWindow;
		break;
	    case 16:
		pScreen->CopyWindow = cfb16CopyWindow;
		pScreen->GetSpans = cfb16GetSpans;
		pScreen->PaintWindowBackground = cfb16PaintWindow;
		pScreen->PaintWindowBorder = cfb16PaintWindow;
		break;
	    }
	    if (!mach32Use4MbAperture) {
		pScreen->PaintWindowBackground = miPaintWindow;
		pScreen->PaintWindowBorder = miPaintWindow;
	    }
	}

	mach32CursorOff();
	mach32SaveLUT(mach32savedLUT);
	LUTissaved = TRUE;
	if (!xf86Resetting) {
	    mach32CleanUp();
	}
	if (vgaBase)
	    xf86UnMapDisplay(screen_idx, VGA_REGION);
	if (mach32VideoMem != vgaBase)
	    xf86UnMapDisplay(screen_idx, LINEAR_REGION);
    }
}

/*
 * mach32CloseScreen --
 *      called to ensure video is enabled when server exits.
 */

/*ARGSUSED*/
Bool
mach32CloseScreen(screen_idx, pScreen)
     int	screen_idx;
     ScreenPtr	pScreen;
{
    extern void mach32ClearSavedCursor();

    /*
     * Hmm... The server may shut down even if it is not running on the
     * current vt. Let's catch this case here.
     */
    xf86Exiting = TRUE;
    if (xf86VTSema)
	mach32EnterLeaveVT(LEAVE, screen_idx);
    else if (ppix) {
    /* 7-Jan-94 CEG: The server is not running on the current vt.
     * Free the screen snapshot taken when the server vt was left.
     */
	    (savepScreen->DestroyPixmap)(ppix);
	    ppix = NULL;
    }
    mach32ClearSavedCursor(screen_idx);
    if (mach32InfoRec.bitsPerPixel == 8)
	cfbCloseScreen(screen_idx, savepScreen);
    else
	cfb16CloseScreen(screen_idx, savepScreen);
    savepScreen = NULL;
    return(TRUE);
}

/*
 * mach32SaveScreen --
 *      blank the screen.
 */

Bool
mach32SaveScreen (pScreen, on)
     ScreenPtr     pScreen;
     Bool          on;
{
     int	   ext_ge_config;

    if (on)
	SetTimeSinceLastInputEvent();

    if (xf86VTSema) {
	/*
	 *	Make sure that register 0-3 are addressed
	 */
	ext_ge_config = inw(R_EXT_GE_CONFIG) & ~0x3000;
	outw(EXT_GE_CONFIG, ext_ge_config);

/* Quick and dirty hack:
 * To save the screen at 16 bpp, disable the CRT controller.  CEG
 */
	if (on) {
	    if (mach32InfoRec.bitsPerPixel == 8) {
		mach32RestoreColor0(pScreen);
		outb(DAC_MASK, 0xff);
	    } else {
		mach32SetCRTCRegs(&mach32CRTCRegs);
	    }
	} else {
	    if (mach32InfoRec.bitsPerPixel == 8) {
		WaitQueue(5);
		outb(DAC_W_INDEX, 0);
		outb(DAC_DATA, 0);
		outb(DAC_DATA, 0);
		outb(DAC_DATA, 0);
		outb(DAC_MASK, 0x00);
	    } else {
		WaitQueue(1);
		outb(DISP_CNTL, DISPEN_DISAB);
	    }
	}
    }
    return(TRUE);
}

/*
 * mach32AdjustFrame --
 *      Modify the CRT_OFFSET for panning the display.
 */
void
mach32AdjustFrame(x, y)
    int x, y;
{
    /* cursor offset in units of bytes/4 */
    int byte_offset = ((x + y * mach32DisplayWidth) *
			(mach32InfoRec.bitsPerPixel / 8)) >> 2;

    mach32CursorOff();
    WaitQueue(2);
    outw(CRT_OFFSET_LO, byte_offset & 0xffff);
    outw(CRT_OFFSET_HI, (byte_offset >> 16) & 0xf);
    mach32RepositionCursor(savepScreen);
}

/*
 * mach32SwitchMode --
 *      Reinitialize the CRTC registers for the new mode.
 */
Bool
mach32SwitchMode(mode)
    DisplayModePtr mode;
{
    mach32CalcCRTCRegs(&mach32CRTCRegs, mode);
    mach32SetCRTCRegs(&mach32CRTCRegs);

    return(TRUE);
}

/*
 * mach32ClockSelect
 */
Bool
mach32ClockSelect(no)
     int no;
{
  switch(no)
  {
    case CLK_REG_SAVE:
      break;
    case CLK_REG_RESTORE:
      break;
    default:
      if (mach32clkprobedif4fix)
         outw(CLOCK_SEL, (no << 2) | 0xac1);
      else
         outw(CLOCK_SEL, (no << 2) | 0xa01);
  }
  return(TRUE);
}

/*
 * mach32ValidMode --
 *
 */
static Bool
mach32ValidMode(mode)
DisplayModePtr mode;
{
return TRUE;
}

