From e03d6e925b944a4d768fc45bb9ecee282aa1896b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 16 May 2007 13:43:16 +1000 Subject: NV50: Steal modesetting code from xf86-video-nv, not hooked up yet --- src/Makefile.am | 7 +- src/nv50_cursor.c | 104 +++++++++++ src/nv50_cursor.h | 7 + src/nv50_dac.c | 199 ++++++++++++++++++++ src/nv50_display.c | 535 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nv50_display.h | 22 +++ src/nv50_output.c | 372 +++++++++++++++++++++++++++++++++++++ src/nv50_output.h | 34 ++++ src/nv50_sor.c | 152 +++++++++++++++ src/nv50_type.h | 22 +++ src/nv_setup.c | 7 +- src/nv_type.h | 8 + 12 files changed, 1467 insertions(+), 2 deletions(-) create mode 100644 src/nv50_cursor.c create mode 100644 src/nv50_cursor.h create mode 100644 src/nv50_dac.c create mode 100644 src/nv50_display.c create mode 100644 src/nv50_display.h create mode 100644 src/nv50_output.c create mode 100644 src/nv50_output.h create mode 100644 src/nv50_sor.c create mode 100644 src/nv50_type.h 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 + +#include + +#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 + +#define DPMS_SERVER +#include + +#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 +#include +#include +#include + +#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 +#include + +#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 + +#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)) -- cgit v1.2.1