diff options
Diffstat (limited to 'src/g80_sor.c')
-rw-r--r-- | src/g80_sor.c | 141 |
1 files changed, 130 insertions, 11 deletions
diff --git a/src/g80_sor.c b/src/g80_sor.c index ea7e015..a51e5e0 100644 --- a/src/g80_sor.c +++ b/src/g80_sor.c @@ -38,8 +38,9 @@ G80SorSetPClk(xf86OutputPtr output, int pclk) G80Ptr pNv = G80PTR(output->scrn); G80OutputPrivPtr pPriv = output->driver_private; const int orOff = 0x800 * pPriv->or; + const int limit = pPriv->panelType == LVDS ? 112000 : 165000; - pNv->reg[(0x00614300+orOff)/4] = (pclk > 165000) ? 0x101 : 0; + pNv->reg[(0x00614300+orOff)/4] = (pclk > limit) ? 0x101 : 0; } static void @@ -61,6 +62,7 @@ G80SorDPMSSet(xf86OutputPtr output, int mode) tmp &= ~1; pNv->reg[(0x0061C004+off)/4] = tmp; + while((pNv->reg[(0x61C030+off)/4] & 0x10000000)); } static void @@ -70,6 +72,7 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode, ScrnInfoPtr pScrn = output->scrn; G80OutputPrivPtr pPriv = output->driver_private; const int sorOff = 0x40 * pPriv->or; + CARD32 type; if(!adjusted_mode) { /* Disconnect the SOR */ @@ -77,6 +80,13 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode, return; } + if(pPriv->panelType == LVDS) + type = 0; + else if(adjusted_mode->Clock > 165000) + type = 0x500; + else + type = 0x100; + // This wouldn't be necessary, but the server is stupid and calls // G80SorDPMSSet after the output is disconnected, even though the hardware // turns it off automatically. @@ -84,7 +94,7 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode, C(0x00000600 + sorOff, (G80CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | - (adjusted_mode->Clock > 165000 ? 0x500 : 0x100) | + type | ((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) | ((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0)); } @@ -92,7 +102,6 @@ G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode, static xf86OutputStatus G80SorDetect(xf86OutputPtr output) { - G80OutputPrivPtr pPriv = output->driver_private; /* Assume physical status isn't going to change before the BlockHandler */ @@ -103,16 +112,71 @@ G80SorDetect(xf86OutputPtr output) return pPriv->cached_status; } +static xf86OutputStatus +G80SorLVDSDetect(xf86OutputPtr output) +{ + /* Assume LVDS is always connected */ + return XF86OutputStatusConnected; +} + static void G80SorDestroy(xf86OutputPtr output) { + G80OutputPrivPtr pPriv = output->driver_private; + G80OutputDestroy(output); + if(pPriv->nativeMode) { + if(pPriv->nativeMode->name) + xfree(pPriv->nativeMode->name); + xfree(pPriv->nativeMode); + } + xfree(output->driver_private); output->driver_private = NULL; } -static const xf86OutputFuncsRec G80SorOutputFuncs = { +/******************** LVDS ********************/ +static Bool +G80SorModeFixupScale(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + G80OutputPrivPtr pPriv = output->driver_private; + DisplayModePtr native = pPriv->nativeMode; + + // Stash the saved mode timings in adjusted_mode + adjusted_mode->Clock = native->Clock; + adjusted_mode->Flags = native->Flags; + adjusted_mode->CrtcHDisplay = native->CrtcHDisplay; + adjusted_mode->CrtcHBlankStart = native->CrtcHBlankStart; + adjusted_mode->CrtcHSyncStart = native->CrtcHSyncStart; + adjusted_mode->CrtcHSyncEnd = native->CrtcHSyncEnd; + adjusted_mode->CrtcHBlankEnd = native->CrtcHBlankEnd; + adjusted_mode->CrtcHTotal = native->CrtcHTotal; + adjusted_mode->CrtcHSkew = native->CrtcHSkew; + adjusted_mode->CrtcVDisplay = native->CrtcVDisplay; + adjusted_mode->CrtcVBlankStart = native->CrtcVBlankStart; + adjusted_mode->CrtcVSyncStart = native->CrtcVSyncStart; + adjusted_mode->CrtcVSyncEnd = native->CrtcVSyncEnd; + adjusted_mode->CrtcVBlankEnd = native->CrtcVBlankEnd; + adjusted_mode->CrtcVTotal = native->CrtcVTotal; + adjusted_mode->CrtcHAdjusted = native->CrtcHAdjusted; + adjusted_mode->CrtcVAdjusted = native->CrtcVAdjusted; + + // This mode is already "fixed" + G80CrtcSkipModeFixup(output->crtc); + + return TRUE; +} + +static DisplayModePtr +G80SorGetLVDSModes(xf86OutputPtr output) +{ + G80OutputPrivPtr pPriv = output->driver_private; + return xf86DuplicateMode(pPriv->nativeMode); +} + +static const xf86OutputFuncsRec G80SorTMDSOutputFuncs = { .dpms = G80SorDPMSSet, .save = NULL, .restore = NULL, @@ -126,33 +190,88 @@ static const xf86OutputFuncsRec G80SorOutputFuncs = { .destroy = G80SorDestroy, }; +static const xf86OutputFuncsRec G80SorLVDSOutputFuncs = { + .dpms = G80SorDPMSSet, + .save = NULL, + .restore = NULL, + .mode_valid = G80OutputModeValid, + .mode_fixup = G80SorModeFixupScale, + .prepare = G80OutputPrepare, + .commit = G80OutputCommit, + .mode_set = G80SorModeSet, + .detect = G80SorLVDSDetect, + .get_modes = G80SorGetLVDSModes, + .destroy = G80SorDestroy, +}; + +static DisplayModePtr +GetLVDSNativeMode(G80Ptr pNv) +{ + DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec)); + const CARD32 size = pNv->reg[0x00610B4C/4]; + const int width = size & 0x3fff; + const int height = (size >> 16) & 0x3fff; + + mode->HDisplay = mode->CrtcHDisplay = width; + mode->VDisplay = mode->CrtcVDisplay = height; + mode->Clock = pNv->reg[0x610AD4/4] & 0x3fffff; + mode->CrtcHBlankStart = pNv->reg[0x610AFC/4]; + mode->CrtcHSyncEnd = pNv->reg[0x610B04/4]; + mode->CrtcHBlankEnd = pNv->reg[0x610AE8/4]; + mode->CrtcHTotal = pNv->reg[0x610AF4/4]; + + mode->next = mode->prev = NULL; + mode->status = MODE_OK; + mode->type = M_T_DRIVER | M_T_PREFERRED; + + xf86SetModeDefaultName(mode); + + return mode; +} + xf86OutputPtr -G80CreateSor(ScrnInfoPtr pScrn, ORNum or) +G80CreateSor(ScrnInfoPtr pScrn, ORNum or, PanelType panelType) { G80Ptr pNv = G80PTR(pScrn); G80OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1); const int off = 0x800 * or; xf86OutputPtr output; char orName[5]; + const xf86OutputFuncsRec *funcs; if(!pPriv) return FALSE; - pNv->reg[(0x61C00C+off)/4] = 0x03010700; - pNv->reg[(0x61C010+off)/4] = 0x0000152f; - pNv->reg[(0x61C014+off)/4] = 0x00000000; - pNv->reg[(0x61C018+off)/4] = 0x00245af8; + if(panelType == LVDS) { + strcpy(orName, "LVDS"); + funcs = &G80SorLVDSOutputFuncs; + } else { + snprintf(orName, 5, "DVI%d", or); + pNv->reg[(0x61C00C+off)/4] = 0x03010700; + pNv->reg[(0x61C010+off)/4] = 0x0000152f; + pNv->reg[(0x61C014+off)/4] = 0x00000000; + pNv->reg[(0x61C018+off)/4] = 0x00245af8; + funcs = &G80SorTMDSOutputFuncs; + } - snprintf(orName, 5, "DVI%i", or); - output = xf86OutputCreate(pScrn, &G80SorOutputFuncs, orName); + output = xf86OutputCreate(pScrn, funcs, orName); pPriv->type = SOR; pPriv->or = or; + pPriv->panelType = panelType; pPriv->cached_status = XF86OutputStatusUnknown; pPriv->set_pclk = G80SorSetPClk; output->driver_private = pPriv; output->interlaceAllowed = TRUE; output->doubleScanAllowed = TRUE; + if(panelType == LVDS) { + pPriv->nativeMode = GetLVDSNativeMode(pNv); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n", + orName, pPriv->nativeMode->HDisplay, + pPriv->nativeMode->VDisplay); + } + return output; } |