aticlock.c revision 32b578d3
132b578d3Smrg/*
232b578d3Smrg * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
332b578d3Smrg *
432b578d3Smrg * Permission to use, copy, modify, distribute, and sell this software and its
532b578d3Smrg * documentation for any purpose is hereby granted without fee, provided that
632b578d3Smrg * the above copyright notice appear in all copies and that both that copyright
732b578d3Smrg * notice and this permission notice appear in supporting documentation, and
832b578d3Smrg * that the name of Marc Aurele La France not be used in advertising or
932b578d3Smrg * publicity pertaining to distribution of the software without specific,
1032b578d3Smrg * written prior permission.  Marc Aurele La France makes no representations
1132b578d3Smrg * about the suitability of this software for any purpose.  It is provided
1232b578d3Smrg * "as-is" without express or implied warranty.
1332b578d3Smrg *
1432b578d3Smrg * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1532b578d3Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
1632b578d3Smrg * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1732b578d3Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1832b578d3Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1932b578d3Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2032b578d3Smrg * PERFORMANCE OF THIS SOFTWARE.
2132b578d3Smrg */
2232b578d3Smrg
2332b578d3Smrg/*
2432b578d3Smrg * For all supported programmable clock generators, the driver will ignore any
2532b578d3Smrg * XF86Config clock line and programme, as needed, the clock number reserved by
2632b578d3Smrg * the BIOS for accelerated drivers.  The driver's mode initialisation routine
2732b578d3Smrg * finds integers N, M and D such that
2832b578d3Smrg *
2932b578d3Smrg *             N
3032b578d3Smrg *      R * -------  MHz
3132b578d3Smrg *           M * D
3232b578d3Smrg *
3332b578d3Smrg * best approximates the mode's clock frequency, where R is the crystal-
3432b578d3Smrg * generated reference frequency (usually 14.318 MHz).  D is a power of 2
3532b578d3Smrg * except for those integrated controllers that also offer odd dividers.
3632b578d3Smrg * Different clock generators have different restrictions on the value N, M and
3732b578d3Smrg * D can assume.  The driver contains an internal table to record these
3832b578d3Smrg * restrictions (among other things).  The resulting values of N, M and D are
3932b578d3Smrg * then encoded in a generator-specific way and used to programme the clock.
4032b578d3Smrg * The Mach64's clock divider is not used in this case.
4132b578d3Smrg */
4232b578d3Smrg
4332b578d3Smrg#ifdef HAVE_CONFIG_H
4432b578d3Smrg#include "config.h"
4532b578d3Smrg#endif
4632b578d3Smrg
4732b578d3Smrg#include <stdlib.h>
4832b578d3Smrg#include "ati.h"
4932b578d3Smrg#include "atichip.h"
5032b578d3Smrg#include "atidac.h"
5132b578d3Smrg#include "atidsp.h"
5232b578d3Smrg#include "atimach64io.h"
5332b578d3Smrg#include "atimode.h"
5432b578d3Smrg#include "atiwonderio.h"
5532b578d3Smrg
5632b578d3Smrg/*
5732b578d3Smrg * Definitions related to programmable clock generators.
5832b578d3Smrg */
5932b578d3Smrgstatic CARD16 ATIPostDividers[] = {1, 2, 4, 8, 16, 32, 64, 128},
6032b578d3Smrg              ATI264xTPostDividers[] = {1, 2, 4, 8, 3, 0, 6, 12};
6132b578d3SmrgClockRec ATIClockDescriptors[] =
6232b578d3Smrg{
6332b578d3Smrg    {
6432b578d3Smrg          0,   0,   0, 1, 1,
6532b578d3Smrg          1,   1,   0,
6632b578d3Smrg          0, NULL,
6732b578d3Smrg        "Non-programmable"
6832b578d3Smrg    },
6932b578d3Smrg    {
7032b578d3Smrg        257, 512, 257, 1, 1,
7132b578d3Smrg         46,  46,   0,
7232b578d3Smrg          4, ATIPostDividers,
7332b578d3Smrg        "ATI 18818 or ICS 2595 or similar"
7432b578d3Smrg    },
7532b578d3Smrg    {
7632b578d3Smrg          2, 129,   2, 1, 1,
7732b578d3Smrg          8,  14,   2,
7832b578d3Smrg          8, ATIPostDividers,
7932b578d3Smrg        "SGS-Thompson 1703 or similar"
8032b578d3Smrg    },
8132b578d3Smrg    {
8232b578d3Smrg         16, 263,   8, 8, 9,
8332b578d3Smrg          4,  12,   2,
8432b578d3Smrg          4, ATIPostDividers,
8532b578d3Smrg        "Chrontel 8398 or similar"
8632b578d3Smrg    },
8732b578d3Smrg    {
8832b578d3Smrg          2, 255,   0, 1, 1,
8932b578d3Smrg         45,  45,   0,
9032b578d3Smrg          4, ATI264xTPostDividers,
9132b578d3Smrg        "Internal"
9232b578d3Smrg    },
9332b578d3Smrg    {
9432b578d3Smrg          2, 257,   2, 1, 1,
9532b578d3Smrg          2,  32,   2,
9632b578d3Smrg          4, ATIPostDividers,
9732b578d3Smrg        "AT&T 20C408 or similar"
9832b578d3Smrg    },
9932b578d3Smrg    {
10032b578d3Smrg         65, 128,  65, 1, 1,
10132b578d3Smrg          2,  14,   0,
10232b578d3Smrg          4, ATIPostDividers,
10332b578d3Smrg        "IBM RGB 514 or similar"
10432b578d3Smrg    }
10532b578d3Smrg};
10632b578d3Smrg
10732b578d3Smrg/*
10832b578d3Smrg * ATIClockPreInit --
10932b578d3Smrg *
11032b578d3Smrg * This function is called by ATIPreInit() and handles the XF86Config clocks
11132b578d3Smrg * line (or lack thereof).
11232b578d3Smrg */
11332b578d3Smrgvoid
11432b578d3SmrgATIClockPreInit
11532b578d3Smrg(
11632b578d3Smrg    ScrnInfoPtr   pScreenInfo,
11732b578d3Smrg    ATIPtr        pATI
11832b578d3Smrg)
11932b578d3Smrg{
12032b578d3Smrg            /*
12132b578d3Smrg             * Recognise supported clock generators.  This involves telling the
12232b578d3Smrg             * rest of the server about it and (re-)initializing the XF86Config
12332b578d3Smrg             * clocks line.
12432b578d3Smrg             */
12532b578d3Smrg            pScreenInfo->progClock = TRUE;
12632b578d3Smrg
12732b578d3Smrg            xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
12832b578d3Smrg                "%s programmable clock generator detected.\n",
12932b578d3Smrg                pATI->ClockDescriptor.ClockName);
13032b578d3Smrg            if (pATI->ReferenceDenominator == 1)
13132b578d3Smrg                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
13232b578d3Smrg                    "Reference clock %.3f MHz.\n",
13332b578d3Smrg                    (double)pATI->ReferenceNumerator / 1000.0);
13432b578d3Smrg            else
13532b578d3Smrg                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
13632b578d3Smrg                    "Reference clock %.6g/%d (%.3f) MHz.\n",
13732b578d3Smrg                    (double)pATI->ReferenceNumerator / 1000.0,
13832b578d3Smrg                    pATI->ReferenceDenominator,
13932b578d3Smrg                    (double)pATI->ReferenceNumerator /
14032b578d3Smrg                        ((double)pATI->ReferenceDenominator * 1000.0));
14132b578d3Smrg
14232b578d3Smrg#if defined(__sparc__)
14332b578d3Smrg            if ((pATI->refclk / 100000) != 286 &&
14432b578d3Smrg                (pATI->refclk / 100000) != 295)
14532b578d3Smrg            {
14632b578d3Smrg                xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
14732b578d3Smrg                    "If modes do not work on Ultra 5/10 or Blade 100/150,\n"
14832b578d3Smrg                    "\tset option \"reference_clock\" to \"28.636 MHz\""
14932b578d3Smrg                    " or \"29.5 MHz\"\n");
15032b578d3Smrg            }
15132b578d3Smrg#endif
15232b578d3Smrg
15332b578d3Smrg            if (pATI->ProgrammableClock == ATI_CLOCK_CH8398)
15432b578d3Smrg            {   /* First two are fixed */
15532b578d3Smrg                pScreenInfo->numClocks = 2;
15632b578d3Smrg                pScreenInfo->clock[0] = 25175;
15732b578d3Smrg                pScreenInfo->clock[1] = 28322;
15832b578d3Smrg            }
15932b578d3Smrg            else if (pATI->ProgrammableClock == ATI_CLOCK_INTERNAL)
16032b578d3Smrg            {
16132b578d3Smrg                /*
16232b578d3Smrg                 * The integrated PLL generates clocks as if the reference
16332b578d3Smrg                 * frequency were doubled.
16432b578d3Smrg                 */
16532b578d3Smrg                pATI->ReferenceNumerator <<= 1;
16632b578d3Smrg            }
16732b578d3Smrg}
16832b578d3Smrg
16932b578d3Smrg/*
17032b578d3Smrg * ATIClockCalculate --
17132b578d3Smrg *
17232b578d3Smrg * This function is called to generate, if necessary, the data needed for clock
17332b578d3Smrg * programming, and set clock select bits in various register values.
17432b578d3Smrg */
17532b578d3SmrgBool
17632b578d3SmrgATIClockCalculate
17732b578d3Smrg(
17832b578d3Smrg    int            iScreen,
17932b578d3Smrg    ATIPtr         pATI,
18032b578d3Smrg    ATIHWPtr       pATIHW,
18132b578d3Smrg    DisplayModePtr pMode
18232b578d3Smrg)
18332b578d3Smrg{
18432b578d3Smrg    int N, M, D;
18532b578d3Smrg    int ClockSelect, N1, MinimumGap;
18632b578d3Smrg    int Frequency, Multiple;            /* Used as temporaries */
18732b578d3Smrg
18832b578d3Smrg    /* Set default values */
18932b578d3Smrg    pATIHW->FeedbackDivider = pATIHW->ReferenceDivider = pATIHW->PostDivider = 0;
19032b578d3Smrg
19132b578d3Smrg    if (((pATI->ProgrammableClock == ATI_CLOCK_CH8398) &&
19232b578d3Smrg         (pMode->ClockIndex < 2)))
19332b578d3Smrg    {
19432b578d3Smrg        xf86DrvMsg(iScreen, X_ERROR,
19532b578d3Smrg            "First two clocks of Chrontel 8398 clock generator are fixed\n");
19632b578d3Smrg        return FALSE;
19732b578d3Smrg    }
19832b578d3Smrg
19932b578d3Smrg    {
20032b578d3Smrg        /* Generate clock programme word, using units of kHz */
20132b578d3Smrg        MinimumGap = ((unsigned int)(-1)) >> 1;
20232b578d3Smrg
20332b578d3Smrg        /* Loop through reference dividers */
20432b578d3Smrg        for (M = pATI->ClockDescriptor.MinM;
20532b578d3Smrg             M <= pATI->ClockDescriptor.MaxM;
20632b578d3Smrg             M++)
20732b578d3Smrg        {
20832b578d3Smrg            /* Loop through post-dividers */
20932b578d3Smrg            for (D = 0;  D < pATI->ClockDescriptor.NumD;  D++)
21032b578d3Smrg            {
21132b578d3Smrg                if (!pATI->ClockDescriptor.PostDividers[D])
21232b578d3Smrg                    continue;
21332b578d3Smrg
21432b578d3Smrg                /* Limit undivided VCO to maxClock */
21532b578d3Smrg                if (pATI->maxClock &&
21632b578d3Smrg                    ((pATI->maxClock / pATI->ClockDescriptor.PostDividers[D]) <
21732b578d3Smrg                     pMode->Clock))
21832b578d3Smrg                    continue;
21932b578d3Smrg
22032b578d3Smrg                /*
22132b578d3Smrg                 * Calculate closest feedback divider and apply its
22232b578d3Smrg                 * restrictions.
22332b578d3Smrg                 */
22432b578d3Smrg                Multiple = M * pATI->ReferenceDenominator *
22532b578d3Smrg                    pATI->ClockDescriptor.PostDividers[D];
22632b578d3Smrg                N = ATIDivide(pMode->Clock * Multiple,
22732b578d3Smrg                    pATI->ReferenceNumerator, 0, 0);
22832b578d3Smrg                if (N < pATI->ClockDescriptor.MinN)
22932b578d3Smrg                    N = pATI->ClockDescriptor.MinN;
23032b578d3Smrg                else if (N > pATI->ClockDescriptor.MaxN)
23132b578d3Smrg                    N = pATI->ClockDescriptor.MaxN;
23232b578d3Smrg                N -= pATI->ClockDescriptor.NAdjust;
23332b578d3Smrg                N1 = (N / pATI->ClockDescriptor.N1) * pATI->ClockDescriptor.N2;
23432b578d3Smrg                if (N > N1)
23532b578d3Smrg                    N = ATIDivide(N1 + 1, pATI->ClockDescriptor.N1, 0, 1);
23632b578d3Smrg                N += pATI->ClockDescriptor.NAdjust;
23732b578d3Smrg                N1 += pATI->ClockDescriptor.NAdjust;
23832b578d3Smrg
23932b578d3Smrg                for (;  ;  N = N1)
24032b578d3Smrg                {
24132b578d3Smrg                    /* Pick the closest setting */
24232b578d3Smrg                    Frequency = abs(ATIDivide(N * pATI->ReferenceNumerator,
24332b578d3Smrg                        Multiple, 0, 0) - pMode->Clock);
24432b578d3Smrg                    if ((Frequency < MinimumGap) ||
24532b578d3Smrg                        ((Frequency == MinimumGap) &&
24632b578d3Smrg                         (pATIHW->FeedbackDivider < N)))
24732b578d3Smrg                    {
24832b578d3Smrg                        /* Save settings */
24932b578d3Smrg                        pATIHW->FeedbackDivider = N;
25032b578d3Smrg                        pATIHW->ReferenceDivider = M;
25132b578d3Smrg                        pATIHW->PostDivider = D;
25232b578d3Smrg                        MinimumGap = Frequency;
25332b578d3Smrg                    }
25432b578d3Smrg
25532b578d3Smrg                    if (N <= N1)
25632b578d3Smrg                        break;
25732b578d3Smrg                }
25832b578d3Smrg            }
25932b578d3Smrg        }
26032b578d3Smrg
26132b578d3Smrg        Multiple = pATIHW->ReferenceDivider * pATI->ReferenceDenominator *
26232b578d3Smrg            pATI->ClockDescriptor.PostDividers[pATIHW->PostDivider];
26332b578d3Smrg        Frequency = pATIHW->FeedbackDivider * pATI->ReferenceNumerator;
26432b578d3Smrg        Frequency = ATIDivide(Frequency, Multiple, 0, 0);
26532b578d3Smrg        if (abs(Frequency - pMode->Clock) > CLOCK_TOLERANCE)
26632b578d3Smrg        {
26732b578d3Smrg            xf86DrvMsg(iScreen, X_ERROR,
26832b578d3Smrg                "Unable to programme clock %.3fMHz for mode %s.\n",
26932b578d3Smrg                (double)(pMode->Clock) / 1000.0, pMode->name);
27032b578d3Smrg            return FALSE;
27132b578d3Smrg        }
27232b578d3Smrg        pMode->SynthClock = Frequency;
27332b578d3Smrg        ClockSelect = pATI->ClockNumberToProgramme;
27432b578d3Smrg
27532b578d3Smrg        xf86ErrorFVerb(4,
27632b578d3Smrg            "\n Programming clock %d to %.3fMHz for mode %s."
27732b578d3Smrg            "  N=%d, M=%d, D=%d.\n",
27832b578d3Smrg            ClockSelect, (double)Frequency / 1000.0, pMode->name,
27932b578d3Smrg            pATIHW->FeedbackDivider, pATIHW->ReferenceDivider,
28032b578d3Smrg            pATIHW->PostDivider);
28132b578d3Smrg
28232b578d3Smrg        if (pATI->Chip >= ATI_CHIP_264VTB)
28332b578d3Smrg            ATIDSPCalculate(pATI, pATIHW, pMode);
28432b578d3Smrg    }
28532b578d3Smrg
28632b578d3Smrg    /* Set clock select bits */
28732b578d3Smrg    pATIHW->clock = ClockSelect;
28832b578d3Smrg
28932b578d3Smrg    {
29032b578d3Smrg            pATIHW->clock_cntl = CLOCK_STROBE |
29132b578d3Smrg                SetBits(ClockSelect, CLOCK_SELECT | CLOCK_DIVIDER);
29232b578d3Smrg    }
29332b578d3Smrg
29432b578d3Smrg    return TRUE;
29532b578d3Smrg}
29632b578d3Smrg
29732b578d3Smrg/*
29832b578d3Smrg * ATIClockSet --
29932b578d3Smrg *
30032b578d3Smrg * This function is called to programme a clock for the mode being set.
30132b578d3Smrg */
30232b578d3Smrgvoid
30332b578d3SmrgATIClockSet
30432b578d3Smrg(
30532b578d3Smrg    ATIPtr      pATI,
30632b578d3Smrg    ATIHWPtr    pATIHW
30732b578d3Smrg)
30832b578d3Smrg{
30932b578d3Smrg    CARD32 crtc_gen_cntl, tmp;
31032b578d3Smrg    CARD8 clock_cntl0;
31132b578d3Smrg    CARD8 tmp2;
31232b578d3Smrg    unsigned int Programme;
31332b578d3Smrg    int N = pATIHW->FeedbackDivider - pATI->ClockDescriptor.NAdjust;
31432b578d3Smrg    int M = pATIHW->ReferenceDivider - pATI->ClockDescriptor.MAdjust;
31532b578d3Smrg    int D = pATIHW->PostDivider;
31632b578d3Smrg
31732b578d3Smrg    /* Temporarily switch to accelerator mode */
31832b578d3Smrg    crtc_gen_cntl = inr(CRTC_GEN_CNTL);
31932b578d3Smrg    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
32032b578d3Smrg        outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);
32132b578d3Smrg
32232b578d3Smrg    switch (pATI->ProgrammableClock)
32332b578d3Smrg    {
32432b578d3Smrg        case ATI_CLOCK_ICS2595:
32532b578d3Smrg            clock_cntl0 = in8(CLOCK_CNTL);
32632b578d3Smrg
32732b578d3Smrg            Programme = (SetBits(pATIHW->clock, ICS2595_CLOCK) |
32832b578d3Smrg                SetBits(N, ICS2595_FB_DIV) | SetBits(D, ICS2595_POST_DIV)) ^
32932b578d3Smrg                ICS2595_TOGGLE;
33032b578d3Smrg
33132b578d3Smrg            ATIDelay(50000);            /* 50 milliseconds */
33232b578d3Smrg
33332b578d3Smrg            (void)xf86DisableInterrupts();
33432b578d3Smrg
33532b578d3Smrg            /* Send all 20 bits of programme word */
33632b578d3Smrg            while (Programme >= CLOCK_BIT)
33732b578d3Smrg            {
33832b578d3Smrg                tmp = (Programme & CLOCK_BIT) | CLOCK_STROBE;
33932b578d3Smrg                out8(CLOCK_CNTL, tmp);
34032b578d3Smrg                ATIDelay(26);           /* 26 microseconds */
34132b578d3Smrg                out8(CLOCK_CNTL, tmp | CLOCK_PULSE);
34232b578d3Smrg                ATIDelay(26);           /* 26 microseconds */
34332b578d3Smrg                Programme >>= 1;
34432b578d3Smrg            }
34532b578d3Smrg
34632b578d3Smrg            xf86EnableInterrupts();
34732b578d3Smrg
34832b578d3Smrg            /* Restore register */
34932b578d3Smrg            out8(CLOCK_CNTL, clock_cntl0 | CLOCK_STROBE);
35032b578d3Smrg            break;
35132b578d3Smrg
35232b578d3Smrg        case ATI_CLOCK_STG1703:
35332b578d3Smrg            (void)ATIGetDACCmdReg(pATI);
35432b578d3Smrg            (void)in8(M64_DAC_MASK);
35532b578d3Smrg            out8(M64_DAC_MASK, (pATIHW->clock << 1) + 0x20U);
35632b578d3Smrg            out8(M64_DAC_MASK, 0);
35732b578d3Smrg            out8(M64_DAC_MASK, SetBits(N, 0xFFU));
35832b578d3Smrg            out8(M64_DAC_MASK, SetBits(M, 0x1FU) | SetBits(D, 0xE0U));
35932b578d3Smrg            break;
36032b578d3Smrg
36132b578d3Smrg        case ATI_CLOCK_CH8398:
36232b578d3Smrg            tmp = inr(DAC_CNTL) | (DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
36332b578d3Smrg            outr(DAC_CNTL, tmp);
36432b578d3Smrg            out8(M64_DAC_WRITE, pATIHW->clock);
36532b578d3Smrg            out8(M64_DAC_DATA, SetBits(N, 0xFFU));
36632b578d3Smrg            out8(M64_DAC_DATA, SetBits(M, 0x3FU) | SetBits(D, 0xC0U));
36732b578d3Smrg            out8(M64_DAC_MASK, 0x04U);
36832b578d3Smrg            outr(DAC_CNTL, tmp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3));
36932b578d3Smrg            tmp2 = in8(M64_DAC_WRITE);
37032b578d3Smrg            out8(M64_DAC_WRITE, (tmp2 & 0x70U) | 0x80U);
37132b578d3Smrg            outr(DAC_CNTL, tmp & ~DAC_EXT_SEL_RS2);
37232b578d3Smrg            break;
37332b578d3Smrg
37432b578d3Smrg        case ATI_CLOCK_INTERNAL:
37532b578d3Smrg            /* Reset VCLK generator */
37632b578d3Smrg            ATIMach64PutPLLReg(PLL_VCLK_CNTL, pATIHW->pll_vclk_cntl);
37732b578d3Smrg
37832b578d3Smrg            /* Set post-divider */
37932b578d3Smrg            tmp2 = pATIHW->clock << 1;
38032b578d3Smrg            tmp = ATIMach64GetPLLReg(PLL_VCLK_POST_DIV);
38132b578d3Smrg            tmp &= ~(0x03U << tmp2);
38232b578d3Smrg            tmp |= SetBits(D, 0x03U) << tmp2;
38332b578d3Smrg            ATIMach64PutPLLReg(PLL_VCLK_POST_DIV, tmp);
38432b578d3Smrg
38532b578d3Smrg            /* Set extended post-divider */
38632b578d3Smrg            tmp = ATIMach64GetPLLReg(PLL_XCLK_CNTL);
38732b578d3Smrg            tmp &= ~(SetBits(1, PLL_VCLK0_XDIV) << pATIHW->clock);
38832b578d3Smrg            tmp |= SetBits(D >> 2, PLL_VCLK0_XDIV) << pATIHW->clock;
38932b578d3Smrg            ATIMach64PutPLLReg(PLL_XCLK_CNTL, tmp);
39032b578d3Smrg
39132b578d3Smrg            /* Set feedback divider */
39232b578d3Smrg            tmp = PLL_VCLK0_FB_DIV + pATIHW->clock;
39332b578d3Smrg            ATIMach64PutPLLReg(tmp, SetBits(N, 0xFFU));
39432b578d3Smrg
39532b578d3Smrg            /* End VCLK generator reset */
39632b578d3Smrg            ATIMach64PutPLLReg(PLL_VCLK_CNTL,
39732b578d3Smrg                pATIHW->pll_vclk_cntl & ~PLL_VCLK_RESET);
39832b578d3Smrg
39932b578d3Smrg            /* Reset write bit */
40032b578d3Smrg            ATIMach64AccessPLLReg(pATI, 0, FALSE);
40132b578d3Smrg            break;
40232b578d3Smrg
40332b578d3Smrg        case ATI_CLOCK_ATT20C408:
40432b578d3Smrg            (void)ATIGetDACCmdReg(pATI);
40532b578d3Smrg            tmp = in8(M64_DAC_MASK);
40632b578d3Smrg            (void)ATIGetDACCmdReg(pATI);
40732b578d3Smrg            out8(M64_DAC_MASK, tmp | 1);
40832b578d3Smrg            out8(M64_DAC_WRITE, 1);
40932b578d3Smrg            out8(M64_DAC_MASK, tmp | 9);
41032b578d3Smrg            ATIDelay(400);              /* 400 microseconds */
41132b578d3Smrg            tmp2 = (pATIHW->clock << 2) + 0x40U;
41232b578d3Smrg            out8(M64_DAC_WRITE, tmp2);
41332b578d3Smrg            out8(M64_DAC_MASK, SetBits(N, 0xFFU));
41432b578d3Smrg            out8(M64_DAC_WRITE, ++tmp2);
41532b578d3Smrg            out8(M64_DAC_MASK, SetBits(M, 0x3FU) | SetBits(D, 0xC0U));
41632b578d3Smrg            out8(M64_DAC_WRITE, ++tmp2);
41732b578d3Smrg            out8(M64_DAC_MASK, 0x77U);
41832b578d3Smrg            ATIDelay(400);              /* 400 microseconds */
41932b578d3Smrg            out8(M64_DAC_WRITE, 1);
42032b578d3Smrg            out8(M64_DAC_MASK, tmp);
42132b578d3Smrg            break;
42232b578d3Smrg
42332b578d3Smrg        case ATI_CLOCK_IBMRGB514:
42432b578d3Smrg            /*
42532b578d3Smrg             * Here, only update in-core data.  It will be written out later by
42632b578d3Smrg             * ATIRGB514Set().
42732b578d3Smrg             */
42832b578d3Smrg            tmp = (pATIHW->clock << 1) + 0x20U;
42932b578d3Smrg            pATIHW->ibmrgb514[tmp] =
43032b578d3Smrg                (SetBits(N, 0x3FU) | SetBits(D, 0xC0U)) ^ 0xC0U;
43132b578d3Smrg            pATIHW->ibmrgb514[tmp + 1] = SetBits(M, 0x3FU);
43232b578d3Smrg            break;
43332b578d3Smrg
43432b578d3Smrg        default:
43532b578d3Smrg            break;
43632b578d3Smrg    }
43732b578d3Smrg
43832b578d3Smrg    (void)in8(M64_DAC_WRITE);    /* Clear DAC counter */
43932b578d3Smrg
44032b578d3Smrg    /* Restore register */
44132b578d3Smrg    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
44232b578d3Smrg        outr(CRTC_GEN_CNTL, crtc_gen_cntl);
44332b578d3Smrg}
444