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