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