summaryrefslogtreecommitdiff
path: root/driver/xf86-video-nv/src/g80_sor.c
diff options
context:
space:
mode:
authorMatthieu Herrb <matthieu@cvs.openbsd.org>2007-09-30 14:18:15 +0000
committerMatthieu Herrb <matthieu@cvs.openbsd.org>2007-09-30 14:18:15 +0000
commitf25cb21322e90ab2c4d34d5bd8027006950662f6 (patch)
treef1986b6c009c7eba0d01e7a0297c5deec3163b97 /driver/xf86-video-nv/src/g80_sor.c
parentda0ef05280ed34be0687c3802ab4724f7a9d063a (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.c500
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;
+}