g80_display.c revision bd2f6fc9
1/* 2 * Copyright (c) 2007 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} G80CrtcPrivRec, *G80CrtcPrivPtr; 46 47static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update); 48 49/* 50 * PLL calculation. pclk is in kHz. 51 */ 52static void 53G80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP) 54{ 55 const float refclk = 27000.0f; 56 const float minVcoA = 100000; 57 const float maxVcoA = 400000; 58 const float minVcoB = 600000; 59 float maxVcoB = 1400000; 60 const float minUA = 2000; 61 const float maxUA = 400000; 62 const float minUB = 50000; 63 const float maxUB = 200000; 64 const int minNA = 1, maxNA = 255; 65 const int minNB = 1, maxNB = 31; 66 const int minMA = 1, maxMA = 255; 67 const int minMB = 1, maxMB = 31; 68 const int minP = 0, maxP = 6; 69 int lowP, highP; 70 float vcoB; 71 72 int na, ma, nb, mb, p; 73 float bestError = FLT_MAX; 74 75 *pNA = *pMA = *pNB = *pMB = *pP = 0; 76 77 if(maxVcoB < pclk + pclk / 200) 78 maxVcoB = pclk + pclk / 200; 79 if(minVcoB / (1 << maxP) > pclk) 80 pclk = minVcoB / (1 << maxP); 81 82 vcoB = maxVcoB - maxVcoB / 200; 83 lowP = minP; 84 vcoB /= 1 << (lowP + 1); 85 86 while(pclk <= vcoB && lowP < maxP) 87 { 88 vcoB /= 2; 89 lowP++; 90 } 91 92 vcoB = maxVcoB + maxVcoB / 200; 93 highP = lowP; 94 vcoB /= 1 << (highP + 1); 95 96 while(pclk <= vcoB && highP < maxP) 97 { 98 vcoB /= 2; 99 highP++; 100 } 101 102 for(p = lowP; p <= highP; p++) 103 { 104 for(ma = minMA; ma <= maxMA; ma++) 105 { 106 if(refclk / ma < minUA) 107 break; 108 else if(refclk / ma > maxUA) 109 continue; 110 111 for(na = minNA; na <= maxNA; na++) 112 { 113 if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA) 114 continue; 115 116 for(mb = minMB; mb <= maxMB; mb++) 117 { 118 if(refclk * na / ma / mb < minUB) 119 break; 120 else if(refclk * na / ma / mb > maxUB) 121 continue; 122 123 nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk); 124 125 if(nb > maxNB) 126 break; 127 else if(nb < minNB) 128 continue; 129 else 130 { 131 float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p); 132 float error = fabsf(pclk - freq); 133 if(error < bestError) { 134 *pNA = na; 135 *pMA = ma; 136 *pNB = nb; 137 *pMB = mb; 138 *pP = p; 139 bestError = error; 140 } 141 } 142 } 143 } 144 } 145 } 146} 147 148static void 149G80CalcPLL2(float pclk, int *pN, int *pM, int *pPL) 150{ 151 const float refclk = 27000.0f; 152 const int minN = 8, maxN = 255; 153 const int minM = 1, maxM = 255; 154 const int minPL = 1, maxPL = 63; 155 const int minU = 25000, maxU = 50000; 156 const int minVco = 500000; 157 int maxVco = 1000000; 158 int lowPL, highPL, pl; 159 float vco, bestError = FLT_MAX; 160 161 vco = pclk + pclk / 50; 162 163 if(maxVco < vco) maxVco = vco; 164 165 highPL = (maxVco + vco - 1) / pclk; 166 if(highPL > maxPL) highPL = maxPL; 167 if(highPL < minPL) highPL = minPL; 168 169 lowPL = minVco / vco; 170 if(lowPL > maxPL) lowPL = maxPL; 171 if(lowPL < minPL) lowPL = minPL; 172 173 for(pl = highPL; pl >= lowPL; pl--) { 174 int m; 175 176 for(m = minM; m <= maxM; m++) { 177 int n; 178 float freq, error; 179 180 if(refclk / m < minU) break; 181 if(refclk / m > maxU) continue; 182 183 n = rint(pclk * pl * m / refclk); 184 if(n > maxN) break; 185 if(n < minN) continue; 186 187 freq = refclk * (n / (float)m) / pl; 188 error = fabsf(pclk - freq); 189 if(error < bestError) { 190 *pN = n; 191 *pM = m; 192 *pPL = pl; 193 bestError = error; 194 } 195 } 196 } 197} 198 199static void 200G80CrtcSetPClk(xf86CrtcPtr crtc) 201{ 202 G80Ptr pNv = G80PTR(crtc->scrn); 203 G80CrtcPrivPtr pPriv = crtc->driver_private; 204 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 205 const int headOff = 0x800 * pPriv->head; 206 int i; 207 208 if(pPriv->pclk == 0) 209 return; 210 211 if(pNv->architecture <= 0xa0 || 212 pNv->architecture == 0xaa || 213 pNv->architecture == 0xac) { 214 int lo_n, lo_m, hi_n, hi_m, p, i; 215 CARD32 lo = pNv->reg[(0x00614104+headOff)/4]; 216 CARD32 hi = pNv->reg[(0x00614108+headOff)/4]; 217 218 pNv->reg[(0x00614100+headOff)/4] = 0x10000610; 219 lo &= 0xff00ff00; 220 hi &= 0x8000ff00; 221 222 G80CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p); 223 224 lo |= (lo_m << 16) | lo_n; 225 hi |= (p << 28) | (hi_m << 16) | hi_n; 226 pNv->reg[(0x00614104+headOff)/4] = lo; 227 pNv->reg[(0x00614108+headOff)/4] = hi; 228 } else { 229 int n, m, pl; 230 CARD32 r = pNv->reg[(0x00614104+headOff)/4]; 231 232 pNv->reg[(0x00614100+headOff)/4] = 0x50000610; 233 r &= 0xffc00000; 234 235 G80CalcPLL2(pPriv->pclk, &n, &m, &pl); 236 r |= pl << 16 | m << 8 | n; 237 238 pNv->reg[(0x00614104+headOff)/4] = r; 239 } 240 pNv->reg[(0x00614200+headOff)/4] = 0; 241 242 for(i = 0; i < xf86_config->num_output; i++) { 243 xf86OutputPtr output = xf86_config->output[i]; 244 245 if(output->crtc != crtc) 246 continue; 247 G80OutputSetPClk(output, pPriv->pclk); 248 } 249} 250 251void 252G80DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data) 253{ 254 G80Ptr pNv = G80PTR(pScrn); 255 256 pNv->reg[0x00610304/4] = data; 257 pNv->reg[0x00610300/4] = addr | 0x80010001; 258 259 while(pNv->reg[0x00610300/4] & 0x80000000) { 260 const int super = ffs((pNv->reg[0x00610024/4] >> 4) & 7); 261 262 if(super) { 263 if(super == 2) { 264 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 265 int i; 266 267 for(i = 0; i < xf86_config->num_crtc; i++) 268 { 269 xf86CrtcPtr crtc = xf86_config->crtc[i]; 270 const int headOff = 0x800 * G80CrtcGetHead(crtc); 271 272 if((pNv->reg[(0x00614200+headOff)/4] & 0xc0) == 0x80) 273 G80CrtcSetPClk(crtc); 274 } 275 } 276 277 pNv->reg[0x00610024/4] = 8 << super; 278 pNv->reg[0x00610030/4] = 0x80000000; 279 } 280 } 281} 282 283Head 284G80CrtcGetHead(xf86CrtcPtr crtc) 285{ 286 G80CrtcPrivPtr pPriv = crtc->driver_private; 287 return pPriv->head; 288} 289 290Bool 291G80DispPreInit(ScrnInfoPtr pScrn) 292{ 293 G80Ptr pNv = G80PTR(pScrn); 294 295 pNv->reg[0x00610184/4] = pNv->reg[0x00614004/4]; 296 pNv->reg[0x00610190/4] = pNv->reg[0x00616100/4]; 297 pNv->reg[0x006101a0/4] = pNv->reg[0x00616900/4]; 298 pNv->reg[0x00610194/4] = pNv->reg[0x00616104/4]; 299 pNv->reg[0x006101a4/4] = pNv->reg[0x00616904/4]; 300 pNv->reg[0x00610198/4] = pNv->reg[0x00616108/4]; 301 pNv->reg[0x006101a8/4] = pNv->reg[0x00616908/4]; 302 pNv->reg[0x0061019C/4] = pNv->reg[0x0061610C/4]; 303 pNv->reg[0x006101ac/4] = pNv->reg[0x0061690c/4]; 304 pNv->reg[0x006101D0/4] = pNv->reg[0x0061A000/4]; 305 pNv->reg[0x006101D4/4] = pNv->reg[0x0061A800/4]; 306 pNv->reg[0x006101D8/4] = pNv->reg[0x0061B000/4]; 307 pNv->reg[0x006101E0/4] = pNv->reg[0x0061C000/4]; 308 pNv->reg[0x006101E4/4] = pNv->reg[0x0061C800/4]; 309 pNv->reg[0x006101E8/4] = pNv->reg[0x0061D000/4]; 310 pNv->reg[0x006101EC/4] = pNv->reg[0x0061D800/4]; 311 pNv->reg[0x0061A004/4] = 0x80550000; 312 pNv->reg[0x0061A010/4] = 0x00000001; 313 pNv->reg[0x0061A804/4] = 0x80550000; 314 pNv->reg[0x0061A810/4] = 0x00000001; 315 pNv->reg[0x0061B004/4] = 0x80550000; 316 pNv->reg[0x0061B010/4] = 0x00000001; 317 318 return TRUE; 319} 320 321Bool 322G80DispInit(ScrnInfoPtr pScrn) 323{ 324 G80Ptr pNv = G80PTR(pScrn); 325 CARD32 val; 326 327 if(pNv->reg[0x00610024/4] & 0x100) { 328 pNv->reg[0x00610024/4] = 0x100; 329 pNv->reg[0x006194E8/4] &= ~1; 330 while(pNv->reg[0x006194E8/4] & 2); 331 } 332 333 pNv->reg[0x00610200/4] = 0x2b00; 334 do { 335 val = pNv->reg[0x00610200/4]; 336 337 if ((val & 0x9f0000) == 0x20000) 338 pNv->reg[0x00610200/4] = val | 0x800000; 339 340 if ((val & 0x3f0000) == 0x30000) 341 pNv->reg[0x00610200/4] = val | 0x200000; 342 } while ((val & 0x1e0000) != 0); 343 pNv->reg[0x00610300/4] = 1; 344 pNv->reg[0x00610200/4] = 0x1000b03; 345 while(!(pNv->reg[0x00610200/4] & 0x40000000)); 346 347 C(0x00000084, 0); 348 C(0x00000088, 0); 349 C(0x00000874, 0); 350 C(0x00000800, 0); 351 C(0x00000810, 0); 352 C(0x0000082C, 0); 353 354 return TRUE; 355} 356 357void 358G80DispShutdown(ScrnInfoPtr pScrn) 359{ 360 G80Ptr pNv = G80PTR(pScrn); 361 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 362 int i; 363 364 for(i = 0; i < xf86_config->num_crtc; i++) { 365 xf86CrtcPtr crtc = xf86_config->crtc[i]; 366 367 G80CrtcBlankScreen(crtc, TRUE); 368 } 369 370 C(0x00000080, 0); 371 372 for(i = 0; i < xf86_config->num_crtc; i++) { 373 xf86CrtcPtr crtc = xf86_config->crtc[i]; 374 375 if(crtc->enabled) { 376 const CARD32 mask = 4 << G80CrtcGetHead(crtc); 377 378 pNv->reg[0x00610024/4] = mask; 379 while(!(pNv->reg[0x00610024/4] & mask)); 380 } 381 } 382 383 pNv->reg[0x00610200/4] = 0; 384 pNv->reg[0x00610300/4] = 0; 385 while((pNv->reg[0x00610200/4] & 0x1e0000) != 0); 386 while((pNv->reg[0x61C030/4] & 0x10000000)); 387 while((pNv->reg[0x61C830/4] & 0x10000000)); 388} 389 390void 391G80CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src) 392{ 393 /* Magic mode timing fudge factor */ 394 const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1; 395 const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1; 396 397 /* Stash the src timings in the Crtc fields in dst */ 398 dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal; 399 dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 | 400 (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1); 401 dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 | 402 (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1); 403 dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 | 404 (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1); 405 dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 | 406 ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2); 407} 408 409static Bool 410G80CrtcModeFixup(xf86CrtcPtr crtc, 411 DisplayModePtr mode, DisplayModePtr adjusted_mode) 412{ 413 G80CrtcPrivPtr pPriv = crtc->driver_private; 414 415 if(pPriv->skipModeFixup) 416 return TRUE; 417 418 G80CrtcDoModeFixup(adjusted_mode, mode); 419 return TRUE; 420} 421 422static void 423G80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, 424 DisplayModePtr adjusted_mode, int x, int y) 425{ 426 ScrnInfoPtr pScrn = crtc->scrn; 427 G80CrtcPrivPtr pPriv = crtc->driver_private; 428 const int HDisplay = adjusted_mode->HDisplay, VDisplay = adjusted_mode->VDisplay; 429 const int headOff = 0x400 * G80CrtcGetHead(crtc); 430 431 pPriv->pclk = adjusted_mode->Clock; 432 433 C(0x00000804 + headOff, adjusted_mode->Clock | 0x800000); 434 C(0x00000808 + headOff, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0); 435 C(0x00000810 + headOff, 0); 436 C(0x0000082C + headOff, 0); 437 C(0x00000814 + headOff, adjusted_mode->CrtcHBlankStart); 438 C(0x00000818 + headOff, adjusted_mode->CrtcHSyncEnd); 439 C(0x0000081C + headOff, adjusted_mode->CrtcHBlankEnd); 440 C(0x00000820 + headOff, adjusted_mode->CrtcHTotal); 441 if(adjusted_mode->Flags & V_INTERLACE) 442 C(0x00000824 + headOff, adjusted_mode->CrtcHSkew); 443 C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX); 444 C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000); 445 switch(pScrn->depth) { 446 case 8: C(0x00000870 + headOff, 0x1E00); break; 447 case 15: C(0x00000870 + headOff, 0xE900); break; 448 case 16: C(0x00000870 + headOff, 0xE800); break; 449 case 24: C(0x00000870 + headOff, 0xCF00); break; 450 } 451 G80CrtcSetDither(crtc, pPriv->dither, FALSE); 452 C(0x000008A8 + headOff, 0x40000); 453 C(0x000008C0 + headOff, y << 16 | x); 454 C(0x000008C8 + headOff, VDisplay << 16 | HDisplay); 455 C(0x000008D4 + headOff, 0); 456 457 G80CrtcBlankScreen(crtc, FALSE); 458} 459 460void 461G80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank) 462{ 463 ScrnInfoPtr pScrn = crtc->scrn; 464 G80Ptr pNv = G80PTR(pScrn); 465 G80CrtcPrivPtr pPriv = crtc->driver_private; 466 const int headOff = 0x400 * pPriv->head; 467 468 if(blank) { 469 G80CrtcShowHideCursor(crtc, FALSE, FALSE); 470 471 C(0x00000840 + headOff, 0); 472 C(0x00000844 + headOff, 0); 473 if(pNv->architecture != 0x50) 474 C(0x0000085C + headOff, 0); 475 C(0x00000874 + headOff, 0); 476 if(pNv->architecture != 0x50) 477 C(0x0000089C + headOff, 0); 478 } else { 479 C(0x00000860 + headOff, 0); 480 C(0x00000864 + headOff, 0); 481 pNv->reg[0x00610380/4] = 0; 482 pNv->reg[0x00610384/4] = pNv->videoRam * 1024 - 1; 483 pNv->reg[0x00610388/4] = 0x150000; 484 pNv->reg[0x0061038C/4] = 0; 485 C(0x00000884 + headOff, (pNv->videoRam << 2) - 0x40); 486 if(pNv->architecture != 0x50) 487 C(0x0000089C + headOff, 1); 488 if(pPriv->cursorVisible) 489 G80CrtcShowHideCursor(crtc, TRUE, FALSE); 490 C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000); 491 C(0x00000844 + headOff, (pNv->videoRam * 1024 - 0x5000) >> 8); 492 if(pNv->architecture != 0x50) 493 C(0x0000085C + headOff, 1); 494 C(0x00000874 + headOff, 1); 495 } 496} 497 498static void 499G80CrtcDPMSSet(xf86CrtcPtr crtc, int mode) 500{ 501} 502 503/******************************** Cursor stuff ********************************/ 504static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update) 505{ 506 ScrnInfoPtr pScrn = crtc->scrn; 507 G80CrtcPrivPtr pPriv = crtc->driver_private; 508 const int headOff = 0x400 * G80CrtcGetHead(crtc); 509 510 C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000); 511 if(update) { 512 pPriv->cursorVisible = show; 513 C(0x00000080, 0); 514 } 515} 516 517static void G80CrtcShowCursor(xf86CrtcPtr crtc) 518{ 519 G80CrtcShowHideCursor(crtc, TRUE, TRUE); 520} 521 522static void G80CrtcHideCursor(xf86CrtcPtr crtc) 523{ 524 G80CrtcShowHideCursor(crtc, FALSE, TRUE); 525} 526 527/******************************** CRTC stuff ********************************/ 528 529static Bool 530G80CrtcLock(xf86CrtcPtr crtc) 531{ 532 return FALSE; 533} 534 535static void 536G80CrtcPrepare(xf86CrtcPtr crtc) 537{ 538 ScrnInfoPtr pScrn = crtc->scrn; 539 G80CrtcPrivPtr pPriv = crtc->driver_private; 540 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 541 int i; 542 543 for(i = 0; i < xf86_config->num_output; i++) { 544 xf86OutputPtr output = xf86_config->output[i]; 545 546 if(!output->crtc) 547 output->funcs->mode_set(output, NULL, NULL); 548 } 549 550 pPriv->skipModeFixup = FALSE; 551} 552 553void 554G80CrtcSkipModeFixup(xf86CrtcPtr crtc) 555{ 556 G80CrtcPrivPtr pPriv = crtc->driver_private; 557 pPriv->skipModeFixup = TRUE; 558} 559 560void 561G80CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update) 562{ 563 ScrnInfoPtr pScrn = crtc->scrn; 564 G80CrtcPrivPtr pPriv = crtc->driver_private; 565 const int headOff = 0x400 * G80CrtcGetHead(crtc); 566 567 pPriv->dither = dither; 568 569 C(0x000008A0 + headOff, dither ? 0x11 : 0); 570 if(update) C(0x00000080, 0); 571} 572 573static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY) 574{ 575 float scaleX, scaleY, scale; 576 577 scaleX = mode->CrtcHDisplay / (float)mode->HDisplay; 578 scaleY = mode->CrtcVDisplay / (float)mode->VDisplay; 579 580 if(scaleX > scaleY) 581 scale = scaleY; 582 else 583 scale = scaleX; 584 585 *outX = mode->HDisplay * scale; 586 *outY = mode->VDisplay * scale; 587} 588 589void G80CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, 590 enum G80ScaleMode scale) 591{ 592 ScrnInfoPtr pScrn = crtc->scrn; 593 G80CrtcPrivPtr pPriv = crtc->driver_private; 594 const int headOff = 0x400 * pPriv->head; 595 int outX, outY; 596 597 switch(scale) { 598 default: 599 case G80_SCALE_ASPECT: 600 ComputeAspectScale(mode, &outX, &outY); 601 break; 602 603 case G80_SCALE_OFF: 604 case G80_SCALE_FILL: 605 outX = mode->CrtcHDisplay; 606 outY = mode->CrtcVDisplay; 607 break; 608 609 case G80_SCALE_CENTER: 610 outX = mode->HDisplay; 611 outY = mode->VDisplay; 612 break; 613 } 614 615 if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) || 616 mode->HDisplay != outX || mode->VDisplay != outY) { 617 C(0x000008A4 + headOff, 9); 618 } else { 619 C(0x000008A4 + headOff, 0); 620 } 621 C(0x000008D8 + headOff, outY << 16 | outX); 622 C(0x000008DC + headOff, outY << 16 | outX); 623} 624 625static void 626G80CrtcCommit(xf86CrtcPtr crtc) 627{ 628 ScrnInfoPtr pScrn = crtc->scrn; 629 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); 630 int i, crtc_mask = 0; 631 632 /* If any heads are unused, blank them */ 633 for(i = 0; i < xf86_config->num_output; i++) { 634 xf86OutputPtr output = xf86_config->output[i]; 635 636 if(output->crtc) 637 /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */ 638 crtc_mask |= 1 << G80CrtcGetHead(output->crtc); 639 } 640 641 for(i = 0; i < xf86_config->num_crtc; i++) 642 if(!((1 << i) & crtc_mask)) 643 G80CrtcBlankScreen(xf86_config->crtc[i], TRUE); 644 645 C(0x00000080, 0); 646} 647 648static const xf86CrtcFuncsRec g80_crtc_funcs = { 649 .dpms = G80CrtcDPMSSet, 650 .save = NULL, 651 .restore = NULL, 652 .lock = G80CrtcLock, 653 .unlock = NULL, 654 .mode_fixup = G80CrtcModeFixup, 655 .prepare = G80CrtcPrepare, 656 .mode_set = G80CrtcModeSet, 657 // .gamma_set = G80DispGammaSet, 658 .commit = G80CrtcCommit, 659 .shadow_create = NULL, 660 .shadow_destroy = NULL, 661 .set_cursor_position = G80SetCursorPosition, 662 .show_cursor = G80CrtcShowCursor, 663 .hide_cursor = G80CrtcHideCursor, 664 .load_cursor_argb = G80LoadCursorARGB, 665 .destroy = NULL, 666}; 667 668void 669G80DispCreateCrtcs(ScrnInfoPtr pScrn) 670{ 671 G80Ptr pNv = G80PTR(pScrn); 672 Head head; 673 xf86CrtcPtr crtc; 674 G80CrtcPrivPtr g80_crtc; 675 676 /* Create a "crtc" object for each head */ 677 for(head = HEAD0; head <= HEAD1; head++) { 678 crtc = xf86CrtcCreate(pScrn, &g80_crtc_funcs); 679 if(!crtc) return; 680 681 g80_crtc = xnfcalloc(sizeof(*g80_crtc), 1); 682 g80_crtc->head = head; 683 g80_crtc->dither = pNv->Dither; 684 crtc->driver_private = g80_crtc; 685 } 686} 687