diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2007-09-30 14:18:15 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2007-09-30 14:18:15 +0000 |
commit | f25cb21322e90ab2c4d34d5bd8027006950662f6 (patch) | |
tree | f1986b6c009c7eba0d01e7a0297c5deec3163b97 /driver/xf86-video-nv/src/g80_sor.c | |
parent | da0ef05280ed34be0687c3802ab4724f7a9d063a (diff) |
xf86-video-nv 2.1.5
Diffstat (limited to 'driver/xf86-video-nv/src/g80_sor.c')
-rw-r--r-- | driver/xf86-video-nv/src/g80_sor.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/driver/xf86-video-nv/src/g80_sor.c b/driver/xf86-video-nv/src/g80_sor.c new file mode 100644 index 000000000..c98b66d42 --- /dev/null +++ b/driver/xf86-video-nv/src/g80_sor.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2007 NVIDIA, Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define DPMS_SERVER +#include <X11/extensions/dpms.h> +#include <X11/Xatom.h> + +#include "g80_type.h" +#include "g80_display.h" +#include "g80_output.h" + +static void +G80SorSetPClk(xf86OutputPtr output, int pclk) +{ + G80Ptr pNv = G80PTR(output->scrn); + G80OutputPrivPtr pPriv = output->driver_private; + const int orOff = 0x800 * pPriv->or; + const int limit = 165000; + + pNv->reg[(0x00614300+orOff)/4] = (pclk > limit) ? 0x101 : 0; +} + +static void +G80SorDPMSSet(xf86OutputPtr output, int mode) +{ + G80Ptr pNv = G80PTR(output->scrn); + G80OutputPrivPtr pPriv = output->driver_private; + const int off = 0x800 * pPriv->or; + CARD32 tmp; + + 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; + while((pNv->reg[(0x61C030+off)/4] & 0x10000000)); +} + +static int +G80TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode) +{ + // Disable dual-link modes until I can find a way to make them work + // reliably. + if (mode->Clock > 165000) + return MODE_CLOCK_HIGH; + + return G80OutputModeValid(output, mode); +} + +static int +G80LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode) +{ + G80OutputPrivPtr pPriv = output->driver_private; + DisplayModePtr native = pPriv->nativeMode; + + // Ignore modes larger than the native res. + if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay) + return MODE_PANEL; + + return G80OutputModeValid(output, mode); +} + +static void +G80SorModeSet(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + ScrnInfoPtr pScrn = output->scrn; + G80OutputPrivPtr pPriv = output->driver_private; + const int sorOff = 0x40 * pPriv->or; + CARD32 type; + + if(!adjusted_mode) { + /* Disconnect the SOR */ + C(0x00000600 + sorOff, 0); + 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. + G80SorDPMSSet(output, DPMSModeOn); + + C(0x00000600 + sorOff, + (G80CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | + type | + ((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) | + ((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0)); + + G80CrtcSetScale(output->crtc, adjusted_mode, pPriv->scale); +} + +static xf86OutputStatus +G80SorDetect(xf86OutputPtr output) +{ + G80OutputPrivPtr pPriv = output->driver_private; + + /* Assume physical status isn't going to change before the BlockHandler */ + if(pPriv->cached_status != XF86OutputStatusUnknown) + return pPriv->cached_status; + + G80OutputPartnersDetect(pPriv->partner, output, pPriv->i2c); + 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); + + xf86DeleteMode(&pPriv->nativeMode, pPriv->nativeMode); + + xfree(output->driver_private); + output->driver_private = NULL; +} + +static void G80SorSetModeBackend(DisplayModePtr dst, const DisplayModePtr src) +{ + // Stash the backend mode timings from src into dst + dst->Clock = src->Clock; + dst->Flags = src->Flags; + dst->CrtcHDisplay = src->CrtcHDisplay; + dst->CrtcHBlankStart = src->CrtcHBlankStart; + dst->CrtcHSyncStart = src->CrtcHSyncStart; + dst->CrtcHSyncEnd = src->CrtcHSyncEnd; + dst->CrtcHBlankEnd = src->CrtcHBlankEnd; + dst->CrtcHTotal = src->CrtcHTotal; + dst->CrtcHSkew = src->CrtcHSkew; + dst->CrtcVDisplay = src->CrtcVDisplay; + dst->CrtcVBlankStart = src->CrtcVBlankStart; + dst->CrtcVSyncStart = src->CrtcVSyncStart; + dst->CrtcVSyncEnd = src->CrtcVSyncEnd; + dst->CrtcVBlankEnd = src->CrtcVBlankEnd; + dst->CrtcVTotal = src->CrtcVTotal; + dst->CrtcHAdjusted = src->CrtcHAdjusted; + dst->CrtcVAdjusted = src->CrtcVAdjusted; +} + +static Bool +G80SorModeFixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + G80OutputPrivPtr pPriv = output->driver_private; + DisplayModePtr native = pPriv->nativeMode; + + if(native && pPriv->scale != G80_SCALE_OFF) { + G80SorSetModeBackend(adjusted_mode, native); + // This mode is already "fixed" + G80CrtcSkipModeFixup(output->crtc); + } + + return TRUE; +} + +static Bool +G80SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ + int scrnIndex = output->scrn->scrnIndex; + G80OutputPrivPtr pPriv = output->driver_private; + DisplayModePtr modes = output->probed_modes; + + xf86DeleteMode(&pPriv->nativeMode, pPriv->nativeMode); + + if(modes) { + // Find the preferred mode and use that as the "native" mode. + // If no preferred mode is available, use the first one. + DisplayModePtr mode; + + // Find the preferred mode. + for(mode = modes; mode; mode = mode->next) { + if(mode->type & M_T_PREFERRED) { + xf86DrvMsgVerb(scrnIndex, X_INFO, 5, + "%s: preferred mode is %s\n", + output->name, mode->name); + break; + } + } + + // XXX: May not want to allow scaling if no preferred mode is found. + if(!mode) { + mode = modes; + xf86DrvMsgVerb(scrnIndex, X_INFO, 5, + "%s: no preferred mode found, using %s\n", + output->name, mode->name); + } + + pPriv->nativeMode = xf86DuplicateMode(mode); + G80CrtcDoModeFixup(pPriv->nativeMode, mode); + } + + return G80SorModeFixup(output, mode, adjusted_mode); +} + +static DisplayModePtr +G80SorGetLVDSModes(xf86OutputPtr output) +{ + G80OutputPrivPtr pPriv = output->driver_private; + return xf86DuplicateMode(pPriv->nativeMode); +} + +#ifdef RANDR_12_INTERFACE +#define MAKE_ATOM(a) MakeAtom((a), sizeof(a) - 1, TRUE); + +struct property { + Atom atom; + INT32 range[2]; +}; + +static struct { + struct property dither; + struct property scale; +} properties; + +static void +G80SorCreateResources(xf86OutputPtr output) +{ + ScrnInfoPtr pScrn = output->scrn; + G80Ptr pNv = G80PTR(pScrn); + int data, err; + const char *s; + + /******** dithering ********/ + properties.dither.atom = MAKE_ATOM("dither"); + properties.dither.range[0] = 0; + properties.dither.range[1] = 1; + err = RRConfigureOutputProperty(output->randr_output, + properties.dither.atom, FALSE, TRUE, FALSE, + 2, properties.dither.range); + if(err) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to configure dithering property for %s: error %d\n", + output->name, err); + + // Set the default value + data = pNv->Dither; + err = RRChangeOutputProperty(output->randr_output, properties.dither.atom, + XA_INTEGER, 32, PropModeReplace, 1, &data, + FALSE, FALSE); + if(err) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to set dithering property for %s: error %d\n", + output->name, err); + + /******** scaling ********/ + properties.scale.atom = MAKE_ATOM("scale"); + err = RRConfigureOutputProperty(output->randr_output, + properties.scale.atom, FALSE, FALSE, + FALSE, 0, NULL); + if(err) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to configure scaling property for %s: error %d\n", + output->name, err); + + // Set the default value + s = "aspect"; + err = RRChangeOutputProperty(output->randr_output, properties.scale.atom, + XA_STRING, 8, PropModeReplace, strlen(s), + (pointer)s, FALSE, FALSE); + if(err) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to set scaling property for %s: error %d\n", + output->name, err); +} + +static Bool +G80SorSetProperty(xf86OutputPtr output, Atom prop, RRPropertyValuePtr val) +{ + G80OutputPrivPtr pPriv = output->driver_private; + + if(prop == properties.dither.atom) { + INT32 i; + + if(val->type != XA_INTEGER || val->format != 32 || val->size != 1) + return FALSE; + + i = *(INT32*)val->data; + if(i < properties.dither.range[0] || i > properties.dither.range[1]) + return FALSE; + + G80CrtcSetDither(output->crtc, i, TRUE); + return TRUE; + } else if(prop == properties.scale.atom) { + const char *s; + enum G80ScaleMode oldScale, scale; + int i; + const struct { + const char *name; + enum G80ScaleMode scale; + } modes[] = { + { "off", G80_SCALE_OFF }, + { "aspect", G80_SCALE_ASPECT }, + { "fill", G80_SCALE_FILL }, + { "center", G80_SCALE_CENTER }, + { NULL, 0 }, + }; + + if(val->type != XA_STRING || val->format != 8) + return FALSE; + s = (char*)val->data; + + for(i = 0; modes[i].name; i++) { + const char *name = modes[i].name; + const int len = strlen(name); + + if(val->size == len && !strncmp(name, s, len)) { + scale = modes[i].scale; + break; + } + } + if(!modes[i].name) + return FALSE; + if(scale == G80_SCALE_OFF && pPriv->panelType == LVDS) + // LVDS requires scaling + return FALSE; + + oldScale = pPriv->scale; + pPriv->scale = scale; + if(output->crtc) { + xf86CrtcPtr crtc = output->crtc; + + if(!xf86CrtcSetMode(crtc, &crtc->desiredMode, crtc->desiredRotation, + crtc->desiredX, crtc->desiredY)) { + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "Failed to set scaling to %s for output %s\n", + modes[i].name, output->name); + + // Restore old scale and try again. + pPriv->scale = oldScale; + if(!xf86CrtcSetMode(crtc, &crtc->desiredMode, + crtc->desiredRotation, crtc->desiredX, + crtc->desiredY)) { + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "Failed to restore old scaling for output %s\n", + output->name); + } + + return FALSE; + } + } + return TRUE; + } + + return FALSE; +} +#endif // RANDR_12_INTERFACE + +static const xf86OutputFuncsRec G80SorTMDSOutputFuncs = { + .dpms = G80SorDPMSSet, + .save = NULL, + .restore = NULL, + .mode_valid = G80TMDSModeValid, + .mode_fixup = G80SorTMDSModeFixup, + .prepare = G80OutputPrepare, + .commit = G80OutputCommit, + .mode_set = G80SorModeSet, + .detect = G80SorDetect, + .get_modes = G80OutputGetDDCModes, +#ifdef RANDR_12_INTERFACE + .create_resources = G80SorCreateResources, + .set_property = G80SorSetProperty, +#endif + .destroy = G80SorDestroy, +}; + +static const xf86OutputFuncsRec G80SorLVDSOutputFuncs = { + .dpms = G80SorDPMSSet, + .save = NULL, + .restore = NULL, + .mode_valid = G80LVDSModeValid, + .mode_fixup = G80SorModeFixup, + .prepare = G80OutputPrepare, + .commit = G80OutputCommit, + .mode_set = G80SorModeSet, + .detect = G80SorLVDSDetect, + .get_modes = G80SorGetLVDSModes, +#ifdef RANDR_12_INTERFACE + .create_resources = G80SorCreateResources, + .set_property = G80SorSetProperty, +#endif + .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, 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; + + 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; + } + + output = xf86OutputCreate(pScrn, funcs, orName); + + pPriv->type = SOR; + pPriv->or = or; + pPriv->panelType = panelType; + pPriv->cached_status = XF86OutputStatusUnknown; + if(panelType == TMDS) + 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; +} |