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