g80_output.c revision bd2f6fc9
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 25fc5a983dSmrg#ifdef HAVE_CONFIG_H 26fc5a983dSmrg#include "config.h" 27fc5a983dSmrg#endif 28fc5a983dSmrg 29fc5a983dSmrg#include <strings.h> 30fc5a983dSmrg 31fc5a983dSmrg#include "g80_type.h" 32fc5a983dSmrg#include "g80_display.h" 33fc5a983dSmrg#include "g80_output.h" 34fc5a983dSmrg 35fc5a983dSmrgstatic unsigned G80FindLoadVal(const unsigned char *table1) 36fc5a983dSmrg{ 37fc5a983dSmrg const unsigned char *p = table1; 38fc5a983dSmrg int count; 39fc5a983dSmrg const CARD32 def = 340; 40fc5a983dSmrg 41fc5a983dSmrg for(p = table1; *(CARD16*)p != 0xb8ff && p < table1 + 64000; p += 2); 42fc5a983dSmrg if(p == table1 + 64000) 43fc5a983dSmrg return def; 44fc5a983dSmrg p += 2; 45fc5a983dSmrg if(*(CARD32*)p != 0x544942) 46fc5a983dSmrg return def; 47fc5a983dSmrg p += 4; 48fc5a983dSmrg if(*(CARD16*)p != 0x100) 49fc5a983dSmrg return def; 50fc5a983dSmrg p += 2; 51fc5a983dSmrg if(*p != 12) 52fc5a983dSmrg return def; 53fc5a983dSmrg p++; 54fc5a983dSmrg if(*p != 6) 55fc5a983dSmrg return def; 56fc5a983dSmrg p++; 57fc5a983dSmrg count = *p; 58fc5a983dSmrg p += 2; 59fc5a983dSmrg for(; *p != 'A' && count >= 0; count--, p += 6); 60fc5a983dSmrg if(count == -1) 61fc5a983dSmrg return def; 62fc5a983dSmrg p += 4; 63fc5a983dSmrg p = table1 + *(CARD16*)p; 64fc5a983dSmrg p = table1 + *(CARD16*)p; 65fc5a983dSmrg if(p[0] != 0x10 || p[1] != 4 || p[2] != 4 || p[3] != 2) 66fc5a983dSmrg return def; 67fc5a983dSmrg return *(CARD32*)(p + 4) & 0x3ff; 68fc5a983dSmrg} 69fc5a983dSmrg 70fc5a983dSmrgstatic Bool G80ReadPortMapping(int scrnIndex, G80Ptr pNv) 71fc5a983dSmrg{ 72fc5a983dSmrg unsigned char *table2, *table3; 73fc5a983dSmrg unsigned char headerSize, entries, table3Entries, table3EntSize; 74fc5a983dSmrg int i; 75fc5a983dSmrg CARD16 a; 76fc5a983dSmrg CARD32 b; 77fc5a983dSmrg 78fc5a983dSmrg /* Clear the i2c map to invalid */ 79fc5a983dSmrg for(i = 0; i < G80_NUM_I2C_PORTS; i++) 80fc5a983dSmrg pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1; 81fc5a983dSmrg 82fc5a983dSmrg if(*(CARD16*)pNv->table1 != 0xaa55) goto fail; 83fc5a983dSmrg 84fc5a983dSmrg a = *(CARD16*)(pNv->table1 + 0x36); 85fc5a983dSmrg table2 = (unsigned char*)pNv->table1 + a; 86fc5a983dSmrg 87fc5a983dSmrg if(table2[0] != 0x40) goto fail; 88fc5a983dSmrg 89fc5a983dSmrg b = *(CARD32*)(table2 + 6); 90fc5a983dSmrg if(b != 0x4edcbdcb) goto fail; 91fc5a983dSmrg 92fc5a983dSmrg table3 = (unsigned char*)pNv->table1 + *(CARD16*)(table2 + 4); 93fc5a983dSmrg table3Entries = table3[2]; 94fc5a983dSmrg table3EntSize = table3[3]; 95fc5a983dSmrg table3 += table3[1]; 96fc5a983dSmrg 97fc5a983dSmrg headerSize = table2[1]; 98fc5a983dSmrg entries = table2[2]; 99fc5a983dSmrg 100fc5a983dSmrg for(i = 0; i < entries; i++) { 101fc5a983dSmrg int type, port, portType; 102fc5a983dSmrg ORNum or; 103fc5a983dSmrg 104fc5a983dSmrg b = *(CARD32*)&table2[headerSize + 8*i]; 105fc5a983dSmrg type = b & 0xf; 106fc5a983dSmrg port = (b >> 4) & 0xf; 107fc5a983dSmrg or = ffs((b >> 24) & 0xf) - 1; 108fc5a983dSmrg 109fc5a983dSmrg if(b & 0x300000) 110fc5a983dSmrg /* Can't handle this type of output yet */ 111fc5a983dSmrg continue; 112fc5a983dSmrg 113fc5a983dSmrg if(type == 0xe) break; 114fc5a983dSmrg 115fc5a983dSmrg switch(type) { 116fc5a983dSmrg case 0: /* CRT */ 117fc5a983dSmrg if(port >= table3Entries) { 118fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 119fc5a983dSmrg "VGA%d: invalid port %d\n", or, port); 120fc5a983dSmrg break; 121fc5a983dSmrg } 122fc5a983dSmrg b = *(CARD32*)&table3[table3EntSize * port]; 123fc5a983dSmrg port = b & 0xff; 124fc5a983dSmrg portType = b >> 24; 125fc5a983dSmrg if(portType != 5) { 126fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 127fc5a983dSmrg "VGA%d: invalid port type %d\n", or, portType); 128fc5a983dSmrg break; 129fc5a983dSmrg } 130bd2f6fc9Smrg if(port >= G80_NUM_I2C_PORTS) { 131bd2f6fc9Smrg xf86DrvMsg(scrnIndex, X_WARNING, 132bd2f6fc9Smrg "VGA%d: unrecognized port %d\n", or, port); 133bd2f6fc9Smrg break; 134bd2f6fc9Smrg } 135fc5a983dSmrg if(pNv->i2cMap[port].dac != -1) { 136fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 137fc5a983dSmrg "DDC routing table corrupt! DAC %i -> %i for " 138fc5a983dSmrg "port %i\n", or, pNv->i2cMap[port].dac, port); 139fc5a983dSmrg } 140fc5a983dSmrg pNv->i2cMap[port].dac = or; 141fc5a983dSmrg break; 142fc5a983dSmrg case 1: /* TV */ 143fc5a983dSmrg /* Ignore TVs */ 144fc5a983dSmrg break; 145fc5a983dSmrg 146fc5a983dSmrg case 2: /* TMDS */ 147fc5a983dSmrg if(port >= table3Entries) { 148fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 149fc5a983dSmrg "DVI%d: invalid port %d\n", or, port); 150fc5a983dSmrg break; 151fc5a983dSmrg } 152fc5a983dSmrg b = *(CARD32*)&table3[table3EntSize * port]; 153fc5a983dSmrg port = b & 0xff; 154fc5a983dSmrg portType = b >> 24; 155fc5a983dSmrg if(portType != 5) { 156fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 157fc5a983dSmrg "DVI%d: invalid port type %d\n", or, portType); 158fc5a983dSmrg break; 159fc5a983dSmrg } 160bd2f6fc9Smrg if(port >= G80_NUM_I2C_PORTS) { 161bd2f6fc9Smrg xf86DrvMsg(scrnIndex, X_WARNING, 162bd2f6fc9Smrg "DVI%d: unrecognized port %d\n", or, port); 163bd2f6fc9Smrg break; 164bd2f6fc9Smrg } 165fc5a983dSmrg if(pNv->i2cMap[port].sor != -1) 166fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 167fc5a983dSmrg "DDC routing table corrupt! SOR %i -> %i for " 168fc5a983dSmrg "port %i\n", or, pNv->i2cMap[port].sor, port); 169fc5a983dSmrg pNv->i2cMap[port].sor = or; 170fc5a983dSmrg break; 171fc5a983dSmrg 172fc5a983dSmrg case 3: /* LVDS */ 173fc5a983dSmrg pNv->lvds.present = TRUE; 174fc5a983dSmrg pNv->lvds.or = or; 175fc5a983dSmrg pNv->lvds.i2cPort = -1; 176fc5a983dSmrg 177fc5a983dSmrg if(port == 15) { 178fc5a983dSmrg xf86DrvMsg(scrnIndex, X_INFO, "LVDS has no I2C port\n"); 179fc5a983dSmrg break; 180fc5a983dSmrg } 181fc5a983dSmrg if(port >= table3Entries) { 182fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 183fc5a983dSmrg "LVDS: invalid port %d\n", port); 184fc5a983dSmrg break; 185fc5a983dSmrg } 186fc5a983dSmrg b = *(CARD32*)&table3[table3EntSize * port]; 187fc5a983dSmrg port = b & 0xff; 188fc5a983dSmrg portType = b >> 24; 189fc5a983dSmrg if(portType != 5) { 190fc5a983dSmrg xf86DrvMsg(scrnIndex, X_WARNING, 191fc5a983dSmrg "LVDS: invalid port type %d\n", portType); 192fc5a983dSmrg break; 193fc5a983dSmrg } 194bd2f6fc9Smrg if(port >= G80_NUM_I2C_PORTS) { 195bd2f6fc9Smrg xf86DrvMsg(scrnIndex, X_WARNING, 196bd2f6fc9Smrg "LVDS: unrecognized port %d\n", port); 197bd2f6fc9Smrg break; 198bd2f6fc9Smrg } 199fc5a983dSmrg pNv->lvds.i2cPort = port; 200fc5a983dSmrg 201fc5a983dSmrg break; 202fc5a983dSmrg 203fc5a983dSmrg default: 204fc5a983dSmrg break; 205fc5a983dSmrg } 206fc5a983dSmrg } 207fc5a983dSmrg 208fc5a983dSmrg xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n"); 209f3561b8bSmrg if(pNv->lvds.present) { 210f3561b8bSmrg if (pNv->lvds.i2cPort != -1) 211f3561b8bSmrg xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i (LVDS)\n", pNv->lvds.i2cPort, pNv->lvds.or); 212f3561b8bSmrg else 213f3561b8bSmrg xf86DrvMsg(scrnIndex, X_PROBED, " [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or); 214f3561b8bSmrg } 215fc5a983dSmrg for(i = 0; i < G80_NUM_I2C_PORTS; i++) { 216fc5a983dSmrg if(pNv->i2cMap[i].dac != -1) 217fc5a983dSmrg xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac); 218fc5a983dSmrg if(pNv->i2cMap[i].sor != -1) 219fc5a983dSmrg xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor); 220fc5a983dSmrg } 221fc5a983dSmrg 222fc5a983dSmrg pNv->loadVal = G80FindLoadVal(pNv->table1); 223fc5a983dSmrg xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal); 224fc5a983dSmrg 225fc5a983dSmrg return TRUE; 226fc5a983dSmrg 227fc5a983dSmrgfail: 228fc5a983dSmrg xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. " 229fc5a983dSmrg "Mode setting will probably fail!\n"); 230fc5a983dSmrg return FALSE; 231fc5a983dSmrg} 232fc5a983dSmrg 233fc5a983dSmrgstatic CARD32 i2cAddr(const int port) 234fc5a983dSmrg{ 235bd2f6fc9Smrg const CARD32 addrs[G80_NUM_I2C_PORTS] = { 236bd2f6fc9Smrg 0xE138, 0xE150, 0xE168, 0xE180, 0xE254, 0xE274, 0xE764, 0xE780, 0xE79C, 237bd2f6fc9Smrg 0xE7B8 238bd2f6fc9Smrg }; 239bd2f6fc9Smrg return addrs[port]; 240fc5a983dSmrg} 241fc5a983dSmrg 242fc5a983dSmrgstatic void G80_I2CPutBits(I2CBusPtr b, int clock, int data) 243fc5a983dSmrg{ 244fc5a983dSmrg G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]); 245fc5a983dSmrg pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1; 246fc5a983dSmrg} 247fc5a983dSmrg 248fc5a983dSmrgstatic void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data) 249fc5a983dSmrg{ 250fc5a983dSmrg G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]); 251fc5a983dSmrg unsigned char val; 252fc5a983dSmrg 253fc5a983dSmrg val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4]; 254fc5a983dSmrg *clock = !!(val & 1); 255fc5a983dSmrg *data = !!(val & 2); 256fc5a983dSmrg} 257fc5a983dSmrg 258fc5a983dSmrgstatic I2CBusPtr 259fc5a983dSmrgG80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port) 260fc5a983dSmrg{ 261fc5a983dSmrg I2CBusPtr i2c; 262fc5a983dSmrg 263fc5a983dSmrg /* Allocate the I2C bus structure */ 264fc5a983dSmrg i2c = xf86CreateI2CBusRec(); 265fc5a983dSmrg if(!i2c) return NULL; 266fc5a983dSmrg 267fc5a983dSmrg i2c->BusName = strdup(name); 268fc5a983dSmrg i2c->scrnIndex = pScrn->scrnIndex; 269fc5a983dSmrg i2c->I2CPutBits = G80_I2CPutBits; 270fc5a983dSmrg i2c->I2CGetBits = G80_I2CGetBits; 271fc5a983dSmrg i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ 272fc5a983dSmrg i2c->StartTimeout = 550; 273fc5a983dSmrg i2c->BitTimeout = 40; 274fc5a983dSmrg i2c->ByteTimeout = 40; 275fc5a983dSmrg i2c->AcknTimeout = 40; 276fc5a983dSmrg i2c->DriverPrivate.val = port; 277fc5a983dSmrg 278fc5a983dSmrg if(xf86I2CBusInit(i2c)) { 279fc5a983dSmrg return i2c; 280fc5a983dSmrg } else { 281fc5a983dSmrg xfree(i2c); 282fc5a983dSmrg return NULL; 283fc5a983dSmrg } 284fc5a983dSmrg} 285fc5a983dSmrg 286fc5a983dSmrgvoid 287fc5a983dSmrgG80OutputSetPClk(xf86OutputPtr output, int pclk) 288fc5a983dSmrg{ 289fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 290fc5a983dSmrg if(pPriv->set_pclk) 291fc5a983dSmrg pPriv->set_pclk(output, pclk); 292fc5a983dSmrg} 293fc5a983dSmrg 294fc5a983dSmrgint 295fc5a983dSmrgG80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode) 296fc5a983dSmrg{ 297fc5a983dSmrg if(mode->Clock > 400000) 298fc5a983dSmrg return MODE_CLOCK_HIGH; 299fc5a983dSmrg if(mode->Clock < 25000) 300fc5a983dSmrg return MODE_CLOCK_LOW; 301fc5a983dSmrg 302fc5a983dSmrg return MODE_OK; 303fc5a983dSmrg} 304fc5a983dSmrg 305fc5a983dSmrgvoid 306fc5a983dSmrgG80OutputPrepare(xf86OutputPtr output) 307fc5a983dSmrg{ 308fc5a983dSmrg} 309fc5a983dSmrg 310fc5a983dSmrgvoid 311fc5a983dSmrgG80OutputCommit(xf86OutputPtr output) 312fc5a983dSmrg{ 313fc5a983dSmrg} 314fc5a983dSmrg 315fc5a983dSmrgstatic xf86MonPtr 316fc5a983dSmrgProbeDDC(I2CBusPtr i2c) 317fc5a983dSmrg{ 318fc5a983dSmrg ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex]; 319fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 320fc5a983dSmrg xf86MonPtr monInfo = NULL; 321fc5a983dSmrg const int bus = i2c->DriverPrivate.val; 322fc5a983dSmrg const CARD32 addr = i2cAddr(bus); 323fc5a983dSmrg 324fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, 325fc5a983dSmrg "Probing for EDID on I2C bus %i...\n", bus); 326fc5a983dSmrg pNv->reg[addr/4] = 7; 327fc5a983dSmrg /* Should probably use xf86OutputGetEDID here */ 328f3561b8bSmrg#ifdef EDID_COMPLETE_RAWDATA 329f3561b8bSmrg monInfo = xf86DoEEDID(pScrn->scrnIndex, i2c, TRUE); 330f3561b8bSmrg#else 331fc5a983dSmrg monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c); 332f3561b8bSmrg#endif 333fc5a983dSmrg pNv->reg[addr/4] = 3; 334fc5a983dSmrg 335fc5a983dSmrg if(monInfo) { 336fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 337fc5a983dSmrg "DDC detected a %s:\n", monInfo->features.input_type ? 338fc5a983dSmrg "DFP" : "CRT"); 339fc5a983dSmrg xf86PrintEDID(monInfo); 340fc5a983dSmrg } else { 341fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n"); 342fc5a983dSmrg } 343fc5a983dSmrg 344fc5a983dSmrg return monInfo; 345fc5a983dSmrg} 346fc5a983dSmrg 347fc5a983dSmrg/* 348fc5a983dSmrg * Read an EDID from the i2c port. Perform load detection on the DAC (if 349fc5a983dSmrg * present) to see if the display is connected via VGA. Sets the cached status 350fc5a983dSmrg * of both outputs. The status is marked dirty again in the BlockHandler. 351fc5a983dSmrg */ 352fc5a983dSmrgvoid G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c) 353fc5a983dSmrg{ 354fc5a983dSmrg xf86MonPtr monInfo = ProbeDDC(i2c); 355fc5a983dSmrg xf86OutputPtr connected = NULL; 356fc5a983dSmrg Bool load = dac && G80DacLoadDetect(dac); 357fc5a983dSmrg 358fc5a983dSmrg if(dac) { 359fc5a983dSmrg G80OutputPrivPtr pPriv = dac->driver_private; 360fc5a983dSmrg 361fc5a983dSmrg if(load) { 362fc5a983dSmrg pPriv->cached_status = XF86OutputStatusConnected; 363fc5a983dSmrg connected = dac; 364fc5a983dSmrg } else { 365fc5a983dSmrg pPriv->cached_status = XF86OutputStatusDisconnected; 366fc5a983dSmrg } 367fc5a983dSmrg } 368fc5a983dSmrg 369fc5a983dSmrg if(sor) { 370fc5a983dSmrg G80OutputPrivPtr pPriv = sor->driver_private; 371fc5a983dSmrg 372fc5a983dSmrg if(monInfo && !load) { 373fc5a983dSmrg pPriv->cached_status = XF86OutputStatusConnected; 374fc5a983dSmrg connected = sor; 375fc5a983dSmrg } else { 376fc5a983dSmrg pPriv->cached_status = XF86OutputStatusDisconnected; 377fc5a983dSmrg } 378fc5a983dSmrg } 379fc5a983dSmrg 380fc5a983dSmrg if(connected) 381fc5a983dSmrg xf86OutputSetEDID(connected, monInfo); 382fc5a983dSmrg} 383fc5a983dSmrg 384fc5a983dSmrg/* 385fc5a983dSmrg * Reset the cached output status for all outputs. Called from G80BlockHandler. 386fc5a983dSmrg */ 387fc5a983dSmrgvoid 388fc5a983dSmrgG80OutputResetCachedStatus(ScrnInfoPtr pScrn) 389fc5a983dSmrg{ 390fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 391fc5a983dSmrg int i; 392fc5a983dSmrg 393fc5a983dSmrg for(i = 0; i < xf86_config->num_output; i++) { 394fc5a983dSmrg G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private; 395fc5a983dSmrg pPriv->cached_status = XF86OutputStatusUnknown; 396fc5a983dSmrg } 397fc5a983dSmrg} 398fc5a983dSmrg 399fc5a983dSmrgDisplayModePtr 400fc5a983dSmrgG80OutputGetDDCModes(xf86OutputPtr output) 401fc5a983dSmrg{ 402fc5a983dSmrg /* The EDID is read as part of the detect step */ 403fc5a983dSmrg output->funcs->detect(output); 404fc5a983dSmrg return xf86OutputGetEDIDModes(output); 405fc5a983dSmrg} 406fc5a983dSmrg 407fc5a983dSmrgvoid 408fc5a983dSmrgG80OutputDestroy(xf86OutputPtr output) 409fc5a983dSmrg{ 410fc5a983dSmrg G80OutputPrivPtr pPriv = output->driver_private; 411fc5a983dSmrg 412fc5a983dSmrg if(pPriv->partner) 413fc5a983dSmrg ((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL; 414fc5a983dSmrg else 415fc5a983dSmrg xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE); 416fc5a983dSmrg pPriv->i2c = NULL; 417fc5a983dSmrg} 418fc5a983dSmrg 419fc5a983dSmrgBool 420fc5a983dSmrgG80CreateOutputs(ScrnInfoPtr pScrn) 421fc5a983dSmrg{ 422fc5a983dSmrg G80Ptr pNv = G80PTR(pScrn); 423fc5a983dSmrg xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 424fc5a983dSmrg int i; 425fc5a983dSmrg 426fc5a983dSmrg if(!G80ReadPortMapping(pScrn->scrnIndex, pNv)) 427fc5a983dSmrg return FALSE; 428fc5a983dSmrg 429fc5a983dSmrg /* For each DDC port, create an output for the attached ORs */ 430fc5a983dSmrg for(i = 0; i < G80_NUM_I2C_PORTS; i++) { 431fc5a983dSmrg xf86OutputPtr dac = NULL, sor = NULL; 432fc5a983dSmrg I2CBusPtr i2c; 433fc5a983dSmrg char i2cName[16]; 434fc5a983dSmrg 435fc5a983dSmrg if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) 436fc5a983dSmrg /* No outputs on this port */ 437fc5a983dSmrg continue; 438fc5a983dSmrg 439fc5a983dSmrg snprintf(i2cName, sizeof(i2cName), "I2C%i", i); 440fc5a983dSmrg i2c = G80I2CInit(pScrn, i2cName, i); 441fc5a983dSmrg if(!i2c) { 442fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 443fc5a983dSmrg "Failed to initialize I2C for port %i.\n", 444fc5a983dSmrg i); 445fc5a983dSmrg continue; 446fc5a983dSmrg } 447fc5a983dSmrg 448fc5a983dSmrg if(pNv->i2cMap[i].dac != -1) 449fc5a983dSmrg dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac); 450fc5a983dSmrg if(pNv->i2cMap[i].sor != -1) 451fc5a983dSmrg sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS); 452fc5a983dSmrg 453fc5a983dSmrg if(dac) { 454fc5a983dSmrg G80OutputPrivPtr pPriv = dac->driver_private; 455fc5a983dSmrg 456fc5a983dSmrg pPriv->partner = sor; 457fc5a983dSmrg pPriv->i2c = i2c; 458fc5a983dSmrg pPriv->scale = G80_SCALE_OFF; 459fc5a983dSmrg } 460fc5a983dSmrg if(sor) { 461fc5a983dSmrg G80OutputPrivPtr pPriv = sor->driver_private; 462fc5a983dSmrg 463fc5a983dSmrg pPriv->partner = dac; 464fc5a983dSmrg pPriv->i2c = i2c; 465fc5a983dSmrg pPriv->scale = G80_SCALE_ASPECT; 466fc5a983dSmrg } 467fc5a983dSmrg } 468fc5a983dSmrg 469fc5a983dSmrg if(pNv->lvds.present) { 470fc5a983dSmrg xf86OutputPtr lvds = G80CreateSor(pScrn, pNv->lvds.or, LVDS); 471fc5a983dSmrg G80OutputPrivPtr pPriv = lvds->driver_private; 472fc5a983dSmrg 473fc5a983dSmrg pPriv->scale = G80_SCALE_ASPECT; 474fc5a983dSmrg 475fc5a983dSmrg if(pNv->lvds.i2cPort != -1) { 476fc5a983dSmrg char i2cName[16]; 477fc5a983dSmrg 478fc5a983dSmrg snprintf(i2cName, sizeof(i2cName), "I2C%i (LVDS)", pNv->lvds.i2cPort); 479fc5a983dSmrg pPriv->i2c = G80I2CInit(pScrn, i2cName, pNv->lvds.i2cPort); 480fc5a983dSmrg if(!pPriv->i2c) { 481fc5a983dSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 482fc5a983dSmrg "Failed to initialize I2C for port %i (LVDS)!\n", 483fc5a983dSmrg pNv->lvds.i2cPort); 484fc5a983dSmrg } 485fc5a983dSmrg } 486fc5a983dSmrg } 487fc5a983dSmrg 488fc5a983dSmrg /* For each output, set the crtc and clone masks */ 489fc5a983dSmrg for(i = 0; i < xf86_config->num_output; i++) { 490fc5a983dSmrg xf86OutputPtr output = xf86_config->output[i]; 491fc5a983dSmrg 492fc5a983dSmrg /* Any output can connect to any head */ 493fc5a983dSmrg output->possible_crtcs = 0x3; 494fc5a983dSmrg output->possible_clones = 0; 495fc5a983dSmrg } 496fc5a983dSmrg 497fc5a983dSmrg return TRUE; 498fc5a983dSmrg} 499