/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/aticlock.c,v 1.21 2003/04/23 21:51:27 tsi Exp $ */
/*
 * Copyright 1997 through 2004 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#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 */
            ATIMach64PutPLLReg(PLL_VCLK_CNTL, pATIHW->pll_vclk_cntl);

            /* Set post-divider */
            tmp2 = pATIHW->clock << 1;
            tmp = ATIMach64GetPLLReg(PLL_VCLK_POST_DIV);
            tmp &= ~(0x03U << tmp2);
            tmp |= SetBits(D, 0x03U) << tmp2;
            ATIMach64PutPLLReg(PLL_VCLK_POST_DIV, tmp);

            /* Set extended post-divider */
            tmp = ATIMach64GetPLLReg(PLL_XCLK_CNTL);
            tmp &= ~(SetBits(1, PLL_VCLK0_XDIV) << pATIHW->clock);
            tmp |= SetBits(D >> 2, PLL_VCLK0_XDIV) << pATIHW->clock;
            ATIMach64PutPLLReg(PLL_XCLK_CNTL, tmp);

            /* Set feedback divider */
            tmp = PLL_VCLK0_FB_DIV + pATIHW->clock;
            ATIMach64PutPLLReg(tmp, SetBits(N, 0xFFU));

            /* End VCLK generator reset */
            ATIMach64PutPLLReg(PLL_VCLK_CNTL,
                pATIHW->pll_vclk_cntl & ~PLL_VCLK_RESET);

            /* Reset write bit */
            ATIMach64AccessPLLReg(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);
}
