1/* 2 * Copyright 2001 Ani Joshi <ajoshi@unixbox.com> 3 * 4 * XFree86 4.x driver for S3 chipsets 5 * 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and its 8 * documentation for any purpose is hereby granted without fee, provided that 9 * the above copyright notice appear in all copies and that both that copyright 10 * notice and this permission notice appear in supporting documentation and 11 * that the name of Ani Joshi not be used in advertising or 12 * publicity pertaining to distribution of the software without specific, 13 * written prior permission. Ani Joshi makes no representations 14 * about the suitability of this software for any purpose. It is provided 15 * "as-is" without express or implied warranty. 16 * 17 * ANI JOSHI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 19 * EVENT SHALL ANI JOSHI BE LIABLE FOR ANY SPECIAL, INDIRECT OR 20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 23 * PERFORMANCE OF THIS SOFTWARE. 24 * 25 * 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include "xf86.h" 33#include "xf86_OSproc.h" 34 35#include "compiler.h" 36 37#include "s3.h" 38#include "s3_reg.h" 39 40/* this is really quite dumb */ 41Bool S3Trio64DACProbe(ScrnInfoPtr pScrn) 42{ 43 S3Ptr pS3 = S3PTR(pScrn); 44 45 if (!S3_TRIO_SERIES()) 46 return FALSE; 47 48 RamDacInit(pScrn, pS3->RamDacRec); 49 50 pS3->RamDac = RamDacHelperCreateInfoRec(); 51 pS3->RamDac->RamDacType = TRIO64_RAMDAC; 52 53 return TRUE; 54} 55 56 57void S3Trio64DAC_Save(ScrnInfoPtr pScrn) 58{ 59 S3Ptr pS3 = S3PTR(pScrn); 60 S3RegPtr save = &pS3->SavedRegs; 61 62 save->dacregs[0] = inb(0x3cc); 63 64 outb(0x3c4, 0x08); 65 save->dacregs[1] = inb(0x3c5); 66 outb(0x3c5, 0x06); 67 68 outb(0x3c4, 0x09); 69 save->dacregs[2] = inb(0x3c5); 70 outb(0x3c4, 0x0a); 71 save->dacregs[3] = inb(0x3c5); 72 outb(0x3c4, 0x0b); 73 save->dacregs[4] = inb(0x3c5); 74 outb(0x3c4, 0x0d); 75 save->dacregs[5] = inb(0x3c5); 76 outb(0x3c4, 0x15); 77 save->dacregs[6] = inb(0x3c5) & 0xfe; 78 outb(0x3c5, save->dacregs[6]); 79 80 outb(0x3c4, 0x18); 81 save->dacregs[7] = inb(0x3c5); 82 outb(0x3c4, 0x10); 83 save->dacregs[8] = inb(0x3c5); 84 outb(0x3c4, 0x11); 85 save->dacregs[9] = inb(0x3c5); 86 outb(0x3c4, 0x12); 87 save->dacregs[10] = inb(0x3c5); 88 outb(0x3c4, 0x13); 89 save->dacregs[11] = inb(0x3c5); 90 outb(0x3c4, 0x1a); 91 save->dacregs[12] = inb(0x3c5); 92 outb(0x3c4, 0x1b); 93 save->dacregs[13] = inb(0x3c5); 94 95 if (pS3->Chipset == PCI_CHIP_AURORA64VP) { 96 int i; 97 98 for (i=0x1a; i <= 0x6f; i++) { 99 outb(0x3c4, i); 100 save->dacregs[i] = inb(0x3c5); 101 } 102 } 103 104 outb(0x3c4, 0x08); 105 outb(0x3c5, 0x00); 106} 107 108 109void S3Trio64DAC_Restore(ScrnInfoPtr pScrn) 110{ 111 S3Ptr pS3 = S3PTR(pScrn); 112 S3RegPtr restore = &pS3->SavedRegs; 113 unsigned char tmp; 114 115 outb(0x3c2, restore->dacregs[0]); 116 outb(0x3c4, 0x08); 117 outb(0x3c5, 0x06); 118 119 outb(0x3c4, 0x09); 120 outb(0x3c5, restore->dacregs[2]); 121 outb(0x3c4, 0x0a); 122 outb(0x3c5, restore->dacregs[3]); 123 outb(0x3c4, 0x0b); 124 outb(0x3c5, restore->dacregs[4]); 125 outb(0x3c4, 0x0d); 126 outb(0x3c5, restore->dacregs[5]); 127 128 outb(0x3c4, 0x10); 129 outb(0x3c5, restore->dacregs[8]); 130 outb(0x3c4, 0x11); 131 outb(0x3c5, restore->dacregs[9]); 132 outb(0x3c4, 0x12); 133 outb(0x3c5, restore->dacregs[10]); 134 outb(0x3c4, 0x13); 135 outb(0x3c5, restore->dacregs[11]); 136 outb(0x3c4, 0x1a); 137 outb(0x3c5, restore->dacregs[12]); 138 outb(0x3c4, 0x1b); 139 outb(0x3c5, restore->dacregs[13]); 140 outb(0x3c4, 0x15); 141 tmp = inb(0x3c5); 142 outb(0x3c4, tmp & ~0x20); 143 outb(0x3c4, tmp | 0x20); 144 outb(0x3c4, tmp & ~0x20); 145 146 outb(0x3c4, 0x15); 147 outb(0x3c5, restore->dacregs[6]); 148 outb(0x3c4, 0x18); 149 outb(0x3c5, restore->dacregs[7]); 150 151 if (pS3->Chipset == PCI_CHIP_AURORA64VP) { 152 int i; 153 154 for (i = 0x1a; i <= 0x6f; i++) { 155 outb(0x3c4, i); 156 outb(0x3c5, restore->dacregs[i]); 157 } 158 } 159 160 outb(0x3c4, 0x08); 161 outb(0x3c5, restore->dacregs[1]); 162} 163 164 165static void 166S3TrioCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2, 167 int max_n2, long freq_min, long freq_max, 168 unsigned char *mdiv, unsigned char *ndiv) 169{ 170 double ffreq, ffreq_min, ffreq_max; 171 double div, diff, best_diff; 172 unsigned int m; 173 unsigned char n1, n2, best_n1=18, best_n2=2, best_m=127; 174 175#define BASE_FREQ 14.31818 176 ffreq = freq / 1000.0 / BASE_FREQ; 177 ffreq_min = freq_min / 1000.0 / BASE_FREQ; 178 ffreq_max = freq_max / 1000.0 / BASE_FREQ; 179 180 if (ffreq < ffreq_min / (1<<max_n2)) { 181 ErrorF("invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n", 182 ffreq*BASE_FREQ, ffreq_min*BASE_FREQ/(1<<max_n2)); 183 ffreq = ffreq_min / (1<<max_n2); 184 } 185 if (ffreq > ffreq_max / (1<<min_n2)) { 186 ErrorF("invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n", 187 ffreq*BASE_FREQ, ffreq_max*BASE_FREQ/(1<<min_n2)); 188 ffreq = ffreq_max / (1<<min_n2); 189 } 190 191 best_diff = ffreq; 192 193 for(n2=min_n2; n2<=max_n2; n2++) { 194 for(n1=min_n1+2; n1<=max_n1+2; n1++) { 195 m = (int)(ffreq*n1*(1<<n2)+0.5); 196 if (m<min_m+2 || m > 127+2) 197 continue; 198 div = (double)(m)/(double)(n1); 199 if ((div >= ffreq_min) && 200 (div <= ffreq_max)) { 201 diff = ffreq - div / (1<<n2); 202 if (diff < 0.0) 203 diff = -diff; 204 if (diff < best_diff) { 205 best_diff = diff; 206 best_m = m; 207 best_n1 = n1; 208 best_n2 = n2; 209 } 210 } 211 } 212 } 213 214 if (max_n1 == 63) 215 *ndiv = (best_n1 - 2) | (best_n2 << 6); 216 else 217 *ndiv = (best_n1 - 2) | (best_n2 << 5); 218 *mdiv = best_m - 2; 219} 220 221 222static void S3TrioSetPLL(ScrnInfoPtr pScrn, int clk, unsigned char m, 223 unsigned char n) 224{ 225 unsigned char tmp; 226 227 if (clk < 2) { 228 tmp = inb(0x3cc); 229 outb(0x3c2, (tmp & 0xf3) | (clk << 2)); 230 } else { 231 tmp = inb(0x3cc); 232 outb(0x3c2, tmp | 0x0c); 233 234 outb(0x3c4, 0x08); 235 outb(0x3c5, 0x06); /* unlock extended CR9-18 */ 236 237 outb(0x3c4, 0x12); /* write N1 and N2 to DCLK PLL */ 238 outb(0x3c5, n); 239 outb(0x3c4, 0x13); /* write M to DCLK PLL */ 240 outb(0x3c5, m); 241 242#if 0 243/* this code was in previous driver version but it was never called. 244 So I decide to comment it. */ 245 outb(0x3c4, 0x10); 246 outb(0x3c5, n); 247 outb(0x3c4, 0x11); 248 outb(0x3c5, m); */ 249 250 outb(0x3c4, 0x1a); 251 outb(0x3c5, n); 252#endif 253 /* Toggle cr15_5 by sequence 0->1->0 to immediately apply 254 new PLL parameters */ 255 outb(0x3c4, 0x15); 256 tmp = inb(0x3c5) & ~0x20; 257 outb(0x3c5, tmp); 258 outb(0x3c5, tmp | 0x20); 259 outb(0x3c5, tmp); 260 261 outb(0x3c4, 0x08); 262 outb(0x3c5, 0x00); /* lock em */ 263 } 264} 265 266 267static void S3TrioSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m, 268 int min_n1, int max_n1, int min_n2, int max_n2, 269 int pll_type, long freq_min, long freq_max) 270{ 271 unsigned char m, n; 272 273 S3TrioCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2, 274 freq_min, freq_max, &m, &n); 275 276 /* XXX for pll_type == TRIO */ 277 S3TrioSetPLL(pScrn, clk, m, n); 278} 279 280void S3Trio64DAC_PreInit(ScrnInfoPtr pScrn) 281{ 282 S3Ptr pS3 = S3PTR(pScrn); 283 unsigned char SR8, SR27; 284 int m, n, n1, n2, mclk; 285 286 outb(0x3c4, 0x08); 287 SR8 = inb(0x3c5); 288 outb(0x3c5, 0x06); 289 290 outb(0x3c4, 0x11); 291 m = inb(0x3c5); 292 outb(0x3c4, 0x10); 293 n = inb(0x3c5); 294 295 m &= 0x7f; 296 n1 = n & 0x1f; 297 n2 = (n >> 5) & 0x03; 298 mclk = ((1431818 * (m+2)) / (n1+2) / (1<<n2)+50)/100; 299 if (pS3->Chipset == PCI_CHIP_AURORA64VP) { 300 outb(0x3c4, 0x27); 301 SR27 = inb(0x3c5); 302 outb(0x3c4, 0x28); 303 (void) inb(0x3c5); 304 mclk /= ((SR27 >> 2) & 0x03) + 1; 305 } 306 pS3->mclk = mclk; 307 308 outb(0x3c4, 0x08); 309 outb(0x3c5, SR8); 310 311 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f Mhz\n", 312 mclk / 1000.0); 313} 314 315 316void S3Trio64DAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode) 317{ 318 S3Ptr pS3 = S3PTR(pScrn); 319 int pixmux=0, invert_vclk=0, sr8, sr15, sr18, cr33; 320 unsigned char blank, tmp; 321 322 if (pS3->Chipset == PCI_CHIP_AURORA64VP) 323 S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 63, 0, 3, 2, 324 135000, 270000); 325 else if (pS3->Chipset == PCI_CHIP_TRIO64V2_DXGX) 326 S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 31, 0, 3, 2, 327 170000, 340000); 328 else 329 S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 31, 0, 3, 2, 330 135000, 270000); 331 332 outb(0x3c4, 0x01); 333 blank = inb(0x3c5); 334 outb(0x3c5, blank | 0x20); /* blank the screen */ 335 336 outb(0x3c4, 0x08); 337 sr8 = inb(0x3c5); 338 outb(0x3c5, 0x06); /* unlock extended sequenser register */ 339 340 outb(0x3c4, 0x0d); 341 tmp = inb(0x3c5) & ~0x01; 342 outb(0x3c5, tmp); /* VCLK, HSYNC, VSYNC are outputs */ 343 344 outb(0x3c4, 0x15); 345 sr15 = inb(0x3c5) & ~0x10; 346 347 outb(0x3c4, 0x18); 348 sr18 = inb(0x3c5) & ~0x80; 349 outb(pS3->vgaCRIndex, 0x33); 350 cr33 = inb(pS3->vgaCRReg) & ~0x28; 351 352 /* ! pixmux */ 353 switch (pScrn->depth) { 354 case 8: 355 break; 356 case 15: 357 cr33 |= 0x08; 358 pixmux = 0x30; 359 break; 360 case 16: 361 cr33 |= 0x08; 362 pixmux = 0x50; 363 break; 364 case 24: 365 case 32: 366 pixmux = 0xd0; 367 break; 368 } 369 370 outb(pS3->vgaCRReg, cr33); 371 372 outb(pS3->vgaCRIndex, 0x67); 373 WaitVSync(); 374 outb(pS3->vgaCRReg, pixmux | invert_vclk); 375 376 outb(0x3c4, 0x15); 377 outb(0x3c5, sr15); 378 outb(0x3c4, 0x18); 379 outb(0x3c5, sr18); 380 381 if (pS3->Chipset == PCI_CHIP_AURORA64VP) { 382 outb(0x3c4, 0x28); 383 outb(0x3c5, 0x00); 384 } 385 386 outb(0x3c4, 0x08); 387 outb(0x3c5, sr8); 388 389 outb(0x3c4, 0x01); 390 outb(0x3c5, blank); /* unblank the screen */ 391} 392