diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 16:48:55 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 16:48:55 +0000 |
commit | d09f463a5d1ce73e0b65d5276fbcca393fa2da46 (patch) | |
tree | bb664ee5f34db07975530b32c7915074aa9515fe /src/aticlock.c | |
parent | d9af6dc32652502d84ea8da5d57a5ab45429c4ad (diff) |
Initial revision
Diffstat (limited to 'src/aticlock.c')
-rw-r--r-- | src/aticlock.c | 1586 |
1 files changed, 1586 insertions, 0 deletions
diff --git a/src/aticlock.c b/src/aticlock.c new file mode 100644 index 00000000..0fc3d6b3 --- /dev/null +++ b/src/aticlock.c @@ -0,0 +1,1586 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/aticlock.c,v 1.20 2003/01/01 19:16:31 tsi Exp $ */ +/* + * Copyright 1997 through 2003 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org + * + * 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 Aurele La France not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Marc Aurele 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 AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL MARC AURELE 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. + */ + +/* + * Adapters prior to V5 use 4 crystals. Adapters V5 and later use a clock + * generator chip. V3 and V4 adapters differ when it comes to choosing clock + * frequencies. + * + * VGA Wonder V3/V4 Adapter 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.000 + * 0 0 1 56.644 + * 0 1 0 Spare 1 + * 0 1 1 44.900 + * 1 0 0 44.900 + * 1 0 1 50.000 + * 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. Mach32 adapters are known to use any one of + * these clock generators. Mach32 adapters also use a different dot clock + * ordering. ATI says there is no reliable way for the driver to determine + * which clock generator is on the adapter, but this driver will do its best to + * do so anyway. + * + * VGA Wonder V5/PLUS/XL/XL24 Clock Frequencies + * R E G I S T E R S + * 1CE 1CE 3C2 3C2 Frequency + * B9h BEh (MHz) 18811-0 18811-1 + * Bit 1 Bit 4 Bit 3 Bit 2 18810 18812-0 18811-2 (*5) + * ------- ------- ------- ------- ------- ------- ------- ------- + * 0 0 0 0 30.240 30.240 135.000 75.000 + * 0 0 0 1 32.000 32.000 32.000 77.500 + * 0 0 1 0 37.500 110.000 110.000 80.000 + * 0 0 1 1 39.000 80.000 80.000 90.000 + * 0 1 0 0 42.954 42.954 100.000 25.175 + * 0 1 0 1 48.771 48.771 126.000 28.322 + * 0 1 1 0 (*1) 92.400 92.400 31.500 + * 0 1 1 1 36.000 36.000 36.000 36.000 + * 1 0 0 0 40.000 39.910 39.910 100.000 + * 1 0 0 1 (*4) 44.900 44.900 110.000 + * 1 0 1 0 75.000 75.000 75.000 126.000 + * 1 0 1 1 65.000 65.000 65.000 135.000 + * 1 1 0 0 50.350 50.350 50.350 40.000 + * 1 1 0 1 56.640 56.640 56.640 44.900 + * 1 1 1 0 (*2) (*3) (*3) 50.000 + * 1 1 1 1 44.900 44.900 44.900 65.000 + * + * (*1) External 0 (supposedly 16.657 Mhz) + * (*2) External 1 (supposedly 28.322 MHz) + * (*3) This setting doesn't seem to generate anything + * (*4) This setting is documented to be 56.644 MHz, but something close to 82 + * MHz has also been encountered. + * (*5) This setting is for Dell OmniPlex 590 systems, with a 68800AX on the + * motherboard, along with an AT&T21C498 DAC (which is reported as an + * STG1700) and ICS2494AM clock generator (a.k.a. ATI 18811-?). + * + * Mach32 Clock Frequencies + * R E G I S T E R S + * 1CE 1CE 3C2 3C2 Frequency + * B9h BEh (MHz) 18811-0 18811-1 + * Bit 1 Bit 4 Bit 3 Bit 2 18810 18812-0 18811-2 (*5) + * ------- ------- ------- ------- ------- ------- ------- ------- + * 0 0 0 0 42.954 42.954 100.000 25.175 + * 0 0 0 1 48.771 48.771 126.000 28.322 + * 0 0 1 0 (*1) 92.400 92.400 31.500 + * 0 0 1 1 36.000 36.000 36.000 36.000 + * 0 1 0 0 30.240 30.240 135.000 75.000 + * 0 1 0 1 32.000 32.000 32.000 77.500 + * 0 1 1 0 37.500 110.000 110.000 80.000 + * 0 1 1 1 39.000 80.000 80.000 90.000 + * 1 0 0 0 50.350 50.350 50.350 40.000 + * 1 0 0 1 56.640 56.640 56.640 44.900 + * 1 0 1 0 (*2) (*3) (*3) 50.000 + * 1 0 1 1 44.900 44.900 44.900 65.000 + * 1 1 0 0 40.000 39.910 39.910 100.000 + * 1 1 0 1 (*4) 44.900 44.900 110.000 + * 1 1 1 0 75.000 75.000 75.000 126.000 + * 1 1 1 1 65.000 65.000 65.000 135.000 + * + * (*1) External 0 (supposedly 16.657 Mhz) + * (*2) External 1 (supposedly 28.322 MHz) + * (*3) This setting doesn't seem to generate anything + * (*4) This setting is documented to be 56.644 MHz, but something close to 82 + * MHz has also been encountered. + * (*5) This setting is for Dell OmniPlex 590 systems, with a 68800AX on the + * motherboard, along with an AT&T21C498 DAC (which is reported as an + * STG1700) and ICS2494AM clock generator (a.k.a. ATI 18811-?). + * + * Note that, to reduce confusion, this driver masks out the different clock + * ordering. + * + * For all adapters, these frequencies can be divided by 1 or 2. For all + * adapters, except Mach32's and Mach64's, frequencies can also be divided by 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 + * + * With respect to clocks, Mach64's are entirely different animals. + * + * The oldest Mach64's use one of the non-programmable clock generators + * described above. In this case, the driver will handle clocks in much the + * same way as it would for a Mach32. + * + * All other Mach64 adapters use a programmable clock generator. BIOS + * initialisation programmes an initial set of frequencies. Two of these are + * reserved to allow for the setting of modes that do not use a frequency from + * this initial set. One of these reserved slots is used by the BIOS mode set + * routine, the other by the particular accelerated driver used (MS-Windows, + * AutoCAD, etc.). The slots reserved in this way are dependent on the + * particular clock generator used by the adapter. + * + * If the driver does not support the adapter's clock generator, it will try to + * match the (probed or specified) clocks to one of the following sets. + * + * Mach64 Clock Frequencies for unsupported programmable clock generators + * R E G I S T E R S + * 1CE 1CE 3C2 3C2 Frequency + * B9h BEh (MHz) + * Bit 1 Bit 4 Bit 3 Bit 2 Set 1 Set 2 Set 3 + * ------- ------- ------- ------- ------- ------- ------- + * 0 0 0 0 50.350 25.180 25.180 + * 0 0 0 1 56.640 28.320 28.320 + * 0 0 1 0 63.000 31.500 0.000 + * 0 0 1 1 72.000 36.000 0.000 + * 0 1 0 0 0.000 0.000 0.000 + * 0 1 0 1 110.000 110.000 0.000 + * 0 1 1 0 126.000 126.000 0.000 + * 0 1 1 1 135.000 135.000 0.000 + * 1 0 0 0 40.000 40.000 0.000 + * 1 0 0 1 44.900 44.900 0.000 + * 1 0 1 0 49.500 49.500 0.000 + * 1 0 1 1 50.000 50.000 0.000 + * 1 1 0 0 0.000 0.000 0.000 + * 1 1 0 1 80.000 80.000 0.000 + * 1 1 1 0 75.000 75.000 0.000 + * 1 1 1 1 65.000 65.000 0.000 + * + * The driver will never select a setting of 0.000 MHz. The above comments on + * clock ordering and clock divider apply here also. + * + * For all supported programmable clock generators, the driver will ignore any + * XF86Config clock line and programme, as needed, the clock number reserved by + * the BIOS for accelerated drivers. The driver's mode initialisation routine + * finds integers N, M and D such that + * + * N + * R * ------- MHz + * M * D + * + * best approximates the mode's clock frequency, where R is the crystal- + * generated reference frequency (usually 14.318 MHz). D is a power of 2 + * except for those integrated controllers that also offer odd dividers. + * Different clock generators have different restrictions on the value N, M and + * D can assume. The driver contains an internal table to record these + * restrictions (among other things). The resulting values of N, M and D are + * then encoded in a generator-specific way and used to programme the clock. + * The Mach64's clock divider is not used in this case. + */ + +#include "ati.h" +#include "atiadapter.h" +#include "atichip.h" +#include "atidac.h" +#include "atidsp.h" +#include "atimach64io.h" +#include "atimode.h" +#include "atiwonderio.h" + +/* + * Definitions related to non-programmable clock generators. + */ +const char *ATIClockNames[] = +{ + "unknown", + "IBM VGA compatible", + "crystals", + "ATI 18810 or similar", + "ATI 18811-0 or similar", + "ATI 18811-1 or similar", + "ICS 2494-AM or similar", + "Programmable (BIOS setting 1)", + "Programmable (BIOS setting 2)", + "Programmable (BIOS setting 3)" +}; + +/* + * Definitions related to programmable clock generators. + */ +static CARD16 ATIPostDividers[] = {1, 2, 4, 8, 16, 32, 64, 128}, + ATI264xTPostDividers[] = {1, 2, 4, 8, 3, 0, 6, 12}; +ClockRec ATIClockDescriptors[] = +{ + { + 0, 0, 0, 1, 1, + 1, 1, 0, + 0, NULL, + "Non-programmable" + }, + { + 257, 512, 257, 1, 1, + 46, 46, 0, + 4, ATIPostDividers, + "ATI 18818 or ICS 2595 or similar" + }, + { + 2, 129, 2, 1, 1, + 8, 14, 2, + 8, ATIPostDividers, + "SGS-Thompson 1703 or similar" + }, + { + 16, 263, 8, 8, 9, + 4, 12, 2, + 4, ATIPostDividers, + "Chrontel 8398 or similar" + }, + { + 2, 255, 0, 1, 1, + 45, 45, 0, + 4, ATI264xTPostDividers, + "Internal" + }, + { + 2, 257, 2, 1, 1, + 2, 32, 2, + 4, ATIPostDividers, + "AT&T 20C408 or similar" + }, + { + 65, 128, 65, 1, 1, + 2, 14, 0, + 4, ATIPostDividers, + "IBM RGB 514 or similar" + } +}; + +/* + * XF86Config clocks line that start with the following will either be rejected + * for ATI adapters, or accepted for non-ATI adapters. + */ +static const int +ATIVGAClocks[] = +{ + 25175, 28322, + -1 +}; + +/* + * The driver will attempt to match fixed clocks to one of the following + * specifications. + */ +static const int +ATICrystalFrequencies[] = +{ + 50000, 56644, 0, 44900, 44900, 50000, 0, 36000, + -1 +}, +ATI18810Frequencies[] = +{ + 30240, 32000, 37500, 39000, 42954, 48771, 0, 36000, + 40000, 0, 75000, 65000, 50350, 56640, 0, 44900 +}, +ATI188110Frequencies[] = +{ + 30240, 32000, 110000, 80000, 42954, 48771, 92400, 36000, + 39910, 44900, 75000, 65000, 50350, 56640, 0, 44900 +}, +ATI188111Frequencies[] = +{ + 135000, 32000, 110000, 80000, 100000, 126000, 92400, 36000, + 39910, 44900, 75000, 65000, 50350, 56640, 0, 44900 +}, +ATI2494AMFrequencies[] = +{ + 75000, 77500, 80000, 90000, 25175, 28322, 31500, 36000, + 100000, 110000, 126000, 135000, 40000, 44900, 50000, 65000 +}, +ATIMach64AFrequencies[] = +{ + 0, 110000, 126000, 135000, 50350, 56640, 63000, 72000, + 0, 80000, 75000, 65000, 40000, 44900, 49500, 50000 +}, +ATIMach64BFrequencies[] = +{ + 0, 110000, 126000, 135000, 25180, 28320, 31500, 36000, + 0, 80000, 75000, 65000, 40000, 44900, 49500, 50000 +}, +ATIMach64CFrequencies[] = +{ + 0, 0, 0, 0, 25180, 28320, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}, +*SpecificationClockLine[] = +{ + NULL, + ATIVGAClocks, + ATICrystalFrequencies, + ATI18810Frequencies, + ATI188110Frequencies, + ATI188111Frequencies, + ATI2494AMFrequencies, + ATIMach64AFrequencies, + ATIMach64BFrequencies, + ATIMach64CFrequencies, + NULL +}; + +/* + * The driver will reject XF86Config clocks lines that start with, or are an + * initial subset of, one of the following. + */ +static const int +ATIPre_2_1_1_Clocks_A[] = /* Based on 18810 */ +{ + 18000, 22450, 25175, 28320, 36000, 44900, 50350, 56640, + 30240, 32000, 37500, 39000, 40000, 0, 75000, 65000, + -1 +}, +ATIPre_2_1_1_Clocks_B[] = /* Based on 18811-0 */ +{ + 18000, 22450, 25175, 28320, 36000, 44900, 50350, 56640, + 30240, 32000, 110000, 80000, 39910, 44900, 75000, 65000, + -1 +}, +ATIPre_2_1_1_Clocks_C[] = /* Based on 18811-1 (or -2) */ +{ + 18000, 22450, 25175, 28320, 36000, 44900, 50350, 56640, + 135000, 32000, 110000, 80000, 39910, 44900, 75000, 65000, + -1 +}, +ATIPre_2_1_1_Clocks_D[] = /* Based on ICS 2494AM */ +{ + 18000, 32500, 20000, 22450, 36000, 65000, 40000, 44900, + 75000, 77500, 80000, 90000, 100000, 110000, 126000, 135000, + -1 +}, +ATIPre_2_1_1_Clocks_E[] = /* Based on programmable setting 1 */ +{ + 36000, 25000, 20000, 22450, 72000, 50000, 40000, 44900, + 0, 110000, 126000, 135000, 0, 80000, 75000, 65000, + -1 +}, +ATIPre_2_1_1_Clocks_F[] = /* Based on programmable setting 2 */ +{ + 18000, 25000, 20000, 22450, 36000, 50000, 40000, 44900, + 0, 110000, 126000, 135000, 0, 80000, 75000, 65000, + -1 +}, +*InvalidClockLine[] = +{ + NULL, + ATIVGAClocks, + ATIPre_2_1_1_Clocks_A, + ATIPre_2_1_1_Clocks_B, + ATIPre_2_1_1_Clocks_C, + ATIPre_2_1_1_Clocks_D, + ATIPre_2_1_1_Clocks_E, + ATIPre_2_1_1_Clocks_F, + NULL +}; + +/* + * Clock maps. + */ +static const CARD8 ClockMaps[][4] = +{ + /* Null map */ + { 0, 1, 2, 3}, + /* VGA Wonder map <-> Mach{8,32,64} */ + { 1, 0, 3, 2}, + /* VGA Wonder map <-> Accelerator */ + { 0, 2, 1, 3}, + /* VGA -> Accelerator map */ + { 2, 0, 3, 1}, + /* Accelerator -> VGA map */ + { 1, 3, 0, 2} +}; +#define ATIVGAWonderClockMap ClockMaps[0] +#define ATIVGAWonderClockUnmap ATIVGAWonderClockMap +#define ATIMachVGAClockMap ClockMaps[1] +#define ATIMachVGAClockUnmap ATIMachVGAClockMap +#define ATIVGAProgrammableClockMap ClockMaps[2] +#define ATIVGAProgrammableClockUnmap ATIVGAProgrammableClockMap +#define ATIAcceleratorClockMap ClockMaps[3] +#define ATIAcceleratorClockUnmap ClockMaps[4] +#define ATIProgrammableClockMap ClockMaps[0] +#define ATIProgrammableClockUnmap ATIProgrammableClockMap +#define MapClockIndex(_ClockMap, _Index) \ + (SetBits((_ClockMap)[GetBits(_Index, 0x0CU)], 0x0CU) | \ + ((_Index) & ~0x0CU)) + +/* + * ATIMatchClockLine -- + * + * This function tries to match the XF86Config clocks to one of an array of + * clock lines. It returns a clock line number or 0. + */ +static int +ATIMatchClockLine +( + ScrnInfoPtr pScreenInfo, + ATIPtr pATI, + const int **ClockLine, + const unsigned short int NumberOfClocks, + const int CalibrationClockNumber, + const int ClockMap +) +{ + int ClockChip = 0, ClockChipIndex = 0; + int NumberOfMatchingClocks = 0; + int MinimumGap = CLOCK_TOLERANCE + 1; + + /* For ATI adapters, reject generic VGA clocks */ + +#ifndef AVOID_CPIO + + if (pATI->Adapter != ATI_ADAPTER_VGA) + +#endif /* AVOID_CPIO */ + + { + if (ClockLine == SpecificationClockLine) + ClockChipIndex++; + } + + /* If checking for XF86Config clock order, skip crystals */ + if (ClockMap) + ClockChipIndex++; + + for (; ClockLine[++ClockChipIndex]; ) + { + int MaximumGap = 0, ClockCount = 0, ClockIndex = 0; + +#ifndef AVOID_CPIO + + /* Only Mach64's and later can have programmable clocks */ + if ((ClockChipIndex >= ATI_CLOCK_MACH64A) && + (pATI->Adapter < ATI_ADAPTER_MACH64)) + break; + +#endif /* AVOID_CPIO */ + + for (; ClockIndex < NumberOfClocks; ClockIndex++) + { + int Gap, XF86ConfigClock, SpecificationClock; + + SpecificationClock = ClockLine[ClockChipIndex] + [MapClockIndex(ClockMaps[ClockMap], ClockIndex)]; + if (SpecificationClock < 0) + break; + if (!SpecificationClock) + continue; + + XF86ConfigClock = pScreenInfo->clock[ClockIndex]; + if (!XF86ConfigClock) + continue; + + Gap = abs(XF86ConfigClock - SpecificationClock); + if (Gap >= MinimumGap) + goto SkipThisClockGenerator; + if (!Gap) + { + if (ClockIndex == CalibrationClockNumber) + continue; + } + else if (Gap > MaximumGap) + MaximumGap = Gap; + ClockCount++; + } + + if (ClockCount <= NumberOfMatchingClocks) + continue; + NumberOfMatchingClocks = ClockCount; + ClockChip = ClockChipIndex; + if (!(MinimumGap = MaximumGap)) + break; + +SkipThisClockGenerator:; + +#ifndef AVOID_CPIO + + /* For non-ATI adapters, only normalise standard VGA clocks */ + if (pATI->Adapter == ATI_ADAPTER_VGA) + break; + +#endif /* AVOID_CPIO */ + + } + + return ClockChip; +} + +/* + * ATIClockPreInit -- + * + * This function is called by ATIPreInit() and handles the XF86Config clocks + * line (or lack thereof). + */ +void +ATIClockPreInit +( + ScrnInfoPtr pScreenInfo, + ATIPtr pATI, + GDevPtr pGDev, + ClockRangePtr pRange +) +{ + double ScaleFactor; + unsigned short int NumberOfUndividedClocks; + unsigned short int NumberOfDividers, NumberOfClocks; + int CalibrationClockNumber, CalibrationClockValue; + int ClockIndex, SpecificationClock, ClockMap = 0, Index; + CARD8 CanDisableInterrupts; + +#ifndef AVOID_CPIO + + CARD8 genmo; + +#endif /* AVOID_CPIO */ + + /* + * Decide what to do about the XF86Config clocks for programmable clock + * generators. + */ + if (pATI->ProgrammableClock != ATI_CLOCK_FIXED) + { + /* Check for those that are not (yet) handled */ + if ((pATI->ProgrammableClock == ATI_CLOCK_UNKNOWN) || + (pATI->ProgrammableClock > NumberOf(ATIClockDescriptors))) + xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 0, + "Unknown programmable clock generator type (0x%02X)" + " detected.\n", pATI->ProgrammableClock); + else if (pATI->ClockDescriptor.MaxN <= 0) + xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 0, + "Unsupported programmable clock generator detected: %s.\n", + pATI->ClockDescriptor.ClockName); + else + { + /* + * Recognise supported clock generators. This involves telling the + * rest of the server about it and (re-)initializing the XF86Config + * clocks line. + */ + pRange->clockIndex = -1; + pScreenInfo->progClock = TRUE; + + /* Set internal clock ordering */ + +#ifndef AVOID_CPIO + + if (pATI->NewHW.crtc == ATI_CRTC_VGA) + { + pATI->NewHW.ClockMap = ATIVGAProgrammableClockMap; + pATI->NewHW.ClockUnmap = ATIVGAProgrammableClockUnmap; + } + else + +#endif /* AVOID_CPIO */ + + { + pATI->NewHW.ClockMap = ATIProgrammableClockMap; + pATI->NewHW.ClockUnmap = ATIProgrammableClockUnmap; + } + + xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, + "%s programmable clock generator detected.\n", + pATI->ClockDescriptor.ClockName); + if (pATI->ReferenceDenominator == 1) + xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, + "Reference clock %.3f MHz.\n", + (double)pATI->ReferenceNumerator / 1000.0); + else + xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, + "Reference clock %.6g/%d (%.3f) MHz.\n", + (double)pATI->ReferenceNumerator / 1000.0, + pATI->ReferenceDenominator, + (double)pATI->ReferenceNumerator / + ((double)pATI->ReferenceDenominator * 1000.0)); + + /* Clobber XF86Config clocks line */ + if (pGDev->numclocks) + xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE, + "XF86Config clocks specification ignored.\n"); + + if (pATI->ProgrammableClock == ATI_CLOCK_CH8398) + { /* First two are fixed */ + pScreenInfo->numClocks = 2; + pScreenInfo->clock[0] = 25175; + pScreenInfo->clock[1] = 28322; + } + else if (pATI->ProgrammableClock == ATI_CLOCK_INTERNAL) + /* + * The integrated PLL generates clocks as if the reference + * frequency were doubled. + */ + pATI->ReferenceNumerator <<= 1; + + return; /* ... to ATIPreInit() */ + } + } + +#ifndef AVOID_CPIO + + /* Set default clock maps */ + pATI->NewHW.ClockMap = ATIVGAWonderClockMap; + pATI->NewHW.ClockUnmap = ATIVGAWonderClockUnmap; + +#endif /* AVOID_CPIO */ + + /* + * Determine the number of clock values the adapter should be able to + * generate and the dot clock to use for probe calibration. + */ +ProbeClocks: + +#ifndef AVOID_CPIO + + if (pATI->Adapter == ATI_ADAPTER_VGA) + { + NumberOfDividers = 1; + NumberOfUndividedClocks = 4; + CalibrationClockNumber = 1; + CalibrationClockValue = 28322; + } + else + +#endif /* AVOID_CPIO */ + + { + +#ifndef AVOID_CPIO + + NumberOfDividers = 4; + if ((pATI->Chip <= ATI_CHIP_18800) || + (pATI->Adapter == ATI_ADAPTER_V4)) + { + NumberOfUndividedClocks = 8; + /* Actually, any undivided clock will do */ + CalibrationClockNumber = 1; + CalibrationClockValue = 56644; + } + else + +#endif /* AVOID_CPIO */ + + { + NumberOfUndividedClocks = 16; + +#ifndef AVOID_CPIO + + CalibrationClockNumber = 7; + CalibrationClockValue = 36000; + if (pATI->Chip >= ATI_CHIP_68800) + +#endif /* AVOID_CPIO */ + + { + NumberOfDividers = 2; + if (pATI->Chip >= ATI_CHIP_264CT) + { + NumberOfDividers = 1; + NumberOfUndividedClocks = 4; + CalibrationClockNumber = 1; + CalibrationClockValue = 28322; + } + else + +#ifndef AVOID_CPIO + + if (pATI->Adapter >= ATI_ADAPTER_MACH64) + +#endif /* AVOID_CPIO */ + + { + CalibrationClockNumber = 10 /* or 11 */; + CalibrationClockValue = 75000 /* or 65000 */; + } + + /* + * When selecting clocks, all ATI accelerators use a different + * clock ordering. + */ + +#ifndef AVOID_CPIO + + if (pATI->NewHW.crtc == ATI_CRTC_VGA) + { + pATI->NewHW.ClockMap = ATIMachVGAClockMap; + pATI->NewHW.ClockUnmap = ATIMachVGAClockUnmap; + } + else + +#endif /* AVOID_CPIO */ + + { + pATI->NewHW.ClockMap = ATIAcceleratorClockMap; + pATI->NewHW.ClockUnmap = ATIAcceleratorClockUnmap; + } + } + } + } + + pATI->OldHW.ClockMap = pATI->NewHW.ClockMap; + pATI->OldHW.ClockUnmap = pATI->NewHW.ClockUnmap; + + NumberOfClocks = NumberOfUndividedClocks * NumberOfDividers; + + /* + * Respect any XF86Config clocks line. Well, that's the theory, anyway. + * In practice, however, the regular use of probed values is widespread, at + * times causing otherwise inexplicable results. So, attempt to normalise + * the clocks to known (i.e. specification) values. + */ + if (!pGDev->numclocks || pATI->OptionProbeClocks || + xf86ServerIsOnlyProbing()) + { + if (pATI->ProgrammableClock != ATI_CLOCK_FIXED) + { + /* + * For unsupported programmable clock generators, pick the highest + * frequency set by BIOS initialisation for clock calibration. + */ + CalibrationClockNumber = CalibrationClockValue = 0; + for (ClockIndex = 0; + ClockIndex < NumberOfUndividedClocks; + ClockIndex++) + if (CalibrationClockValue < pATI->BIOSClocks[ClockIndex]) + { + CalibrationClockNumber = ClockIndex; + CalibrationClockValue = pATI->BIOSClocks[ClockIndex]; + } + CalibrationClockNumber = + MapClockIndex(pATI->NewHW.ClockUnmap, CalibrationClockNumber); + CalibrationClockValue *= 10; + } + +#ifndef AVOID_CPIO + + if (pATI->VGAAdapter != ATI_ADAPTER_NONE) + { + /* + * The current video state needs to be saved before the clock + * probe, and restored after. Video memory corruption and other + * effects occur because, at this early stage, the clock probe + * cannot reliably be prevented from enabling frequencies that are + * greater than what the adapter can handle. + */ + ATIModeSave(pScreenInfo, pATI, &pATI->OldHW); + + /* Ensure clock select pins are not OR'ed with anything */ + if (pATI->CPIO_VGAWonder && (pATI->OldHW.crtc == ATI_CRTC_VGA)) + ATIModifyExtReg(pATI, 0xB5U, pATI->OldHW.b5, 0x7FU, 0x00U); + } + +#endif /* AVOID_CPIO */ + + /* + * Probe the adapter for clock values. The following is essentially + * the common layer's xf86GetClocks() reworked to fit. One difference + * is the ability to monitor a VSync bit in MMIO space. + */ + CanDisableInterrupts = TRUE; /* An assumption verified below */ + + for (ClockIndex = 0; ClockIndex < NumberOfClocks; ClockIndex++) + { + pScreenInfo->clock[ClockIndex] = 0; + + /* Remap clock number */ + Index = MapClockIndex(pATI->OldHW.ClockMap, ClockIndex); + + /* Select the clock */ + switch (pATI->OldHW.crtc) + { + +#ifndef AVOID_CPIO + + case ATI_CRTC_VGA: + /* Get generic two low-order bits */ + genmo = (inb(R_GENMO) & 0xF3U) | ((Index << 2) & 0x0CU); + + if (pATI->CPIO_VGAWonder) + { + /* + * On adapters with crystals, switching to one of the + * spare assignments doesn't do anything (i.e. the + * previous setting remains in effect). So, disable + * their selection. + */ + if (((Index & 0x03U) == 0x02U) && + ((pATI->Chip <= ATI_CHIP_18800) || + (pATI->Adapter == ATI_ADAPTER_V4))) + continue; + + /* Start sequencer reset */ + PutReg(SEQX, 0x00U, 0x00U); + + /* Set high-order bits */ + if (pATI->Chip <= ATI_CHIP_18800) + ATIModifyExtReg(pATI, 0xB2U, -1, 0xBFU, + Index << 4); + else + { + ATIModifyExtReg(pATI, 0xBEU, -1, 0xEFU, + Index << 2); + if (pATI->Adapter != ATI_ADAPTER_V4) + { + Index >>= 1; + ATIModifyExtReg(pATI, 0xB9U, -1, 0xFDU, + Index >> 1); + } + } + + /* Set clock divider bits */ + ATIModifyExtReg(pATI, 0xB8U, -1, 0x00U, + (Index << 3) & 0xC0U); + } + else + { + /* + * Reject clocks that cannot be selected. + */ + if (Index & ~0x03U) + continue; + + /* Start sequencer reset */ + PutReg(SEQX, 0x00U, 0x00U); + } + + /* Must set miscellaneous output register last */ + outb(GENMO, genmo); + + /* End sequencer reset */ + PutReg(SEQX, 0x00U, 0x03U); + + break; + +#endif /* AVOID_CPIO */ + + case ATI_CRTC_MACH64: + out8(CLOCK_CNTL, CLOCK_STROBE | + SetBits(Index, CLOCK_SELECT | CLOCK_DIVIDER)); + break; + + default: + continue; + } + + usleep(50000); /* Let clock stabilise */ + + xf86SetPriority(TRUE); + + /* Try to disable interrupts */ + if (CanDisableInterrupts && !xf86DisableInterrupts()) + { + xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING, + "Unable to disable interrupts; Clock probe will not be as" + " accurate.\n"); + CanDisableInterrupts = FALSE; + } + + /* + * Generate a count while monitoring the vertical sync or blanking + * pulse. This is dependent on the CRTC used by the mode on server + * entry. + */ + switch (pATI->OldHW.crtc) + { + +#ifndef AVOID_CPIO + + case ATI_CRTC_VGA: + /* Verify vertical sync pulses are in fact occurring */ + Index = 1 << 19; + while (!(inb(GENS1(pATI->CPIO_VGABase)) & 0x08U)) + if (Index-- <= 0) + goto EnableInterrupts; + Index = 1 << 19; + while (inb(GENS1(pATI->CPIO_VGABase)) & 0x08U) + if (Index-- <= 0) + goto EnableInterrupts; + Index = 1 << 19; + while (!(inb(GENS1(pATI->CPIO_VGABase)) & 0x08U)) + if (Index-- <= 0) + goto EnableInterrupts; + + /* Generate the count */ + for (Index = 0; Index < 8; Index++) + { + while (inb(GENS1(pATI->CPIO_VGABase)) & 0x08U) + pScreenInfo->clock[ClockIndex]++; + while (!(inb(GENS1(pATI->CPIO_VGABase)) & 0x08U)) + pScreenInfo->clock[ClockIndex]++; + } + break; + +#endif /* AVOID_CPIO */ + + case ATI_CRTC_MACH64: + /* Verify vertical blanking pulses are in fact occurring */ + Index = 1 << 19; + while (!(inr(CRTC_INT_CNTL) & CRTC_VBLANK)) + if (Index-- <= 0) + goto EnableInterrupts; + Index = 1 << 19; + while (inr(CRTC_INT_CNTL) & CRTC_VBLANK) + if (Index-- <= 0) + goto EnableInterrupts; + Index = 1 << 19; + while (!(inr(CRTC_INT_CNTL) & CRTC_VBLANK)) + if (Index-- <= 0) + goto EnableInterrupts; + + /* Generate the count */ + for (Index = 0; Index < 4; Index++) + { + while (inr(CRTC_INT_CNTL) & CRTC_VBLANK) + pScreenInfo->clock[ClockIndex]++; + while (!(inr(CRTC_INT_CNTL) & CRTC_VBLANK)) + pScreenInfo->clock[ClockIndex]++; + } + break; + + default: + break; + } + + EnableInterrupts: + if (CanDisableInterrupts) + xf86EnableInterrupts(); + + xf86SetPriority(FALSE); + } + + ScaleFactor = (double)CalibrationClockValue * + (double)pScreenInfo->clock[CalibrationClockNumber]; + + /* Scale the clocks from counts to kHz */ + for (ClockIndex = 0; ClockIndex < NumberOfClocks; ClockIndex++) + { + if (ClockIndex == CalibrationClockNumber) + pScreenInfo->clock[ClockIndex] = CalibrationClockValue; + else if (pScreenInfo->clock[ClockIndex]) + /* Round to the nearest 10 kHz */ + pScreenInfo->clock[ClockIndex] = + (int)(((ScaleFactor / + (double)pScreenInfo->clock[ClockIndex]) + + 5) / 10) * 10; + } + + pScreenInfo->numClocks = NumberOfClocks; + +#ifndef AVOID_CPIO + + if (pATI->VGAAdapter != ATI_ADAPTER_NONE) + { + /* Restore video state */ + ATIModeSet(pScreenInfo, pATI, &pATI->OldHW); + xfree(pATI->OldHW.frame_buffer); + pATI->OldHW.frame_buffer = NULL; + } + +#endif /* AVOID_CPIO */ + + /* Tell user clocks were probed, instead of supplied */ + pATI->OptionProbeClocks = TRUE; + + /* Attempt to match probed clocks to a known specification */ + pATI->Clock = ATIMatchClockLine(pScreenInfo, pATI, + SpecificationClockLine, NumberOfUndividedClocks, + CalibrationClockNumber, 0); + +#ifndef AVOID_CPIO + + if ((pATI->Chip <= ATI_CHIP_18800) || + (pATI->Adapter == ATI_ADAPTER_V4)) + { + /* V3 and V4 adapters don't have clock chips */ + if (pATI->Clock > ATI_CLOCK_CRYSTALS) + pATI->Clock = ATI_CLOCK_NONE; + } + else + +#endif /* AVOID_CPIO */ + + { + /* All others don't have crystals */ + if (pATI->Clock == ATI_CLOCK_CRYSTALS) + pATI->Clock = ATI_CLOCK_NONE; + } + } + else + { + /* + * Allow for an initial subset of specification clocks. Can't allow + * for any more than that though... + */ + if (NumberOfClocks > pGDev->numclocks) + { + NumberOfClocks = pGDev->numclocks; + if (NumberOfUndividedClocks > NumberOfClocks) + NumberOfUndividedClocks = NumberOfClocks; + } + + /* Move XF86Config clocks into the ScrnInfoRec */ + for (ClockIndex = 0; ClockIndex < NumberOfClocks; ClockIndex++) + pScreenInfo->clock[ClockIndex] = pGDev->clock[ClockIndex]; + pScreenInfo->numClocks = NumberOfClocks; + + /* Attempt to match clocks to a known specification */ + pATI->Clock = ATIMatchClockLine(pScreenInfo, pATI, + SpecificationClockLine, NumberOfUndividedClocks, -1, 0); + +#ifndef AVOID_CPIO + + if (pATI->Adapter != ATI_ADAPTER_VGA) + +#endif /* AVOID_CPIO */ + + { + if (pATI->Clock == ATI_CLOCK_NONE) + { + /* + * Reject certain clock lines that are obviously wrong. This + * includes the standard VGA clocks for ATI adapters, and clock + * lines that could have been used with the pre-2.1.1 driver. + */ + if (ATIMatchClockLine(pScreenInfo, pATI, InvalidClockLine, + NumberOfClocks, -1, 0)) + pATI->OptionProbeClocks = TRUE; + else + +#ifndef AVOID_CPIO + + if ((pATI->Chip >= ATI_CHIP_18800) && + (pATI->Adapter != ATI_ADAPTER_V4)) + +#endif /* AVOID_CPIO */ + + { + /* + * Check for clocks that are specified in the wrong order. + * This is meant to catch those who are trying to use the + * clock order intended for the old accelerated servers. + */ + while ((++ClockMap, ClockMap %= NumberOf(ClockMaps))) + { + pATI->Clock = ATIMatchClockLine(pScreenInfo, pATI, + SpecificationClockLine, NumberOfUndividedClocks, + -1, ClockMap); + if (pATI->Clock != ATI_CLOCK_NONE) + { + xf86DrvMsgVerb(pScreenInfo->scrnIndex, + X_WARNING, 0, + "XF86Config clock ordering incorrect. Clocks" + " will be reordered.\n"); + break; + } + } + } + } + else + /* Ensure crystals are not matched to clock chips, and vice versa */ + +#ifndef AVOID_CPIO + + if ((pATI->Chip <= ATI_CHIP_18800) || + (pATI->Adapter == ATI_ADAPTER_V4)) + { + if (pATI->Clock > ATI_CLOCK_CRYSTALS) + pATI->OptionProbeClocks = TRUE; + } + else + +#endif /* AVOID_CPIO */ + + { + if (pATI->Clock == ATI_CLOCK_CRYSTALS) + pATI->OptionProbeClocks = TRUE; + } + + if (pATI->OptionProbeClocks) + { + xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 0, + "Invalid or obsolete XF86Config clocks line rejected.\n" + " Clocks will be probed.\n"); + goto ProbeClocks; + } + } + } + + if (pATI->ProgrammableClock != ATI_CLOCK_FIXED) + pATI->ProgrammableClock = ATI_CLOCK_FIXED; + else if (pATI->Clock == ATI_CLOCK_NONE) + xf86DrvMsgVerb(pScreenInfo->scrnIndex, X_WARNING, 0, + "Unknown clock generator detected.\n"); + else + +#ifndef AVOID_CPIO + + if (pATI->Clock == ATI_CLOCK_CRYSTALS) + xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, + "This adapter uses crystals to generate clock frequencies.\n"); + else if (pATI->Clock != ATI_CLOCK_VGA) + +#endif /* AVOID_CPIO */ + + { + xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED, + "%s clock chip detected.\n", ATIClockNames[pATI->Clock]); + } + + if (pATI->Clock != ATI_CLOCK_NONE) + { + /* Replace undivided clocks with specification values */ + for (ClockIndex = 0; + ClockIndex < NumberOfUndividedClocks; + ClockIndex++) + { + /* + * Don't replace clocks that are probed, documented, or set by the + * user to zero. One exception is that we need to override the + * user's value for the spare settings on a crystal-based adapter. + * Another exception is when the user specifies the clock ordering + * intended for the old accelerated servers. + */ + SpecificationClock = + SpecificationClockLine[pATI->Clock][ClockIndex]; + if (SpecificationClock < 0) + break; + if (!ClockMap) + { + if (!pScreenInfo->clock[ClockIndex]) + continue; + if (!SpecificationClock) + { + if (pATI->Clock != ATI_CLOCK_CRYSTALS) + continue; + } + else + { + /* + * Due to the way clock lines are matched, the following + * can prevent the override if the clock is probed, + * documented or set by the user to a value greater than + * maxClock. + */ + if (abs(SpecificationClock - + pScreenInfo->clock[ClockIndex]) > CLOCK_TOLERANCE) + continue; + } + } + pScreenInfo->clock[ClockIndex] = SpecificationClock; + } + + /* Adjust divided clocks */ + for (ClockIndex = NumberOfUndividedClocks; + ClockIndex < NumberOfClocks; + ClockIndex++) + pScreenInfo->clock[ClockIndex] = ATIDivide( + pScreenInfo->clock[ClockIndex % NumberOfUndividedClocks], + (ClockIndex / NumberOfUndividedClocks) + 1, 0, 0); + } + + /* Tell user about fixed clocks */ + xf86ShowClocks(pScreenInfo, pATI->OptionProbeClocks ? X_PROBED : X_CONFIG); + + /* Prevent selection of high clocks, even by V_CLKDIV2 modes */ + for (ClockIndex = 0; ClockIndex < NumberOfClocks; ClockIndex++) + if (pScreenInfo->clock[ClockIndex] > pRange->maxClock) + pScreenInfo->clock[ClockIndex] = 0; +} + +/* + * ATIClockSave -- + * + * This function saves that part of an ATIHWRec that relates to clocks. + */ +void +ATIClockSave +( + ScrnInfoPtr pScreenInfo, + ATIPtr pATI, + ATIHWPtr pATIHW +) +{ + if (pScreenInfo->vtSema && (pATI->ProgrammableClock > ATI_CLOCK_FIXED)) + { + +#ifndef AVOID_CPIO + + if (pATIHW->crtc == ATI_CRTC_VGA) + { + pATIHW->ClockMap = ATIVGAProgrammableClockMap; + pATIHW->ClockUnmap = ATIVGAProgrammableClockUnmap; + } + else + +#endif /* AVOID_CPIO */ + + { + pATIHW->ClockMap = ATIProgrammableClockMap; + pATIHW->ClockUnmap = ATIProgrammableClockUnmap; + } + } + else + { + +#ifndef AVOID_CPIO + + if (pATIHW->crtc != ATI_CRTC_VGA) + +#endif /* AVOID_CPIO */ + + { + pATIHW->ClockMap = ATIAcceleratorClockMap; + pATIHW->ClockUnmap = ATIAcceleratorClockUnmap; + } + +#ifndef AVOID_CPIO + + else if (pATI->Chip < ATI_CHIP_68800) + { + pATIHW->ClockMap = ATIVGAWonderClockMap; + pATIHW->ClockUnmap = ATIVGAWonderClockUnmap; + } + else + { + pATIHW->ClockMap = ATIMachVGAClockMap; + pATIHW->ClockUnmap = ATIMachVGAClockUnmap; + } + +#endif /* AVOID_CPIO */ + + } +} + +/* + * ATIClockCalculate -- + * + * This function is called to generate, if necessary, the data needed for clock + * programming, and set clock select bits in various register values. + */ +Bool +ATIClockCalculate +( + int iScreen, + ATIPtr pATI, + ATIHWPtr pATIHW, + DisplayModePtr pMode +) +{ + int N, M, D; + int ClockSelect, N1, MinimumGap; + int Frequency, Multiple; /* Used as temporaries */ + + /* Set default values */ + pATIHW->FeedbackDivider = pATIHW->ReferenceDivider = pATIHW->PostDivider = 0; + + if ((pATI->ProgrammableClock <= ATI_CLOCK_FIXED) || + ((pATI->ProgrammableClock == ATI_CLOCK_CH8398) && + (pMode->ClockIndex < 2))) + { + /* Use a fixed clock */ + ClockSelect = pMode->ClockIndex; + } + else + { + /* Generate clock programme word, using units of kHz */ + MinimumGap = ((unsigned int)(-1)) >> 1; + + /* Loop through reference dividers */ + for (M = pATI->ClockDescriptor.MinM; + M <= pATI->ClockDescriptor.MaxM; + M++) + { + /* Loop through post-dividers */ + for (D = 0; D < pATI->ClockDescriptor.NumD; D++) + { + if (!pATI->ClockDescriptor.PostDividers[D]) + continue; + + /* Limit undivided VCO to maxClock */ + if (pATI->maxClock && + ((pATI->maxClock / pATI->ClockDescriptor.PostDividers[D]) < + pMode->Clock)) + continue; + + /* + * Calculate closest feedback divider and apply its + * restrictions. + */ + Multiple = M * pATI->ReferenceDenominator * + pATI->ClockDescriptor.PostDividers[D]; + N = ATIDivide(pMode->Clock * Multiple, + pATI->ReferenceNumerator, 0, 0); + if (N < pATI->ClockDescriptor.MinN) + N = pATI->ClockDescriptor.MinN; + else if (N > pATI->ClockDescriptor.MaxN) + N = pATI->ClockDescriptor.MaxN; + N -= pATI->ClockDescriptor.NAdjust; + N1 = (N / pATI->ClockDescriptor.N1) * pATI->ClockDescriptor.N2; + if (N > N1) + N = ATIDivide(N1 + 1, pATI->ClockDescriptor.N1, 0, 1); + N += pATI->ClockDescriptor.NAdjust; + N1 += pATI->ClockDescriptor.NAdjust; + + for (; ; N = N1) + { + /* Pick the closest setting */ + Frequency = abs(ATIDivide(N * pATI->ReferenceNumerator, + Multiple, 0, 0) - pMode->Clock); + if ((Frequency < MinimumGap) || + ((Frequency == MinimumGap) && + (pATIHW->FeedbackDivider < N))) + { + /* Save settings */ + pATIHW->FeedbackDivider = N; + pATIHW->ReferenceDivider = M; + pATIHW->PostDivider = D; + MinimumGap = Frequency; + } + + if (N <= N1) + break; + } + } + } + + Multiple = pATIHW->ReferenceDivider * pATI->ReferenceDenominator * + pATI->ClockDescriptor.PostDividers[pATIHW->PostDivider]; + Frequency = pATIHW->FeedbackDivider * pATI->ReferenceNumerator; + Frequency = ATIDivide(Frequency, Multiple, 0, 0); + if (abs(Frequency - pMode->Clock) > CLOCK_TOLERANCE) + { + xf86DrvMsg(iScreen, X_ERROR, + "Unable to programme clock %.3fMHz for mode %s.\n", + (double)(pMode->Clock) / 1000.0, pMode->name); + return FALSE; + } + pMode->SynthClock = Frequency; + ClockSelect = pATI->ClockNumberToProgramme; + + xf86ErrorFVerb(4, + "\n Programming clock %d to %.3fMHz for mode %s." + " N=%d, M=%d, D=%d.\n", + ClockSelect, (double)Frequency / 1000.0, pMode->name, + pATIHW->FeedbackDivider, pATIHW->ReferenceDivider, + pATIHW->PostDivider); + + if (pATI->Chip >= ATI_CHIP_264VTB) + ATIDSPCalculate(pATI, pATIHW, pMode); + } + + /* Set clock select bits, after remapping them */ + pATIHW->clock = ClockSelect; /* Save pre-map clock number */ + ClockSelect = MapClockIndex(pATIHW->ClockMap, ClockSelect); + + switch (pATIHW->crtc) + { + +#ifndef AVOID_CPIO + + case ATI_CRTC_VGA: + pATIHW->genmo = (pATIHW->genmo & 0xF3U) | + ((ClockSelect << 2) & 0x0CU); + + if (pATI->CPIO_VGAWonder) + { + /* Set ATI clock select bits */ + if (pATI->Chip <= ATI_CHIP_18800) + pATIHW->b2 = (pATIHW->b2 & 0xBFU) | + ((ClockSelect << 4) & 0x40U); + else + { + pATIHW->be = (pATIHW->be & 0xEFU) | + ((ClockSelect << 2) & 0x10U); + if (pATI->Adapter != ATI_ADAPTER_V4) + { + ClockSelect >>= 1; + pATIHW->b9 = (pATIHW->b9 & 0xFDU) | + ((ClockSelect >> 1) & 0x02U); + } + } + + /* Set clock divider bits */ + pATIHW->b8 = (pATIHW->b8 & 0x3FU) | + ((ClockSelect << 3) & 0xC0U); + } + break; + +#endif /* AVOID_CPIO */ + + case ATI_CRTC_MACH64: + pATIHW->clock_cntl = CLOCK_STROBE | + SetBits(ClockSelect, CLOCK_SELECT | CLOCK_DIVIDER); + break; + + default: + break; + } + + return TRUE; +} + +/* + * ATIClockSet -- + * + * This function is called to programme a clock for the mode being set. + */ +void +ATIClockSet +( + ATIPtr pATI, + ATIHWPtr pATIHW +) +{ + CARD32 crtc_gen_cntl, tmp; + CARD8 clock_cntl0; + CARD8 tmp2; + unsigned int Programme; + int N = pATIHW->FeedbackDivider - pATI->ClockDescriptor.NAdjust; + int M = pATIHW->ReferenceDivider - pATI->ClockDescriptor.MAdjust; + int D = pATIHW->PostDivider; + + /* Temporarily switch to accelerator mode */ + crtc_gen_cntl = inr(CRTC_GEN_CNTL); + if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN)) + outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN); + + switch (pATI->ProgrammableClock) + { + case ATI_CLOCK_ICS2595: + clock_cntl0 = in8(CLOCK_CNTL); + + Programme = (SetBits(pATIHW->clock, ICS2595_CLOCK) | + SetBits(N, ICS2595_FB_DIV) | SetBits(D, ICS2595_POST_DIV)) ^ + ICS2595_TOGGLE; + + ATIDelay(50000); /* 50 milliseconds */ + + (void)xf86DisableInterrupts(); + + /* Send all 20 bits of programme word */ + while (Programme >= CLOCK_BIT) + { + tmp = (Programme & CLOCK_BIT) | CLOCK_STROBE; + out8(CLOCK_CNTL, tmp); + ATIDelay(26); /* 26 microseconds */ + out8(CLOCK_CNTL, tmp | CLOCK_PULSE); + ATIDelay(26); /* 26 microseconds */ + Programme >>= 1; + } + + xf86EnableInterrupts(); + + /* Restore register */ + out8(CLOCK_CNTL, clock_cntl0 | CLOCK_STROBE); + break; + + case ATI_CLOCK_STG1703: + (void)ATIGetDACCmdReg(pATI); + (void)in8(M64_DAC_MASK); + out8(M64_DAC_MASK, (pATIHW->clock << 1) + 0x20U); + out8(M64_DAC_MASK, 0); + out8(M64_DAC_MASK, SetBits(N, 0xFFU)); + out8(M64_DAC_MASK, SetBits(M, 0x1FU) | SetBits(D, 0xE0U)); + break; + + case ATI_CLOCK_CH8398: + tmp = inr(DAC_CNTL) | (DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3); + outr(DAC_CNTL, tmp); + out8(M64_DAC_WRITE, pATIHW->clock); + out8(M64_DAC_DATA, SetBits(N, 0xFFU)); + out8(M64_DAC_DATA, SetBits(M, 0x3FU) | SetBits(D, 0xC0U)); + out8(M64_DAC_MASK, 0x04U); + outr(DAC_CNTL, tmp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)); + tmp2 = in8(M64_DAC_WRITE); + out8(M64_DAC_WRITE, (tmp2 & 0x70U) | 0x80U); + outr(DAC_CNTL, tmp & ~DAC_EXT_SEL_RS2); + break; + + case ATI_CLOCK_INTERNAL: + /* Reset VCLK generator */ + ATIPutMach64PLLReg(PLL_VCLK_CNTL, pATIHW->pll_vclk_cntl); + + /* Set post-divider */ + tmp2 = pATIHW->clock << 1; + tmp = ATIGetMach64PLLReg(PLL_VCLK_POST_DIV); + tmp &= ~(0x03U << tmp2); + tmp |= SetBits(D, 0x03U) << tmp2; + ATIPutMach64PLLReg(PLL_VCLK_POST_DIV, tmp); + + /* Set extended post-divider */ + tmp = ATIGetMach64PLLReg(PLL_XCLK_CNTL); + tmp &= ~(SetBits(1, PLL_VCLK0_XDIV) << pATIHW->clock); + tmp |= SetBits(D >> 2, PLL_VCLK0_XDIV) << pATIHW->clock; + ATIPutMach64PLLReg(PLL_XCLK_CNTL, tmp); + + /* Set feedback divider */ + tmp = PLL_VCLK0_FB_DIV + pATIHW->clock; + ATIPutMach64PLLReg(tmp, SetBits(N, 0xFFU)); + + /* End VCLK generator reset */ + ATIPutMach64PLLReg(PLL_VCLK_CNTL, + pATIHW->pll_vclk_cntl & ~PLL_VCLK_RESET); + + /* Reset write bit */ + ATIAccessMach64PLLReg(pATI, 0, FALSE); + break; + + case ATI_CLOCK_ATT20C408: + (void)ATIGetDACCmdReg(pATI); + tmp = in8(M64_DAC_MASK); + (void)ATIGetDACCmdReg(pATI); + out8(M64_DAC_MASK, tmp | 1); + out8(M64_DAC_WRITE, 1); + out8(M64_DAC_MASK, tmp | 9); + ATIDelay(400); /* 400 microseconds */ + tmp2 = (pATIHW->clock << 2) + 0x40U; + out8(M64_DAC_WRITE, tmp2); + out8(M64_DAC_MASK, SetBits(N, 0xFFU)); + out8(M64_DAC_WRITE, ++tmp2); + out8(M64_DAC_MASK, SetBits(M, 0x3FU) | SetBits(D, 0xC0U)); + out8(M64_DAC_WRITE, ++tmp2); + out8(M64_DAC_MASK, 0x77U); + ATIDelay(400); /* 400 microseconds */ + out8(M64_DAC_WRITE, 1); + out8(M64_DAC_MASK, tmp); + break; + + case ATI_CLOCK_IBMRGB514: + /* + * Here, only update in-core data. It will be written out later by + * ATIRGB514Set(). + */ + tmp = (pATIHW->clock << 1) + 0x20U; + pATIHW->ibmrgb514[tmp] = + (SetBits(N, 0x3FU) | SetBits(D, 0xC0U)) ^ 0xC0U; + pATIHW->ibmrgb514[tmp + 1] = SetBits(M, 0x3FU); + break; + + default: + break; + } + + (void)in8(M64_DAC_WRITE); /* Clear DAC counter */ + + /* Restore register */ + if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN)) + outr(CRTC_GEN_CNTL, crtc_gen_cntl); +} |