/* * Copyright 1994 by Marc La France (TSI @ UQV), tsi@gpu.srv.ualberta.ca * * 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 Marc La France not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Marc La France makes no representations * about the suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. * * MARC LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO * EVENT SHALL MARC LA FRANCE 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. */ /* $XFree86: mit/server/ddx/x386/vga256/drivers/ati/driver.c,v 2.15 1994/04/16 04:29:09 dawes Exp $ */ /*************************************************************************/ /* * Author: Marc La France (TSI @ UQV), tsi@gpu.srv.ualberta.ca * * This is a rewrite of the ATI VGA WONDER driver included with XFree86 2.0 * and 2.1. Contributions to the previous versions of this driver by the * following people are hereby acknowledged: * * Thomas Roell, roell@informatik.tu-muenchen.de * Per Lindqvist, pgd@compuram.bbt.se * Doug Evans, dje@cygnus.com * Rik Faith, faith@cs.unc.edu * Arthur Tateishi, ruhtra@turing.toronto.edu * Alain Hebert, aal@broue.rot.qc.ca * * ... and, doubtless, many others whom I do not know about. * * This rewrite exists to fix interlacing, virtual console switching and clock * selection, implement clock probing, allow for an external clock selection * program, (hopefully) enhance support for V3 boards, add support for the * 16-colour and monochrome servers and lots of other nitpicky reasons I don't * recall right now. * * The driver is intended to support ATI VGA WONDER V3, V4, V5, PLUS, XL and * XL24 boards, although it probably works best with the last four. It will * also work with Mach8 and Mach32 boards but will not use their accelerated * features. * * The ATI x8800 chips use special registers for their extended features. * These registers are accessible through an index I/O port and a data I/O * port. The index port number is found in the 16-bit integer at real address * 0x000C0010 (which is usually 0x01CE) and the data port is one more * (usually 0x01CF). These ports differ in their I/O behaviour from the * normal VGA ones: * * write: outw(0x01CE, (data << 8) | index); * read: outb(0x01CE, index); data = inb(0x01CF); * * Two consecutive byte-writes are NOT allowed. Furthermore an index * written to 0x01CE is only usable ONCE! Note also that the setting of ATI * extended registers (especially those with clock selection bits) should be * bracketed by a sequencer reset. * * Boards prior to V5 use 4 crystals. Boards V5 and later use a clock * generator chip. V3 and V4 boards differ when it comes to choosing clock * frequencies. * * V3/V4 Board Clock Frequencies * R E G I S T E R S * 1CE(*) 3C2 3C2 Frequency * B2h/BEh * Bit 6/4 Bit 3 Bit 2 (MHz) * ------- ------- ------- ------- * 0 0 0 50.175 * 0 0 1 56.644 * 0 1 0 Spare 1 * 0 1 1 44.900 * 1 0 0 44.900 * 1 0 1 50.175 * 1 1 0 Spare 2 * 1 1 1 36.000 * * (*): V3 uses index B2h, bit 6; V4 uses index BEh, bit 4 * * V5, PLUS, XL and XL24 usually have an ATI 18810 clock generator chip, but * some have an ATI 18811-0, and it's quite conceivable that some exist with * ATI 18811-1's or ATI 18811-2's. ATI says there is no way for the driver to * determine which clock generator is on the board (their BIOS's are tailored * to the board). * * V5/PLUS/XL/XL24 Board Clock Frequencies * R E G I S T E R S * 1CE 1CE 3C2 3C2 Frequency * B9h BEh (MHz) 18811-1 * Bit 1 Bit 4 Bit 3 Bit 2 18810 18811-0 18811-2 * ------- ------- ------- ------- ------- ------- ------- * 0 0 0 0 30.240 30.240 135.000 * 0 0 0 1 32.000 32.000 32.000 * 0 0 1 0 37.500 110.000 110.000 * 0 0 1 1 39.000 80.000 80.000 * 0 1 0 0 42.954 42.954 100.000 * 0 1 0 1 48.771 48.771 126.000 * 0 1 1 0 (*1) 92.400 92.400 * 0 1 1 1 36.000 36.000 36.000 * 1 0 0 0 40.000 39.910 39.910 * 1 0 0 1 56.644 44.900 44.900 * 1 0 1 0 75.000 75.000 75.000 * 1 0 1 1 65.000 65.000 65.000 * 1 1 0 0 50.350 50.350 50.350 * 1 1 0 1 56.640 56.640 56.640 * 1 1 1 0 (*2) (*3) (*3) * 1 1 1 1 44.900 44.900 44.900 * * (*1) External 0 (supposedly 16.657 Mhz) * (*2) External 1 (supposedly 28.322 MHz) * (*3) This setting doesn't seem to generate anything * * For all boards, these frequencies can be divided by 1, 2, 3 or 4. * * Register 1CE, index B8h * Bit 7 Bit 6 * ------- ------- * 0 0 Divide by 1 * 0 1 Divide by 2 * 1 0 Divide by 3 * 1 1 Divide by 4 * * Because 36 MHz is the only (undivided) frequency available at the same index * on all boards, it will be used for calibration during clock probing. This * replaces vgaGetClocks's assumption that clock 1 is 28.322 MHz. * * There is some question as to whether or not bit 1 of index 0xB9 can * be used for clock selection on a V4 board. This driver makes it * available only if the "undocumented_clocks" option (itself * undocumented :-)) is specified in Xconfig. * * Also it appears that bit 0 of index 0xB9 can also be used for clock * selection on some boards. It is also only available under Xconfig * option "undocumented_clocks". */ /*************************************************************************/ /* * These are X and server generic header files. */ #include "X.h" #include "input.h" #include "screenint.h" /* * These are XFree86-specific header files */ #include "compiler.h" #include "x386.h" #include "x386Priv.h" #include "xf86_OSlib.h" #include "xf86_HWlib.h" #define XCONFIG_FLAGS_ONLY #include "xf86_Config.h" #include "vga.h" /* * Driver data structures. */ typedef struct { vgaHWRec std; /* good old IBM VGA */ unsigned char a3, a6, a7, ac, ad, ae, b0, b1, b2, b3, b5, b6, b8, b9, bd, be; } vgaATIRec, *vgaATIPtr; /* * Forward definitions for the functions that make up the driver. See * the definitions of these functions for the real scoop. */ static Bool ATIProbe(); static char * ATIIdent(); static Bool ATIClockSelect(); static void ATIEnterLeave(); static Bool ATIInit(); static void * ATISave(); static void ATIRestore(); static void ATIAdjust(); /* * These are the bank select functions. They are defined in bank.s */ extern void ATISetRead(); extern void ATISetWrite(); extern void ATISetReadWrite(); /* * Bank selection functions for V3 boards. These are also defined in bank.s */ extern void ATIV3SetRead(); extern void ATIV3SetWrite(); extern void ATIV3SetReadWrite(); /* * This data structure defines the driver itself. The data structure is * initialized with the functions that make up the driver and some data * that defines how the driver operates. */ vgaVideoChipRec ATI = { ATIProbe, /* Probe */ ATIIdent, /* Ident */ ATIEnterLeave, /* EnterLeave */ ATIInit, /* Init */ ATISave, /* Save */ ATIRestore, /* Restore */ ATIAdjust, /* Adjust */ NoopDDA, /* SaveScreen */ NoopDDA, /* GetMode */ NoopDDA, /* FbInit */ ATISetRead, /* SetRead */ ATISetWrite, /* SetWrite */ ATISetReadWrite, /* SetReadWrite */ 0x10000, /* Mapped memory window size (64k) */ 0x10000, /* Video memory bank size (64k) */ 16, /* Shift factor to get bank number */ 0xFFFF, /* Bit mask for address within a bank */ 0x00000, 0x10000, /* Boundaries for reads within a bank */ 0x00000, 0x10000, /* Boundaries for writes within a bank */ TRUE, /* Uses two banks */ VGA_DIVIDE_VERT, /* Divide interlaced vertical timings */ {0,}, /* Options are set by ATIProbe */ 16, /* Virtual X rounding */ }; /* * This is a convenience macro, so that entries in the driver structure * can simply be dereferenced with 'new->xxx'. */ #define new ((vgaATIPtr)vgaNewVideoState) /* * This driver needs two non-standard I/O ports. These are determined * by ATIProbe and are initialized to their most probable values here. */ static unsigned ATI_IOPorts[2] = {0x01CE, 0x01CF}; static int Num_ATI_IOPorts = (sizeof(ATI_IOPorts)/sizeof(ATI_IOPorts[0])); short ATIExtReg = 0x01CE; /* Used by bank.s; Must be short */ /* * Handy macros to read and write extended registers */ #define ATIGetExtReg(Index) \ ( \ outb(ATIExtReg, Index), \ inb(ATIExtReg + 1) \ ) #define ATIPutExtReg(Index, Register_Value) \ outw(ATIExtReg, ((Register_Value) << 8) | Index) static unsigned char ATIChipVersion; static unsigned char ATIBoard; #define ATI_BOARD_V3 0 /* ATIBoard values; Keep chronological */ #define ATI_BOARD_V4 1 #define ATI_BOARD_V5 2 #define ATI_BOARD_PLUS 3 #define ATI_BOARD_XL 4 #define ATI_BOARD_XL24 5 static unsigned char saved_b9_bits_0_and_1 = 0; /* * ATIIdent -- * * Returns a string name for this driver or NULL. */ static char * ATIIdent(n) int n; { static char *chipsets[] = {"ati", "vgawonder"}; if (n >= (sizeof(chipsets) / sizeof(char *))) return(NULL); else return(chipsets[n]); } /* * ATIClockSelect -- * * This function selects the dot-clock with index 'no'. In most cases * this is done my setting the correct bits in various registers (generic * VGA uses two bits in the Miscellaneous Output Register to select from * 4 clocks). Care must be taken to protect any other bits in these * registers by fetching their values and masking off the other bits. * * This function returns FALSE if the passed index is invalid or if the * clock can't be set for some reason. */ static Bool ATIClockSelect(no) int no; { static unsigned char saved_misc, saved_b2, saved_b5, saved_b8, saved_b9, saved_be; unsigned char misc, b9; switch(no) { case CLK_REG_SAVE: /* * Here all of the registers that can be affected by * clock setting are saved into static variables */ saved_misc = inb(0x3CC); saved_b2 = ATIGetExtReg(0xB2); saved_b5 = ATIGetExtReg(0xB5); saved_b8 = ATIGetExtReg(0xB8); saved_b9 = ATIGetExtReg(0xB9); ATIPutExtReg(0xB5, (saved_b5 & 0x7F) ); if (ATIBoard != ATI_BOARD_V3) saved_be = ATIGetExtReg(0xBE); break; case CLK_REG_RESTORE: /* * Here all the previously saved registers are restored */ outw(0x3C4, 0x0100); /* Start synchronous reset */ if (ATIBoard != ATI_BOARD_V3) ATIPutExtReg(0xBE, saved_be); ATIPutExtReg(0xB2, saved_b2); ATIPutExtReg(0xB5, saved_b5); ATIPutExtReg(0xB9, saved_b9); ATIPutExtReg(0xB8, saved_b8); outb(0x3C2, saved_misc); outw(0x3C4, 0x0300); /* End synchronous reset */ break; default: /* * Set the generic two low-order bits of the clock * select */ misc = (inb(0x3CC) & 0xF3) | ((no << 2) & 0x0C); b9 = ATIGetExtReg(0xB9); /* * Set the high order bits */ if (ATIBoard == ATI_BOARD_V3) ATIPutExtReg(0xB2, (ATIGetExtReg(0xB2) & 0xBF) | ((no << 4) & 0x40)); else { ATIPutExtReg(0xBE, (ATIGetExtReg(0xBE) & 0xEF) | ((no << 2) & 0x10)); if ((ATIBoard != ATI_BOARD_V4) || (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options))) { b9 = (b9 & 0xFD) | ((no >> 2) & 0x02); no >>= 1; } } if (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options)) { b9 = (b9 & 0xFE) | ((no >> 3) & 0x01); b9 ^= saved_b9_bits_0_and_1; no >>= 1; } ATIPutExtReg(0xB9, b9); /* * Set clock divider bits */ ATIPutExtReg(0xB8, (no << 3) & 0xC0); /* * Must set miscellaneous output register last */ outb(0x3C2, misc); break; } return(TRUE); } /* * ATIProbe -- * * This is the function that makes a yes/no decision about whether or not * a chipset supported by this driver is present or not. The server will * call each driver's probe function in sequence, until one returns TRUE * or they all fail. */ static Bool ATIProbe() { # define BIOS_DATA_SIZE 0x50 unsigned char BIOS_Data[BIOS_DATA_SIZE]; # define Signature "761295520" # define Signature_Offset 0x31 # define BIOS_Signature &BIOS_Data[Signature_Offset] # define Signature_Size 9 /* * Get out if this isn't the driver the user wants */ if (vga256InfoRec.chipset) if (!StrCaseCmp(vga256InfoRec.chipset, ATIIdent(0))) { ErrorF( "Driver specification changed from \"ati\" to \"vgawonder\"\n"); OFLG_CLR(XCONFIG_CHIPSET, &vga256InfoRec.xconfigFlag); if (vga256InfoRec.clocks) { vga256InfoRec.clocks = 0; OFLG_CLR(XCONFIG_CLOCKS, &vga256InfoRec.xconfigFlag); if (!OFLG_ISSET(OPTION_PROBE_CLKS, &vga256InfoRec.options)) ErrorF("Clocks will be probed\n"); } } else if (StrCaseCmp(vga256InfoRec.chipset, ATIIdent(1))) return FALSE; /* * Get BIOS data this driver will use */ if (xf86ReadBIOS(vga256InfoRec.BIOSbase, 0, BIOS_Data, sizeof(BIOS_Data)) != sizeof(BIOS_Data)) return FALSE; /* * Get out if this is the wrong driver for installed chipset */ if (strncmp(Signature, BIOS_Signature, Signature_Size)) return FALSE; ATIChipVersion = BIOS_Data[0x43]; ErrorF("ATI BIOS Information:\n"); ErrorF(" Chip version: %c = ", ATIChipVersion); switch (ATIChipVersion) { case '1': ErrorF("ATI 18800"); ATIBoard = ATI_BOARD_V3; /* Reset a few things for V3 boards */ ATI.ChipSetRead = ATIV3SetRead; ATI.ChipSetWrite = ATIV3SetWrite; ATI.ChipSetReadWrite = ATIV3SetReadWrite; ATI.ChipUse2Banks = FALSE; break; case '2': ErrorF("ATI 18800-1"); /* Is a clock chip present? */ if (BIOS_Data[0x42] & 0x10) ATIBoard = ATI_BOARD_V5; else ATIBoard = ATI_BOARD_V4; break; case '3': ErrorF("ATI 28800-2"); ATIBoard = ATI_BOARD_PLUS; break; case '4': ErrorF("ATI 28800-4"); ATIBoard = ATI_BOARD_PLUS; break; case '5': ErrorF("ATI 28800-5"); /* Is there a 32K colour DAC on board? */ if (BIOS_Data[0x44] & 0x80) ATIBoard = ATI_BOARD_XL; else ATIBoard = ATI_BOARD_PLUS; break; case '6': ErrorF("ATI 28800-6"); ATIBoard = ATI_BOARD_XL24; break; case 'a': ErrorF("Mach-32"); /* Is there a 32K colour DAC on board? */ if (BIOS_Data[0x44] & 0x80) ATIBoard = ATI_BOARD_XL; else ATIBoard = ATI_BOARD_PLUS; break; default: ErrorF("Unknown!"); /* Is there a 32K colour DAC on board? */ if (BIOS_Data[0x44] & 0x80) ATIBoard = ATI_BOARD_XL; else ATIBoard = ATI_BOARD_PLUS; break; } ErrorF("\n This video adapter is a VGA WONDER "); switch (ATIBoard) { case ATI_BOARD_V3: ErrorF("V3"); break; case ATI_BOARD_V4: ErrorF("V4"); break; case ATI_BOARD_V5: ErrorF("V5"); break; case ATI_BOARD_PLUS: ErrorF("PLUS (V6)"); break; case ATI_BOARD_XL: ErrorF("XL (V7)"); break; case ATI_BOARD_XL24: ErrorF("XL24 (V8)"); /* sic */ break; default: ErrorF("Unknown"); break; } ErrorF("\n"); /* * Set up extended register addressing */ ATIExtReg = *((short int *)BIOS_Data + 0x08); ATI_IOPorts[0] = ATIExtReg; ATI_IOPorts[1] = ATIExtReg + 1; /* * Set up I/O ports to be used by this driver */ xf86ClearIOPortList(vga256InfoRec.scrnIndex); xf86AddIOPorts(vga256InfoRec.scrnIndex, Num_VGA_IOPorts, VGA_IOPorts); xf86AddIOPorts(vga256InfoRec.scrnIndex, Num_ATI_IOPorts, ATI_IOPorts); ATIEnterLeave(ENTER); /* Unlock registers */ if (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options)) { /* * Remember initial settings of undocumented clock selection * bits */ saved_b9_bits_0_and_1 = ATIGetExtReg(0xB9); if (ATIBoard == ATI_BOARD_V4) saved_b9_bits_0_and_1 &= 0x03; else saved_b9_bits_0_and_1 &= 0x01; } /* * If the user has specified the amount of memory in the Xconfig * file, we respect that setting. */ if (!vga256InfoRec.videoRam) { /* * Otherwise, probe for the value */ if (ATIChipVersion <= '2') vga256InfoRec.videoRam = (ATIGetExtReg(0xBB) & 0x20) ? 512 : 256; else switch (ATIGetExtReg(0xB0) & 0x18) { case 0x00: vga256InfoRec.videoRam = 256; break; case 0x10: vga256InfoRec.videoRam = 512; break; case 0x08: vga256InfoRec.videoRam = 1024; break; } } /* * Again, if the user has specified the clock values in the Xconfig * file, we respect those choices. */ if ((!vga256InfoRec.clocks) || (OFLG_ISSET(OPTION_PROBE_CLKS, &vga256InfoRec.options))) { int Number_Of_Clocks; /* * Determine the number of clock values to probe for */ if (ATIBoard <= ATI_BOARD_V4) Number_Of_Clocks = 8*4; else Number_Of_Clocks = 16*4; if (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options)) Number_Of_Clocks <<= 1 + (ATIBoard == ATI_BOARD_V4); if (Number_Of_Clocks > MAXCLOCKS) Number_Of_Clocks = MAXCLOCKS; /* * Probe the board for clock values. Note that vgaGetClocks * cannot be used for this purpose because it assumes clock * 1 is 28.322 MHz. Instead call xf86GetClocks directly * passing it slighly different parameters. */ xf86GetClocks(Number_Of_Clocks, ATIClockSelect, vgaProtect, (void (*)())vgaSaveScreen, (vgaIOBase + 0x0A), 0x08, 7, 36000, &vga256InfoRec); } /* * Set the maximum allowable dot-clock frequency (in kHz) */ vga256InfoRec.maxClock = 80000; /* * Set chipset name */ vga256InfoRec.chipset = ATIIdent(1); /* * Tell monochrome and 16-colour servers banked operation is * supported */ vga256InfoRec.bankedMono = TRUE; /* * Indicate supported options */ OFLG_SET(OPTION_PROBE_CLKS, &ATI.ChipOptionFlags); OFLG_SET(OPTION_UNDOC_CLKS, &ATI.ChipOptionFlags); /* * Return success */ return TRUE; } /* * ATIEnterLeave -- * * This function is called when the virtual terminal on which the server * is running is entered or left, as well as when the server starts up * and is shut down. Its function is to obtain and relinquish I/O * permissions for the SVGA device. This includes unlocking access to * any registers that may be protected on the chipset, and locking those * registers again on exit. */ static void ATIEnterLeave(enter) Bool enter; { static unsigned char saved_b8; if (enter) { xf86EnableIOPorts(vga256InfoRec.scrnIndex); vgaIOBase = (inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0; /* Unprotect CRTC[0-7] */ outb(vgaIOBase + 4, 0x11); outb(vgaIOBase + 5, inb(vgaIOBase + 5) & 0x7F); /* Unprotect ATI extended registers */ saved_b8 = ATIGetExtReg(0xB8); ATIPutExtReg(0xB8, saved_b8 & 0xC0); } else { /* Protect CRTC[0-7] */ outb(vgaIOBase + 4, 0x11); outb(vgaIOBase + 5, (inb(vgaIOBase + 5) & 0x7F) | 0x80); /* Protect ATI extended registers */ ATIPutExtReg(0xB8, (saved_b8 & 0x3F) | (ATIGetExtReg(0xB8) & 0xC0)); xf86DisableIOPorts(vga256InfoRec.scrnIndex); } } /* * ATIRestore -- * * This function restores a video mode. It basically writes out all of * the registers that have previously been saved in the vgaATIRec data * structure. * * Note that "Restore" is a slightly incorrect. This function is also * used when the server enters/changes video modes. The mode definitions * have previously been initialized by the Init() function, below. */ static void ATIRestore(restore) vgaATIPtr restore; { /* * Unlock ATI extended registers */ ATIPutExtReg(0xB8, ATIGetExtReg(0xB8) & 0xC0); /* * Get (back) to bank 0. */ if (ATIBoard == ATI_BOARD_V3) ATIPutExtReg(0xB2, ATIGetExtReg(0xB2) & 0xE1); else ATIPutExtReg(0xB2, 0); /* * Restore ATI registers. * * A special case - when using an external clock-setting program, * clock selection bits must not be changed. This condition can * be checked by the condition: * * if (restore->std.NoClock >= 0) * restore clock-select bits. */ if (restore->std.NoClock < 0) { unsigned char b9 = ATIGetExtReg(0xB9); /* * Retrieve current setting of clock select bits */ restore->b8 = (restore->b8 & 0x3F) | (ATIGetExtReg(0xB8) & 0xC0); if (ATIBoard == ATI_BOARD_V3) restore->b2 = (restore->b2 & 0xBF) | (ATIGetExtReg(0xB2) & 0x40); else { restore->be = (restore->be & 0xEF) | (ATIGetExtReg(0xBE) & 0x10); if ((ATIBoard != ATI_BOARD_V4) || (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options))) { restore->b9 = (restore->b9 & 0xFD) | (b9 & 0x02); } } if (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options)) restore->b9 = (restore->b9 & 0xFE) | (b9 & 0x01); } outw(0x3C4, 0x0100); /* Start synchronous reset */ if (ATIBoard != ATI_BOARD_V3) { ATIPutExtReg(0xBE, restore->be); if (ATIBoard > ATI_BOARD_V5) { ATIPutExtReg(0xA3, restore->a3); ATIPutExtReg(0xA6, restore->a6); ATIPutExtReg(0xA7, restore->a7); ATIPutExtReg(0xAC, restore->ac); ATIPutExtReg(0xAD, restore->ad); ATIPutExtReg(0xAE, restore->ae); } } ATIPutExtReg(0xB0, restore->b0); ATIPutExtReg(0xB1, restore->b1); ATIPutExtReg(0xB2, restore->b2); if (ATIChipVersion != '2') ATIPutExtReg(0xB3, restore->b3); ATIPutExtReg(0xB5, restore->b5); ATIPutExtReg(0xB6, restore->b6); ATIPutExtReg(0xB9, restore->b9); ATIPutExtReg(0xBD, restore->bd); ATIPutExtReg(0xB8, restore->b8); /* Must be last */ /* * Restore the generic VGA registers */ vgaHWRestore(restore); outb(0x3C2, restore->std.MiscOutReg); outw(0x3C4, 0x0300); /* End synchronous reset */ } /* * ATISave -- * * This function saves the video state. It reads all of the SVGA registers * into the vgaATIRec data structure. There is in general no need to * mask out bits here - just read the registers. */ static void * ATISave(save) vgaATIPtr save; { unsigned char b2, b8; /* The oddballs */ /* * Unlock ATI extended registers */ b8 = ATIGetExtReg(0xB8); ATIPutExtReg(0xB8, b8 & 0xC0); /* * Get back to bank zero. */ b2 = ATIGetExtReg(0xB2); if (ATIBoard == ATI_BOARD_V3) ATIPutExtReg(0xB2, b2 & 0xE1); else ATIPutExtReg(0xB2, 0); /* * This function will handle creating the data structure and filling * in the generic VGA portion. */ save = (vgaATIPtr)vgaHWSave(save, sizeof(vgaATIRec)); /* * Save ATI-specific registers */ save->b0 = ATIGetExtReg(0xB0); save->b1 = ATIGetExtReg(0xB1); save->b2 = b2; save->b3 = ATIGetExtReg(0xB3); save->b5 = ATIGetExtReg(0xB5); save->b6 = ATIGetExtReg(0xB6); save->b8 = b8; save->b9 = ATIGetExtReg(0xB9); save->bd = ATIGetExtReg(0xBD); if (ATIBoard != ATI_BOARD_V3) { save->be = ATIGetExtReg(0xBE); if (ATIBoard > ATI_BOARD_V5) { save->a3 = ATIGetExtReg(0xA3); save->a6 = ATIGetExtReg(0xA6); save->a7 = ATIGetExtReg(0xA7); save->ac = ATIGetExtReg(0xAC); save->ad = ATIGetExtReg(0xAD); save->ae = ATIGetExtReg(0xAE); } } return ((void *) save); } /* * ATIInit -- * * This is the most important function (after the Probe) function. This * function fills in the vgaATIRec with all of the register values needed * to enable a video mode. */ static Bool ATIInit(mode) DisplayModePtr mode; { /* * The VGA Wonder boards have a bit that multiplies all vertical * timing values by 2. This feature is only used if it's actually * needed (i.e. when VTotal > 1024). If the feature is needed, fake * out an interlaced mode and let vgaHWInit divide things by two. * Note that this prevents the (incorrect) use of this feature with * interlaced modes. */ int saved_mode_flags = mode->Flags; if (mode->VTotal > 1024) mode->Flags |= V_INTERLACE; /* * This will allocate the data structure and initialize all of the * generic VGA registers. */ if (!vgaHWInit(mode,sizeof(vgaATIRec))) { mode->Flags = saved_mode_flags; return(FALSE); } /* * Override a few things */ #if !defined(MONOVGA) && !defined(XF86VGA16) new->std.Sequencer[4] = 0x0A; #endif new->std.Graphics[5] = 0x00; new->std.Attribute[16] = 0x01; if (saved_mode_flags != mode->Flags) { /* Use "double vertical timings" bit */ new->std.CRTC[23] |= 0x04; mode->Flags = saved_mode_flags; } /* * Set up ATI registers */ #if defined(MONOVGA) || defined(XF86VGA16) new->b0 = (ATIGetExtReg(0xB0) & 0x98) | 0x01; #else new->b0 = (ATIGetExtReg(0xB0) & 0x98) | 0x21; #endif if (ATIBoard >= ATI_BOARD_PLUS) new->b0 &= 0x7F; new->b1 = (ATIGetExtReg(0xB1) & 0x87) ; new->b3 = (ATIGetExtReg(0xB3) & 0xAF) ; new->b5 = (ATIGetExtReg(0xB5) & 0x7F) ; #if defined(MONOVGA) || defined(XF86VGA16) new->b6 = (ATIGetExtReg(0xB6) & 0xA2) | 0x40; #else new->b6 = (ATIGetExtReg(0xB6) & 0xA2) | 0x44; #endif new->b8 = (ATIGetExtReg(0xB8) ) ; new->b9 = (ATIGetExtReg(0xB9) ) ; new->bd = (ATIGetExtReg(0xBD) & 0xFB) ; if (ATIBoard == ATI_BOARD_V3) new->b2 = (ATIGetExtReg(0xB2) & 0xE0) ; else { new->b2 = 0; new->be = (ATIGetExtReg(0xBE) & 0x35) | 0x08; if (ATIBoard > ATI_BOARD_V5) { new->a3 = (ATIGetExtReg(0xA3) & 0xEF) ; new->a6 = (ATIGetExtReg(0xA6) ) | 0x01; new->a7 = (ATIGetExtReg(0xA7) & 0xFE) ; new->ac = (ATIGetExtReg(0xAC) & 0xBE) ; new->ad = (ATIGetExtReg(0xAD) & 0xF0) ; new->ae = (ATIGetExtReg(0xAE) & 0xF0) ; } } if (mode->Flags & V_INTERLACE) /* Enable interlacing */ { if (ATIBoard == ATI_BOARD_V3) new->b2 |= 0x01; else new->be |= 0x02; } #if defined(MONOVGA) || defined(XF86VGA16) if (vga256InfoRec.videoRam >= 512) new->b6 |= 0x01; /* Enable beyond 256K */ #else if (vga256InfoRec.videoRam >= 1024) new->b6 |= 0x01; /* Enable upper 512K */ #endif /* * Set clock select bits */ if (new->std.NoClock >= 0) { int Clock = mode->Clock; /* * Set generic clock select bits just in case */ new->std.MiscOutReg = (new->std.MiscOutReg & 0xF3) | ((Clock << 2) & 0x0C); /* * Set ATI clock select bits */ if (ATIBoard == ATI_BOARD_V3) new->b2 = (new->b2 & 0xBF) | ((Clock << 4) & 0x40); else { new->be = (new->be & 0xEF) | ((Clock << 2) & 0x10); if ((ATIBoard != ATI_BOARD_V4) || (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options))) { new->b9 = (new->b9 & 0xFD) | ((Clock >> 2) & 0x02); Clock >>= 1; } } if (OFLG_ISSET(OPTION_UNDOC_CLKS, &vga256InfoRec.options)) { new->b9 = (new->b9 & 0xFE) | ((Clock >> 3) & 0x01); new->b9 ^= saved_b9_bits_0_and_1; Clock >>= 1; } /* * Set clock divider bits */ new->b8 = (new->b8 & 0x3F) | ((Clock << 3) & 0xC0); } return(TRUE); } /* * ATIAdjust -- * * This function is used to initialize the SVGA Start Address - the first * displayed location in the video memory. This is used to implement the * virtual window. */ static void ATIAdjust(x, y) int x, y; { int Base = (y * vga256InfoRec.virtualX + x) >> 3; outw(vgaIOBase + 4, (Base & 0x00FF00) | 0x0C); outw(vgaIOBase + 4, ((Base & 0x00FF) << 8) | 0x0D); if (ATIBoard < ATI_BOARD_PLUS) ATIPutExtReg(0xB0, (ATIGetExtReg(0xB0) & 0x3F) | ((Base & 0x030000) >> 10)); else ATIPutExtReg(0xB0, (ATIGetExtReg(0xB0) & 0xBF) | ((Base & 0x010000) >> 10)); ATIPutExtReg(0xA3, (ATIGetExtReg(0xA3) & 0xEF) | ((Base & 0x020000) >> 13)); }