Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi (at) 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  */
     59 static CARD16 ATIPostDividers[] = {1, 2, 4, 8, 16, 32, 64, 128},
     60               ATI264xTPostDividers[] = {1, 2, 4, 8, 3, 0, 6, 12};
     61 ClockRec 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  */
    113 void
    114 ATIClockPreInit
    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  */
    175 Bool
    176 ATIClockCalculate
    177 (
    178     int            iScreen,
    179     ATIPtr         pATI,
    180     ATIHWPtr       pATIHW,
    181     DisplayModePtr pMode
    182 )
    183 {
    184     int ClockSelect;
    185 
    186     /* Set default values */
    187     pATIHW->FeedbackDivider = pATIHW->ReferenceDivider = pATIHW->PostDivider = 0;
    188 
    189     if (((pATI->ProgrammableClock == ATI_CLOCK_CH8398) &&
    190          (pMode->ClockIndex < 2)))
    191     {
    192         xf86DrvMsg(iScreen, X_ERROR,
    193             "First two clocks of Chrontel 8398 clock generator are fixed\n");
    194         return FALSE;
    195     }
    196 
    197     {
    198         /* Generate clock programme word, using units of kHz */
    199         int MinimumGap = ((unsigned int)(-1)) >> 1;
    200         int Frequency, Multiple;            /* Used as temporaries */
    201 
    202         /* Loop through reference dividers */
    203         for (int M = pATI->ClockDescriptor.MinM;
    204              M <= pATI->ClockDescriptor.MaxM;
    205              M++)
    206         {
    207             /* Loop through post-dividers */
    208             for (int D = 0;  D < pATI->ClockDescriptor.NumD;  D++)
    209             {
    210                 int N, N1;
    211 
    212                 if (!pATI->ClockDescriptor.PostDividers[D])
    213                     continue;
    214 
    215                 /* Limit undivided VCO to maxClock */
    216                 if (pATI->maxClock &&
    217                     ((pATI->maxClock / pATI->ClockDescriptor.PostDividers[D]) <
    218                      pMode->Clock))
    219                     continue;
    220 
    221                 /*
    222                  * Calculate closest feedback divider and apply its
    223                  * restrictions.
    224                  */
    225                 Multiple = M * pATI->ReferenceDenominator *
    226                     pATI->ClockDescriptor.PostDividers[D];
    227                 N = ATIDivide(pMode->Clock * Multiple,
    228                     pATI->ReferenceNumerator, 0, 0);
    229                 if (N < pATI->ClockDescriptor.MinN)
    230                     N = pATI->ClockDescriptor.MinN;
    231                 else if (N > pATI->ClockDescriptor.MaxN)
    232                     N = pATI->ClockDescriptor.MaxN;
    233                 N -= pATI->ClockDescriptor.NAdjust;
    234                 N1 = (N / pATI->ClockDescriptor.N1) * pATI->ClockDescriptor.N2;
    235                 if (N > N1)
    236                     N = ATIDivide(N1 + 1, pATI->ClockDescriptor.N1, 0, 1);
    237                 N += pATI->ClockDescriptor.NAdjust;
    238                 N1 += pATI->ClockDescriptor.NAdjust;
    239 
    240                 for (;  ;  N = N1)
    241                 {
    242                     /* Pick the closest setting */
    243                     Frequency = abs(ATIDivide(N * pATI->ReferenceNumerator,
    244                         Multiple, 0, 0) - pMode->Clock);
    245                     if ((Frequency < MinimumGap) ||
    246                         ((Frequency == MinimumGap) &&
    247                          (pATIHW->FeedbackDivider < N)))
    248                     {
    249                         /* Save settings */
    250                         pATIHW->FeedbackDivider = N;
    251                         pATIHW->ReferenceDivider = M;
    252                         pATIHW->PostDivider = D;
    253                         MinimumGap = Frequency;
    254                     }
    255 
    256                     if (N <= N1)
    257                         break;
    258                 }
    259             }
    260         }
    261 
    262         Multiple = pATIHW->ReferenceDivider * pATI->ReferenceDenominator *
    263             pATI->ClockDescriptor.PostDividers[pATIHW->PostDivider];
    264         Frequency = pATIHW->FeedbackDivider * pATI->ReferenceNumerator;
    265         Frequency = ATIDivide(Frequency, Multiple, 0, 0);
    266         if (abs(Frequency - pMode->Clock) > CLOCK_TOLERANCE)
    267         {
    268             xf86DrvMsg(iScreen, X_ERROR,
    269                 "Unable to programme clock %.3fMHz for mode %s.\n",
    270                 (double)(pMode->Clock) / 1000.0, pMode->name);
    271             return FALSE;
    272         }
    273         pMode->SynthClock = Frequency;
    274         ClockSelect = pATI->ClockNumberToProgramme;
    275 
    276         xf86ErrorFVerb(4,
    277             "\n Programming clock %d to %.3fMHz for mode %s."
    278             "  N=%d, M=%d, D=%d.\n",
    279             ClockSelect, (double)Frequency / 1000.0, pMode->name,
    280             pATIHW->FeedbackDivider, pATIHW->ReferenceDivider,
    281             pATIHW->PostDivider);
    282 
    283         if (pATI->Chip >= ATI_CHIP_264VTB)
    284             ATIDSPCalculate(pATI, pATIHW, pMode);
    285     }
    286 
    287     /* Set clock select bits */
    288     pATIHW->clock = ClockSelect;
    289 
    290     {
    291             pATIHW->clock_cntl = CLOCK_STROBE |
    292                 SetBits(ClockSelect, CLOCK_SELECT | CLOCK_DIVIDER);
    293     }
    294 
    295     return TRUE;
    296 }
    297 
    298 /*
    299  * ATIClockSet --
    300  *
    301  * This function is called to programme a clock for the mode being set.
    302  */
    303 void
    304 ATIClockSet
    305 (
    306     ATIPtr      pATI,
    307     ATIHWPtr    pATIHW
    308 )
    309 {
    310     CARD32 crtc_gen_cntl, tmp;
    311     CARD8 clock_cntl0;
    312     CARD8 tmp2;
    313     unsigned int Programme;
    314     int N = pATIHW->FeedbackDivider - pATI->ClockDescriptor.NAdjust;
    315     int M = pATIHW->ReferenceDivider - pATI->ClockDescriptor.MAdjust;
    316     int D = pATIHW->PostDivider;
    317 
    318     /* Temporarily switch to accelerator mode */
    319     crtc_gen_cntl = inr(CRTC_GEN_CNTL);
    320     if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
    321         outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);
    322 
    323     switch (pATI->ProgrammableClock)
    324     {
    325         case ATI_CLOCK_ICS2595:
    326             clock_cntl0 = in8(CLOCK_CNTL);
    327 
    328             Programme = (SetBits(pATIHW->clock, ICS2595_CLOCK) |
    329                 SetBits(N, ICS2595_FB_DIV) | SetBits(D, ICS2595_POST_DIV)) ^
    330                 ICS2595_TOGGLE;
    331 
    332             ATIDelay(50000);            /* 50 milliseconds */
    333 
    334             /* Send all 20 bits of programme word */
    335             while (Programme >= CLOCK_BIT)
    336             {
    337                 tmp = (Programme & CLOCK_BIT) | CLOCK_STROBE;
    338                 out8(CLOCK_CNTL, tmp);
    339                 ATIDelay(26);           /* 26 microseconds */
    340                 out8(CLOCK_CNTL, tmp | CLOCK_PULSE);
    341                 ATIDelay(26);           /* 26 microseconds */
    342                 Programme >>= 1;
    343             }
    344 
    345             /* Restore register */
    346             out8(CLOCK_CNTL, clock_cntl0 | CLOCK_STROBE);
    347             break;
    348 
    349         case ATI_CLOCK_STG1703:
    350             (void)ATIGetDACCmdReg(pATI);
    351             (void)in8(M64_DAC_MASK);
    352             out8(M64_DAC_MASK, (pATIHW->clock << 1) + 0x20U);
    353             out8(M64_DAC_MASK, 0);
    354             out8(M64_DAC_MASK, SetBits(N, 0xFFU));
    355             out8(M64_DAC_MASK, SetBits(M, 0x1FU) | SetBits(D, 0xE0U));
    356             break;
    357 
    358         case ATI_CLOCK_CH8398:
    359             tmp = inr(DAC_CNTL) | (DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
    360             outr(DAC_CNTL, tmp);
    361             out8(M64_DAC_WRITE, pATIHW->clock);
    362             out8(M64_DAC_DATA, SetBits(N, 0xFFU));
    363             out8(M64_DAC_DATA, SetBits(M, 0x3FU) | SetBits(D, 0xC0U));
    364             out8(M64_DAC_MASK, 0x04U);
    365             outr(DAC_CNTL, tmp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3));
    366             tmp2 = in8(M64_DAC_WRITE);
    367             out8(M64_DAC_WRITE, (tmp2 & 0x70U) | 0x80U);
    368             outr(DAC_CNTL, tmp & ~DAC_EXT_SEL_RS2);
    369             break;
    370 
    371         case ATI_CLOCK_INTERNAL:
    372             /* Reset VCLK generator */
    373             ATIMach64PutPLLReg(PLL_VCLK_CNTL, pATIHW->pll_vclk_cntl);
    374 
    375             /* Set post-divider */
    376             tmp2 = pATIHW->clock << 1;
    377             tmp = ATIMach64GetPLLReg(PLL_VCLK_POST_DIV);
    378             tmp &= ~(0x03U << tmp2);
    379             tmp |= SetBits(D, 0x03U) << tmp2;
    380             ATIMach64PutPLLReg(PLL_VCLK_POST_DIV, tmp);
    381 
    382             /* Set extended post-divider */
    383             tmp = ATIMach64GetPLLReg(PLL_XCLK_CNTL);
    384             tmp &= ~(SetBits(1, PLL_VCLK0_XDIV) << pATIHW->clock);
    385             tmp |= SetBits(D >> 2, PLL_VCLK0_XDIV) << pATIHW->clock;
    386             ATIMach64PutPLLReg(PLL_XCLK_CNTL, tmp);
    387 
    388             /* Set feedback divider */
    389             tmp = PLL_VCLK0_FB_DIV + pATIHW->clock;
    390             ATIMach64PutPLLReg(tmp, SetBits(N, 0xFFU));
    391 
    392             /* End VCLK generator reset */
    393             ATIMach64PutPLLReg(PLL_VCLK_CNTL,
    394                 pATIHW->pll_vclk_cntl & ~PLL_VCLK_RESET);
    395 
    396             /* Reset write bit */
    397             ATIMach64AccessPLLReg(pATI, 0, FALSE);
    398             break;
    399 
    400         case ATI_CLOCK_ATT20C408:
    401             (void)ATIGetDACCmdReg(pATI);
    402             tmp = in8(M64_DAC_MASK);
    403             (void)ATIGetDACCmdReg(pATI);
    404             out8(M64_DAC_MASK, tmp | 1);
    405             out8(M64_DAC_WRITE, 1);
    406             out8(M64_DAC_MASK, tmp | 9);
    407             ATIDelay(400);              /* 400 microseconds */
    408             tmp2 = (pATIHW->clock << 2) + 0x40U;
    409             out8(M64_DAC_WRITE, tmp2);
    410             out8(M64_DAC_MASK, SetBits(N, 0xFFU));
    411             out8(M64_DAC_WRITE, ++tmp2);
    412             out8(M64_DAC_MASK, SetBits(M, 0x3FU) | SetBits(D, 0xC0U));
    413             out8(M64_DAC_WRITE, ++tmp2);
    414             out8(M64_DAC_MASK, 0x77U);
    415             ATIDelay(400);              /* 400 microseconds */
    416             out8(M64_DAC_WRITE, 1);
    417             out8(M64_DAC_MASK, tmp);
    418             break;
    419 
    420         case ATI_CLOCK_IBMRGB514:
    421             /*
    422              * Here, only update in-core data.  It will be written out later by
    423              * ATIRGB514Set().
    424              */
    425             tmp = (pATIHW->clock << 1) + 0x20U;
    426             pATIHW->ibmrgb514[tmp] =
    427                 (SetBits(N, 0x3FU) | SetBits(D, 0xC0U)) ^ 0xC0U;
    428             pATIHW->ibmrgb514[tmp + 1] = SetBits(M, 0x3FU);
    429             break;
    430 
    431         default:
    432             break;
    433     }
    434 
    435     (void)in8(M64_DAC_WRITE);    /* Clear DAC counter */
    436 
    437     /* Restore register */
    438     if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
    439         outr(CRTC_GEN_CNTL, crtc_gen_cntl);
    440 }
    441