diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2007-10-15 20:13:06 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2007-10-15 20:13:06 +0000 |
commit | 7d37ae1f983d0c3947aa6292d8c7432e930c8283 (patch) | |
tree | 69625e13c2fe5d0645c9aac80ad8da10a627196e | |
parent | f0b8eeea88c5a04af4bc893dfdf0b529ffd0796a (diff) |
From Aaron Plattner in X.Org git:
G80 bug #12637: Unwedge the hardware if the BIOS left it stuck.
Fixes Thinkpad R61 display lockup for niklas@ and espie@.
-rw-r--r-- | driver/xf86-video-nv/src/g80_display.c | 565 |
1 files changed, 327 insertions, 238 deletions
diff --git a/driver/xf86-video-nv/src/g80_display.c b/driver/xf86-video-nv/src/g80_display.c index 068e902d9..dda1c7f02 100644 --- a/driver/xf86-video-nv/src/g80_display.c +++ b/driver/xf86-video-nv/src/g80_display.c @@ -32,10 +32,19 @@ #include <unistd.h> #include "g80_type.h" +#include "g80_cursor.h" #include "g80_display.h" +#include "g80_output.h" -#define DPMS_SERVER -#include <X11/extensions/dpms.h> +typedef struct G80CrtcPrivRec { + Head head; + int pclk; /* Target pixel clock in kHz */ + Bool cursorVisible; + Bool skipModeFixup; + Bool dither; +} G80CrtcPrivRec, *G80CrtcPrivPtr; + +static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update); /* * PLL calculation. pclk is in kHz. @@ -137,8 +146,42 @@ G80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP) } static void -G80DispCommand(G80Ptr pNv, CARD32 addr, CARD32 data) +G80CrtcSetPClk(xf86CrtcPtr crtc) +{ + G80Ptr pNv = G80PTR(crtc->scrn); + G80CrtcPrivPtr pPriv = crtc->driver_private; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + const int headOff = 0x800 * pPriv->head; + int lo_n, lo_m, hi_n, hi_m, p, i; + CARD32 lo = pNv->reg[(0x00614104+headOff)/4]; + CARD32 hi = pNv->reg[(0x00614108+headOff)/4]; + + pNv->reg[(0x00614100+headOff)/4] = 0x10000610; + lo &= 0xff00ff00; + hi &= 0x8000ff00; + + G80CalcPLL(pPriv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p); + + lo |= (lo_m << 16) | lo_n; + hi |= (p << 28) | (hi_m << 16) | hi_n; + pNv->reg[(0x00614104+headOff)/4] = lo; + pNv->reg[(0x00614108+headOff)/4] = hi; + pNv->reg[(0x00614200+headOff)/4] = 0; + + for(i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + + if(output->crtc != crtc) + continue; + G80OutputSetPClk(output, pPriv->pclk); + } +} + +void +G80DispCommand(ScrnInfoPtr pScrn, CARD32 addr, CARD32 data) { + G80Ptr pNv = G80PTR(pScrn); + pNv->reg[0x00610304/4] = data; pNv->reg[0x00610300/4] = addr | 0x80010001; @@ -147,35 +190,17 @@ G80DispCommand(G80Ptr pNv, CARD32 addr, CARD32 data) if(super) { if(super == 2) { - const int headOff = 0x800 * pNv->head; - const int orOff = 0x800 * pNv->or; - - if(pNv->reg[0x00610030/4] & 0x600) { - int lo_n, lo_m, hi_n, hi_m, p; - CARD32 lo = pNv->reg[(0x00614104+headOff)/4]; - CARD32 hi = pNv->reg[(0x00614108+headOff)/4]; - - pNv->reg[(0x00614100+headOff)/4] = 0x10000610; - lo &= 0xff00ff00; - hi &= 0x8000ff00; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + const CARD32 r = pNv->reg[0x00610030/4]; + int i; - G80CalcPLL(pNv->pclk, &lo_n, &lo_m, &hi_n, &hi_m, &p); - - lo |= (lo_m << 16) | lo_n; - hi |= (p << 28) | (hi_m << 16) | hi_n; - pNv->reg[(0x00614104+headOff)/4] = lo; - pNv->reg[(0x00614108+headOff)/4] = hi; - } + for(i = 0; i < xf86_config->num_crtc; i++) + { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + G80CrtcPrivPtr pPriv = crtc->driver_private; - pNv->reg[(0x00614200+headOff)/4] = 0; - switch(pNv->orType) { - case DAC: - pNv->reg[(0x00614280+orOff)/4] = 0; - break; - case SOR: - pNv->reg[(0x00614300+orOff)/4] = - (pNv->pclk > 165000) ? 0x101 : 0; - break; + if(r & (0x200 << pPriv->head)) + G80CrtcSetPClk(crtc); } } @@ -184,59 +209,16 @@ G80DispCommand(G80Ptr pNv, CARD32 addr, CARD32 data) } } } -#define C(mthd, data) G80DispCommand(pNv, (mthd), (data)) -/* - * Performs load detection on a single DAC. - */ -Bool G80DispDetectLoad(ScrnInfoPtr pScrn, ORNum or) +Head +G80CrtcGetHead(xf86CrtcPtr crtc) { - G80Ptr pNv = G80PTR(pScrn); - const int dacOff = 2048 * or; - CARD32 load, tmp; - - pNv->reg[(0x0061A010+dacOff)/4] = 0x00000001; - pNv->reg[(0x0061A004+dacOff)/4] = 0x80150000; - while(pNv->reg[(0x0061A004+dacOff)/4] & 0x80000000); - tmp = pNv->architecture == 0x50 ? 420 : 340; - pNv->reg[(0x0061A00C+dacOff)/4] = tmp | 0x100000; - usleep(4500); - load = pNv->reg[(0x0061A00C+dacOff)/4]; - pNv->reg[(0x0061A00C+dacOff)/4] = 0; - pNv->reg[(0x0061A004+dacOff)/4] = 0x80550000; - - return (load & 0x38000000) == 0x38000000; -} - -/* - * Performs load detection on the DACs. Sets pNv->orType and pNv->or - * accordingly. - */ -Bool G80LoadDetect(ScrnInfoPtr pScrn) -{ - G80Ptr pNv = G80PTR(pScrn); - const int scrnIndex = pScrn->scrnIndex; - ORNum or; - - pNv->orType = DAC; - - for(or = DAC0; or <= DAC2; or++) { - xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on DAC%i ... ", or); - - if(G80DispDetectLoad(pScrn, or)) { - xf86ErrorF("found one!\n"); - pNv->or = or; - return TRUE; - } - - xf86ErrorF("nothing.\n"); - } - - return FALSE; + G80CrtcPrivPtr pPriv = crtc->driver_private; + return pPriv->head; } Bool -G80DispInit(ScrnInfoPtr pScrn) +G80DispPreInit(ScrnInfoPtr pScrn) { G80Ptr pNv = G80PTR(pScrn); @@ -254,14 +236,6 @@ G80DispInit(ScrnInfoPtr pScrn) pNv->reg[0x006101D8/4] = pNv->reg[0x0061B000/4]; pNv->reg[0x006101E0/4] = pNv->reg[0x0061C000/4]; pNv->reg[0x006101E4/4] = pNv->reg[0x0061C800/4]; - pNv->reg[0x0061c00c/4] = 0x03010700; - pNv->reg[0x0061c010/4] = 0x0000152f; - pNv->reg[0x0061c014/4] = 0x00000000; - pNv->reg[0x0061c018/4] = 0x00245af8; - pNv->reg[0x0061c80c/4] = 0x03010700; - pNv->reg[0x0061c810/4] = 0x0000152f; - pNv->reg[0x0061c814/4] = 0x00000000; - pNv->reg[0x0061c818/4] = 0x00245af8; pNv->reg[0x0061A004/4] = 0x80550000; pNv->reg[0x0061A010/4] = 0x00000001; pNv->reg[0x0061A804/4] = 0x80550000; @@ -269,6 +243,15 @@ G80DispInit(ScrnInfoPtr pScrn) pNv->reg[0x0061B004/4] = 0x80550000; pNv->reg[0x0061B010/4] = 0x00000001; + return TRUE; +} + +Bool +G80DispInit(ScrnInfoPtr pScrn) +{ + G80Ptr pNv = G80PTR(pScrn); + CARD32 val; + if(pNv->reg[0x00610024/4] & 0x100) { pNv->reg[0x00610024/4] = 0x100; pNv->reg[0x006194E8/4] &= ~1; @@ -276,7 +259,15 @@ G80DispInit(ScrnInfoPtr pScrn) } pNv->reg[0x00610200/4] = 0x2b00; - while((pNv->reg[0x00610200/4] & 0x1e0000) != 0); + do { + val = pNv->reg[0x00610200/4]; + + if ((val & 0x9f0000) == 0x20000) + pNv->reg[0x00610200/4] = val | 0x800000; + + if ((val & 0x3f0000) == 0x30000) + pNv->reg[0x00610200/4] = val | 0x200000; + } while ((val & 0x1e0000) != 0); pNv->reg[0x00610300/4] = 1; pNv->reg[0x00610200/4] = 0x1000b03; while(!(pNv->reg[0x00610200/4] & 0x40000000)); @@ -295,86 +286,88 @@ void G80DispShutdown(ScrnInfoPtr pScrn) { G80Ptr pNv = G80PTR(pScrn); - CARD32 mask; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int i; + + for(i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + + G80CrtcBlankScreen(crtc, TRUE); + } + + C(0x00000080, 0); + + for(i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + + if(crtc->enabled) { + const CARD32 mask = 4 << G80CrtcGetHead(crtc); - G80DispBlankScreen(pScrn, TRUE); + pNv->reg[0x00610024/4] = mask; + while(!(pNv->reg[0x00610024/4] & mask)); + } + } - mask = 4 << pNv->head; - pNv->reg[0x00610024/4] = mask; - while(!(pNv->reg[0x00610024/4] & mask)); pNv->reg[0x00610200/4] = 0; pNv->reg[0x00610300/4] = 0; while((pNv->reg[0x00610200/4] & 0x1e0000) != 0); + while((pNv->reg[0x61C030/4] & 0x10000000)); + while((pNv->reg[0x61C830/4] & 0x10000000)); } -static void -setupDAC(G80Ptr pNv, Head head, ORNum or, DisplayModePtr mode) +void +G80CrtcDoModeFixup(DisplayModePtr dst, const DisplayModePtr src) { - const int dacOff = 0x80 * pNv->or; - - C(0x00000400 + dacOff, (head == HEAD0 ? 1 : 2) | 0x40); - C(0x00000404 + dacOff, - (mode->Flags & V_NHSYNC) ? 1 : 0 | - (mode->Flags & V_NVSYNC) ? 2 : 0); + /* Magic mode timing fudge factor */ + const int fudge = ((src->Flags & V_INTERLACE) && (src->Flags & V_DBLSCAN)) ? 2 : 1; + const int interlaceDiv = (src->Flags & V_INTERLACE) ? 2 : 1; + + /* Stash the src timings in the Crtc fields in dst */ + dst->CrtcHBlankStart = src->CrtcVTotal << 16 | src->CrtcHTotal; + dst->CrtcHSyncEnd = ((src->CrtcVSyncEnd - src->CrtcVSyncStart) / interlaceDiv - 1) << 16 | + (src->CrtcHSyncEnd - src->CrtcHSyncStart - 1); + dst->CrtcHBlankEnd = ((src->CrtcVBlankEnd - src->CrtcVSyncStart) / interlaceDiv - fudge) << 16 | + (src->CrtcHBlankEnd - src->CrtcHSyncStart - 1); + dst->CrtcHTotal = ((src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / interlaceDiv - fudge) << 16 | + (src->CrtcHTotal - src->CrtcHSyncStart + src->CrtcHBlankStart - 1); + dst->CrtcHSkew = ((src->CrtcVTotal + src->CrtcVBlankEnd - src->CrtcVSyncStart) / 2 - 2) << 16 | + ((2*src->CrtcVTotal - src->CrtcVSyncStart + src->CrtcVBlankStart) / 2 - 2); } -static void -setupSOR(G80Ptr pNv, Head head, ORNum or, DisplayModePtr mode) +static Bool +G80CrtcModeFixup(xf86CrtcPtr crtc, + DisplayModePtr mode, DisplayModePtr adjusted_mode) { - const int sorOff = 0x40 * pNv->or; + G80CrtcPrivPtr pPriv = crtc->driver_private; + + if(pPriv->skipModeFixup) + return TRUE; - C(0x00000600 + sorOff, - (head == HEAD0 ? 1 : 2) | - (mode->SynthClock > 165000 ? 0x500 : 0x100) | - ((mode->Flags & V_NHSYNC) ? 0x1000 : 0) | - ((mode->Flags & V_NVSYNC) ? 0x2000 : 0)); + G80CrtcDoModeFixup(adjusted_mode, mode); + return TRUE; } -Bool -G80DispSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode) +static void +G80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode, int x, int y) { - G80Ptr pNv = G80PTR(pScrn); - const int HDisplay = mode->HDisplay, VDisplay = mode->VDisplay; - const int headOff = 0x400 * pNv->head; - int interlaceDiv, fudge; - - if(pNv->BackendMode) - mode = pNv->BackendMode; + ScrnInfoPtr pScrn = crtc->scrn; + G80CrtcPrivPtr pPriv = crtc->driver_private; + const int HDisplay = adjusted_mode->HDisplay, VDisplay = adjusted_mode->VDisplay; + const int headOff = 0x400 * G80CrtcGetHead(crtc); - pNv->pclk = mode->SynthClock; - - /* Magic mode timing fudge factor */ - fudge = ((mode->Flags & V_INTERLACE) && (mode->Flags & V_DBLSCAN)) ? 2 : 1; - interlaceDiv = (mode->Flags & V_INTERLACE) ? 2 : 1; - - switch(pNv->orType) { - case DAC: - setupDAC(pNv, pNv->head, pNv->or, mode); - break; - case SOR: - setupSOR(pNv, pNv->head, pNv->or, mode); - break; - } + pPriv->pclk = adjusted_mode->Clock; - C(0x00000804 + headOff, mode->SynthClock | 0x800000); - C(0x00000808 + headOff, (mode->Flags & V_INTERLACE) ? 2 : 0); + C(0x00000804 + headOff, adjusted_mode->Clock | 0x800000); + C(0x00000808 + headOff, (adjusted_mode->Flags & V_INTERLACE) ? 2 : 0); C(0x00000810 + headOff, 0); C(0x0000082C + headOff, 0); - C(0x00000814 + headOff, mode->CrtcVTotal << 16 | mode->CrtcHTotal); - C(0x00000818 + headOff, - ((mode->CrtcVSyncEnd - mode->CrtcVSyncStart) / interlaceDiv - 1) << 16 | - (mode->CrtcHSyncEnd - mode->CrtcHSyncStart - 1)); - C(0x0000081C + headOff, - ((mode->CrtcVBlankEnd - mode->CrtcVSyncStart) / interlaceDiv - fudge) << 16 | - (mode->CrtcHBlankEnd - mode->CrtcHSyncStart - 1)); - C(0x00000820 + headOff, - ((mode->CrtcVTotal - mode->CrtcVSyncStart + mode->CrtcVBlankStart) / interlaceDiv - fudge) << 16 | - (mode->CrtcHTotal - mode->CrtcHSyncStart + mode->CrtcHBlankStart - 1)); - if(mode->Flags & V_INTERLACE) { - C(0x00000824 + headOff, - ((mode->CrtcVTotal + mode->CrtcVBlankEnd - mode->CrtcVSyncStart) / 2 - 2) << 16 | - ((2*mode->CrtcVTotal - mode->CrtcVSyncStart + mode->CrtcVBlankStart) / 2 - 2)); - } + C(0x00000814 + headOff, adjusted_mode->CrtcHBlankStart); + C(0x00000818 + headOff, adjusted_mode->CrtcHSyncEnd); + C(0x0000081C + headOff, adjusted_mode->CrtcHBlankEnd); + C(0x00000820 + headOff, adjusted_mode->CrtcHTotal); + if(adjusted_mode->Flags & V_INTERLACE) + C(0x00000824 + headOff, adjusted_mode->CrtcHSkew); C(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX); C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000); switch(pScrn->depth) { @@ -383,49 +376,25 @@ G80DispSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode) case 16: C(0x00000870 + headOff, 0xE800); break; case 24: C(0x00000870 + headOff, 0xCF00); break; } - C(0x000008A0 + headOff, 0); - if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) || - mode->CrtcHDisplay != HDisplay || mode->CrtcVDisplay != VDisplay) { - C(0x000008A4 + headOff, 9); - } else { - C(0x000008A4 + headOff, 0); - } + G80CrtcSetDither(crtc, pPriv->dither, FALSE); C(0x000008A8 + headOff, 0x40000); - /* Use the screen's panning, but not if it's bogus */ - if(pScrn->frameX0 >= 0 && pScrn->frameY0 >= 0 && - pScrn->frameX0 + HDisplay <= pScrn->virtualX && - pScrn->frameY0 + VDisplay <= pScrn->virtualY) { - C(0x000008C0 + headOff, pScrn->frameY0 << 16 | pScrn->frameX0); - } else { - C(0x000008C0 + headOff, 0); - } + C(0x000008C0 + headOff, y << 16 | x); C(0x000008C8 + headOff, VDisplay << 16 | HDisplay); C(0x000008D4 + headOff, 0); - C(0x000008D8 + headOff, mode->CrtcVDisplay << 16 | mode->CrtcHDisplay); - C(0x000008DC + headOff, mode->CrtcVDisplay << 16 | mode->CrtcHDisplay); - - G80DispBlankScreen(pScrn, FALSE); - - return TRUE; -} -void -G80DispAdjustFrame(G80Ptr pNv, int x, int y) -{ - const int headOff = 0x400 * pNv->head; - - C(0x000008C0 + headOff, y << 16 | x); - C(0x00000080, 0); + G80CrtcBlankScreen(crtc, FALSE); } void -G80DispBlankScreen(ScrnInfoPtr pScrn, Bool blank) +G80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank) { + ScrnInfoPtr pScrn = crtc->scrn; G80Ptr pNv = G80PTR(pScrn); - const int headOff = 0x400 * pNv->head; + G80CrtcPrivPtr pPriv = crtc->driver_private; + const int headOff = 0x400 * pPriv->head; if(blank) { - G80DispHideCursor(pNv, FALSE); + G80CrtcShowHideCursor(crtc, FALSE, FALSE); C(0x00000840 + headOff, 0); C(0x00000844 + headOff, 0); @@ -444,82 +413,202 @@ G80DispBlankScreen(ScrnInfoPtr pScrn, Bool blank) C(0x00000884 + headOff, (pNv->videoRam << 2) - 0x40); if(pNv->architecture != 0x50) C(0x0000089C + headOff, 1); - if(pNv->cursorVisible) - G80DispShowCursor(pNv, FALSE); + if(pPriv->cursorVisible) + G80CrtcShowHideCursor(crtc, TRUE, FALSE); C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000); C(0x00000844 + headOff, (pNv->videoRam * 1024 - 0x5000) >> 8); if(pNv->architecture != 0x50) C(0x0000085C + headOff, 1); C(0x00000874 + headOff, 1); } - - C(0x00000080, 0); } void -G80DispDPMSSet(ScrnInfoPtr pScrn, int mode, int flags) +G80CrtcDPMSSet(xf86CrtcPtr crtc, int mode) { - G80Ptr pNv = G80PTR(pScrn); - const int off = 0x800 * pNv->or; - CARD32 tmp; - - /* - * DPMSModeOn everything on - * DPMSModeStandby hsync disabled, vsync enabled - * DPMSModeSuspend hsync enabled, vsync disabled - * DPMSModeOff sync disabled - */ - switch(pNv->orType) { - case DAC: - while(pNv->reg[(0x0061A004+off)/4] & 0x80000000); - - tmp = pNv->reg[(0x0061A004+off)/4]; - tmp &= ~0x7f; - tmp |= 0x80000000; - - if(mode == DPMSModeStandby || mode == DPMSModeOff) - tmp |= 1; - if(mode == DPMSModeSuspend || mode == DPMSModeOff) - tmp |= 4; - if(mode != DPMSModeOn) - tmp |= 0x10; - if(mode == DPMSModeOff) - tmp |= 0x40; - - pNv->reg[(0x0061A004+off)/4] = tmp; - - break; - - case SOR: - while(pNv->reg[(0x0061C004+off)/4] & 0x80000000); - - tmp = pNv->reg[(0x0061C004+off)/4]; - tmp |= 0x80000000; - - if(mode == DPMSModeOn) - tmp |= 1; - else - tmp &= ~1; - - pNv->reg[(0x0061C004+off)/4] = tmp; - - break; - } } /******************************** Cursor stuff ********************************/ -void G80DispShowCursor(G80Ptr pNv, Bool update) +static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update) { - const int headOff = 0x400 * pNv->head; + ScrnInfoPtr pScrn = crtc->scrn; + G80CrtcPrivPtr pPriv = crtc->driver_private; + const int headOff = 0x400 * G80CrtcGetHead(crtc); + + C(0x00000880 + headOff, show ? 0x85000000 : 0x5000000); + if(update) { + pPriv->cursorVisible = show; + C(0x00000080, 0); + } +} - C(0x00000880 + headOff, 0x85000000); - if(update) C(0x00000080, 0); +void G80CrtcShowCursor(xf86CrtcPtr crtc) +{ + G80CrtcShowHideCursor(crtc, TRUE, TRUE); } -void G80DispHideCursor(G80Ptr pNv, Bool update) +void G80CrtcHideCursor(xf86CrtcPtr crtc) { - const int headOff = 0x400 * pNv->head; + G80CrtcShowHideCursor(crtc, FALSE, TRUE); +} - C(0x00000880 + headOff, 0x5000000); +/******************************** CRTC stuff ********************************/ + +static Bool +G80CrtcLock(xf86CrtcPtr crtc) +{ + return FALSE; +} + +static void +G80CrtcPrepare(xf86CrtcPtr crtc) +{ + ScrnInfoPtr pScrn = crtc->scrn; + G80CrtcPrivPtr pPriv = crtc->driver_private; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int i; + + for(i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + + if(!output->crtc) + output->funcs->mode_set(output, NULL, NULL); + } + + pPriv->skipModeFixup = FALSE; +} + +void +G80CrtcSkipModeFixup(xf86CrtcPtr crtc) +{ + G80CrtcPrivPtr pPriv = crtc->driver_private; + pPriv->skipModeFixup = TRUE; +} + +void +G80CrtcSetDither(xf86CrtcPtr crtc, Bool dither, Bool update) +{ + ScrnInfoPtr pScrn = crtc->scrn; + G80CrtcPrivPtr pPriv = crtc->driver_private; + const int headOff = 0x400 * G80CrtcGetHead(crtc); + + pPriv->dither = dither; + + C(0x000008A0 + headOff, dither ? 0x11 : 0); if(update) C(0x00000080, 0); } + +static void ComputeAspectScale(DisplayModePtr mode, int *outX, int *outY) +{ + float scaleX, scaleY, scale; + + scaleX = mode->CrtcHDisplay / (float)mode->HDisplay; + scaleY = mode->CrtcVDisplay / (float)mode->VDisplay; + + if(scaleX > scaleY) + scale = scaleY; + else + scale = scaleX; + + *outX = mode->HDisplay * scale; + *outY = mode->VDisplay * scale; +} + +void G80CrtcSetScale(xf86CrtcPtr crtc, DisplayModePtr mode, + enum G80ScaleMode scale) +{ + ScrnInfoPtr pScrn = crtc->scrn; + G80CrtcPrivPtr pPriv = crtc->driver_private; + const int headOff = 0x400 * pPriv->head; + int outX, outY; + + switch(scale) { + default: + case G80_SCALE_ASPECT: + ComputeAspectScale(mode, &outX, &outY); + break; + + case G80_SCALE_OFF: + case G80_SCALE_FILL: + outX = mode->CrtcHDisplay; + outY = mode->CrtcVDisplay; + break; + + case G80_SCALE_CENTER: + outX = mode->HDisplay; + outY = mode->VDisplay; + break; + } + + if((mode->Flags & V_DBLSCAN) || (mode->Flags & V_INTERLACE) || + mode->HDisplay != outX || mode->VDisplay != outY) { + C(0x000008A4 + headOff, 9); + } else { + C(0x000008A4 + headOff, 0); + } + C(0x000008D8 + headOff, outY << 16 | outX); + C(0x000008DC + headOff, outY << 16 | outX); +} + +static void +G80CrtcCommit(xf86CrtcPtr crtc) +{ + ScrnInfoPtr pScrn = crtc->scrn; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + int i, crtc_mask = 0; + + /* If any heads are unused, blank them */ + for(i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + + if(output->crtc) + /* XXXagp: This assumes that xf86_config->crtc[i] is HEADi */ + crtc_mask |= 1 << G80CrtcGetHead(output->crtc); + } + + for(i = 0; i < xf86_config->num_crtc; i++) + if(!((1 << i) & crtc_mask)) + G80CrtcBlankScreen(xf86_config->crtc[i], TRUE); + + C(0x00000080, 0); +} + +static const xf86CrtcFuncsRec g80_crtc_funcs = { + .dpms = G80CrtcDPMSSet, + .save = NULL, + .restore = NULL, + .lock = G80CrtcLock, + .unlock = NULL, + .mode_fixup = G80CrtcModeFixup, + .prepare = G80CrtcPrepare, + .mode_set = G80CrtcModeSet, + // .gamma_set = G80DispGammaSet, + .commit = G80CrtcCommit, + .shadow_create = NULL, + .shadow_destroy = NULL, + .set_cursor_position = G80SetCursorPosition, + .show_cursor = G80CrtcShowCursor, + .hide_cursor = G80CrtcHideCursor, + .load_cursor_argb = G80LoadCursorARGB, + .destroy = NULL, +}; + +void +G80DispCreateCrtcs(ScrnInfoPtr pScrn) +{ + G80Ptr pNv = G80PTR(pScrn); + Head head; + xf86CrtcPtr crtc; + G80CrtcPrivPtr g80_crtc; + + /* Create a "crtc" object for each head */ + for(head = HEAD0; head <= HEAD1; head++) { + crtc = xf86CrtcCreate(pScrn, &g80_crtc_funcs); + if(!crtc) return; + + g80_crtc = xnfcalloc(sizeof(*g80_crtc), 1); + g80_crtc->head = head; + g80_crtc->dither = pNv->Dither; + crtc->driver_private = g80_crtc; + } +} |