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 "IBM.h" 38 39#include "s3.h" 40 41 42#define IBMRGB_WRITE_ADDR 0x3C8 /* CR55 low bit == 0 */ 43#define IBMRGB_RAMDAC_DATA 0x3C9 /* CR55 low bit == 0 */ 44#define IBMRGB_PIXEL_MASK 0x3C6 /* CR55 low bit == 0 */ 45#define IBMRGB_READ_ADDR 0x3C7 /* CR55 low bit == 0 */ 46#define IBMRGB_INDEX_LOW 0x3C8 /* CR55 low bit == 1 */ 47#define IBMRGB_INDEX_HIGH 0x3C9 /* CR55 low bit == 1 */ 48#define IBMRGB_INDEX_DATA 0x3C6 /* CR55 low bit == 1 */ 49#define IBMRGB_INDEX_CONTROL 0x3C7 /* CR55 low bit == 1 */ 50 51 52static void S3OutIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg, 53 unsigned char mask, unsigned char data) 54{ 55 S3Ptr pS3 = S3PTR(pScrn); 56 unsigned char tmp, tmp2 = 0x00; 57 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 58 59 outb(vgaCRIndex, 0x55); 60 tmp = inb(vgaCRReg) & 0xfc; 61 outb(vgaCRReg, tmp | 0x01); 62 63 outb(IBMRGB_INDEX_LOW, reg); 64 65 if (mask != 0x00) 66 tmp2 = inb(IBMRGB_INDEX_DATA) & mask; 67 outb(IBMRGB_INDEX_DATA, tmp2 | data); 68 69 outb(vgaCRIndex, 0x55); 70 outb(vgaCRReg, tmp); 71} 72 73 74static unsigned char S3InIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg) 75{ 76 S3Ptr pS3 = S3PTR(pScrn); 77 unsigned char tmp, ret; 78 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 79 80 outb(vgaCRIndex, 0x55); 81 tmp = inb(vgaCRReg) & 0xfc; 82 outb(vgaCRReg, tmp | 0x01); 83 84 outb(IBMRGB_INDEX_LOW, reg); 85 ret = inb(IBMRGB_INDEX_DATA); 86 87 outb(vgaCRIndex, 0x55); 88 outb(vgaCRReg, tmp); 89 90 return ret; 91} 92 93 94static void S3IBMWriteAddress(ScrnInfoPtr pScrn, CARD32 index) 95{ 96 outb(IBMRGB_WRITE_ADDR, index); 97} 98 99static void S3IBMWriteData(ScrnInfoPtr pScrn, unsigned char data) 100{ 101 outb(IBMRGB_INDEX_DATA, data); 102} 103 104static void S3IBMReadAddress(ScrnInfoPtr pScrn, CARD32 index) 105{ 106 outb(IBMRGB_READ_ADDR, index); 107} 108 109static unsigned char S3IBMReadData(ScrnInfoPtr pScrn) 110{ 111 return inb(IBMRGB_RAMDAC_DATA); 112} 113 114 115Bool S3ProbeIBMramdac(ScrnInfoPtr pScrn) 116{ 117 S3Ptr pS3 = S3PTR(pScrn); 118 119 if (pS3->Chipset != PCI_CHIP_968) 120 return FALSE; 121 122 pS3->RamDacRec = RamDacCreateInfoRec(); 123 pS3->RamDacRec->ReadDAC = S3InIBMRGBIndReg; 124 pS3->RamDacRec->WriteDAC = S3OutIBMRGBIndReg; 125 pS3->RamDacRec->ReadAddress = S3IBMReadAddress; 126 pS3->RamDacRec->WriteAddress = S3IBMWriteAddress; 127 pS3->RamDacRec->ReadData = S3IBMReadData; 128 pS3->RamDacRec->WriteData = S3IBMWriteData; 129 pS3->RamDacRec->LoadPalette = NULL; 130 131 if (!RamDacInit(pScrn, pS3->RamDacRec)) { 132 RamDacDestroyInfoRec(pS3->RamDacRec); 133 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RamDacInit failed\n"); 134 return FALSE; 135 } 136 137 pS3->RamDac = IBMramdacProbe(pScrn, S3IBMRamdacs); 138 if (pS3->RamDac) 139 return TRUE; 140 141 return FALSE; 142} 143 144static void S3ProgramIBMRGBClock(ScrnInfoPtr pScrn, int clk, unsigned char m, 145 unsigned char n, unsigned char df) 146{ 147 S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, ~1, 1); 148 149 S3OutIBMRGBIndReg(pScrn, IBMRGB_m0+2*clk, 0, (df<<6)|(m&0x3f)); 150 S3OutIBMRGBIndReg(pScrn, IBMRGB_n0+2*clk, 0, n); 151 152 S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl2, 0xf0, clk); 153 S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl1, 0xf8, 3); 154} 155 156 157static void S3IBMRGBSetClock(ScrnInfoPtr pScrn, long freq, int clk, long dacspeed, 158 long fref) 159{ 160 volatile double ffreq, ffref; 161 volatile int df, n, m, max_n, min_df; 162 volatile int best_m=69, best_n=17, best_df=0; 163 volatile double diff, mindiff; 164 165#define FREQ_MIN 16250 166#define FREQ_MAX dacspeed 167 168 if (freq < FREQ_MIN) 169 ffreq = FREQ_MIN / 1000.0; 170 else if (freq > FREQ_MAX) 171 ffreq = FREQ_MAX / 1000.0; 172 else 173 ffreq = freq / 1000.0; 174 175 ffref = fref / 1e3; 176 177 ffreq /= ffref; 178 ffreq *= 16; 179 mindiff = ffreq; 180 181 if (freq <= dacspeed/4) 182 min_df = 0; 183 else if (freq <= dacspeed/2) 184 min_df = 1; 185 else 186 min_df = 2; 187 188 for (df=0; df<4; df++) { 189 ffreq /= 2; 190 mindiff /= 2; 191 if (df < min_df) 192 continue; 193 194 if (df < 3) 195 max_n = fref / 1000 / 2; 196 else 197 max_n = fref / 1000; 198 if (max_n > 31) 199 max_n = 31; 200 201 for (n=2; n <= max_n; n++) { 202 m = (int)(ffreq * n + 0.5) - 65; 203 if (m < 0) 204 m = 0; 205 else if (m > 63) 206 m = 63; 207 diff = (m+65.0)/n-ffreq; 208 if (diff < 0) 209 diff = -diff; 210 if (diff < mindiff) { 211 mindiff = diff; 212 best_n = n; 213 best_m = m; 214 best_df = df; 215 } 216 } 217 } 218 219 S3ProgramIBMRGBClock(pScrn, clk, best_m, best_n, best_df); 220} 221 222void S3IBMRGB_Restore(ScrnInfoPtr pScrn) 223{ 224 S3Ptr pS3 = S3PTR(pScrn); 225 S3RegPtr restore = &pS3->SavedRegs; 226 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 227 int i; 228 229 for(i=0; i<0x100; i++) 230 S3OutIBMRGBIndReg(pScrn, i, 0, restore->dacregs[i]); 231 232 outb(vgaCRIndex, 0x22); 233 outb(vgaCRReg, restore->dacregs[0x100]); 234} 235 236 237void S3IBMRGB_Save(ScrnInfoPtr pScrn) 238{ 239 S3Ptr pS3 = S3PTR(pScrn); 240 S3RegPtr save = &pS3->SavedRegs; 241 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 242 int i; 243 244 for (i=0; i<0x100; i++) 245 save->dacregs[i] = S3InIBMRGBIndReg(pScrn, i); 246 247 outb(vgaCRIndex, 0x22); 248 save->dacregs[0x100] = inb(vgaCRReg); 249} 250 251 252void S3IBMRGB_PreInit(ScrnInfoPtr pScrn) 253{ 254 S3Ptr pS3 = S3PTR(pScrn); 255 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 256 unsigned char cr55, tmp; 257 258 outb(vgaCRIndex, 0x43); 259 tmp = inb(vgaCRReg); 260 outb(vgaCRReg, tmp & ~0x02); 261 262 outb(vgaCRIndex, 0x55); 263 cr55 = inb(vgaCRReg); 264 outb(vgaCRReg, (cr55 & ~0x03) | 0x01); /* set rs2 */ 265 266 tmp = inb(IBMRGB_INDEX_CONTROL); 267 outb(IBMRGB_INDEX_CONTROL, tmp & ~1); 268 outb(IBMRGB_INDEX_HIGH, 0); 269 270 outb(vgaCRIndex, 0x55); 271 outb(vgaCRReg, cr55 & ~0x03); 272 273 { 274 int m, n, df, mclk=0; 275 276 m = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_vco_div); 277 n = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_ref_div) & 0x1f; 278 df = m >> 6; 279 m &= 0x3f; 280 if (!n) { 281 m = 0; 282 n = 1; 283 } 284 mclk = ((pS3->RefClock*100 * (m+65)) / n / (8 >> df) + 50) / 100; 285 pS3->mclk = mclk; 286 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n", 287 mclk / 1000.0); 288 } 289} 290 291 292void S3IBMRGB_Init(ScrnInfoPtr pScrn, DisplayModePtr mode) 293{ 294 S3Ptr pS3 = S3PTR(pScrn); 295 unsigned char tmp, blank; 296 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 297 298 S3IBMRGBSetClock(pScrn, mode->Clock, 2, pS3->MaxClock, 299 pS3->RefClock); 300 301 outb(0x3c4, 1); 302 blank = inb(0x3c5); 303 outb(0x3c5, blank | 0x20); 304 305 S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, 0xf0, 0x03); 306 S3OutIBMRGBIndReg(pScrn, IBMRGB_sync, 0, 0); 307 S3OutIBMRGBIndReg(pScrn, IBMRGB_hsync_pos, 0, 0); 308 S3OutIBMRGBIndReg(pScrn, IBMRGB_pwr_mgmt, 0, 0); 309 S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~8, 0); 310 S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~2, 2); 311 S3OutIBMRGBIndReg(pScrn, IBMRGB_pal_ctrl, 0, 0); 312 S3OutIBMRGBIndReg(pScrn, IBMRGB_misc1, ~0x43, 1); 313 S3OutIBMRGBIndReg(pScrn, IBMRGB_misc2, 0, 0x47); 314 315 outb(vgaCRIndex, 0x22); 316 tmp = inb(vgaCRReg); 317 if (pS3->s3Bpp == 1) 318 outb(vgaCRReg, tmp | 8); 319 else 320 outb(vgaCRReg, tmp & ~8); 321 322 outb(vgaCRIndex, 0x65); 323 outb(vgaCRReg, 0x00); /* ! 528 */ 324 325 outb(vgaCRIndex, 0x40); 326 outb(vgaCRReg, 0x11); 327 outb(vgaCRIndex, 0x55); 328 outb(vgaCRReg, 0x00); 329 330 switch (pScrn->depth) { 331 case 8: 332 S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 3); 333 S3OutIBMRGBIndReg(pScrn, IBMRGB_8bpp, 0, 0); 334 break; 335 case 15: 336 S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4); 337 S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc0); 338 break; 339 case 16: 340 S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4); 341 S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc2); 342 break; 343 } 344 345 outb(vgaCRIndex, 0x66); 346 tmp = inb(vgaCRReg) & 0xf8; 347 outb(vgaCRReg, tmp); 348 349 outb(vgaCRIndex, 0x58); 350 tmp = (inb(vgaCRReg) & 0xbf) | 0x40; 351 outb(vgaCRReg, tmp); 352 353 outb(vgaCRIndex, 0x67); 354 outb(vgaCRReg, 0x11); 355 356 switch (pScrn->bitsPerPixel) { 357 case 8: 358 tmp = 0x21; 359 break; 360 case 16: 361 tmp = 0x10; 362 break; 363 } 364 outb(vgaCRIndex, 0x6d); 365 outb(vgaCRReg, tmp); 366 367 outb(0x3c4, 1); 368 outb(0x3c5, blank); 369} 370 371 372/* hardware cursor */ 373 374static void S3IBMRGBSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg) 375{ 376 S3Ptr pS3 = S3PTR(pScrn); 377 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 378 unsigned char tmp; 379 380 /* unlock sys regs */ 381 outb(vgaCRIndex, 0x39); 382 outb(vgaCRReg, 0xa5); 383 384 outb(vgaCRIndex, 0x55); 385 tmp = inb(vgaCRReg) & 0xfc; 386 outb(vgaCRReg, tmp | 0x01); 387 388 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_r); 389 outb(IBMRGB_INDEX_DATA, (bg & 0x00ff0000) >> 16); 390 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_g); 391 outb(IBMRGB_INDEX_DATA, (bg & 0x0000ff00) >> 8); 392 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_b); 393 outb(IBMRGB_INDEX_DATA, (bg & 0x000000ff)); 394 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_r); 395 outb(IBMRGB_INDEX_DATA, (fg & 0x00ff0000) >> 16); 396 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_g); 397 outb(IBMRGB_INDEX_DATA, (fg & 0x0000ff00) >> 8); 398 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_b); 399 outb(IBMRGB_INDEX_DATA, (fg & 0x000000ff)); 400 401 outb(vgaCRReg, tmp); 402} 403 404 405static void S3IBMRGBSetCursorPosition(ScrnInfoPtr pScrn, int x, int y) 406{ 407 S3Ptr pS3 = S3PTR(pScrn); 408 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 409 unsigned char tmp; 410 411 /* unlock sys regs */ 412 outb(vgaCRIndex, 0x39); 413 outb(vgaCRReg, 0xa5); 414 415 outb(vgaCRIndex, 0x55); 416 tmp = inb(vgaCRReg) & 0xfc; 417 outb(vgaCRReg, tmp | 0x01); 418 419 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl); 420 outb(IBMRGB_INDEX_DATA, x); 421 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh); 422 outb(IBMRGB_INDEX_DATA, x >> 8); 423 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl); 424 outb(IBMRGB_INDEX_DATA, y); 425 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh); 426 outb(IBMRGB_INDEX_DATA, y >> 8); 427 428 outb(vgaCRReg, tmp); 429} 430 431 432static void S3IBMRGBHideCursor(ScrnInfoPtr pScrn) 433{ 434 S3Ptr pS3 = S3PTR(pScrn); 435 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 436 437 /* unlock sys regs */ 438 outb(vgaCRIndex, 0x39); 439 outb(vgaCRReg, 0xa5); 440 441 S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, ~3, 0x00); 442} 443 444 445static void S3IBMRGBShowCursor(ScrnInfoPtr pScrn) 446{ 447 S3Ptr pS3 = S3PTR(pScrn); 448 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 449 unsigned char tmp; 450 451 /* unlock sys regs */ 452 outb(vgaCRIndex, 0x39); 453 outb(vgaCRReg, 0xa5); 454 455 outb(vgaCRIndex, 0x55); 456 tmp = (inb(vgaCRReg) & 0xdf) | 0x20; 457 outb(vgaCRReg, tmp); 458 459 outb(vgaCRIndex, 0x45); 460 tmp = inb(vgaCRReg) & ~0x20; 461 outb(vgaCRReg, tmp); 462 463 S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, 0, 0x27); 464} 465 466 467static void S3IBMRGBLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *image) 468{ 469 S3Ptr pS3 = S3PTR(pScrn); 470 int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 471 unsigned char tmp, tmp2; 472 register int i; 473 474 /* unlock sys regs */ 475 outb(vgaCRIndex, 0x39); 476 outb(vgaCRReg, 0xa5); 477 478 outb(vgaCRIndex, 0x55); 479 tmp = inb(vgaCRReg) & 0xfc; 480 outb(vgaCRReg, tmp | 0x01); 481 482 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_x); 483 outb(IBMRGB_INDEX_DATA, 0); 484 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_y); 485 outb(IBMRGB_INDEX_DATA, 0); 486 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl); 487 outb(IBMRGB_INDEX_DATA, 0xff); 488 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh); 489 outb(IBMRGB_INDEX_DATA, 0x7f); 490 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl); 491 outb(IBMRGB_INDEX_DATA, 0xff); 492 outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh); 493 outb(IBMRGB_INDEX_DATA, 0x7f); 494 495 tmp2 = inb(IBMRGB_INDEX_CONTROL) & 0xfe; 496 outb(IBMRGB_INDEX_CONTROL, tmp2 | 1); /* enable auto increment */ 497 498 outb(IBMRGB_INDEX_HIGH, (unsigned char) (IBMRGB_curs_array >> 8)); 499 outb(IBMRGB_INDEX_LOW, (unsigned char) (IBMRGB_curs_array)); 500 501 for (i=0; i<1024; i++) 502 outb(IBMRGB_INDEX_DATA, *image++); 503 504 outb(IBMRGB_INDEX_HIGH, 0); 505 outb(IBMRGB_INDEX_CONTROL, tmp2); /* disable auto increment */ 506 outb(vgaCRIndex, 0x55); 507 outb(vgaCRReg, tmp); 508} 509 510 511static Bool S3IBMRGBUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs) 512{ 513 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 514 S3Ptr pS3 = S3PTR(pScrn); 515 return (pS3->hwCursor); 516} 517 518 519Bool S3IBMRGB_CursorInit(ScreenPtr pScreen) 520{ 521 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); 522 S3Ptr pS3 = S3PTR(pScrn); 523 xf86CursorInfoPtr pCurs; 524 525 if (!(pCurs = pS3->pCurs = xf86CreateCursorInfoRec())) 526 return FALSE; 527 528 pCurs->MaxWidth = 64; 529 pCurs->MaxHeight = 64; 530 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP | 531 HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 | 532 HARDWARE_CURSOR_AND_SOURCE_WITH_MASK | 533 HARDWARE_CURSOR_NIBBLE_SWAPPED | 534 HARDWARE_CURSOR_BIT_ORDER_MSBFIRST; 535 536 pCurs->SetCursorColors = S3IBMRGBSetCursorColors; 537 pCurs->SetCursorPosition = S3IBMRGBSetCursorPosition; 538 pCurs->LoadCursorImage = S3IBMRGBLoadCursorImage; 539 pCurs->HideCursor = S3IBMRGBHideCursor; 540 pCurs->ShowCursor = S3IBMRGBShowCursor; 541 pCurs->UseHWCursor = S3IBMRGBUseHWCursor; 542 543 return xf86InitCursor(pScreen, pCurs); 544} 545 546 547