1/* 2 * Copyright (c) 2007,2010 NVIDIA Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included 13 * in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24 25#ifdef HAVE_CONFIG_H 26#include "config.h" 27#endif 28 29#include <float.h> 30#include <math.h> 31#include <strings.h> 32#include <unistd.h> 33 34#include "g80_type.h" 35#include "g80_cursor.h" 36#include "g80_display.h" 37#include "g80_output.h" 38 39typedef struct G80CrtcPrivRec { 40 Head head; 41 int pclk; /* Target pixel clock in kHz */ 42 Bool cursorVisible; 43 Bool skipModeFixup; 44 Bool dither; 45 /* Look-up table values to be set when the CRTC is enabled */ 46 uint16_t lut_r[256], lut_g[256], lut_b[256]; 47} G80CrtcPrivRec, *G80CrtcPrivPtr; 48 49static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update); 50 51/* 52 * PLL calculation. pclk is in kHz. 53 */ 54static void 55G80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP) 56{ 57 const float refclk = 27000.0f; 58 const float minVcoA = 100000; 59 const float maxVcoA = 400000; 60 const float minVcoB = 600000; 61 float maxVcoB = 1400000; 62 const float minUA = 2000; 63 const float maxUA = 400000; 64 const float minUB = 50000; 65 const float maxUB = 200000; 66 const int minNA = 1, maxNA = 255; 67 const int minNB = 1, maxNB = 31; 68 const int minMA = 1, maxMA = 255; 69 const int minMB = 1, maxMB = 31; 70 const int minP = 0, maxP = 6; 71 int lowP, highP; 72 float vcoB; 73 74 int na, ma, nb, mb, p; 75 float bestError = FLT_MAX; 76 77 *pNA = *pMA = *pNB = *pMB = *pP = 0; 78 79 if(maxVcoB < pclk + pclk / 200) 80 maxVcoB = pclk + pclk / 200; 81 if(minVcoB / (1 << maxP) > pclk) 82 pclk = minVcoB / (1 << maxP); 83 84 vcoB = maxVcoB - maxVcoB / 200; 85 lowP = minP; 86 vcoB /= 1 << (lowP + 1); 87 88 while(pclk <= vcoB && lowP < maxP) 89 { 90 vcoB /= 2; 91 lowP++; 92 } 93 94 vcoB = maxVcoB + maxVcoB / 200; 95 highP = lowP; 96 vcoB /= 1 << (highP + 1); 97 98 while(pclk <= vcoB && highP < maxP) 99 { 100 vcoB /= 2; 101 highP++; 102 } 103 104 for(p = lowP; p <= highP; p++) 105 { 106 for(ma = minMA; ma <= maxMA; ma++) 107 { 108 if(refclk / ma < minUA) 109 break; 110 else if(refclk / ma > maxUA) 111 continue; 112 113 for(na = minNA; na <= maxNA; na++) 114 { 115 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA) 116 continue; 117 118 for(mb = minMB; mb <= maxMB; mb++) 119 { 120 if(refclk * na / ma / mb < minUB) 121 break; 122 else if(refclk * na / ma / mb > maxUB) 123 continue; 124 125 nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk); 126 127 if(nb > maxNB) 128 break; 129 else if(nb < minNB) 130 continue; 131 else 132 { 133 float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p); 134 float error = fabsf(pclk - freq); 135 if(error < bestError) { 136 *pNA = na; 137 *pMA = ma; 138 *pNB = nb; 139 *pMB = mb; 140 *pP = p; 141 bestError = error; 142 } 143 } 144 } 145 } 146 } 147 } 148} 149 150static void 151G80CalcPLL2(float pclk, int *pN, int *pM, int *pPL) 152{ 153 const float refclk = 27000.0f; 154 const int minN = 8, maxN = 255; 155 const int minM = 1, maxM = 255; 156 const int minPL = 1, maxPL = 63; 157 const int minU = 25000, maxU = 50000; 158 const int minVco = 500000; 159 int maxVco = 1000000; 160 int lowPL, highPL, pl; 161 float vco, bestError = FLT_MAX; 162 163 vco = pclk + pclk / 50; 164 165 if(maxVco < vco) maxVco = vco; 166 167 highPL = (maxVco + vco - 1) / pclk; 168 if(highPL > maxPL) highPL = maxPL; 169 if(highPL < minPL) highPL = minPL; 170 171 lowPL = minVco / vco; 172 if(lowPL > maxPL) lowPL = maxPL; 173 if(lowPL < minPL) lowPL = minPL; 174 175 for(pl = highPL; pl >= lowPL; pl--) { 176 int m; 177 178 for(m = minM; m <= maxM; m++) { 179 int n; 180 float freq, error; 181 182 if(refclk / m < minU) break; 183 if(refclk / m > maxU) continue; 184 185 n = rint(pclk * pl * m / refclk); 186 if(n > maxN) break; 187 if(n < minN) continue; 188 189 freq = refclk * (n / (float)m) / pl; 190 error = fabsf(pclk - freq); 191 if(error < bestError) { 192 *pN = n; 193 *pM = m; 194 *pPL = pl; 195 bestError = error; 196 } 197 } 198 } 199} 200 201static void 202G80CrtcSetPClk(xf86CrtcPtr crtc) 203{ 204 G80Ptr pNv = G80PTR(crtc->scrn); 205 G80CrtcPrivPtr pPriv = crtc->driver_private; 206 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 207 const int headOff = 0x800 * pPriv->head; 208 int i; 209 210 if(pPriv->pclk == 0) 211 return; 212 213 if(pNv->architecture <= 0xa0 || 214 pNv->architecture == 0xaa || 215 pNv->architecture == 0xac) { 216 int lo_n, lo_m, hi_n, hi_m, p; 217 CARD32 lo = pNv->reg[(0x00614104+headOff)/4]; 218 CARD32 hi = pNv->reg[(0x00614108+headOff)/4]; 219 220 pNv->reg[(0x00614100+headOff)/4] = 0x10000610; 221 lo &= 0xff00ff00; 222 hi &= 0x8000ff00; 223 224 G80CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p); 225 226 lo |= (lo_m << 16) | lo_n; 227 hi |= (p << 28) | (hi_m << 16) | hi_n; 228 pNv->reg[(0x00614104+headOff)/4] = lo; 229 pNv->reg[(0x00614108+headOff)/4] = hi; 230 } else { 231 int n, m, pl; 232 CARD32 r = pNv->reg[(0x00614104+headOff)/4]; 233 234 pNv->reg[(0x00614100+headOff)/4] = 0x50000610; 235 r &= 0xffc00000; 236 237 G80CalcPLL2(pPriv->pclk, &n, &m, &pl); 238 r |= pl << 16 | m << 8 | n; 239 240 pNv->reg[(0x00614104+headOff)/4] = r; 241 } 242 pNv->reg[(0x00614200+headOff)/4] = 0; 243 244 for(i = 0; i < xf86_config->num_output; i++) { 245 xf86OutputPtr output = xf86_config->output[i]; 246 247 if(output->crtc != crtc) 248 continue; 249 G80OutputSetPClk(output, pPriv->pclk); 250 } 251} 252 253void 254G80DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data) 255{ 256 G80Ptr pNv = G80PTR(pScrn); 257 258 pNv->reg[0x00610304/4] = data; 259 pNv->reg[0x00610300/4] = addr | 0x80010001; 260 261 while(pNv->reg[0x00610300/4] & 0x80000000) { 262 const int super = ffs((pNv->reg[0x00610024/4] >> 4) & 7); 263 264 if(super) { 265 if(super == 2) { 266 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 267 int i; 268 269 for(i = 0; i < xf86_config->num_crtc; i++) 270 { 271 xf86CrtcPtr crtc = xf86_config->crtc[i]; 272 const int headOff = 0x800 * G80CrtcGetHead(crtc); 273 274 if((pNv->reg[(0x00614200+headOff)/4] & 0xc0) == 0x80) 275 G80CrtcSetPClk(crtc); 276 } 277 } 278 279 pNv->reg[0x00610024/4] = 8 << super; 280 pNv->reg[0x00610030/4] = 0x80000000; 281 } 282 } 283} 284 285Head 286G80CrtcGetHead(xf86CrtcPtr crtc) 287{ 288 G80CrtcPrivPtr pPriv = crtc->driver_private; 289 return pPriv->head; 290} 291 292Bool 293G80DispPreInit(ScrnInfoPtr pScrn) 294{ 295 G80Ptr pNv = G80PTR(pScrn); 296 297 pNv->reg[0x00610184/4] = pNv->reg[0x00614004/4]; 298 pNv->reg[0x00610190/4] = pNv->reg[0x00616100/4]; 299 pNv->reg[0x006101a0/4] = pNv->reg[0x00616900/4]; 300 pNv->reg[0x00610194/4] = pNv->reg[0x00616104/4]; 301 pNv->reg[0x006101a4/4] = pNv->reg[0x00616904/4]; 302 pNv->reg[0x00610198/4] = pNv->reg[0x00616108/4]; 303 pNv->reg[0x006101a8/4] = pNv->reg[0x00616908/4]; 304 pNv->reg[0x0061019C/4] = pNv->reg[0x0061610C/4]; 305 pNv->reg[0x006101ac/4] = pNv->reg[0x0061690c/4]; 306 pNv->reg[0x006101D0/4] = pNv->reg[0x0061A000/4]; 307 pNv->reg[0x006101D4/4] = pNv->reg[0x0061A800/4]; 308 pNv->reg[0x006101D8/4] = pNv->reg[0x0061B000/4]; 309 pNv->reg[0x006101E0/4] = pNv->reg[0x0061C000/4]; 310 pNv->reg[0x006101E4/4] = pNv->reg[0x0061C800/4]; 311 pNv->reg[0x006101E8/4] = pNv->reg[0x0061D000/4]; 312 pNv->reg[0x006101EC/4] = pNv->reg[0x0061D800/4]; 313 pNv->reg[0x0061A004/4] = 0x80550000; 314 pNv->reg[0x0061A010/4] = 0x00000001; 315 pNv->reg[0x0061A804/4] = 0x80550000; 316 pNv->reg[0x0061A810/4] = 0x00000001; 317 pNv->reg[0x0061B004/4] = 0x80550000; 318 pNv->reg[0x0061B010/4] = 0x00000001; 319 320 return TRUE; 321} 322 323Bool 324G80DispInit(ScrnInfoPtr pScrn) 325{ 326 G80Ptr pNv = G80PTR(pScrn); 327 CARD32 val; 328 329 if(pNv->reg[0x00610024/4] & 0x100) { 330 pNv->reg[0x00610024/4] = 0x100; 331 pNv->reg[0x006194E8/4] &= ~1; 332 while(pNv->reg[0x006194E8/4] & 2); 333 } 334 335 pNv->reg[0x00610200/4] = 0x2b00; 336 do { 337 val = pNv->reg[0x00610200/4]; 338 339 if ((val & 0x9f0000) == 0x20000) 340 pNv->reg[0x00610200/4] = val | 0x800000; 341 342 if ((val & 0x3f0000) == 0x30000) 343 pNv->reg[0x00610200/4] = val | 0x200000; 344 } while ((val & 0x1e0000) != 0); 345 pNv->reg[0x00610300/4] = 1; 346 pNv->reg[0x00610200/4] = 0x1000b03; 347 while(!(pNv->reg[0x00610200/4] & 0x40000000)); 348 349 C(0x00000084, 0); 350 C(0x00000088, 0); 351 C(0x00000874, 0); 352 C(0x00000800, 0); 353 C(0x00000810, 0); 354 C(0x0000082C, 0); 355 356 return TRUE; 357} 358 359void 360G80DispShutdown(ScrnInfoPtr pScrn) 361{ 362 G80Ptr pNv = G80PTR(pScrn); 363 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 364 int i; 365 366 for(i = 0; i < xf86_config->num_crtc; i++) { 367 xf86CrtcPtr crtc = xf86_config->crtc[i]; 368 369 G80CrtcBlankScreen(crtc, TRUE); 370 } 371 372 C(0x00000080, 0); 373 374 for(i = 0; i < xf86_config->num_crtc; i++) { 375 xf86CrtcPtr crtc = xf86_config->crtc[i]; 376 377 if(crtc->enabled) { 378 const CARD32 mask = 4 << G80CrtcGetHead(crtc); 379 380 pNv->reg[0x00610024/4] = mask; 381 while(!(pNv->reg[0x00610024/4] & mask)); 382 } 383 } 384 385 pNv->reg[0x00610200/4] = 0; 386 pNv->reg[0x00610300/4] = 0; 387 while((pNv->reg[0x00610200/4] & 0x1e0000) != 0); 388 while((pNv->reg[0x61C030/4] & 0x10000000)); 389 while((pNv->reg[0x61C830/4] & 0x10000000)); 390} 391 392void 393G80CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src) 394{ 395 /* Magic mode timing fudge factor */ 396 const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1; 397 const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1; 398 399 /* Stash the src timings in the Crtc fields in dst */ 400 dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal; 401 dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 | 402 (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1); 403 dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 | 404 (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1); 405 dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 | 406 (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1); 407 dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 | 408 ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2); 409} 410 411static Bool 412G80CrtcModeFixup(xf86CrtcPtr crtc, 413 DisplayModePtr mode, DisplayModePtr adjusted_mode) 414{ 415 G80CrtcPrivPtr pPriv = crtc->driver_private; 416 417 if(pPriv->skipModeFixup) 418 return TRUE; 419 420 G80CrtcDoModeFixup(adjusted_mode, mode); 421 return TRUE; 422} 423 424static void 425G80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, 426 DisplayModePtr adjusted_mode, int x, int y) 427{ 428 ScrnInfoPtr pScrn = crtc->scrn; 429 G80CrtcPrivPtr pPriv = crtc->driver_private; 430 const int HDisplay = adjusted_mode->HDisplay, VDisplay = adjusted_mode->VDisplay; 431 const int headOff = 0x400 * G80CrtcGetHead(crtc); 432 433 pPriv->pclk = adjusted_mode->Clock; 434 435 C(0x00000804 + headOff, adjusted_mode->Clock | 0x800000); 436 C(0x00000808 + headOff, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0); 437 C(0x00000810 + headOff, 0); 438 C(0x0000082C + headOff, 0); 439 C(0x00000814 + headOff, adjusted_mode->CrtcHBlankStart); 440 C(0x00000818 + headOff, adjusted_mode->CrtcHSyncEnd); 441 C(0x0000081C + headOff, adjusted_mode->CrtcHBlankEnd); 442 C(0x00000820 + headOff, adjusted_mode->CrtcHTotal); 443 if(adjusted_mode->Flags & V_INTERLACE) 444 C(0x00000824 + headOff, adjusted_mode->CrtcHSkew); 445 C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX); 446 C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000); 447 switch(pScrn->depth) { 448 case 8: C(0x00000870 + headOff, 0x1E00); break; 449 case 15: C(0x00000870 + headOff, 0xE900); break; 450 case 16: C(0x00000870 + headOff, 0xE800); break; 451 case 24: C(0x00000870 + headOff, 0xCF00); break; 452 } 453 G80CrtcSetDither(crtc, pPriv->dither, FALSE); 454 C(0x000008A8 + headOff, 0x40000); 455 C(0x000008C0 + headOff, y << 16 | x); 456 C(0x000008C8 + headOff, VDisplay << 16 | HDisplay); 457 C(0x000008D4 + headOff, 0); 458 459 G80CrtcBlankScreen(crtc, FALSE); 460} 461 462void 463G80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank) 464{ 465 ScrnInfoPtr pScrn = crtc->scrn; 466 G80Ptr pNv = G80PTR(pScrn); 467 G80CrtcPrivPtr pPriv = crtc->driver_private; 468 const int headOff = 0x400 * pPriv->head; 469 470 if(blank) { 471 G80CrtcShowHideCursor(crtc, FALSE, FALSE); 472 473 C(0x00000840 + headOff, 0); 474 C(0x00000844 + headOff, 0); 475 if(pNv->architecture != 0x50) 476 C(0x0000085C + headOff, 0); 477 C(0x00000874 + headOff, 0); 478 if(pNv->architecture != 0x50) 479 C(0x0000089C + headOff, 0); 480 } else { 481 C(0x00000860 + headOff, 0); 482 C(0x00000864 + headOff, 0); 483 pNv->reg[0x00610380/4] = 0; 484 pNv->reg[0x00610384/4] = pNv->videoRam * 1024 - 1; 485 pNv->reg[0x00610388/4] = 0x150000; 486 pNv->reg[0x0061038C/4] = 0; 487 C(0x00000884 + headOff, (pNv->videoRam << 2) - 0x40); 488 if(pNv->architecture != 0x50) 489 C(0x0000089C + headOff, 1); 490 if(pPriv->cursorVisible) 491 G80CrtcShowHideCursor(crtc, TRUE, FALSE); 492 C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000); 493 C(0x00000844 + headOff, (pNv->videoRam * 1024 - 0x5000 - 0x1000 * pPriv->head) >> 8); 494 if(pNv->architecture != 0x50) 495 C(0x0000085C + headOff, 1); 496 C(0x00000874 + headOff, 1); 497 } 498} 499 500static void 501G80CrtcDPMSSet(xf86CrtcPtr crtc, int mode) 502{ 503} 504 505/******************************** Cursor stuff ********************************/ 506static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update) 507{ 508 ScrnInfoPtr pScrn = crtc->scrn; 509 G80CrtcPrivPtr pPriv = crtc->driver_private; 510 const int headOff = 0x400 * G80CrtcGetHead(crtc); 511 512 C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000); 513 if(update) { 514 pPriv->cursorVisible = show; 515 C(0x00000080, 0); 516 } 517} 518 519static void G80CrtcShowCursor(xf86CrtcPtr crtc) 520{ 521 G80CrtcShowHideCursor(crtc, TRUE, TRUE); 522} 523 524static void G80CrtcHideCursor(xf86CrtcPtr crtc) 525{ 526 G80CrtcShowHideCursor(crtc, FALSE, TRUE); 527} 528 529/******************************** CRTC stuff ********************************/ 530 531static Bool 532G80CrtcLock(xf86CrtcPtr crtc) 533{ 534 return FALSE; 535} 536 537static void 538G80CrtcPrepare(xf86CrtcPtr crtc) 539{ 540 ScrnInfoPtr pScrn = crtc->scrn; 541 G80CrtcPrivPtr pPriv = crtc->driver_private; 542 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 543 int i; 544 545 for(i = 0; i < xf86_config->num_output; i++) { 546 xf86OutputPtr output = xf86_config->output[i]; 547 548 if(!output->crtc) 549 output->funcs->mode_set(output, NULL, NULL); 550 } 551 552 pPriv->skipModeFixup = FALSE; 553} 554 555void 556G80CrtcSkipModeFixup(xf86CrtcPtr crtc) 557{ 558 G80CrtcPrivPtr pPriv = crtc->driver_private; 559 pPriv->skipModeFixup = TRUE; 560} 561 562void 563G80CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update) 564{ 565 ScrnInfoPtr pScrn = crtc->scrn; 566 G80CrtcPrivPtr pPriv = crtc->driver_private; 567 const int headOff = 0x400 * G80CrtcGetHead(crtc); 568 569 pPriv->dither = dither; 570 571 C(0x000008A0 + headOff, dither ? 0x11 : 0); 572 if(update) C(0x00000080, 0); 573} 574 575static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY) 576{ 577 float scaleX, scaleY, scale; 578 579 scaleX = mode->CrtcHDisplay / (float)mode->HDisplay; 580 scaleY = mode->CrtcVDisplay / (float)mode->VDisplay; 581 582 if(scaleX > scaleY) 583 scale = scaleY; 584 else 585 scale = scaleX; 586 587 *outX = mode->HDisplay * scale; 588 *outY = mode->VDisplay * scale; 589} 590 591void G80CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, 592 enum G80ScaleMode scale) 593{ 594 ScrnInfoPtr pScrn = crtc->scrn; 595 G80CrtcPrivPtr pPriv = crtc->driver_private; 596 const int headOff = 0x400 * pPriv->head; 597 int outX, outY; 598 599 switch(scale) { 600 default: 601 case G80_SCALE_ASPECT: 602 ComputeAspectScale(mode, &outX, &outY); 603 break; 604 605 case G80_SCALE_OFF: 606 case G80_SCALE_FILL: 607 outX = mode->CrtcHDisplay; 608 outY = mode->CrtcVDisplay; 609 break; 610 611 case G80_SCALE_CENTER: 612 outX = mode->HDisplay; 613 outY = mode->VDisplay; 614 break; 615 } 616 617 if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) || 618 mode->HDisplay != outX || mode->VDisplay != outY) { 619 C(0x000008A4 + headOff, 9); 620 } else { 621 C(0x000008A4 + headOff, 0); 622 } 623 C(0x000008D8 + headOff, outY << 16 | outX); 624 C(0x000008DC + headOff, outY << 16 | outX); 625} 626 627static void 628G80CrtcCommit(xf86CrtcPtr crtc) 629{ 630 ScrnInfoPtr pScrn = crtc->scrn; 631 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 632 int i, crtc_mask = 0; 633 634 /* If any heads are unused, blank them */ 635 for(i = 0; i < xf86_config->num_output; i++) { 636 xf86OutputPtr output = xf86_config->output[i]; 637 638 if(output->crtc) 639 /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */ 640 crtc_mask |= 1 << G80CrtcGetHead(output->crtc); 641 } 642 643 for(i = 0; i < xf86_config->num_crtc; i++) 644 if(!((1 << i) & crtc_mask)) 645 G80CrtcBlankScreen(xf86_config->crtc[i], TRUE); 646 647 C(0x00000080, 0); 648} 649 650static void 651G80CrtcGammaSet(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, 652 int size) 653{ 654 ScrnInfoPtr pScrn = crtc->scrn; 655 G80Ptr pNv = G80PTR(pScrn); 656 G80CrtcPrivPtr pPriv = crtc->driver_private; 657 int i; 658 volatile struct { 659 uint16_t red, green, blue, unused; 660 } *lut = (void*)&pNv->mem[pNv->videoRam * 1024 - 0x5000 - 0x1000 * pPriv->head]; 661 662 assert(size == 256); 663 664 for(i = 0; i < size; i++) { 665 pPriv->lut_r[i] = lut[i].red = red[i] >> 2; 666 pPriv->lut_g[i] = lut[i].green = green[i] >> 2; 667 pPriv->lut_b[i] = lut[i].blue = blue[i] >> 2; 668 } 669 670 lut[256] = lut[255]; 671} 672 673void 674G80LoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors, 675 VisualPtr pVisual) 676{ 677 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 678 int i, j, index; 679 int p; 680 uint16_t lut_r[256], lut_g[256], lut_b[256]; 681 682 for(p = 0; p < xf86_config->num_crtc; p++) { 683 xf86CrtcPtr crtc = xf86_config->crtc[p]; 684 G80CrtcPrivPtr pPriv = crtc->driver_private; 685 686 /* Initialize to the old lookup table values. */ 687 for(i = 0; i < 256; i++) { 688 lut_r[i] = pPriv->lut_r[i] << 2; 689 lut_g[i] = pPriv->lut_g[i] << 2; 690 lut_b[i] = pPriv->lut_b[i] << 2; 691 } 692 693 switch(pScrn->depth) { 694 case 15: 695 for(i = 0; i < numColors; i++) { 696 index = indices[i]; 697 for(j = 0; j < 8; j++) { 698 lut_r[index * 8 + j] = 699 colors[index].red << 8; 700 lut_g[index * 8 + j] = 701 colors[index].green << 8; 702 lut_b[index * 8 + j] = 703 colors[index].blue << 8; 704 } 705 } 706 break; 707 case 16: 708 for(i = 0; i < numColors; i++) { 709 index = indices[i]; 710 711 if(index <= 31) { 712 for(j = 0; j < 8; j++) { 713 lut_r[index * 8 + j] = 714 colors[index].red << 8; 715 lut_b[index * 8 + j] = 716 colors[index].blue << 8; 717 } 718 } 719 720 for(j = 0; j < 4; j++) { 721 lut_g[index * 4 + j] = 722 colors[index].green << 8; 723 } 724 } 725 break; 726 default: 727 for(i = 0; i < numColors; i++) { 728 index = indices[i]; 729 lut_r[index] = colors[index].red << 8; 730 lut_g[index] = colors[index].green << 8; 731 lut_b[index] = colors[index].blue << 8; 732 } 733 break; 734 } 735 736 /* Make the change through RandR */ 737 RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b); 738 } 739} 740 741static const xf86CrtcFuncsRec g80_crtc_funcs = { 742 .dpms = G80CrtcDPMSSet, 743 .save = NULL, 744 .restore = NULL, 745 .lock = G80CrtcLock, 746 .unlock = NULL, 747 .mode_fixup = G80CrtcModeFixup, 748 .prepare = G80CrtcPrepare, 749 .mode_set = G80CrtcModeSet, 750 .gamma_set = G80CrtcGammaSet, 751 .commit = G80CrtcCommit, 752 .shadow_create = NULL, 753 .shadow_destroy = NULL, 754 .set_cursor_position = G80SetCursorPosition, 755 .show_cursor = G80CrtcShowCursor, 756 .hide_cursor = G80CrtcHideCursor, 757 .load_cursor_argb = G80LoadCursorARGB, 758 .destroy = NULL, 759}; 760 761void 762G80DispCreateCrtcs(ScrnInfoPtr pScrn) 763{ 764 G80Ptr pNv = G80PTR(pScrn); 765 Head head; 766 xf86CrtcPtr crtc; 767 G80CrtcPrivPtr g80_crtc; 768 769 /* Create a "crtc" object for each head */ 770 for(head = HEAD0; head <= HEAD1; head++) { 771 crtc = xf86CrtcCreate(pScrn, &g80_crtc_funcs); 772 if(!crtc) return; 773 774 g80_crtc = xnfcalloc(sizeof(*g80_crtc), 1); 775 g80_crtc->head = head; 776 g80_crtc->dither = pNv->Dither; 777 crtc->driver_private = g80_crtc; 778 } 779} 780