aticlock.c revision 32b578d3
1/*
2 * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of Marc Aurele La France not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  Marc Aurele La France makes no representations
11 * about the suitability of this software for any purpose.  It is provided
12 * "as-is" without express or implied warranty.
13 *
14 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
16 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23/*
24 * For all supported programmable clock generators, the driver will ignore any
25 * XF86Config clock line and programme, as needed, the clock number reserved by
26 * the BIOS for accelerated drivers.  The driver's mode initialisation routine
27 * finds integers N, M and D such that
28 *
29 *             N
30 *      R * -------  MHz
31 *           M * D
32 *
33 * best approximates the mode's clock frequency, where R is the crystal-
34 * generated reference frequency (usually 14.318 MHz).  D is a power of 2
35 * except for those integrated controllers that also offer odd dividers.
36 * Different clock generators have different restrictions on the value N, M and
37 * D can assume.  The driver contains an internal table to record these
38 * restrictions (among other things).  The resulting values of N, M and D are
39 * then encoded in a generator-specific way and used to programme the clock.
40 * The Mach64's clock divider is not used in this case.
41 */
42
43#ifdef HAVE_CONFIG_H
44#include "config.h"
45#endif
46
47#include <stdlib.h>
48#include "ati.h"
49#include "atichip.h"
50#include "atidac.h"
51#include "atidsp.h"
52#include "atimach64io.h"
53#include "atimode.h"
54#include "atiwonderio.h"
55
56/*
57 * Definitions related to programmable clock generators.
58 */
59static CARD16 ATIPostDividers[] = {1, 2, 4, 8, 16, 32, 64, 128},
60              ATI264xTPostDividers[] = {1, 2, 4, 8, 3, 0, 6, 12};
61ClockRec ATIClockDescriptors[] =
62{
63    {
64          0,   0,   0, 1, 1,
65          1,   1,   0,
66          0, NULL,
67        "Non-programmable"
68    },
69    {
70        257, 512, 257, 1, 1,
71         46,  46,   0,
72          4, ATIPostDividers,
73        "ATI 18818 or ICS 2595 or similar"
74    },
75    {
76          2, 129,   2, 1, 1,
77          8,  14,   2,
78          8, ATIPostDividers,
79        "SGS-Thompson 1703 or similar"
80    },
81    {
82         16, 263,   8, 8, 9,
83          4,  12,   2,
84          4, ATIPostDividers,
85        "Chrontel 8398 or similar"
86    },
87    {
88          2, 255,   0, 1, 1,
89         45,  45,   0,
90          4, ATI264xTPostDividers,
91        "Internal"
92    },
93    {
94          2, 257,   2, 1, 1,
95          2,  32,   2,
96          4, ATIPostDividers,
97        "AT&T 20C408 or similar"
98    },
99    {
100         65, 128,  65, 1, 1,
101          2,  14,   0,
102          4, ATIPostDividers,
103        "IBM RGB 514 or similar"
104    }
105};
106
107/*
108 * ATIClockPreInit --
109 *
110 * This function is called by ATIPreInit() and handles the XF86Config clocks
111 * line (or lack thereof).
112 */
113void
114ATIClockPreInit
115(
116    ScrnInfoPtr   pScreenInfo,
117    ATIPtr        pATI
118)
119{
120            /*
121             * Recognise supported clock generators.  This involves telling the
122             * rest of the server about it and (re-)initializing the XF86Config
123             * clocks line.
124             */
125            pScreenInfo->progClock = TRUE;
126
127            xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
128                "%s programmable clock generator detected.\n",
129                pATI->ClockDescriptor.ClockName);
130            if (pATI->ReferenceDenominator == 1)
131                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
132                    "Reference clock %.3f MHz.\n",
133                    (double)pATI->ReferenceNumerator / 1000.0);
134            else
135                xf86DrvMsg(pScreenInfo->scrnIndex, X_PROBED,
136                    "Reference clock %.6g/%d (%.3f) MHz.\n",
137                    (double)pATI->ReferenceNumerator / 1000.0,
138                    pATI->ReferenceDenominator,
139                    (double)pATI->ReferenceNumerator /
140                        ((double)pATI->ReferenceDenominator * 1000.0));
141
142#if defined(__sparc__)
143            if ((pATI->refclk / 100000) != 286 &&
144                (pATI->refclk / 100000) != 295)
145            {
146                xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
147                    "If modes do not work on Ultra 5/10 or Blade 100/150,\n"
148                    "\tset option \"reference_clock\" to \"28.636 MHz\""
149                    " or \"29.5 MHz\"\n");
150            }
151#endif
152
153            if (pATI->ProgrammableClock == ATI_CLOCK_CH8398)
154            {   /* First two are fixed */
155                pScreenInfo->numClocks = 2;
156                pScreenInfo->clock[0] = 25175;
157                pScreenInfo->clock[1] = 28322;
158            }
159            else if (pATI->ProgrammableClock == ATI_CLOCK_INTERNAL)
160            {
161                /*
162                 * The integrated PLL generates clocks as if the reference
163                 * frequency were doubled.
164                 */
165                pATI->ReferenceNumerator <<= 1;
166            }
167}
168
169/*
170 * ATIClockCalculate --
171 *
172 * This function is called to generate, if necessary, the data needed for clock
173 * programming, and set clock select bits in various register values.
174 */
175Bool
176ATIClockCalculate
177(
178    int            iScreen,
179    ATIPtr         pATI,
180    ATIHWPtr       pATIHW,
181    DisplayModePtr pMode
182)
183{
184    int N, M, D;
185    int ClockSelect, N1, MinimumGap;
186    int Frequency, Multiple;            /* Used as temporaries */
187
188    /* Set default values */
189    pATIHW->FeedbackDivider = pATIHW->ReferenceDivider = pATIHW->PostDivider = 0;
190
191    if (((pATI->ProgrammableClock == ATI_CLOCK_CH8398) &&
192         (pMode->ClockIndex < 2)))
193    {
194        xf86DrvMsg(iScreen, X_ERROR,
195            "First two clocks of Chrontel 8398 clock generator are fixed\n");
196        return FALSE;
197    }
198
199    {
200        /* Generate clock programme word, using units of kHz */
201        MinimumGap = ((unsigned int)(-1)) >> 1;
202
203        /* Loop through reference dividers */
204        for (M = pATI->ClockDescriptor.MinM;
205             M <= pATI->ClockDescriptor.MaxM;
206             M++)
207        {
208            /* Loop through post-dividers */
209            for (D = 0;  D < pATI->ClockDescriptor.NumD;  D++)
210            {
211                if (!pATI->ClockDescriptor.PostDividers[D])
212                    continue;
213
214                /* Limit undivided VCO to maxClock */
215                if (pATI->maxClock &&
216                    ((pATI->maxClock / pATI->ClockDescriptor.PostDividers[D]) <
217                     pMode->Clock))
218                    continue;
219
220                /*
221                 * Calculate closest feedback divider and apply its
222                 * restrictions.
223                 */
224                Multiple = M * pATI->ReferenceDenominator *
225                    pATI->ClockDescriptor.PostDividers[D];
226                N = ATIDivide(pMode->Clock * Multiple,
227                    pATI->ReferenceNumerator, 0, 0);
228                if (N < pATI->ClockDescriptor.MinN)
229                    N = pATI->ClockDescriptor.MinN;
230                else if (N > pATI->ClockDescriptor.MaxN)
231                    N = pATI->ClockDescriptor.MaxN;
232                N -= pATI->ClockDescriptor.NAdjust;
233                N1 = (N / pATI->ClockDescriptor.N1) * pATI->ClockDescriptor.N2;
234                if (N > N1)
235                    N = ATIDivide(N1 + 1, pATI->ClockDescriptor.N1, 0, 1);
236                N += pATI->ClockDescriptor.NAdjust;
237                N1 += pATI->ClockDescriptor.NAdjust;
238
239                for (;  ;  N = N1)
240                {
241                    /* Pick the closest setting */
242                    Frequency = abs(ATIDivide(N * pATI->ReferenceNumerator,
243                        Multiple, 0, 0) - pMode->Clock);
244                    if ((Frequency < MinimumGap) ||
245                        ((Frequency == MinimumGap) &&
246                         (pATIHW->FeedbackDivider < N)))
247                    {
248                        /* Save settings */
249                        pATIHW->FeedbackDivider = N;
250                        pATIHW->ReferenceDivider = M;
251                        pATIHW->PostDivider = D;
252                        MinimumGap = Frequency;
253                    }
254
255                    if (N <= N1)
256                        break;
257                }
258            }
259        }
260
261        Multiple = pATIHW->ReferenceDivider * pATI->ReferenceDenominator *
262            pATI->ClockDescriptor.PostDividers[pATIHW->PostDivider];
263        Frequency = pATIHW->FeedbackDivider * pATI->ReferenceNumerator;
264        Frequency = ATIDivide(Frequency, Multiple, 0, 0);
265        if (abs(Frequency - pMode->Clock) > CLOCK_TOLERANCE)
266        {
267            xf86DrvMsg(iScreen, X_ERROR,
268                "Unable to programme clock %.3fMHz for mode %s.\n",
269                (double)(pMode->Clock) / 1000.0, pMode->name);
270            return FALSE;
271        }
272        pMode->SynthClock = Frequency;
273        ClockSelect = pATI->ClockNumberToProgramme;
274
275        xf86ErrorFVerb(4,
276            "\n Programming clock %d to %.3fMHz for mode %s."
277            "  N=%d, M=%d, D=%d.\n",
278            ClockSelect, (double)Frequency / 1000.0, pMode->name,
279            pATIHW->FeedbackDivider, pATIHW->ReferenceDivider,
280            pATIHW->PostDivider);
281
282        if (pATI->Chip >= ATI_CHIP_264VTB)
283            ATIDSPCalculate(pATI, pATIHW, pMode);
284    }
285
286    /* Set clock select bits */
287    pATIHW->clock = ClockSelect;
288
289    {
290            pATIHW->clock_cntl = CLOCK_STROBE |
291                SetBits(ClockSelect, CLOCK_SELECT | CLOCK_DIVIDER);
292    }
293
294    return TRUE;
295}
296
297/*
298 * ATIClockSet --
299 *
300 * This function is called to programme a clock for the mode being set.
301 */
302void
303ATIClockSet
304(
305    ATIPtr      pATI,
306    ATIHWPtr    pATIHW
307)
308{
309    CARD32 crtc_gen_cntl, tmp;
310    CARD8 clock_cntl0;
311    CARD8 tmp2;
312    unsigned int Programme;
313    int N = pATIHW->FeedbackDivider - pATI->ClockDescriptor.NAdjust;
314    int M = pATIHW->ReferenceDivider - pATI->ClockDescriptor.MAdjust;
315    int D = pATIHW->PostDivider;
316
317    /* Temporarily switch to accelerator mode */
318    crtc_gen_cntl = inr(CRTC_GEN_CNTL);
319    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
320        outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);
321
322    switch (pATI->ProgrammableClock)
323    {
324        case ATI_CLOCK_ICS2595:
325            clock_cntl0 = in8(CLOCK_CNTL);
326
327            Programme = (SetBits(pATIHW->clock, ICS2595_CLOCK) |
328                SetBits(N, ICS2595_FB_DIV) | SetBits(D, ICS2595_POST_DIV)) ^
329                ICS2595_TOGGLE;
330
331            ATIDelay(50000);            /* 50 milliseconds */
332
333            (void)xf86DisableInterrupts();
334
335            /* Send all 20 bits of programme word */
336            while (Programme >= CLOCK_BIT)
337            {
338                tmp = (Programme & CLOCK_BIT) | CLOCK_STROBE;
339                out8(CLOCK_CNTL, tmp);
340                ATIDelay(26);           /* 26 microseconds */
341                out8(CLOCK_CNTL, tmp | CLOCK_PULSE);
342                ATIDelay(26);           /* 26 microseconds */
343                Programme >>= 1;
344            }
345
346            xf86EnableInterrupts();
347
348            /* Restore register */
349            out8(CLOCK_CNTL, clock_cntl0 | CLOCK_STROBE);
350            break;
351
352        case ATI_CLOCK_STG1703:
353            (void)ATIGetDACCmdReg(pATI);
354            (void)in8(M64_DAC_MASK);
355            out8(M64_DAC_MASK, (pATIHW->clock << 1) + 0x20U);
356            out8(M64_DAC_MASK, 0);
357            out8(M64_DAC_MASK, SetBits(N, 0xFFU));
358            out8(M64_DAC_MASK, SetBits(M, 0x1FU) | SetBits(D, 0xE0U));
359            break;
360
361        case ATI_CLOCK_CH8398:
362            tmp = inr(DAC_CNTL) | (DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
363            outr(DAC_CNTL, tmp);
364            out8(M64_DAC_WRITE, pATIHW->clock);
365            out8(M64_DAC_DATA, SetBits(N, 0xFFU));
366            out8(M64_DAC_DATA, SetBits(M, 0x3FU) | SetBits(D, 0xC0U));
367            out8(M64_DAC_MASK, 0x04U);
368            outr(DAC_CNTL, tmp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3));
369            tmp2 = in8(M64_DAC_WRITE);
370            out8(M64_DAC_WRITE, (tmp2 & 0x70U) | 0x80U);
371            outr(DAC_CNTL, tmp & ~DAC_EXT_SEL_RS2);
372            break;
373
374        case ATI_CLOCK_INTERNAL:
375            /* Reset VCLK generator */
376            ATIMach64PutPLLReg(PLL_VCLK_CNTL, pATIHW->pll_vclk_cntl);
377
378            /* Set post-divider */
379            tmp2 = pATIHW->clock << 1;
380            tmp = ATIMach64GetPLLReg(PLL_VCLK_POST_DIV);
381            tmp &= ~(0x03U << tmp2);
382            tmp |= SetBits(D, 0x03U) << tmp2;
383            ATIMach64PutPLLReg(PLL_VCLK_POST_DIV, tmp);
384
385            /* Set extended post-divider */
386            tmp = ATIMach64GetPLLReg(PLL_XCLK_CNTL);
387            tmp &= ~(SetBits(1, PLL_VCLK0_XDIV) << pATIHW->clock);
388            tmp |= SetBits(D >> 2, PLL_VCLK0_XDIV) << pATIHW->clock;
389            ATIMach64PutPLLReg(PLL_XCLK_CNTL, tmp);
390
391            /* Set feedback divider */
392            tmp = PLL_VCLK0_FB_DIV + pATIHW->clock;
393            ATIMach64PutPLLReg(tmp, SetBits(N, 0xFFU));
394
395            /* End VCLK generator reset */
396            ATIMach64PutPLLReg(PLL_VCLK_CNTL,
397                pATIHW->pll_vclk_cntl & ~PLL_VCLK_RESET);
398
399            /* Reset write bit */
400            ATIMach64AccessPLLReg(pATI, 0, FALSE);
401            break;
402
403        case ATI_CLOCK_ATT20C408:
404            (void)ATIGetDACCmdReg(pATI);
405            tmp = in8(M64_DAC_MASK);
406            (void)ATIGetDACCmdReg(pATI);
407            out8(M64_DAC_MASK, tmp | 1);
408            out8(M64_DAC_WRITE, 1);
409            out8(M64_DAC_MASK, tmp | 9);
410            ATIDelay(400);              /* 400 microseconds */
411            tmp2 = (pATIHW->clock << 2) + 0x40U;
412            out8(M64_DAC_WRITE, tmp2);
413            out8(M64_DAC_MASK, SetBits(N, 0xFFU));
414            out8(M64_DAC_WRITE, ++tmp2);
415            out8(M64_DAC_MASK, SetBits(M, 0x3FU) | SetBits(D, 0xC0U));
416            out8(M64_DAC_WRITE, ++tmp2);
417            out8(M64_DAC_MASK, 0x77U);
418            ATIDelay(400);              /* 400 microseconds */
419            out8(M64_DAC_WRITE, 1);
420            out8(M64_DAC_MASK, tmp);
421            break;
422
423        case ATI_CLOCK_IBMRGB514:
424            /*
425             * Here, only update in-core data.  It will be written out later by
426             * ATIRGB514Set().
427             */
428            tmp = (pATIHW->clock << 1) + 0x20U;
429            pATIHW->ibmrgb514[tmp] =
430                (SetBits(N, 0x3FU) | SetBits(D, 0xC0U)) ^ 0xC0U;
431            pATIHW->ibmrgb514[tmp + 1] = SetBits(M, 0x3FU);
432            break;
433
434        default:
435            break;
436    }
437
438    (void)in8(M64_DAC_WRITE);    /* Clear DAC counter */
439
440    /* Restore register */
441    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
442        outr(CRTC_GEN_CNTL, crtc_gen_cntl);
443}
444