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