From 51c6425bea6e4ef02f7b76e58e759f99b0e993e8 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Thu, 10 May 2007 17:09:36 -0700 Subject: Add rudimentary VBE-based dual head support for pre-G80. --- man/nv.man | 5 ++ src/nv_driver.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/nv_hw.c | 15 +++- src/nv_type.h | 9 +++ 4 files changed, 228 insertions(+), 15 deletions(-) diff --git a/man/nv.man b/man/nv.man index 9f5917f..e9bcec9 100644 --- a/man/nv.man +++ b/man/nv.man @@ -93,6 +93,11 @@ the wrong one, this option may be used to force usage of a particular output. The options are "0" or "1". Default: autodetected. .TP +.BI "Option \*qDualhead\*q \*q" boolean \*q +Enables simple VBE-based dual head mode. +This sets the same resolution on both outputs and lays them out side-by-side. +The screens will be panned together as one big metamode if the virtual desktop is larger than both screens combined. +.TP .BI "Option \*qFlatPanel\*q \*q" boolean \*q The driver usually can autodetect the presence of a digital flat panel. In the case that this fails, this option can be used to force the driver to diff --git a/src/nv_driver.c b/src/nv_driver.c index a20bf64..4c2a395 100644 --- a/src/nv_driver.c +++ b/src/nv_driver.c @@ -34,6 +34,7 @@ #include "nv_include.h" #include "xf86int10.h" +#include "vbeModes.h" const OptionInfoRec * RivaAvailableOptions(int chipid, int busid); Bool RivaGetScrnInfoRec(PciChipsets *chips, int chip); @@ -70,8 +71,10 @@ static Bool NVMapMem(ScrnInfoPtr pScrn); static Bool NVMapMemFBDev(ScrnInfoPtr pScrn); static Bool NVUnmapMem(ScrnInfoPtr pScrn); static void NVSave(ScrnInfoPtr pScrn); +static void NVSaveRestoreVBE(ScrnInfoPtr, vbeSaveRestoreFunction); static void NVRestore(ScrnInfoPtr pScrn); static Bool NVModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode); +static Bool NVSetModeVBE(ScrnInfoPtr pScrn, DisplayModePtr pMode); /* @@ -410,6 +413,17 @@ static const char *vbeSymbols[] = { "vbeDoEDID", NULL }; + +static const char *vbeModeSymbols[] = { + "VBEExtendedInit", + "VBEGetVBEInfo", + "VBEGetModePool", + "VBEValidateModes", + "VBESetModeParameters", + "VBEGetVBEMode", + "VBESetVBEMode", + NULL +}; #endif static const char *i2cSymbols[] = { @@ -488,7 +502,8 @@ typedef enum { OPTION_FP_DITHER, OPTION_CRTC_NUMBER, OPTION_FP_SCALE, - OPTION_FP_TWEAK + OPTION_FP_TWEAK, + OPTION_DUALHEAD, } NVOpts; @@ -505,6 +520,7 @@ static const OptionInfoRec NVOptions[] = { { OPTION_CRTC_NUMBER, "CrtcNumber", OPTV_INTEGER, {0}, FALSE }, { OPTION_FP_SCALE, "FPScale", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_FP_TWEAK, "FPTweak", OPTV_INTEGER, {0}, FALSE }, + { OPTION_DUALHEAD, "DualHead", OPTV_BOOLEAN, {0}, FALSE }, { -1, NULL, OPTV_NONE, {0}, FALSE } }; @@ -821,6 +837,27 @@ NVSwitchMode(int scrnIndex, DisplayModePtr mode, int flags) return NVModeInit(pScrn, mode); } +Bool +NVSwitchModeVBE(int scrnIndex, DisplayModePtr mode, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + NVPtr pNv = NVPTR(pScrn); + const Bool disableAccess = pNv->accessEnabled; + + if(disableAccess) + pScrn->EnableDisableFBAccess(scrnIndex, FALSE); + + NVSync(pScrn); + if (!NVSetModeVBE(pScrn, mode)) + return FALSE; + NVAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0); + + if(disableAccess) + pScrn->EnableDisableFBAccess(scrnIndex, TRUE); + + return TRUE; +} + /* * This function is used to initialize the Start Address - the first * displayed location in the video memory. @@ -869,6 +906,17 @@ NVEnterVTFBDev(int scrnIndex, int flags) return TRUE; } +static Bool +NVEnterVTVBE(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + + if (!NVSetModeVBE(pScrn, pScrn->currentMode)) + return FALSE; + NVAdjustFrame(scrnIndex, 0, 0, 0); + return TRUE; +} + /* * This is called when VT switching away from the X server. Its job is * to restore the previous (text) mode. @@ -888,7 +936,14 @@ NVLeaveVT(int scrnIndex, int flags) NVLockUnlock(pNv, 1); } +static void +NVLeaveVTVBE(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + NVSync(pScrn); + NVSaveRestoreVBE(pScrn, MODE_RESTORE); +} static void NVBlockHandler ( @@ -930,9 +985,15 @@ NVCloseScreen(int scrnIndex, ScreenPtr pScreen) NVPtr pNv = NVPTR(pScrn); if (pScrn->vtSema) { - NVSync(pScrn); - NVRestore(pScrn); - NVLockUnlock(pNv, 1); + if (!pNv->NoAccel) + NVSync(pScrn); + + if (pNv->VBEDualhead) { + NVSaveRestoreVBE(pScrn, MODE_RESTORE); + } else { + NVRestore(pScrn); + NVLockUnlock(pNv, 1); + } } NVUnmapMem(pScrn); @@ -956,6 +1017,16 @@ NVCloseScreen(int scrnIndex, ScreenPtr pScreen) return (*pScreen->CloseScreen)(scrnIndex, pScreen); } +static void +NVEnableDisableFBAccess(int scrnIndex, Bool enable) +{ + NVPtr pNv = NVPTR(xf86Screens[scrnIndex]); + + pNv->accessEnabled = enable; + pNv->EnableDisableFBAccess(scrnIndex, enable); +} + + /* Free up any persistent data structures */ /* Optional */ @@ -1404,7 +1475,43 @@ NVPreInit(ScrnInfoPtr pScrn, int flags) } else { pNv->usePanelTweak = FALSE; } - + + if (xf86ReturnOptValBool(pNv->Options, OPTION_DUALHEAD, FALSE)) { + if (pNv->FBDev) + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "FBDev and Dualhead are incompatible.\n"); + else + pNv->VBEDualhead = TRUE; + } + + if (pNv->VBEDualhead) { + if (!xf86LoadSubModule(pScrn, "vbe")) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Couldn't load the VBE module and Dualhead is " + "enabled.\n"); + return FALSE; + } + xf86LoaderReqSymLists(vbeModeSymbols, NULL); + pNv->pVbe = VBEExtendedInit(NULL, pNv->pEnt->index, + SET_BIOS_SCRATCH | RESTORE_BIOS_SCRATCH); + if (!pNv->pVbe) return FALSE; + + pNv->pVbeInfo = VBEGetVBEInfo(pNv->pVbe); + if (!pNv->pVbeInfo) return FALSE; + + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "Using VBE dual-head mode.\n"); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Using software cursor.\n"); + pNv->HWCursor = FALSE; + + pScrn->SwitchMode = NVSwitchModeVBE; + pScrn->EnterVT = NVEnterVTVBE; + pScrn->LeaveVT = NVLeaveVTVBE; + pScrn->ValidMode = NULL; + } + if (pNv->pEnt->device->MemBase != 0) { /* Require that the config file value matches one of the PCI values. */ if (!xf86CheckPciMemBase(pNv->PciInfo, pNv->pEnt->device->MemBase)) { @@ -1622,14 +1729,31 @@ NVPreInit(ScrnInfoPtr pScrn, int flags) * pScrn->maxVValue are set. Since our NVValidMode() already takes * care of this, we don't worry about setting them here. */ - i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, - pScrn->display->modes, clockRanges, - NULL, 256, max_width, - 512, 128, max_height, - pScrn->display->virtualX, - pScrn->display->virtualY, - pNv->ScratchBufferStart, - LOOKUP_BEST_REFRESH); + if (pNv->VBEDualhead) { + pScrn->modePool = VBEGetModePool(pScrn, pNv->pVbe, pNv->pVbeInfo, + V_MODETYPE_VBE); + + VBESetModeNames(pScrn->modePool); + i = VBEValidateModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, clockRanges, + NULL, 256, max_width, + 512, 128, max_height, + pScrn->display->virtualX, + pScrn->display->virtualY, + pNv->ScratchBufferStart, + LOOKUP_BEST_REFRESH); + if (i > 0) + VBESetModeParameters(pScrn, pNv->pVbe); + } else { + i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, clockRanges, + NULL, 256, max_width, + 512, 128, max_height, + pScrn->display->virtualX, + pScrn->display->virtualY, + pNv->ScratchBufferStart, + LOOKUP_BEST_REFRESH); + } if (i < 1 && pNv->FBDev) { fbdevHWUseBuildinMode(pScrn); @@ -1662,6 +1786,22 @@ NVPreInit(ScrnInfoPtr pScrn, int flags) */ xf86SetCrtcForModes(pScrn, 0); + if (pNv->VBEDualhead) { + DisplayModePtr p = pScrn->modes; + + /* + * Loop through modes and double their widths. Stash the real width in + * CrtcHDisplay. Also adjust the screen dimensions. + */ + do { + p->CrtcHDisplay = p->HDisplay; + p->HDisplay *= 2; + } while ((p = p->next) != pScrn->modes); + + pScrn->virtualX *= 2; + pScrn->displayWidth *= 2; + } + /* Set the current mode to the first in the list */ pScrn->currentMode = pScrn->modes; @@ -1843,6 +1983,32 @@ NVModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode) return TRUE; } +static Bool +NVSetModeVBE(ScrnInfoPtr pScrn, DisplayModePtr pMode) +{ + NVPtr pNv = NVPTR(pScrn); + VbeModeInfoData *data; + int mode; + + data = (VbeModeInfoData*)pMode->Private; + mode = data->mode | 1 << 14; + + if(!VBESetVBEMode(pNv->pVbe, mode, data->block)) + return FALSE; + pNv->PCRTC0[0x820/4] = pNv->PCRTC0[0x2820/4] = + pScrn->displayWidth * (pScrn->bitsPerPixel / 8); + pNv->vbeCRTC1Offset = pMode->CrtcHDisplay * (pScrn->bitsPerPixel / 8); + + pScrn->vtSema = TRUE; + + NVLoadStateExt(pNv, NULL); + NVResetGraphics(pScrn); + + pNv->CurrentLayout.mode = pMode; + + return TRUE; +} + /* * Restore the initial (text) mode. */ @@ -2015,6 +2181,10 @@ NVScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) fbdevHWSave(pScrn); if (!fbdevHWModeInit(pScrn, pScrn->currentMode)) return FALSE; + } else if (pNv->VBEDualhead) { + NVSaveRestoreVBE(pScrn, MODE_SAVE); + if (!NVSetModeVBE(pScrn, pScrn->currentMode)) + return FALSE; } else { /* Save the current state */ NVSave(pScrn); @@ -2210,6 +2380,10 @@ NVScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) pNv->BlockHandler = pScreen->BlockHandler; pScreen->BlockHandler = NVBlockHandler; + pNv->accessEnabled = TRUE; + pNv->EnableDisableFBAccess = pScrn->EnableDisableFBAccess; + pScrn->EnableDisableFBAccess = NVEnableDisableFBAccess; + #ifdef RANDR /* Install our DriverFunc. We have to do it this way instead of using the * HaveDriverFuncs argument to xf86AddDriver, because InitOutput clobbers @@ -2248,6 +2422,20 @@ NVSave(ScrnInfoPtr pScrn) NVDACSave(pScrn, vgaReg, nvReg, pNv->Primary); } +static void +NVSaveRestoreVBE(ScrnInfoPtr pScrn, vbeSaveRestoreFunction function) +{ + NVPtr pNv = NVPTR(pScrn); + + if (function == MODE_SAVE) { + VBEGetVBEMode(pNv->pVbe, &pNv->vbeMode); + NVSave(pScrn); + } else if (function == MODE_RESTORE) { + NVRestore(pScrn); + VBESetVBEMode(pNv->pVbe, pNv->vbeMode, NULL); + } +} + #ifdef RANDR static Bool NVRandRGetInfo(ScrnInfoPtr pScrn, Rotation *rotations) diff --git a/src/nv_hw.c b/src/nv_hw.c index a221d11..ae499fa 100644 --- a/src/nv_hw.c +++ b/src/nv_hw.c @@ -948,7 +948,8 @@ void NVLoadStateExt ( pNv->PTIMER[0x0100] = 0xFFFFFFFF; if(pNv->Architecture == NV_ARCH_04) { - pNv->PFB[0x0200/4] = state->config; + if (state) + pNv->PFB[0x0200/4] = state->config; } else if((pNv->Architecture < NV_ARCH_40) || ((pNv->Chipset & 0xfff0) == 0x0040)) @@ -1411,6 +1412,11 @@ void NVLoadStateExt ( pNv->PFIFO[0x0495] = 0x00000001; pNv->PFIFO[0x0140] = 0x00000001; + if(!state) { + pNv->CurrentState = NULL; + return; + } + if(pNv->Architecture >= NV_ARCH_10) { if(pNv->twoHeads) { pNv->PCRTC0[0x0860/4] = state->head; @@ -1590,7 +1596,12 @@ void NVSetStartAddress ( CARD32 start ) { - pNv->PCRTC[0x800/4] = start; + if (pNv->VBEDualhead) { + pNv->PCRTC0[0x800/4] = start; + pNv->PCRTC0[0x2800/4] = start + pNv->vbeCRTC1Offset; + } else { + pNv->PCRTC[0x800/4] = start; + } } diff --git a/src/nv_type.h b/src/nv_type.h index 6b29d99..b10babc 100644 --- a/src/nv_type.h +++ b/src/nv_type.h @@ -133,6 +133,8 @@ typedef struct { void (*PointerMoved)(int index, int x, int y); ScreenBlockHandlerProcPtr BlockHandler; CloseScreenProcPtr CloseScreen; + xf86EnableDisableFBAccessProc *EnableDisableFBAccess; + Bool accessEnabled; Bool FBDev; int Rotate; NVFBLayout CurrentLayout; @@ -174,6 +176,13 @@ typedef struct { Bool WaitVSyncPossible; Bool BlendingPossible; Bool RandRRotation; + + /* VBE dual-head */ + Bool VBEDualhead; + vbeInfoPtr pVbe; + VbeInfoBlock *pVbeInfo; + int vbeMode; + CARD32 vbeCRTC1Offset; } NVRec, *NVPtr; #define NVPTR(p) ((NVPtr)((p)->driverPrivate)) -- cgit v1.2.3