1fc5a983dSmrg/* 2fc5a983dSmrg * Copyright (c) 2007-2008 NVIDIA, Corporation 3fc5a983dSmrg * 4fc5a983dSmrg * Permission is hereby granted, free of charge, to any person obtaining a 5fc5a983dSmrg * copy of this software and associated documentation files (the 6fc5a983dSmrg * "Software"), to deal in the Software without restriction, including 7fc5a983dSmrg * without limitation the rights to use, copy, modify, merge, publish, 8fc5a983dSmrg * distribute, sublicense, and/or sell copies of the Software, and to 9fc5a983dSmrg * permit persons to whom the Software is furnished to do so, subject to 10fc5a983dSmrg * the following conditions: 11fc5a983dSmrg * 12fc5a983dSmrg * The above copyright notice and this permission notice shall be included 13fc5a983dSmrg * in all copies or substantial portions of the Software. 14fc5a983dSmrg * 15fc5a983dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16fc5a983dSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17fc5a983dSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18fc5a983dSmrg * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19fc5a983dSmrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20fc5a983dSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21fc5a983dSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22fc5a983dSmrg */ 23fc5a983dSmrg 24fc5a983dSmrg#ifdef HAVE_CONFIG_H 25fc5a983dSmrg#include "config.h" 26fc5a983dSmrg#endif 27fc5a983dSmrg 28f5683d00Smrg#ifdef HAVE_XEXTPROTO_71 299c32e6c9Smrg#include <X11/extensions/dpmsconst.h> 30f5683d00Smrg#else 31f5683d00Smrg#define DPMS_SERVER 32f5683d00Smrg#include <X11/extensions/dpms.h> 33f5683d00Smrg#endif 34f5683d00Smrg 35fc5a983dSmrg#include <X11/Xatom.h> 36fc5a983dSmrg 37fc5a983dSmrg#include "g80_type.h" 38fc5a983dSmrg#include "g80_display.h" 39fc5a983dSmrg#include "g80_output.h" 40fc5a983dSmrg 41fc5a983dSmrgstatic void 42fc5a983dSmrgG80SorSetPClk(xf86OutputPtr output, int pclk) 43fc5a983dSmrg{ 44fc5a983dSmrg G80Ptr pNv = G80PTR(output->scrn); 45fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 46fc5a983dSmrg const int orOff = 0x800 * pPriv->or; 47fc5a983dSmrg const int limit = 165000; 48fc5a983dSmrg 49fc5a983dSmrg pNv->reg[(0x00614300+orOff)/4] = 0x70000 | (pclk > limit ? 0x101 : 0); 50fc5a983dSmrg} 51fc5a983dSmrg 52fc5a983dSmrgstatic void 53fc5a983dSmrgG80SorDPMSSet(xf86OutputPtr output, int mode) 54fc5a983dSmrg{ 55fc5a983dSmrg G80Ptr pNv = G80PTR(output->scrn); 56fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 57fc5a983dSmrg const int off = 0x800 * pPriv->or; 58fc5a983dSmrg CARD32 tmp; 59fc5a983dSmrg 60fc5a983dSmrg while(pNv->reg[(0x0061C004+off)/4] & 0x80000000); 61fc5a983dSmrg 62fc5a983dSmrg tmp = pNv->reg[(0x0061C004+off)/4]; 63fc5a983dSmrg tmp |= 0x80000000; 64fc5a983dSmrg 65fc5a983dSmrg if(mode == DPMSModeOn) 66fc5a983dSmrg tmp |= 1; 67fc5a983dSmrg else 68fc5a983dSmrg tmp &= ~1; 69fc5a983dSmrg 70fc5a983dSmrg pNv->reg[(0x0061C004+off)/4] = tmp; 71fc5a983dSmrg while((pNv->reg[(0x61C030+off)/4] & 0x10000000)); 72fc5a983dSmrg} 73fc5a983dSmrg 74fc5a983dSmrgstatic int 75fc5a983dSmrgG80TMDSModeValid(xf86OutputPtr output, DisplayModePtr mode) 76fc5a983dSmrg{ 77fc5a983dSmrg G80Ptr pNv = G80PTR(output->scrn); 78fc5a983dSmrg 79fc5a983dSmrg // Disable dual-link modes unless enabled in the config file. 80fc5a983dSmrg if (mode->Clock > 165000 && !pNv->AllowDualLink) 81fc5a983dSmrg return MODE_CLOCK_HIGH; 82fc5a983dSmrg 83fc5a983dSmrg return G80OutputModeValid(output, mode); 84fc5a983dSmrg} 85fc5a983dSmrg 86fc5a983dSmrgstatic int 87fc5a983dSmrgG80LVDSModeValid(xf86OutputPtr output, DisplayModePtr mode) 88fc5a983dSmrg{ 89fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 90fc5a983dSmrg DisplayModePtr native = pPriv->nativeMode; 91fc5a983dSmrg 92fc5a983dSmrg // Ignore modes larger than the native res. 93fc5a983dSmrg if (mode->HDisplay > native->HDisplay || mode->VDisplay > native->VDisplay) 94fc5a983dSmrg return MODE_PANEL; 95fc5a983dSmrg 96fc5a983dSmrg return G80OutputModeValid(output, mode); 97fc5a983dSmrg} 98fc5a983dSmrg 99fc5a983dSmrgstatic void 100fc5a983dSmrgG80SorModeSet(xf86OutputPtr output, DisplayModePtr mode, 101fc5a983dSmrg DisplayModePtr adjusted_mode) 102fc5a983dSmrg{ 103fc5a983dSmrg ScrnInfoPtr pScrn = output->scrn; 104fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 105fc5a983dSmrg const int sorOff = 0x40 * pPriv->or; 106fc5a983dSmrg CARD32 type; 107fc5a983dSmrg 108fc5a983dSmrg if(!adjusted_mode) { 109fc5a983dSmrg /* Disconnect the SOR */ 110fc5a983dSmrg C(0x00000600 + sorOff, 0); 111fc5a983dSmrg return; 112fc5a983dSmrg } 113fc5a983dSmrg 114fc5a983dSmrg if(pPriv->panelType == LVDS) 115fc5a983dSmrg type = 0; 116fc5a983dSmrg else if(adjusted_mode->Clock > 165000) 117fc5a983dSmrg type = 0x500; 118fc5a983dSmrg else 119fc5a983dSmrg type = 0x100; 120fc5a983dSmrg 121fc5a983dSmrg // This wouldn't be necessary, but the server is stupid and calls 122fc5a983dSmrg // G80SorDPMSSet after the output is disconnected, even though the hardware 123fc5a983dSmrg // turns it off automatically. 124fc5a983dSmrg G80SorDPMSSet(output, DPMSModeOn); 125fc5a983dSmrg 126fc5a983dSmrg C(0x00000600 + sorOff, 127fc5a983dSmrg (G80CrtcGetHead(output->crtc) == HEAD0 ? 1 : 2) | 128fc5a983dSmrg type | 129fc5a983dSmrg ((adjusted_mode->Flags & V_NHSYNC) ? 0x1000 : 0) | 130fc5a983dSmrg ((adjusted_mode->Flags & V_NVSYNC) ? 0x2000 : 0)); 131fc5a983dSmrg 132fc5a983dSmrg G80CrtcSetScale(output->crtc, adjusted_mode, pPriv->scale); 133fc5a983dSmrg} 134fc5a983dSmrg 135fc5a983dSmrgstatic xf86OutputStatus 136fc5a983dSmrgG80SorDetect(xf86OutputPtr output) 137fc5a983dSmrg{ 138fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 139fc5a983dSmrg 140fc5a983dSmrg /* Assume physical status isn't going to change before the BlockHandler */ 141fc5a983dSmrg if(pPriv->cached_status != XF86OutputStatusUnknown) 142fc5a983dSmrg return pPriv->cached_status; 143fc5a983dSmrg 144fc5a983dSmrg G80OutputPartnersDetect(pPriv->partner, output, pPriv->i2c); 145fc5a983dSmrg return pPriv->cached_status; 146fc5a983dSmrg} 147fc5a983dSmrg 148fc5a983dSmrgstatic xf86OutputStatus 149fc5a983dSmrgG80SorLVDSDetect(xf86OutputPtr output) 150fc5a983dSmrg{ 151fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 152fc5a983dSmrg 153fc5a983dSmrg if(pPriv->i2c) { 154fc5a983dSmrg /* If LVDS has an I2C port, use the normal probe routine to get the 155fc5a983dSmrg * EDID, if possible. */ 156fc5a983dSmrg G80SorDetect(output); 157fc5a983dSmrg } 158fc5a983dSmrg 159fc5a983dSmrg /* Ignore G80SorDetect and assume LVDS is always connected */ 160fc5a983dSmrg return XF86OutputStatusConnected; 161fc5a983dSmrg} 162fc5a983dSmrg 163fc5a983dSmrgstatic void 164fc5a983dSmrgG80SorDestroy(xf86OutputPtr output) 165fc5a983dSmrg{ 166fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 167fc5a983dSmrg 168fc5a983dSmrg G80OutputDestroy(output); 169fc5a983dSmrg 170fc5a983dSmrg xf86DeleteMode(&pPriv->nativeMode, pPriv->nativeMode); 171fc5a983dSmrg 172008c6922Smrg free(output->driver_private); 173fc5a983dSmrg output->driver_private = NULL; 174fc5a983dSmrg} 175fc5a983dSmrg 176fc5a983dSmrgstatic void G80SorSetModeBackend(DisplayModePtr dst, const DisplayModePtr src) 177fc5a983dSmrg{ 178fc5a983dSmrg // Stash the backend mode timings from src into dst 179fc5a983dSmrg dst->Clock = src->Clock; 180fc5a983dSmrg dst->Flags = src->Flags; 181fc5a983dSmrg dst->CrtcHDisplay = src->CrtcHDisplay; 182fc5a983dSmrg dst->CrtcHBlankStart = src->CrtcHBlankStart; 183fc5a983dSmrg dst->CrtcHSyncStart = src->CrtcHSyncStart; 184fc5a983dSmrg dst->CrtcHSyncEnd = src->CrtcHSyncEnd; 185fc5a983dSmrg dst->CrtcHBlankEnd = src->CrtcHBlankEnd; 186fc5a983dSmrg dst->CrtcHTotal = src->CrtcHTotal; 187fc5a983dSmrg dst->CrtcHSkew = src->CrtcHSkew; 188fc5a983dSmrg dst->CrtcVDisplay = src->CrtcVDisplay; 189fc5a983dSmrg dst->CrtcVBlankStart = src->CrtcVBlankStart; 190fc5a983dSmrg dst->CrtcVSyncStart = src->CrtcVSyncStart; 191fc5a983dSmrg dst->CrtcVSyncEnd = src->CrtcVSyncEnd; 192fc5a983dSmrg dst->CrtcVBlankEnd = src->CrtcVBlankEnd; 193fc5a983dSmrg dst->CrtcVTotal = src->CrtcVTotal; 194fc5a983dSmrg dst->CrtcHAdjusted = src->CrtcHAdjusted; 195fc5a983dSmrg dst->CrtcVAdjusted = src->CrtcVAdjusted; 196fc5a983dSmrg} 197fc5a983dSmrg 198fc5a983dSmrgstatic Bool 199fc5a983dSmrgG80SorModeFixup(xf86OutputPtr output, DisplayModePtr mode, 200fc5a983dSmrg DisplayModePtr adjusted_mode) 201fc5a983dSmrg{ 202fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 203fc5a983dSmrg DisplayModePtr native = pPriv->nativeMode; 204fc5a983dSmrg 205fc5a983dSmrg if(native && pPriv->scale != G80_SCALE_OFF) { 206fc5a983dSmrg G80SorSetModeBackend(adjusted_mode, native); 207fc5a983dSmrg // This mode is already "fixed" 208fc5a983dSmrg G80CrtcSkipModeFixup(output->crtc); 209fc5a983dSmrg } 210fc5a983dSmrg 211fc5a983dSmrg return TRUE; 212fc5a983dSmrg} 213fc5a983dSmrg 214fc5a983dSmrgstatic Bool 215fc5a983dSmrgG80SorTMDSModeFixup(xf86OutputPtr output, DisplayModePtr mode, 216fc5a983dSmrg DisplayModePtr adjusted_mode) 217fc5a983dSmrg{ 218fc5a983dSmrg int scrnIndex = output->scrn->scrnIndex; 219fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 220fc5a983dSmrg DisplayModePtr modes = output->probed_modes; 221fc5a983dSmrg 222fc5a983dSmrg xf86DeleteMode(&pPriv->nativeMode, pPriv->nativeMode); 223fc5a983dSmrg 224fc5a983dSmrg if(modes) { 225fc5a983dSmrg // Find the preferred mode and use that as the "native" mode. 226fc5a983dSmrg // If no preferred mode is available, use the first one. 227fc5a983dSmrg DisplayModePtr mode; 228fc5a983dSmrg 229fc5a983dSmrg // Find the preferred mode. 230fc5a983dSmrg for(mode = modes; mode; mode = mode->next) { 231fc5a983dSmrg if(mode->type & M_T_PREFERRED) { 232fc5a983dSmrg xf86DrvMsgVerb(scrnIndex, X_INFO, 5, 233fc5a983dSmrg "%s: preferred mode is %s\n", 234fc5a983dSmrg output->name, mode->name); 235fc5a983dSmrg break; 236fc5a983dSmrg } 237fc5a983dSmrg } 238fc5a983dSmrg 239fc5a983dSmrg // XXX: May not want to allow scaling if no preferred mode is found. 240fc5a983dSmrg if(!mode) { 241fc5a983dSmrg mode = modes; 242fc5a983dSmrg xf86DrvMsgVerb(scrnIndex, X_INFO, 5, 243fc5a983dSmrg "%s: no preferred mode found, using %s\n", 244fc5a983dSmrg output->name, mode->name); 245fc5a983dSmrg } 246fc5a983dSmrg 247fc5a983dSmrg pPriv->nativeMode = xf86DuplicateMode(mode); 248fc5a983dSmrg G80CrtcDoModeFixup(pPriv->nativeMode, mode); 249fc5a983dSmrg } 250fc5a983dSmrg 251fc5a983dSmrg return G80SorModeFixup(output, mode, adjusted_mode); 252fc5a983dSmrg} 253fc5a983dSmrg 254fc5a983dSmrgstatic DisplayModePtr 255fc5a983dSmrgG80SorGetLVDSModes(xf86OutputPtr output) 256fc5a983dSmrg{ 257fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 258fc5a983dSmrg 259fc5a983dSmrg /* If an EDID was read during detection, use the modes from that. */ 260fc5a983dSmrg DisplayModePtr modes = G80OutputGetDDCModes(output); 261fc5a983dSmrg if(modes) 262fc5a983dSmrg return modes; 263fc5a983dSmrg 264fc5a983dSmrg /* Otherwise, feed in the mode we read during initialization. */ 265fc5a983dSmrg return xf86DuplicateMode(pPriv->nativeMode); 266fc5a983dSmrg} 267fc5a983dSmrg 268fc5a983dSmrg#define MAKE_ATOM(a) MakeAtom((a), sizeof(a) - 1, TRUE); 269fc5a983dSmrg 270fc5a983dSmrgstruct property { 271fc5a983dSmrg Atom atom; 272fc5a983dSmrg INT32 range[2]; 273fc5a983dSmrg}; 274fc5a983dSmrg 275fc5a983dSmrgstatic struct { 276fc5a983dSmrg struct property dither; 277fc5a983dSmrg struct property scale; 278fc5a983dSmrg} properties; 279fc5a983dSmrg 280fc5a983dSmrgstatic void 281fc5a983dSmrgG80SorCreateResources(xf86OutputPtr output) 282fc5a983dSmrg{ 283fc5a983dSmrg ScrnInfoPtr pScrn = output->scrn; 284fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 285fc5a983dSmrg int data, err; 286fc5a983dSmrg const char *s; 287fc5a983dSmrg 288fc5a983dSmrg /******** dithering ********/ 289fc5a983dSmrg properties.dither.atom = MAKE_ATOM("dither"); 290fc5a983dSmrg properties.dither.range[0] = 0; 291fc5a983dSmrg properties.dither.range[1] = 1; 292fc5a983dSmrg err = RRConfigureOutputProperty(output->randr_output, 293fc5a983dSmrg properties.dither.atom, FALSE, TRUE, FALSE, 294fc5a983dSmrg 2, properties.dither.range); 295fc5a983dSmrg if(err) 296fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 297fc5a983dSmrg "Failed to configure dithering property for %s: error %d\n", 298fc5a983dSmrg output->name, err); 299fc5a983dSmrg 300fc5a983dSmrg // Set the default value 301fc5a983dSmrg data = pNv->Dither; 302fc5a983dSmrg err = RRChangeOutputProperty(output->randr_output, properties.dither.atom, 303fc5a983dSmrg XA_INTEGER, 32, PropModeReplace, 1, &data, 304fc5a983dSmrg FALSE, FALSE); 305fc5a983dSmrg if(err) 306fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 307fc5a983dSmrg "Failed to set dithering property for %s: error %d\n", 308fc5a983dSmrg output->name, err); 309fc5a983dSmrg 310fc5a983dSmrg /******** scaling ********/ 311fc5a983dSmrg properties.scale.atom = MAKE_ATOM("scale"); 312fc5a983dSmrg err = RRConfigureOutputProperty(output->randr_output, 313fc5a983dSmrg properties.scale.atom, FALSE, FALSE, 314fc5a983dSmrg FALSE, 0, NULL); 315fc5a983dSmrg if(err) 316fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 317fc5a983dSmrg "Failed to configure scaling property for %s: error %d\n", 318fc5a983dSmrg output->name, err); 319fc5a983dSmrg 320fc5a983dSmrg // Set the default value 321fc5a983dSmrg s = "aspect"; 322fc5a983dSmrg err = RRChangeOutputProperty(output->randr_output, properties.scale.atom, 323fc5a983dSmrg XA_STRING, 8, PropModeReplace, strlen(s), 324fc5a983dSmrg (pointer)s, FALSE, FALSE); 325fc5a983dSmrg if(err) 326fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 327fc5a983dSmrg "Failed to set scaling property for %s: error %d\n", 328fc5a983dSmrg output->name, err); 329fc5a983dSmrg} 330fc5a983dSmrg 331fc5a983dSmrgstatic Bool 332fc5a983dSmrgG80SorSetProperty(xf86OutputPtr output, Atom prop, RRPropertyValuePtr val) 333fc5a983dSmrg{ 334fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 335fc5a983dSmrg 336fc5a983dSmrg if(prop == properties.dither.atom) { 337fc5a983dSmrg INT32 i; 338fc5a983dSmrg 339fc5a983dSmrg if(val->type != XA_INTEGER || val->format != 32 || val->size != 1) 340fc5a983dSmrg return FALSE; 341fc5a983dSmrg 342fc5a983dSmrg i = *(INT32*)val->data; 343fc5a983dSmrg if(i < properties.dither.range[0] || i > properties.dither.range[1]) 344fc5a983dSmrg return FALSE; 345fc5a983dSmrg 346fc5a983dSmrg G80CrtcSetDither(output->crtc, i, TRUE); 347fc5a983dSmrg } else if(prop == properties.scale.atom) { 348fc5a983dSmrg const char *s; 349fc5a983dSmrg enum G80ScaleMode oldScale, scale; 350fc5a983dSmrg int i; 351fc5a983dSmrg const struct { 352fc5a983dSmrg const char *name; 353fc5a983dSmrg enum G80ScaleMode scale; 354fc5a983dSmrg } modes[] = { 355fc5a983dSmrg { "off", G80_SCALE_OFF }, 356fc5a983dSmrg { "aspect", G80_SCALE_ASPECT }, 357fc5a983dSmrg { "fill", G80_SCALE_FILL }, 358fc5a983dSmrg { "center", G80_SCALE_CENTER }, 359fc5a983dSmrg { NULL, 0 }, 360fc5a983dSmrg }; 361fc5a983dSmrg 362fc5a983dSmrg if(val->type != XA_STRING || val->format != 8) 363fc5a983dSmrg return FALSE; 364fc5a983dSmrg s = (char*)val->data; 365fc5a983dSmrg 366fc5a983dSmrg for(i = 0; modes[i].name; i++) { 367fc5a983dSmrg const char *name = modes[i].name; 368fc5a983dSmrg const int len = strlen(name); 369fc5a983dSmrg 370fc5a983dSmrg if(val->size == len && !strncmp(name, s, len)) { 371fc5a983dSmrg scale = modes[i].scale; 372fc5a983dSmrg break; 373fc5a983dSmrg } 374fc5a983dSmrg } 375fc5a983dSmrg if(!modes[i].name) 376fc5a983dSmrg return FALSE; 377fc5a983dSmrg if(scale == G80_SCALE_OFF && pPriv->panelType == LVDS) 378fc5a983dSmrg // LVDS requires scaling 379fc5a983dSmrg return FALSE; 380fc5a983dSmrg 381fc5a983dSmrg oldScale = pPriv->scale; 382fc5a983dSmrg pPriv->scale = scale; 383fc5a983dSmrg if(output->crtc) { 384fc5a983dSmrg xf86CrtcPtr crtc = output->crtc; 385fc5a983dSmrg 386fc5a983dSmrg if(!xf86CrtcSetMode(crtc, &crtc->desiredMode, crtc->desiredRotation, 387fc5a983dSmrg crtc->desiredX, crtc->desiredY)) { 388fc5a983dSmrg xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, 389fc5a983dSmrg "Failed to set scaling to %s for output %s\n", 390fc5a983dSmrg modes[i].name, output->name); 391fc5a983dSmrg 392fc5a983dSmrg // Restore old scale and try again. 393fc5a983dSmrg pPriv->scale = oldScale; 394fc5a983dSmrg if(!xf86CrtcSetMode(crtc, &crtc->desiredMode, 395fc5a983dSmrg crtc->desiredRotation, crtc->desiredX, 396fc5a983dSmrg crtc->desiredY)) { 397fc5a983dSmrg xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, 398fc5a983dSmrg "Failed to restore old scaling for output %s\n", 399fc5a983dSmrg output->name); 400fc5a983dSmrg } 401fc5a983dSmrg 402fc5a983dSmrg return FALSE; 403fc5a983dSmrg } 404fc5a983dSmrg } 405fc5a983dSmrg } 406fc5a983dSmrg 407fc5a983dSmrg return TRUE; 408fc5a983dSmrg} 409fc5a983dSmrg 410fc5a983dSmrgstatic const xf86OutputFuncsRec G80SorTMDSOutputFuncs = { 411fc5a983dSmrg .dpms = G80SorDPMSSet, 412fc5a983dSmrg .save = NULL, 413fc5a983dSmrg .restore = NULL, 414fc5a983dSmrg .mode_valid = G80TMDSModeValid, 415fc5a983dSmrg .mode_fixup = G80SorTMDSModeFixup, 416fc5a983dSmrg .prepare = G80OutputPrepare, 417fc5a983dSmrg .commit = G80OutputCommit, 418fc5a983dSmrg .mode_set = G80SorModeSet, 419fc5a983dSmrg .detect = G80SorDetect, 420fc5a983dSmrg .get_modes = G80OutputGetDDCModes, 421fc5a983dSmrg .create_resources = G80SorCreateResources, 422fc5a983dSmrg .set_property = G80SorSetProperty, 423fc5a983dSmrg .destroy = G80SorDestroy, 424fc5a983dSmrg}; 425fc5a983dSmrg 426fc5a983dSmrgstatic const xf86OutputFuncsRec G80SorLVDSOutputFuncs = { 427fc5a983dSmrg .dpms = G80SorDPMSSet, 428fc5a983dSmrg .save = NULL, 429fc5a983dSmrg .restore = NULL, 430fc5a983dSmrg .mode_valid = G80LVDSModeValid, 431fc5a983dSmrg .mode_fixup = G80SorModeFixup, 432fc5a983dSmrg .prepare = G80OutputPrepare, 433fc5a983dSmrg .commit = G80OutputCommit, 434fc5a983dSmrg .mode_set = G80SorModeSet, 435fc5a983dSmrg .detect = G80SorLVDSDetect, 436fc5a983dSmrg .get_modes = G80SorGetLVDSModes, 437fc5a983dSmrg .create_resources = G80SorCreateResources, 438fc5a983dSmrg .set_property = G80SorSetProperty, 439fc5a983dSmrg .destroy = G80SorDestroy, 440fc5a983dSmrg}; 441fc5a983dSmrg 442fc5a983dSmrgstatic DisplayModePtr 443fc5a983dSmrgReadLVDSNativeMode(G80Ptr pNv, const int off) 444fc5a983dSmrg{ 445fc5a983dSmrg DisplayModePtr mode = xnfcalloc(1, sizeof(DisplayModeRec)); 446fc5a983dSmrg const CARD32 size = pNv->reg[(0x00610B4C+off)/4]; 447fc5a983dSmrg const int width = size & 0x3fff; 448fc5a983dSmrg const int height = (size >> 16) & 0x3fff; 449fc5a983dSmrg 450fc5a983dSmrg mode->HDisplay = mode->CrtcHDisplay = width; 451fc5a983dSmrg mode->VDisplay = mode->CrtcVDisplay = height; 452fc5a983dSmrg mode->Clock = pNv->reg[(0x610AD4+off)/4] & 0x3fffff; 453fc5a983dSmrg mode->CrtcHBlankStart = pNv->reg[(0x610AFC+off)/4]; 454fc5a983dSmrg mode->CrtcHSyncEnd = pNv->reg[(0x610B04+off)/4]; 455fc5a983dSmrg mode->CrtcHBlankEnd = pNv->reg[(0x610AE8+off)/4]; 456fc5a983dSmrg mode->CrtcHTotal = pNv->reg[(0x610AF4+off)/4]; 457fc5a983dSmrg 458fc5a983dSmrg mode->next = mode->prev = NULL; 459fc5a983dSmrg mode->status = MODE_OK; 460fc5a983dSmrg mode->type = M_T_DRIVER | M_T_PREFERRED; 461fc5a983dSmrg 462fc5a983dSmrg xf86SetModeDefaultName(mode); 463fc5a983dSmrg 464fc5a983dSmrg return mode; 465fc5a983dSmrg} 466fc5a983dSmrg 467fc5a983dSmrgstatic DisplayModePtr 468fc5a983dSmrgGetLVDSNativeMode(G80Ptr pNv) 469fc5a983dSmrg{ 470fc5a983dSmrg CARD32 val = pNv->reg[0x00610050/4]; 471fc5a983dSmrg 472fc5a983dSmrg if((val & 3) == 2) 473fc5a983dSmrg return ReadLVDSNativeMode(pNv, 0); 474fc5a983dSmrg else if((val & 0x300) == 0x200) 475fc5a983dSmrg return ReadLVDSNativeMode(pNv, 0x540); 476fc5a983dSmrg 477fc5a983dSmrg return NULL; 478fc5a983dSmrg} 479fc5a983dSmrg 480fc5a983dSmrgxf86OutputPtr 481fc5a983dSmrgG80CreateSor(ScrnInfoPtr pScrn, ORNum or, PanelType panelType) 482fc5a983dSmrg{ 483fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 484fc5a983dSmrg G80OutputPrivPtr pPriv = xnfcalloc(sizeof(*pPriv), 1); 485fc5a983dSmrg const int off = 0x800 * or; 486fc5a983dSmrg xf86OutputPtr output; 487fc5a983dSmrg char orName[5]; 488fc5a983dSmrg const xf86OutputFuncsRec *funcs; 489fc5a983dSmrg 490fc5a983dSmrg if(!pPriv) 491fc5a983dSmrg return NULL; 492fc5a983dSmrg 493fc5a983dSmrg if(panelType == LVDS) { 494fc5a983dSmrg strcpy(orName, "LVDS"); 495fc5a983dSmrg funcs = &G80SorLVDSOutputFuncs; 496fc5a983dSmrg 497fc5a983dSmrg pPriv->nativeMode = GetLVDSNativeMode(pNv); 498fc5a983dSmrg 499fc5a983dSmrg if(!pPriv->nativeMode) { 500fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 501fc5a983dSmrg "Failed to find LVDS native mode\n"); 502008c6922Smrg free(pPriv); 503fc5a983dSmrg return NULL; 504fc5a983dSmrg } 505fc5a983dSmrg 506fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s native size %dx%d\n", 507fc5a983dSmrg orName, pPriv->nativeMode->HDisplay, 508fc5a983dSmrg pPriv->nativeMode->VDisplay); 509fc5a983dSmrg } else { 510fc5a983dSmrg snprintf(orName, 5, "DVI%d", or); 511fc5a983dSmrg pNv->reg[(0x61C00C+off)/4] = 0x03010700; 512fc5a983dSmrg pNv->reg[(0x61C010+off)/4] = 0x0000152f; 513fc5a983dSmrg pNv->reg[(0x61C014+off)/4] = 0x00000000; 514fc5a983dSmrg pNv->reg[(0x61C018+off)/4] = 0x00245af8; 515fc5a983dSmrg funcs = &G80SorTMDSOutputFuncs; 516fc5a983dSmrg } 517fc5a983dSmrg 518fc5a983dSmrg output = xf86OutputCreate(pScrn, funcs, orName); 519fc5a983dSmrg 520fc5a983dSmrg pPriv->type = SOR; 521fc5a983dSmrg pPriv->or = or; 522fc5a983dSmrg pPriv->panelType = panelType; 523fc5a983dSmrg pPriv->cached_status = XF86OutputStatusUnknown; 524fc5a983dSmrg if(panelType == TMDS) 525fc5a983dSmrg pPriv->set_pclk = G80SorSetPClk; 526fc5a983dSmrg output->driver_private = pPriv; 527fc5a983dSmrg output->interlaceAllowed = TRUE; 528fc5a983dSmrg output->doubleScanAllowed = TRUE; 529fc5a983dSmrg 530fc5a983dSmrg return output; 531fc5a983dSmrg} 532