s3_GENDAC.c revision 96cdd0b9
1/* 2 * Copyright (c) 2009 KIYOHARA Takashi 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27#ifdef HAVE_CONFIG_H 28#include "config.h" 29#endif 30 31#include "xf86.h" 32#include "xf86_OSproc.h" 33 34#include "compiler.h" 35 36#include "s3.h" 37 38#define GENDAC_INDEX 0x3C8 39#define GENDAC_DATA 0x3C9 40 41 42static void S3GENDACSetClock(ScrnInfoPtr, long, int, int, int, int, int, int, 43 int, long, long); 44static void S3GENDACCalcClock(long, int, int, int, int, int, long, long, 45 unsigned char *, unsigned char *); 46static void S3GENDACSetPLL(ScrnInfoPtr, int, unsigned char, unsigned char); 47 48 49static void xf86dactopel(void); 50 51static void 52xf86dactopel() 53{ 54 outb(0x3C8,0); 55 return; 56} 57 58 59Bool S3GENDACProbe(ScrnInfoPtr pScrn) 60{ 61 /* probe for S3 GENDAC/SDAC */ 62 /* 63 * S3 GENDAC and SDAC have two fixed read only PLL clocks 64 * CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61 65 * CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62 66 * which can be used to detect GENDAC and SDAC since there is no chip-id 67 * for the GENDAC. 68 * 69 * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values 70 * for CLK0 f0 and f1 always returns 0x7f 71 * (but is documented "read only") 72 */ 73 74 S3Ptr pS3 = S3PTR(pScrn); 75 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 76 unsigned char saveCR55, saveCR45, saveCR43, savelut[6]; 77 unsigned int i; /* don't use signed int, UW2.0 compiler bug */ 78 long clock01, clock23; 79 int found = 0; 80 81 if (!S3_864_SERIES()) /* need? */ 82 return FALSE; 83 84 outb(vgaCRIndex, 0x43); 85 saveCR43 = inb(vgaCRReg); 86 outb(vgaCRReg, saveCR43 & ~0x02); 87 88 outb(vgaCRIndex, 0x45); 89 saveCR45 = inb(vgaCRReg); 90 outb(vgaCRReg, saveCR45 & ~0x20); 91 92 outb(vgaCRIndex, 0x55); 93 saveCR55 = inb(vgaCRReg); 94 outb(vgaCRReg, saveCR55 & ~1); 95 96 outb(0x3c7,0); 97 for(i = 0; i < 2 * 3; i++) /* save first two LUT entries */ 98 savelut[i] = inb(0x3c9); 99 outb(0x3c8,0); 100 for(i = 0; i < 2 * 3; i++) /* set first two LUT entries to zero */ 101 outb(0x3c9, 0); 102 103 outb(vgaCRIndex, 0x55); 104 outb(vgaCRReg, saveCR55 | 1); 105 106 outb(0x3c7,0); 107 for(i = clock01 = 0; i < 4; i++) 108 clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff); 109 for(i = clock23 = 0; i < 4; i++) 110 clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff); 111 112 outb(vgaCRIndex, 0x55); 113 outb(vgaCRReg, saveCR55 & ~1); 114 115 outb(0x3c8,0); 116 for(i = 0; i < 2 * 3; i++) /* restore first two LUT entries */ 117 outb(0x3c9, savelut[i]); 118 119 outb(vgaCRIndex, 0x55); 120 outb(vgaCRReg, saveCR55); 121 122 if (clock01 == 0x28613d62 || 123 (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) { 124 xf86dactopel(); 125 inb(0x3c6); 126 inb(0x3c6); 127 inb(0x3c6); 128 129 /* the fourth read will show the SDAC chip ID and revision */ 130 if (((i = inb(0x3c6)) & 0xf0) == 0x70) 131 found = SDAC_RAMDAC; 132 else 133 found = GENDAC_RAMDAC; 134 saveCR43 &= ~0x02; 135 saveCR45 &= ~0x20; 136 xf86dactopel(); 137 } 138 139 outb(vgaCRIndex, 0x45); 140 outb(vgaCRReg, saveCR45); 141 142 outb(vgaCRIndex, 0x43); 143 outb(vgaCRReg, saveCR43); 144 145 if (found) { 146 RamDacInit(pScrn, pS3->RamDacRec); 147 pS3->RamDac = RamDacHelperCreateInfoRec(); 148 pS3->RamDac->RamDacType = found; 149 return TRUE; 150 } 151 return FALSE; 152} 153 154void S3GENDAC_PreInit(ScrnInfoPtr pScrn) 155{ 156 S3Ptr pS3 = S3PTR(pScrn); 157 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 158 unsigned char saveCR55; 159 int m, n, n1, n2, mclk; 160 161 outb(vgaCRIndex, 0x55); 162 saveCR55 = inb(vgaCRReg); 163 outb(vgaCRReg, saveCR55 | 1); 164 165 outb(0x3C7, 10); /* read MCLK */ 166 m = inb(0x3C9); 167 n = inb(0x3C9); 168 169 outb(vgaCRIndex, 0x55); 170 outb(vgaCRReg, saveCR55); 171 172 m &= 0x7f; 173 n1 = n & 0x1f; 174 n2 = (n >> 5) & 0x03; 175 mclk = ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100; 176 177 pS3->mclk = mclk; 178 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n", 179 mclk / 1000.0); 180} 181 182void S3GENDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode) 183{ 184 S3Ptr pS3 = S3PTR(pScrn); 185 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 186 int daccomm = 0; /* GENDAC command */ 187 unsigned char blank, tmp; 188 189 S3GENDACSetClock(pScrn, mode->Clock * (pScrn->depth >> 3), 2, 1, 1, 31, 190 0, 3, 1, 100000, 250000); 191 192 outb(0x3C4, 1); 193 blank = inb(0x3C5); 194 outb(0x3C5, blank | 0x20); /* blank the screen */ 195 196 switch (pScrn->depth) 197 { 198 case 8: /* 8-bit color, 1VCLK/pixel */ 199 break; 200 201 case 15: /* 15-bit color, 2VCLK/pixel */ 202 daccomm = 0x20; 203 break; 204 205 case 16: /* 16-bit color, 2VCLK/pixel */ 206 daccomm = 0x60; 207 break; 208 209 case 32: /* 32-bit color, 3VCLK/pixel */ 210 daccomm = 0x40; 211 } 212 213 outb(vgaCRIndex, 0x55); 214 tmp = inb(vgaCRReg) | 1; 215 outb(vgaCRReg, tmp); 216 217 outb(0x3c6, daccomm); /* set GENDAC mux mode */ 218 219 outb(vgaCRIndex, 0x55); 220 tmp = inb(vgaCRReg) & ~1; 221 outb(vgaCRReg, tmp); 222 223 outb(0x3C4, 1); 224 outb(0x3C5, blank); /* unblank the screen */ 225} 226 227void S3SDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode) 228{ 229 S3Ptr pS3 = S3PTR(pScrn); 230 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 231 int pixmux = 0; /* SDAC command and CR67 */ 232 int blank_delay = 0; /* CR6D */ 233 int invert_vclk = 0; /* CR66 bit 0 */ 234 unsigned char blank, tmp; 235 236#if 0 237 S3GENDACSetClock(pScrn, 238 (pScrn->depth == 32) ? mode->Clock * 2 : mode->Clock, 239 2, 1, 1, 31, 0, 3, 1, 100000, 250000); 240#else 241 /* XXXX: for prep */ 242 long freq; 243 244 switch (pScrn->depth) { 245 case 32: 246 freq = mode->Clock * 2; /* XXXX: frem xfree86 3.x */ 247 break; 248 case 16: 249 freq = mode->Clock / 2; 250 break; 251 default: 252 freq = mode->Clock; 253 break; 254 } 255 S3GENDACSetClock(pScrn, freq, 256 2, 1, 1, 31, 0, 3, 1, 100000, 250000); 257#endif 258 259 outb(vgaCRIndex, 0x42);/* select the clock */ 260 tmp = inb(vgaCRReg) & 0xf0; 261 outb(vgaCRReg, tmp | 0x02); 262 usleep(150000); 263 264 outb(0x3C4, 1); 265 blank = inb(0x3C5); 266 outb(0x3C5, blank | 0x20); /* blank the screen */ 267 268 switch (pScrn->depth) 269 { 270 case 8: /* 8-bit color, 1 VCLK/pixel */ 271 pixmux = 0x10; 272 blank_delay = 2; 273 invert_vclk = 1; 274 break; 275 276 case 15: /* 15-bit color, 1VCLK/pixel */ 277 pixmux = 0x30; 278 blank_delay = 2; 279 break; 280 281 case 16: /* 16-bit color, 1VCLK/pixel */ 282 pixmux = 0x50; 283 blank_delay = 2; 284 break; 285 286 case 32: /* 32-bit color, 2VCLK/pixel */ 287 pixmux = 0x70; 288 blank_delay = 2; 289 } 290 291 outb(vgaCRIndex, 0x55); 292 tmp = inb(vgaCRReg) | 1; 293 outb(vgaCRReg, tmp); 294 295 outb(vgaCRIndex, 0x67); 296 outb(vgaCRReg, pixmux | invert_vclk); /* set S3 mux mode */ 297 outb(0x3c6, pixmux); /* set SDAC mux mode */ 298 299 outb(vgaCRIndex, 0x6D); 300 outb(vgaCRReg, blank_delay); /* set blank delay */ 301 302 outb(vgaCRIndex, 0x55); 303 tmp = inb(vgaCRReg) & ~1; 304 outb(vgaCRReg, tmp); 305 306 outb(0x3C4, 1); 307 outb(0x3C5, blank); /* unblank the screen */ 308} 309 310void S3GENDAC_Save(ScrnInfoPtr pScrn) 311{ 312 S3Ptr pS3 = S3PTR(pScrn); 313 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 314 S3RegPtr save = &pS3->SavedRegs; 315 unsigned char tmp; 316 317 outb(vgaCRIndex, 0x55); 318 tmp = inb(vgaCRReg); 319 outb(vgaCRReg, tmp | 1); 320 321 save->dacregs[0] = inb(0x3c6); /* Enhanced command register */ 322 save->dacregs[2] = inb(0x3c8); /* PLL write index */ 323 save->dacregs[1] = inb(0x3c7); /* PLL read index */ 324 outb(0x3c7, 2); /* index to f2 reg */ 325 save->dacregs[3] = inb(0x3c9); /* f2 PLL M divider */ 326 save->dacregs[4] = inb(0x3c9); /* f2 PLL N1/N2 divider */ 327 outb(0x3c7, 0x0e); /* index to PLL control */ 328 save->dacregs[5] = inb(0x3c9); /* PLL control */ 329 330 outb(vgaCRReg, tmp & ~1); 331} 332 333void S3GENDAC_Restore(ScrnInfoPtr pScrn) 334{ 335 S3Ptr pS3 = S3PTR(pScrn); 336 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 337 S3RegPtr restore = &pS3->SavedRegs; 338 unsigned char tmp; 339 340 outb(vgaCRIndex, 0x55); 341 tmp = inb(vgaCRReg); 342 outb(vgaCRReg, tmp | 1); 343 344 outb(0x3c6, restore->dacregs[0]); /* Enhanced command register */ 345 outb(0x3c8, 2); /* index to f2 reg */ 346 outb(0x3c9, restore->dacregs[3]); /* f2 PLL M divider */ 347 outb(0x3c9, restore->dacregs[4]); /* f2 PLL N1/N2 divider */ 348 outb(0x3c8, 0x0e); /* index to PLL control */ 349 outb(0x3c9, restore->dacregs[5]); /* PLL control */ 350 outb(0x3c8, restore->dacregs[2]); /* PLL write index */ 351 outb(0x3c7, restore->dacregs[1]); /* PLL read index */ 352 353 outb(vgaCRReg, tmp & ~1); 354} 355 356static void 357S3GENDACSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m, int min_n1, 358 int max_n1, int min_n2, int max_n2, int pll_type, long freq_min, 359 long freq_max) 360{ 361 unsigned char m, n; 362 363 S3GENDACCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2, 364 freq_min, freq_max, &m, &n); 365 366 /* XXX for pll_type == GENDAC */ 367 S3GENDACSetPLL(pScrn, clk, m, n); 368} 369 370/* This function is copy from S3GENDACCalcClock() */ 371static void 372S3GENDACCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2, 373 int max_n2, long freq_min, long freq_max, 374 unsigned char *mdiv, unsigned char *ndiv) 375{ 376 double ffreq, ffreq_min, ffreq_max; 377 double div, diff, best_diff; 378 unsigned int m; 379 unsigned char n1, n2, best_n1=18, best_n2=2, best_m=127; 380 381#define BASE_FREQ 14.31818 382 ffreq = freq / 1000.0 / BASE_FREQ; 383 ffreq_min = freq_min / 1000.0 / BASE_FREQ; 384 ffreq_max = freq_max / 1000.0 / BASE_FREQ; 385 386 if (ffreq < ffreq_min / (1 << max_n2)) { 387 ErrorF("invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n", 388 ffreq*BASE_FREQ, ffreq_min*BASE_FREQ / (1 << max_n2)); 389 ffreq = ffreq_min / (1 << max_n2); 390 } 391 if (ffreq > ffreq_max / (1 << min_n2)) { 392 ErrorF("invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n", 393 ffreq*BASE_FREQ, ffreq_max*BASE_FREQ / (1 << min_n2)); 394 ffreq = ffreq_max / (1<<min_n2); 395 } 396 397 /* work out suitable timings */ 398 399 best_diff = ffreq; 400 401 for (n2 = min_n2; n2 <= max_n2; n2++) { 402 for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) { 403 m = (int)(ffreq * n1 * (1 << n2) + 0.5); 404 if (m < min_m + 2 || m > 127 + 2) 405 continue; 406 div = (double)(m) / (double)(n1); 407 if ((div >= ffreq_min) && (div <= ffreq_max)) { 408 diff = ffreq - div / (1 << n2); 409 if (diff < 0.0) 410 diff = -diff; 411 if (diff < best_diff) { 412 best_diff = diff; 413 best_m = m; 414 best_n1 = n1; 415 best_n2 = n2; 416 } 417 } 418 } 419 } 420 421 if (max_n1 == 63) 422 *ndiv = (best_n1 - 2) | (best_n2 << 6); 423 else 424 *ndiv = (best_n1 - 2) | (best_n2 << 5); 425 *mdiv = best_m - 2; 426} 427 428static void 429S3GENDACSetPLL(ScrnInfoPtr pScrn, int clk, unsigned char m, unsigned char n) 430{ 431 S3Ptr pS3 = S3PTR(pScrn); 432 unsigned char tmp, tmp1; 433 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 434 435 /* set RS2 via CR55, yuck */ 436 outb(vgaCRIndex, 0x55); 437 tmp = inb(vgaCRReg) & 0xFC; 438 outb(vgaCRReg, tmp | 0x01); 439 tmp1 = inb(GENDAC_INDEX); 440 441 outb(GENDAC_INDEX, clk); 442 outb(GENDAC_DATA, m); 443 outb(GENDAC_DATA, n); 444 445 /* Now clean up our mess */ 446 outb(GENDAC_INDEX, tmp1); 447 outb(vgaCRReg, tmp); 448} 449