summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <skeggsb@gmail.com>2007-05-16 13:43:16 +1000
committerBen Skeggs <skeggsb@gmail.com>2007-05-16 13:43:16 +1000
commite03d6e925b944a4d768fc45bb9ecee282aa1896b (patch)
treeb71f62171ca5621b25745f44155f0403cfc35f18
parent371db8e8eafc1b669ecf560150ab6249432ca51f (diff)
downloadxorg-driver-xf86-video-nouveau-e03d6e925b944a4d768fc45bb9ecee282aa1896b.tar.gz
NV50: Steal modesetting code from xf86-video-nv, not hooked up yet
-rw-r--r--src/Makefile.am7
-rw-r--r--src/nv50_cursor.c104
-rw-r--r--src/nv50_cursor.h7
-rw-r--r--src/nv50_dac.c199
-rw-r--r--src/nv50_display.c535
-rw-r--r--src/nv50_display.h22
-rw-r--r--src/nv50_output.c372
-rw-r--r--src/nv50_output.h34
-rw-r--r--src/nv50_sor.c152
-rw-r--r--src/nv50_type.h22
-rw-r--r--src/nv_setup.c7
-rw-r--r--src/nv_type.h8
12 files changed, 1467 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 2fdf330..93ed3dc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -54,7 +54,12 @@ nouveau_drv_la_SOURCES = \
nv_xaa.c \
nv_output.c \
nv_crtc.c \
- nv_i2c.c
+ nv_i2c.c \
+ nv50_cursor.c \
+ nv50_dac.c \
+ nv50_display.c \
+ nv50_output.c \
+ nv50_sor.c
#riva128_la_LTLIBRARIES = riva128.la
#riva128_la_LDFLAGS = -module -avoid-version
diff --git a/src/nv50_cursor.c b/src/nv50_cursor.c
new file mode 100644
index 0000000..4c3a44a
--- /dev/null
+++ b/src/nv50_cursor.c
@@ -0,0 +1,104 @@
+/*
+ * 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
+
+#include <string.h>
+
+#include <cursorstr.h>
+
+#include "nv_include.h"
+#include "nv50_type.h"
+#include "nv50_cursor.h"
+#include "nv50_display.h"
+
+#define CURSOR_PTR ((CARD32*)pNv->Cursor->map)
+
+void G80SetCursorPosition(xf86CrtcPtr crtc, int x, int y)
+{
+ NVPtr pNv = NVPTR(crtc->scrn);
+ const int headOff = 0x1000*G80CrtcGetHead(crtc);
+
+ x &= 0xffff;
+ y &= 0xffff;
+ pNv->REGS[(0x00647084 + headOff)/4] = y << 16 | x;
+ pNv->REGS[(0x00647080 + headOff)/4] = 0;
+}
+
+void G80LoadCursorARGB(xf86CrtcPtr crtc, CARD32 *src)
+{
+ NVPtr pNv = NVPTR(crtc->scrn);
+ CARD32 *dst = CURSOR_PTR;
+
+ /* Assume cursor is 64x64 */
+ memcpy(dst, src, 64 * 64 * 4);
+}
+
+Bool G80CursorAcquire(ScrnInfoPtr pScrn)
+{
+ NVPtr pNv = NVPTR(pScrn);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ if(!pNv->HWCursor) return TRUE;
+
+ /* Initialize the cursor on each head */
+ for(i = 0; i < xf86_config->num_crtc; i++) {
+ const int headOff = 0x10 * G80CrtcGetHead(xf86_config->crtc[i]);
+
+ pNv->REGS[(0x00610270+headOff)/4] = 0x2000;
+ while(pNv->REGS[(0x00610270+headOff)/4] & 0x30000);
+
+ pNv->REGS[(0x00610270+headOff)/4] = 1;
+ while((pNv->REGS[(0x00610270+headOff)/4] & 0x30000) != 0x10000);
+ }
+
+ return TRUE;
+}
+
+void G80CursorRelease(ScrnInfoPtr pScrn)
+{
+ NVPtr pNv = NVPTR(pScrn);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ if(!pNv->HWCursor) return;
+
+ /* Release the cursor on each head */
+ for(i = 0; i < xf86_config->num_crtc; i++) {
+ const int headOff = 0x10 * G80CrtcGetHead(xf86_config->crtc[i]);
+
+ pNv->REGS[(0x00610270+headOff)/4] = 0;
+ while(pNv->REGS[(0x00610270+headOff)/4] & 0x30000);
+ }
+}
+
+Bool G80CursorInit(ScreenPtr pScreen)
+{
+ return xf86_cursors_init(pScreen, 64, 64,
+ HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
+ HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_32 |
+ HARDWARE_CURSOR_ARGB);
+}
diff --git a/src/nv50_cursor.h b/src/nv50_cursor.h
new file mode 100644
index 0000000..4d81d80
--- /dev/null
+++ b/src/nv50_cursor.h
@@ -0,0 +1,7 @@
+Bool G80CursorInit(ScreenPtr);
+Bool G80CursorAcquire(ScrnInfoPtr);
+void G80CursorRelease(ScrnInfoPtr);
+
+/* CRTC cursor functions */
+void G80SetCursorPosition(xf86CrtcPtr crtc, int x, int y);
+void G80LoadCursorARGB(xf86CrtcPtr crtc, CARD32 *src);
diff --git a/src/nv50_dac.c b/src/nv50_dac.c
new file mode 100644
index 0000000..2ce8346
--- /dev/null
+++ b/src/nv50_dac.c
@@ -0,0 +1,199 @@
+/*
+ * 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
+
+#include <unistd.h>
+
+#define DPMS_SERVER
+#include <X11/extensions/dpms.h>
+
+#include "nv_include.h"
+#include "nv50_display.h"
+#include "nv50_output.h"
+
+static void
+G80DacSetPClk(xf86OutputPtr output, int pclk)
+{
+ NVPtr pNv = NVPTR(output->scrn);
+ G80OutputPrivPtr pPriv = output->driver_private;
+ const int orOff = 0x800 * pPriv->or;
+
+ pNv->REGS[(0x00614280+orOff)/4] = 0;
+}
+
+static void
+G80DacDPMSSet(xf86OutputPtr output, int mode)
+{
+ NVPtr pNv = NVPTR(output->scrn);
+ G80OutputPrivPtr pPriv = output->driver_private;
+ const int off = 0x800 * pPriv->or;
+ CARD32 tmp;
+
+ /*
+ * DPMSModeOn everything on
+ * DPMSModeStandby hsync disabled, vsync enabled
+ * DPMSModeSuspend hsync enabled, vsync disabled
+ * DPMSModeOff sync disabled
+ */
+ while(pNv->REGS[(0x0061A004+off)/4] & 0x80000000);
+
+ tmp = pNv->REGS[(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->REGS[(0x0061A004+off)/4] = tmp;
+}
+
+static void
+G80DacModeSet(xf86OutputPtr output, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode)
+{
+ ScrnInfoPtr pScrn = output->scrn;
+ G80OutputPrivPtr pPriv = output->driver_private;
+ const int dacOff = 0x80 * pPriv->or;
+
+ if(!adjusted_mode) {
+ C(0x00000400 + dacOff, 0);
+ return;
+ }
+
+ // This wouldn't be necessary, but the server is stupid and calls
+ // G80DacDPMSSet after the output is disconnected, even though the hardware
+ // turns it off automatically.
+ G80DacDPMSSet(output, DPMSModeOn);
+
+ C(0x00000400 + dacOff,
+ (G80CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | 0x40);
+ C(0x00000404 + dacOff,
+ (adjusted_mode->Flags & V_NHSYNC) ? 1 : 0 |
+ (adjusted_mode->Flags & V_NVSYNC) ? 2 : 0);
+}
+
+/*
+ * Perform DAC load detection to determine if there is a connected display.
+ */
+static xf86OutputStatus
+G80DacDetect(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(output, pPriv->partner, pPriv->i2c);
+ return pPriv->cached_status;
+}
+
+Bool
+G80DacLoadDetect(xf86OutputPtr output)
+{
+ ScrnInfoPtr pScrn = output->scrn;
+ NVPtr pNv = NVPTR(pScrn);
+ G80OutputPrivPtr pPriv = output->driver_private;
+ const int scrnIndex = pScrn->scrnIndex;
+ const int dacOff = 2048 * pPriv->or;
+ CARD32 load, tmp, tmp2;
+
+ xf86DrvMsg(scrnIndex, X_PROBED, "Trying load detection on VGA%i ... ",
+ pPriv->or);
+
+ pNv->REGS[(0x0061A010+dacOff)/4] = 0x00000001;
+ tmp2 = pNv->REGS[(0x0061A004+dacOff)/4];
+ pNv->REGS[(0x0061A004+dacOff)/4] = 0x80150000;
+ while(pNv->REGS[(0x0061A004+dacOff)/4] & 0x80000000);
+ tmp = pNv->_Chipset == 0x50 ? 420 : 340;
+ pNv->REGS[(0x0061A00C+dacOff)/4] = tmp | 0x100000;
+ usleep(4500);
+ load = pNv->REGS[(0x0061A00C+dacOff)/4];
+ pNv->REGS[(0x0061A00C+dacOff)/4] = 0;
+ pNv->REGS[(0x0061A004+dacOff)/4] = 0x80000000 | tmp2;
+
+ // Use this DAC if all three channels show load.
+ if((load & 0x38000000) == 0x38000000) {
+ xf86ErrorF("found one!\n");
+ return TRUE;
+ }
+
+ xf86ErrorF("nothing.\n");
+ return FALSE;
+}
+
+static void
+G80DacDestroy(xf86OutputPtr output)
+{
+ G80OutputDestroy(output);
+
+ xfree(output->driver_private);
+ output->driver_private = NULL;
+}
+
+static const xf86OutputFuncsRec G80DacOutputFuncs = {
+ .dpms = G80DacDPMSSet,
+ .save = NULL,
+ .restore = NULL,
+ .mode_valid = G80OutputModeValid,
+ .mode_fixup = G80OutputModeFixup,
+ .prepare = G80OutputPrepare,
+ .commit = G80OutputCommit,
+ .mode_set = G80DacModeSet,
+ .detect = G80DacDetect,
+ .get_modes = G80OutputGetDDCModes,
+ .destroy = G80DacDestroy,
+};
+
+xf86OutputPtr
+G80CreateDac(ScrnInfoPtr pScrn, ORNum or)
+{
+ G80OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1);
+ xf86OutputPtr output;
+ char orName[5];
+
+ if(!pPriv)
+ return FALSE;
+
+ snprintf(orName, 5, "VGA%i", or);
+ output = xf86OutputCreate(pScrn, &G80DacOutputFuncs, orName);
+
+ pPriv->type = DAC;
+ pPriv->or = or;
+ pPriv->cached_status = XF86OutputStatusUnknown;
+ pPriv->set_pclk = G80DacSetPClk;
+ output->driver_private = pPriv;
+ output->interlaceAllowed = TRUE;
+ output->doubleScanAllowed = TRUE;
+
+ return output;
+}
diff --git a/src/nv50_display.c b/src/nv50_display.c
new file mode 100644
index 0000000..a1d531b
--- /dev/null
+++ b/src/nv50_display.c
@@ -0,0 +1,535 @@
+/*
+ * 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
+
+#include <float.h>
+#include <math.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "nv_include.h"
+#include "nv50_type.h"
+#include "nv50_cursor.h"
+#include "nv50_display.h"
+#include "nv50_output.h"
+
+typedef struct G80CrtcPrivRec {
+ Head head;
+ int pclk; /* Target pixel clock in kHz */
+ Bool cursorVisible;
+} G80CrtcPrivRec, *G80CrtcPrivPtr;
+
+static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update);
+
+/*
+ * PLL calculation. pclk is in kHz.
+ */
+static void
+G80CalcPLL(float pclk, int *pNA, int *pMA, int *pNB, int *pMB, int *pP)
+{
+ const float refclk = 27000.0f;
+ const float minVcoA = 100000;
+ const float maxVcoA = 400000;
+ const float minVcoB = 600000;
+ float maxVcoB = 1400000;
+ const float minUA = 2000;
+ const float maxUA = 400000;
+ const float minUB = 50000;
+ const float maxUB = 200000;
+ const int minNA = 1, maxNA = 255;
+ const int minNB = 1, maxNB = 31;
+ const int minMA = 1, maxMA = 255;
+ const int minMB = 1, maxMB = 31;
+ const int minP = 0, maxP = 6;
+ int lowP, highP;
+ float vcoB;
+
+ int na, ma, nb, mb, p;
+ float bestError = FLT_MAX;
+
+ *pNA = *pMA = *pNB = *pMB = *pP = 0;
+
+ if(maxVcoB < pclk + pclk / 200)
+ maxVcoB = pclk + pclk / 200;
+ if(minVcoB / (1 << maxP) > pclk)
+ pclk = minVcoB / (1 << maxP);
+
+ vcoB = maxVcoB - maxVcoB / 200;
+ lowP = minP;
+ vcoB /= 1 << (lowP + 1);
+
+ while(pclk <= vcoB && lowP < maxP)
+ {
+ vcoB /= 2;
+ lowP++;
+ }
+
+ vcoB = maxVcoB + maxVcoB / 200;
+ highP = lowP;
+ vcoB /= 1 << (highP + 1);
+
+ while(pclk <= vcoB && highP < maxP)
+ {
+ vcoB /= 2;
+ highP++;
+ }
+
+ for(p = lowP; p <= highP; p++)
+ {
+ for(ma = minMA; ma <= maxMA; ma++)
+ {
+ if(refclk / ma < minUA)
+ break;
+ else if(refclk / ma > maxUA)
+ continue;
+
+ for(na = minNA; na <= maxNA; na++)
+ {
+ if(refclk * na / ma < minVcoA || refclk * na / ma > maxVcoA)
+ continue;
+
+ for(mb = minMB; mb <= maxMB; mb++)
+ {
+ if(refclk * na / ma / mb < minUB)
+ break;
+ else if(refclk * na / ma / mb > maxUB)
+ continue;
+
+ nb = rint(pclk * (1 << p) * (ma / (float)na) * mb / refclk);
+
+ if(nb > maxNB)
+ break;
+ else if(nb < minNB)
+ continue;
+ else
+ {
+ float freq = refclk * (na / (float)ma) * (nb / (float)mb) / (1 << p);
+ float error = fabsf(pclk - freq);
+ if(error < bestError) {
+ *pNA = na;
+ *pMA = ma;
+ *pNB = nb;
+ *pMB = mb;
+ *pP = p;
+ bestError = error;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+G80CrtcSetPClk(xf86CrtcPtr crtc)
+{
+ NVPtr pNv = NVPTR(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->REGS[(0x00614104+headOff)/4];
+ CARD32 hi = pNv->REGS[(0x00614108+headOff)/4];
+
+ pNv->REGS[(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->REGS[(0x00614104+headOff)/4] = lo;
+ pNv->REGS[(0x00614108+headOff)/4] = hi;
+ pNv->REGS[(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)
+{
+ NVPtr pNv = NVPTR(pScrn);
+
+ pNv->REGS[0x00610304/4] = data;
+ pNv->REGS[0x00610300/4] = addr | 0x80010001;
+
+ while(pNv->REGS[0x00610300/4] & 0x80000000) {
+ const int super = ffs((pNv->REGS[0x00610024/4] >> 4) & 7);
+
+ if(super) {
+ if(super == 2) {
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ const CARD32 r = pNv->REGS[0x00610030/4];
+ int i;
+
+ for(i = 0; i < xf86_config->num_crtc; i++)
+ {
+ xf86CrtcPtr crtc = xf86_config->crtc[i];
+ G80CrtcPrivPtr pPriv = crtc->driver_private;
+
+ if(r & (0x200 << pPriv->head))
+ G80CrtcSetPClk(crtc);
+ }
+ }
+
+ pNv->REGS[0x00610024/4] = 8 << super;
+ pNv->REGS[0x00610030/4] = 0x80000000;
+ }
+ }
+}
+
+Head
+G80CrtcGetHead(xf86CrtcPtr crtc)
+{
+ G80CrtcPrivPtr pPriv = crtc->driver_private;
+ return pPriv->head;
+}
+
+Bool
+G80DispPreInit(ScrnInfoPtr pScrn)
+{
+ NVPtr pNv = NVPTR(pScrn);
+
+ pNv->REGS[0x00610184/4] = pNv->REGS[0x00614004/4];
+ pNv->REGS[0x00610190/4] = pNv->REGS[0x00616100/4];
+ pNv->REGS[0x006101a0/4] = pNv->REGS[0x00616900/4];
+ pNv->REGS[0x00610194/4] = pNv->REGS[0x00616104/4];
+ pNv->REGS[0x006101a4/4] = pNv->REGS[0x00616904/4];
+ pNv->REGS[0x00610198/4] = pNv->REGS[0x00616108/4];
+ pNv->REGS[0x006101a8/4] = pNv->REGS[0x00616908/4];
+ pNv->REGS[0x0061019C/4] = pNv->REGS[0x0061610C/4];
+ pNv->REGS[0x006101ac/4] = pNv->REGS[0x0061690c/4];
+ pNv->REGS[0x006101D0/4] = pNv->REGS[0x0061A000/4];
+ pNv->REGS[0x006101D4/4] = pNv->REGS[0x0061A800/4];
+ pNv->REGS[0x006101D8/4] = pNv->REGS[0x0061B000/4];
+ pNv->REGS[0x006101E0/4] = pNv->REGS[0x0061C000/4];
+ pNv->REGS[0x006101E4/4] = pNv->REGS[0x0061C800/4];
+ pNv->REGS[0x0061c00c/4] = 0x03010700;
+ pNv->REGS[0x0061c010/4] = 0x0000152f;
+ pNv->REGS[0x0061c014/4] = 0x00000000;
+ pNv->REGS[0x0061c018/4] = 0x00245af8;
+ pNv->REGS[0x0061c80c/4] = 0x03010700;
+ pNv->REGS[0x0061c810/4] = 0x0000152f;
+ pNv->REGS[0x0061c814/4] = 0x00000000;
+ pNv->REGS[0x0061c818/4] = 0x00245af8;
+ pNv->REGS[0x0061A004/4] = 0x80550000;
+ pNv->REGS[0x0061A010/4] = 0x00000001;
+ pNv->REGS[0x0061A804/4] = 0x80550000;
+ pNv->REGS[0x0061A810/4] = 0x00000001;
+ pNv->REGS[0x0061B004/4] = 0x80550000;
+ pNv->REGS[0x0061B010/4] = 0x00000001;
+
+ return TRUE;
+}
+
+Bool
+G80DispInit(ScrnInfoPtr pScrn)
+{
+ NVPtr pNv = NVPTR(pScrn);
+
+ if(pNv->REGS[0x00610024/4] & 0x100) {
+ pNv->REGS[0x00610024/4] = 0x100;
+ pNv->REGS[0x006194E8/4] &= ~1;
+ while(pNv->REGS[0x006194E8/4] & 2);
+ }
+
+ pNv->REGS[0x00610200/4] = 0x2b00;
+ while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
+ pNv->REGS[0x00610300/4] = 1;
+ pNv->REGS[0x00610200/4] = 0x1000b03;
+ while(!(pNv->REGS[0x00610200/4] & 0x40000000));
+
+ C(0x00000084, 0);
+ C(0x00000088, 0);
+ C(0x00000874, 0);
+ C(0x00000800, 0);
+ C(0x00000810, 0);
+ C(0x0000082C, 0);
+
+ return TRUE;
+}
+
+void
+G80DispShutdown(ScrnInfoPtr pScrn)
+{
+ NVPtr pNv = NVPTR(pScrn);
+ 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);
+
+ pNv->REGS[0x00610024/4] = mask;
+ while(!(pNv->REGS[0x00610024/4] & mask));
+ }
+ }
+
+ pNv->REGS[0x00610200/4] = 0;
+ pNv->REGS[0x00610300/4] = 0;
+ while((pNv->REGS[0x00610200/4] & 0x1e0000) != 0);
+}
+
+static Bool
+G80CrtcModeFixup(xf86CrtcPtr crtc,
+ DisplayModePtr mode, DisplayModePtr adjusted_mode)
+{
+ // TODO: Fix up the mode here
+ return TRUE;
+}
+
+static void
+G80CrtcModeSet(xf86CrtcPtr crtc, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode, int x, int y)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ G80CrtcPrivPtr pPriv = crtc->driver_private;
+ const int HDisplay = mode->HDisplay, VDisplay = mode->VDisplay;
+ const int headOff = 0x400 * G80CrtcGetHead(crtc);
+ int interlaceDiv, fudge;
+
+ // TODO: Use adjusted_mode and fix it up in G80CrtcModeFixup
+ pPriv->pclk = mode->Clock;
+
+ /* Magic mode timing fudge factor */
+ fudge = ((mode->Flags & V_INTERLACE) && (mode->Flags & V_DBLSCAN)) ? 2 : 1;
+ interlaceDiv = (mode->Flags & V_INTERLACE) ? 2 : 1;
+
+ C(0x00000804 + headOff, mode->Clock | 0x800000);
+ C(0x00000808 + headOff, (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(0x00000868 + headOff, pScrn->virtualY << 16 | pScrn->virtualX);
+ C(0x0000086C + headOff, pScrn->displayWidth * (pScrn->bitsPerPixel / 8) | 0x100000);
+ switch(pScrn->depth) {
+ case 8: C(0x00000870 + headOff, 0x1E00); break;
+ case 15: C(0x00000870 + headOff, 0xE900); break;
+ 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);
+ }
+ C(0x000008A8 + headOff, 0x40000);
+ 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);
+
+ G80CrtcBlankScreen(crtc, FALSE);
+}
+
+void
+G80CrtcBlankScreen(xf86CrtcPtr crtc, Bool blank)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ NVPtr pNv = NVPTR(pScrn);
+ G80CrtcPrivPtr pPriv = crtc->driver_private;
+ const int headOff = 0x400 * pPriv->head;
+
+ if(blank) {
+ G80CrtcShowHideCursor(crtc, FALSE, FALSE);
+
+ C(0x00000840 + headOff, 0);
+ C(0x00000844 + headOff, 0);
+ if(pNv->_Chipset != 0x50)
+ C(0x0000085C + headOff, 0);
+ C(0x00000874 + headOff, 0);
+ if(pNv->_Chipset != 0x50)
+ C(0x0000089C + headOff, 0);
+ } else {
+ C(0x00000860 + headOff, 0);
+ C(0x00000864 + headOff, 0);
+ pNv->REGS[0x00610380/4] = 0;
+ /*XXX: in "nv" this is total vram size. our RamAmountKBytes is clamped
+ * to 256MiB.
+ */
+ pNv->REGS[0x00610384/4] = pNv->RamAmountKBytes * 1024 - 1;
+ pNv->REGS[0x00610388/4] = 0x150000;
+ pNv->REGS[0x0061038C/4] = 0;
+ C(0x00000884 + headOff, (pNv->RamAmountKBytes << 2) - 0x40);
+ if(pNv->_Chipset != 0x50)
+ C(0x0000089C + headOff, 1);
+ if(pPriv->cursorVisible)
+ G80CrtcShowHideCursor(crtc, TRUE, FALSE);
+ C(0x00000840 + headOff, pScrn->depth == 8 ? 0x80000000 : 0xc0000000);
+ C(0x00000844 + headOff, (pNv->RamAmountKBytes * 1024 - 0x5000) >> 8);
+ if(pNv->_Chipset != 0x50)
+ C(0x0000085C + headOff, 1);
+ C(0x00000874 + headOff, 1);
+ }
+}
+
+void
+G80CrtcDPMSSet(xf86CrtcPtr crtc, int mode)
+{
+}
+
+/******************************** Cursor stuff ********************************/
+static void G80CrtcShowHideCursor(xf86CrtcPtr crtc, Bool show, Bool update)
+{
+ 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);
+ }
+}
+
+void G80CrtcShowCursor(xf86CrtcPtr crtc)
+{
+ G80CrtcShowHideCursor(crtc, TRUE, TRUE);
+}
+
+void G80CrtcHideCursor(xf86CrtcPtr crtc)
+{
+ G80CrtcShowHideCursor(crtc, FALSE, TRUE);
+}
+
+/******************************** CRTC stuff ********************************/
+
+static Bool
+G80CrtcLock(xf86CrtcPtr crtc)
+{
+ return FALSE;
+}
+
+static void
+G80CrtcPrepare(xf86CrtcPtr crtc)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ 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);
+ }
+}
+
+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)
+{
+ 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;
+ crtc->driver_private = g80_crtc;
+ }
+}
diff --git a/src/nv50_display.h b/src/nv50_display.h
new file mode 100644
index 0000000..c0b2e19
--- /dev/null
+++ b/src/nv50_display.h
@@ -0,0 +1,22 @@
+#ifndef __NV50_DISPLAY_H__
+#define __NV50_DISPLAY_H__
+
+#include "nv50_type.h"
+
+Bool G80DispPreInit(ScrnInfoPtr);
+Bool G80DispInit(ScrnInfoPtr);
+void G80DispShutdown(ScrnInfoPtr);
+
+void G80DispCommand(ScrnInfoPtr, CARD32 addr, CARD32 data);
+#define C(mthd, data) G80DispCommand(pScrn, (mthd), (data))
+
+Head G80CrtcGetHead(xf86CrtcPtr);
+
+void G80CrtcBlankScreen(xf86CrtcPtr, Bool blank);
+void G80CrtcEnableCursor(xf86CrtcPtr, Bool update);
+void G80CrtcDisableCursor(xf86CrtcPtr, Bool update);
+void G80CrtcSetCursorPosition(xf86CrtcPtr, int x, int y);
+
+void G80DispCreateCrtcs(ScrnInfoPtr pScrn);
+
+#endif
diff --git a/src/nv50_output.c b/src/nv50_output.c
new file mode 100644
index 0000000..fc14f19
--- /dev/null
+++ b/src/nv50_output.c
@@ -0,0 +1,372 @@
+/*
+ * 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
+
+#include <strings.h>
+#include <xf86DDC.h>
+
+#include "nv_include.h"
+#include "nv50_type.h"
+#include "nv50_output.h"
+
+static unsigned const char *
+NV50GetVBIOSImage(NVPtr pNv)
+{
+ unsigned char *VBIOS;
+ uint32_t old_bar0_pramin;
+
+ VBIOS = xalloc(65536);
+ if (VBIOS) {
+ old_bar0_pramin = pNv->REGS[0x1700/4];
+ pNv->REGS[0x1700/4] = pNv->REGS[0x00619f04/4] >> 8;
+
+ memcpy(VBIOS, (const void *)pNv->PRAMIN, 65536);
+
+ pNv->REGS[0x1700/4] = old_bar0_pramin;
+ }
+
+ return (unsigned const char *)VBIOS;
+}
+
+static Bool G80ReadPortMapping(int scrnIndex, NVPtr pNv)
+{
+ unsigned const char *VBIOS;
+ unsigned char *table2;
+ unsigned char headerSize, entries;
+ int i;
+ CARD16 a;
+ CARD32 b;
+
+ VBIOS = NV50GetVBIOSImage(pNv);
+ if (!VBIOS)
+ goto fail;
+
+ /* Clear the i2c map to invalid */
+ for(i = 0; i < 4; i++)
+ pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
+
+ if(*(CARD16*)VBIOS != 0xaa55) goto fail;
+
+ a = *(CARD16*)(VBIOS + 0x36);
+ table2 = (unsigned char*)VBIOS + a;
+
+ if(table2[0] != 0x40) goto fail;
+
+ b = *(CARD32*)(table2 + 6);
+ if(b != 0x4edcbdcb) goto fail;
+
+ headerSize = table2[1];
+ entries = table2[2];
+
+ for(i = 0; i < entries; i++) {
+ int type, port;
+ ORNum or;
+
+ b = *(CARD32*)&table2[headerSize + 8*i];
+ type = b & 0xf;
+ port = (b >> 4) & 0xf;
+ or = ffs((b >> 24) & 0xf) - 1;
+
+ if(type < 4 && port != 0xf) {
+ switch(type) {
+ case 0: /* CRT */
+ case 1: /* TV */
+ if(pNv->i2cMap[port].dac != -1) {
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "DDC routing table corrupt! DAC %i -> %i "
+ "for port %i\n",
+ or, pNv->i2cMap[port].dac, port);
+ }
+ pNv->i2cMap[port].dac = or;
+ break;
+ case 2: /* TMDS */
+ case 3: /* LVDS */
+ if(pNv->i2cMap[port].sor != -1)
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "DDC routing table corrupt! SOR %i -> %i "
+ "for port %i\n",
+ or, pNv->i2cMap[port].sor, port);
+ pNv->i2cMap[port].sor = or;
+ break;
+ }
+ }
+ }
+
+ xf86DrvMsg(scrnIndex, X_PROBED, "I2C map:\n");
+ for(i = 0; i < 4; i++) {
+ if(pNv->i2cMap[i].dac != -1)
+ xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
+ if(pNv->i2cMap[i].sor != -1)
+ xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
+ }
+
+ return TRUE;
+
+fail:
+ xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. "
+ "Mode setting will probably fail!\n");
+ return FALSE;
+}
+
+static void G80_I2CPutBits(I2CBusPtr b, int clock, int data)
+{
+ NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
+ const int off = b->DriverPrivate.val * 0x18;
+
+ pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
+}
+
+static void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data)
+{
+ NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
+ const int off = b->DriverPrivate.val * 0x18;
+ unsigned char val;
+
+ val = pNv->REGS[(0x0000E138+off)/4];
+ *clock = !!(val & 1);
+ *data = !!(val & 2);
+}
+
+static I2CBusPtr
+G80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
+{
+ I2CBusPtr i2c;
+
+ /* Allocate the I2C bus structure */
+ i2c = xf86CreateI2CBusRec();
+ if(!i2c) return NULL;
+
+ i2c->BusName = strdup(name);
+ i2c->scrnIndex = pScrn->scrnIndex;
+ i2c->I2CPutBits = G80_I2CPutBits;
+ i2c->I2CGetBits = G80_I2CGetBits;
+ i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
+ i2c->StartTimeout = 550;
+ i2c->BitTimeout = 40;
+ i2c->ByteTimeout = 40;
+ i2c->AcknTimeout = 40;
+ i2c->DriverPrivate.val = port;
+
+ if(xf86I2CBusInit(i2c)) {
+ return i2c;
+ } else {
+ xfree(i2c);
+ return NULL;
+ }
+}
+
+void
+G80OutputSetPClk(xf86OutputPtr output, int pclk)
+{
+ G80OutputPrivPtr pPriv = output->driver_private;
+ pPriv->set_pclk(output, pclk);
+}
+
+int
+G80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
+{
+ if(mode->Clock > 400000 || mode->Clock < 25000)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+Bool
+G80OutputModeFixup(xf86OutputPtr output, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode)
+{
+ return TRUE;
+}
+
+void
+G80OutputPrepare(xf86OutputPtr output)
+{
+}
+
+void
+G80OutputCommit(xf86OutputPtr output)
+{
+}
+
+static xf86MonPtr
+ProbeDDC(I2CBusPtr i2c)
+{
+ ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
+ NVPtr pNv = NVPTR(pScrn);
+ xf86MonPtr monInfo = NULL;
+ const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Probing for EDID on I2C bus %i...\n", bus);
+ pNv->REGS[(0x0000E138+off)/4] = 7;
+ /* Should probably use xf86OutputGetEDID here */
+ monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
+ pNv->REGS[(0x0000E138+off)/4] = 3;
+
+ if(monInfo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
+ "DDC detected a %s:\n", monInfo->features.input_type ?
+ "DFP" : "CRT");
+ xf86PrintEDID(monInfo);
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n");
+ }
+
+ return monInfo;
+}
+
+/*
+ * Read an EDID from the i2c port. Perform load detection on the DAC (if
+ * present) to see if the display is connected via VGA. Sets the cached status
+ * of both outputs. The status is marked dirty again in the BlockHandler.
+ */
+void G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c)
+{
+ xf86MonPtr monInfo = ProbeDDC(i2c);
+ xf86OutputPtr connected = NULL;
+ Bool load = dac && G80DacLoadDetect(dac);
+
+ if(dac) {
+ G80OutputPrivPtr pPriv = dac->driver_private;
+
+ if(load) {
+ pPriv->cached_status = XF86OutputStatusConnected;
+ connected = dac;
+ } else {
+ pPriv->cached_status = XF86OutputStatusDisconnected;
+ }
+ }
+
+ if(sor) {
+ G80OutputPrivPtr pPriv = sor->driver_private;
+
+ if(monInfo && !load) {
+ pPriv->cached_status = XF86OutputStatusConnected;
+ connected = sor;
+ } else {
+ pPriv->cached_status = XF86OutputStatusDisconnected;
+ }
+ }
+
+ if(connected)
+ xf86OutputSetEDID(connected, monInfo);
+}
+
+/*
+ * Reset the cached output status for all outputs. Called from G80BlockHandler.
+ */
+void
+G80OutputResetCachedStatus(ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ for(i = 0; i < xf86_config->num_output; i++) {
+ G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private;
+ pPriv->cached_status = XF86OutputStatusUnknown;
+ }
+}
+
+DisplayModePtr
+G80OutputGetDDCModes(xf86OutputPtr output)
+{
+ /* The EDID is read as part of the detect step */
+ output->funcs->detect(output);
+ return xf86OutputGetEDIDModes(output);
+}
+
+void
+G80OutputDestroy(xf86OutputPtr output)
+{
+ G80OutputPrivPtr pPriv = output->driver_private;
+
+ if(pPriv->partner)
+ ((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL;
+ else
+ xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE);
+ pPriv->i2c = NULL;
+}
+
+Bool
+G80CreateOutputs(ScrnInfoPtr pScrn)
+{
+ NVPtr pNv = NVPTR(pScrn);
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ if(!G80ReadPortMapping(pScrn->scrnIndex, pNv))
+ return FALSE;
+
+ /* For each DDC port, create an output for the attached ORs */
+ for(i = 0; i < 4; i++) {
+ xf86OutputPtr dac = NULL, sor = NULL;
+ I2CBusPtr i2c;
+ char i2cName[16];
+
+ if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1)
+ /* No outputs on this port */
+ continue;
+
+ snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
+ i2c = G80I2CInit(pScrn, i2cName, i);
+ if(!i2c) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to initialize I2C for port %i.\n",
+ i);
+ continue;
+ }
+
+ if(pNv->i2cMap[i].dac != -1)
+ dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac);
+ if(pNv->i2cMap[i].sor != -1)
+ sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor);
+
+ if(dac) {
+ G80OutputPrivPtr pPriv = dac->driver_private;
+
+ pPriv->partner = sor;
+ pPriv->i2c = i2c;
+ }
+ if(sor) {
+ G80OutputPrivPtr pPriv = sor->driver_private;
+
+ pPriv->partner = dac;
+ pPriv->i2c = i2c;
+ }
+ }
+
+ /* For each output, set the crtc and clone masks */
+ for(i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+
+ /* Any output can connect to any head */
+ output->possible_crtcs = 0x3;
+ output->possible_clones = 0;
+ }
+
+ return TRUE;
+}
diff --git a/src/nv50_output.h b/src/nv50_output.h
new file mode 100644
index 0000000..ce100ea
--- /dev/null
+++ b/src/nv50_output.h
@@ -0,0 +1,34 @@
+#ifndef __NV50_OUTPUT_H__
+#define __NV50_OUTPUT_H__
+
+typedef struct G80OutputPrivRec {
+ ORType type;
+ ORNum or;
+
+ xf86OutputPtr partner;
+ I2CBusPtr i2c;
+
+ xf86OutputStatus cached_status;
+
+ void (*set_pclk)(xf86OutputPtr, int pclk);
+} G80OutputPrivRec, *G80OutputPrivPtr;
+
+void G80OutputSetPClk(xf86OutputPtr, int pclk);
+int G80OutputModeValid(xf86OutputPtr, DisplayModePtr);
+Bool G80OutputModeFixup(xf86OutputPtr, DisplayModePtr mode, DisplayModePtr adjusted_mode);
+void G80OutputPrepare(xf86OutputPtr);
+void G80OutputCommit(xf86OutputPtr);
+void G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c);
+void G80OutputResetCachedStatus(ScrnInfoPtr);
+DisplayModePtr G80OutputGetDDCModes(xf86OutputPtr);
+void G80OutputDestroy(xf86OutputPtr);
+Bool G80CreateOutputs(ScrnInfoPtr);
+
+/* g80_dac.c */
+xf86OutputPtr G80CreateDac(ScrnInfoPtr, ORNum);
+Bool G80DacLoadDetect(xf86OutputPtr);
+
+/* g80_sor.c */
+xf86OutputPtr G80CreateSor(ScrnInfoPtr, ORNum);
+
+#endif
diff --git a/src/nv50_sor.c b/src/nv50_sor.c
new file mode 100644
index 0000000..3181991
--- /dev/null
+++ b/src/nv50_sor.c
@@ -0,0 +1,152 @@
+/*
+ * 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 "nv_include.h"
+#include "nv50_type.h"
+#include "nv50_display.h"
+#include "nv50_output.h"
+
+static void
+G80SorSetPClk(xf86OutputPtr output, int pclk)
+{
+ NVPtr pNv = NVPTR(output->scrn);
+ G80OutputPrivPtr pPriv = output->driver_private;
+ const int orOff = 0x800 * pPriv->or;
+
+ pNv->REGS[(0x00614300+orOff)/4] = (pclk > 165000) ? 0x101 : 0;
+}
+
+static void
+G80SorDPMSSet(xf86OutputPtr output, int mode)
+{
+ NVPtr pNv = NVPTR(output->scrn);
+ G80OutputPrivPtr pPriv = output->driver_private;
+ const int off = 0x800 * pPriv->or;
+ CARD32 tmp;
+
+ while(pNv->REGS[(0x0061C004+off)/4] & 0x80000000);
+
+ tmp = pNv->REGS[(0x0061C004+off)/4];
+ tmp |= 0x80000000;
+
+ if(mode == DPMSModeOn)
+ tmp |= 1;
+ else
+ tmp &= ~1;
+
+ pNv->REGS[(0x0061C004+off)/4] = tmp;
+}
+
+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;
+
+ if(!adjusted_mode) {
+ /* Disconnect the SOR */
+ C(0x00000600 + sorOff, 0);
+ return;
+ }
+
+ // 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) |
+ (adjusted_mode->Clock > 165000 ? 0x500 : 0x100) |
+ ((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) |
+ ((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0));
+}
+
+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 void
+G80SorDestroy(xf86OutputPtr output)
+{
+ G80OutputDestroy(output);
+
+ xfree(output->driver_private);
+ output->driver_private = NULL;
+}
+
+static const xf86OutputFuncsRec G80SorOutputFuncs = {
+ .dpms = G80SorDPMSSet,
+ .save = NULL,
+ .restore = NULL,
+ .mode_valid = G80OutputModeValid,
+ .mode_fixup = G80OutputModeFixup,
+ .prepare = G80OutputPrepare,
+ .commit = G80OutputCommit,
+ .mode_set = G80SorModeSet,
+ .detect = G80SorDetect,
+ .get_modes = G80OutputGetDDCModes,
+ .destroy = G80SorDestroy,
+};
+
+xf86OutputPtr
+G80CreateSor(ScrnInfoPtr pScrn, ORNum or)
+{
+ G80OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1);
+ xf86OutputPtr output;
+ char orName[5];
+
+ if(!pPriv)
+ return FALSE;
+
+ snprintf(orName, 5, "DVI%i", or);
+ output = xf86OutputCreate(pScrn, &G80SorOutputFuncs, orName);
+
+ pPriv->type = SOR;
+ pPriv->or = or;
+ pPriv->cached_status = XF86OutputStatusUnknown;
+ pPriv->set_pclk = G80SorSetPClk;
+ output->driver_private = pPriv;
+ output->interlaceAllowed = TRUE;
+ output->doubleScanAllowed = TRUE;
+
+ return output;
+}
diff --git a/src/nv50_type.h b/src/nv50_type.h
new file mode 100644
index 0000000..e3cba74
--- /dev/null
+++ b/src/nv50_type.h
@@ -0,0 +1,22 @@
+#ifndef __NV50_TYPE_H__
+#define __NV50_TYPE_H__
+
+typedef enum Head {
+ HEAD0 = 0,
+ HEAD1
+} Head;
+
+typedef enum ORType {
+ DAC,
+ SOR
+} ORType;
+
+typedef enum ORNum {
+ DAC0 = 0,
+ DAC1 = 1,
+ DAC2 = 2,
+ SOR0 = 0,
+ SOR1 = 1
+} ORNum;
+
+#endif
diff --git a/src/nv_setup.c b/src/nv_setup.c
index 310f46f..dcb3a17 100644
--- a/src/nv_setup.c
+++ b/src/nv_setup.c
@@ -357,7 +357,12 @@ NVCommonSetup(ScrnInfoPtr pScrn)
pNv->BlendingPossible = ((pNv->Chipset & 0xffff) > CHIPSET_NV04);
-
+ /* Chipset from PMC_BOOT_0 register */
+ if (pNv->Architecture == NV_ARCH_04) {
+ pNv->_Chipset = 0x04;
+ } else {
+ pNv->_Chipset = (nvReadMC(pNv, 0) >> 20) & 0xff;
+ }
/* Parse the bios to initialize the card */
NVSelectHeadRegisters(pScrn, 0);
diff --git a/src/nv_type.h b/src/nv_type.h
index 36ec7b5..bb85b8f 100644
--- a/src/nv_type.h
+++ b/src/nv_type.h
@@ -20,6 +20,8 @@
#error "This driver requires a DRI-enabled X server"
#endif
+#include "nv50_type.h"
+
#define NV_ARCH_03 0x03
#define NV_ARCH_04 0x04
#define NV_ARCH_10 0x10
@@ -174,6 +176,7 @@ typedef struct _NVRec {
pciVideoPtr PciInfo;
PCITAG PciTag;
int Chipset;
+ int _Chipset;
int ChipRev;
Bool Primary;
CARD32 IOAddress;
@@ -309,6 +312,11 @@ typedef struct _NVRec {
int analog_count;
int digital_count;
CARD32 dcb_table[NV40_NUM_DCB_ENTRIES]; /* 10 is a good limit */
+
+ struct {
+ ORNum dac;
+ ORNum sor;
+ } i2cMap[4];
} NVRec;
#define NVPTR(p) ((NVPtr)((p)->driverPrivate))