g80_output.c revision fc5a983d
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 continue; 112 113 if(type == 0xe) break; 114 115 switch(type) { 116 case 0: /* CRT */ 117 if(port >= table3Entries) { 118 xf86DrvMsg(scrnIndex, X_WARNING, 119 "VGA%d: invalid port %d\n", or, port); 120 break; 121 } 122 b = *(CARD32*)&table3[table3EntSize * port]; 123 port = b & 0xff; 124 portType = b >> 24; 125 if(portType != 5) { 126 xf86DrvMsg(scrnIndex, X_WARNING, 127 "VGA%d: invalid port type %d\n", or, portType); 128 break; 129 } 130 if(pNv->i2cMap[port].dac != -1) { 131 xf86DrvMsg(scrnIndex, X_WARNING, 132 "DDC routing table corrupt! DAC %i -> %i for " 133 "port %i\n", or, pNv->i2cMap[port].dac, port); 134 } 135 pNv->i2cMap[port].dac = or; 136 break; 137 case 1: /* TV */ 138 /* Ignore TVs */ 139 break; 140 141 case 2: /* TMDS */ 142 if(port >= table3Entries) { 143 xf86DrvMsg(scrnIndex, X_WARNING, 144 "DVI%d: invalid port %d\n", or, port); 145 break; 146 } 147 b = *(CARD32*)&table3[table3EntSize * port]; 148 port = b & 0xff; 149 portType = b >> 24; 150 if(portType != 5) { 151 xf86DrvMsg(scrnIndex, X_WARNING, 152 "DVI%d: invalid port type %d\n", or, portType); 153 break; 154 } 155 if(pNv->i2cMap[port].sor != -1) 156 xf86DrvMsg(scrnIndex, X_WARNING, 157 "DDC routing table corrupt! SOR %i -> %i for " 158 "port %i\n", or, pNv->i2cMap[port].sor, port); 159 pNv->i2cMap[port].sor = or; 160 break; 161 162 case 3: /* LVDS */ 163 pNv->lvds.present = TRUE; 164 pNv->lvds.or = or; 165 pNv->lvds.i2cPort = -1; 166 167 if(port == 15) { 168 xf86DrvMsg(scrnIndex, X_INFO, "LVDS has no I2C port\n"); 169 break; 170 } 171 if(port >= table3Entries) { 172 xf86DrvMsg(scrnIndex, X_WARNING, 173 "LVDS: invalid port %d\n", port); 174 break; 175 } 176 b = *(CARD32*)&table3[table3EntSize * port]; 177 port = b & 0xff; 178 portType = b >> 24; 179 if(portType != 5) { 180 xf86DrvMsg(scrnIndex, X_WARNING, 181 "LVDS: invalid port type %d\n", portType); 182 break; 183 } 184 pNv->lvds.i2cPort = port; 185 186 break; 187 188 default: 189 break; 190 } 191 } 192 193 xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n"); 194 if(pNv->lvds.present) 195 xf86DrvMsg(scrnIndex, X_PROBED, " [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or); 196 for(i = 0; i < G80_NUM_I2C_PORTS; i++) { 197 if(pNv->i2cMap[i].dac != -1) 198 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac); 199 if(pNv->i2cMap[i].sor != -1) 200 xf86DrvMsg(scrnIndex, X_PROBED, " Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor); 201 } 202 203 pNv->loadVal = G80FindLoadVal(pNv->table1); 204 xf86DrvMsg(scrnIndex, X_PROBED, "Load detection: %d\n", pNv->loadVal); 205 206 return TRUE; 207 208fail: 209 xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table. " 210 "Mode setting will probably fail!\n"); 211 return FALSE; 212} 213 214static CARD32 i2cAddr(const int port) 215{ 216 const CARD32 base = (port > 3) ? 0x0000E1E0 : 0x0000E138; 217 return base + port * 0x18; 218} 219 220static void G80_I2CPutBits(I2CBusPtr b, int clock, int data) 221{ 222 G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]); 223 pNv->reg[i2cAddr(b->DriverPrivate.val)/4] = 4 | clock | data << 1; 224} 225 226static void G80_I2CGetBits(I2CBusPtr b, int *clock, int *data) 227{ 228 G80Ptr pNv = G80PTR(xf86Screens[b->scrnIndex]); 229 unsigned char val; 230 231 val = pNv->reg[i2cAddr(b->DriverPrivate.val)/4]; 232 *clock = !!(val & 1); 233 *data = !!(val & 2); 234} 235 236static I2CBusPtr 237G80I2CInit(ScrnInfoPtr pScrn, const char *name, const int port) 238{ 239 I2CBusPtr i2c; 240 241 /* Allocate the I2C bus structure */ 242 i2c = xf86CreateI2CBusRec(); 243 if(!i2c) return NULL; 244 245 i2c->BusName = strdup(name); 246 i2c->scrnIndex = pScrn->scrnIndex; 247 i2c->I2CPutBits = G80_I2CPutBits; 248 i2c->I2CGetBits = G80_I2CGetBits; 249 i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ 250 i2c->StartTimeout = 550; 251 i2c->BitTimeout = 40; 252 i2c->ByteTimeout = 40; 253 i2c->AcknTimeout = 40; 254 i2c->DriverPrivate.val = port; 255 256 if(xf86I2CBusInit(i2c)) { 257 return i2c; 258 } else { 259 xfree(i2c); 260 return NULL; 261 } 262} 263 264void 265G80OutputSetPClk(xf86OutputPtr output, int pclk) 266{ 267 G80OutputPrivPtr pPriv = output->driver_private; 268 if(pPriv->set_pclk) 269 pPriv->set_pclk(output, pclk); 270} 271 272int 273G80OutputModeValid(xf86OutputPtr output, DisplayModePtr mode) 274{ 275 if(mode->Clock > 400000) 276 return MODE_CLOCK_HIGH; 277 if(mode->Clock < 25000) 278 return MODE_CLOCK_LOW; 279 280 return MODE_OK; 281} 282 283void 284G80OutputPrepare(xf86OutputPtr output) 285{ 286} 287 288void 289G80OutputCommit(xf86OutputPtr output) 290{ 291} 292 293static xf86MonPtr 294ProbeDDC(I2CBusPtr i2c) 295{ 296 ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex]; 297 G80Ptr pNv = G80PTR(pScrn); 298 xf86MonPtr monInfo = NULL; 299 const int bus = i2c->DriverPrivate.val; 300 const CARD32 addr = i2cAddr(bus); 301 302 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 303 "Probing for EDID on I2C bus %i...\n", bus); 304 pNv->reg[addr/4] = 7; 305 /* Should probably use xf86OutputGetEDID here */ 306 monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c); 307 pNv->reg[addr/4] = 3; 308 309 if(monInfo) { 310 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, 311 "DDC detected a %s:\n", monInfo->features.input_type ? 312 "DFP" : "CRT"); 313 xf86PrintEDID(monInfo); 314 } else { 315 xf86DrvMsg(pScrn->scrnIndex, X_INFO, " ... none found\n"); 316 } 317 318 return monInfo; 319} 320 321/* 322 * Read an EDID from the i2c port. Perform load detection on the DAC (if 323 * present) to see if the display is connected via VGA. Sets the cached status 324 * of both outputs. The status is marked dirty again in the BlockHandler. 325 */ 326void G80OutputPartnersDetect(xf86OutputPtr dac, xf86OutputPtr sor, I2CBusPtr i2c) 327{ 328 xf86MonPtr monInfo = ProbeDDC(i2c); 329 xf86OutputPtr connected = NULL; 330 Bool load = dac && G80DacLoadDetect(dac); 331 332 if(dac) { 333 G80OutputPrivPtr pPriv = dac->driver_private; 334 335 if(load) { 336 pPriv->cached_status = XF86OutputStatusConnected; 337 connected = dac; 338 } else { 339 pPriv->cached_status = XF86OutputStatusDisconnected; 340 } 341 } 342 343 if(sor) { 344 G80OutputPrivPtr pPriv = sor->driver_private; 345 346 if(monInfo && !load) { 347 pPriv->cached_status = XF86OutputStatusConnected; 348 connected = sor; 349 } else { 350 pPriv->cached_status = XF86OutputStatusDisconnected; 351 } 352 } 353 354 if(connected) 355 xf86OutputSetEDID(connected, monInfo); 356} 357 358/* 359 * Reset the cached output status for all outputs. Called from G80BlockHandler. 360 */ 361void 362G80OutputResetCachedStatus(ScrnInfoPtr pScrn) 363{ 364 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 365 int i; 366 367 for(i = 0; i < xf86_config->num_output; i++) { 368 G80OutputPrivPtr pPriv = xf86_config->output[i]->driver_private; 369 pPriv->cached_status = XF86OutputStatusUnknown; 370 } 371} 372 373DisplayModePtr 374G80OutputGetDDCModes(xf86OutputPtr output) 375{ 376 /* The EDID is read as part of the detect step */ 377 output->funcs->detect(output); 378 return xf86OutputGetEDIDModes(output); 379} 380 381void 382G80OutputDestroy(xf86OutputPtr output) 383{ 384 G80OutputPrivPtr pPriv = output->driver_private; 385 386 if(pPriv->partner) 387 ((G80OutputPrivPtr)pPriv->partner->driver_private)->partner = NULL; 388 else 389 xf86DestroyI2CBusRec(pPriv->i2c, TRUE, TRUE); 390 pPriv->i2c = NULL; 391} 392 393Bool 394G80CreateOutputs(ScrnInfoPtr pScrn) 395{ 396 G80Ptr pNv = G80PTR(pScrn); 397 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 398 int i; 399 400 if(!G80ReadPortMapping(pScrn->scrnIndex, pNv)) 401 return FALSE; 402 403 /* For each DDC port, create an output for the attached ORs */ 404 for(i = 0; i < G80_NUM_I2C_PORTS; i++) { 405 xf86OutputPtr dac = NULL, sor = NULL; 406 I2CBusPtr i2c; 407 char i2cName[16]; 408 409 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) 410 /* No outputs on this port */ 411 continue; 412 413 snprintf(i2cName, sizeof(i2cName), "I2C%i", i); 414 i2c = G80I2CInit(pScrn, i2cName, i); 415 if(!i2c) { 416 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 417 "Failed to initialize I2C for port %i.\n", 418 i); 419 continue; 420 } 421 422 if(pNv->i2cMap[i].dac != -1) 423 dac = G80CreateDac(pScrn, pNv->i2cMap[i].dac); 424 if(pNv->i2cMap[i].sor != -1) 425 sor = G80CreateSor(pScrn, pNv->i2cMap[i].sor, TMDS); 426 427 if(dac) { 428 G80OutputPrivPtr pPriv = dac->driver_private; 429 430 pPriv->partner = sor; 431 pPriv->i2c = i2c; 432 pPriv->scale = G80_SCALE_OFF; 433 } 434 if(sor) { 435 G80OutputPrivPtr pPriv = sor->driver_private; 436 437 pPriv->partner = dac; 438 pPriv->i2c = i2c; 439 pPriv->scale = G80_SCALE_ASPECT; 440 } 441 } 442 443 if(pNv->lvds.present) { 444 xf86OutputPtr lvds = G80CreateSor(pScrn, pNv->lvds.or, LVDS); 445 G80OutputPrivPtr pPriv = lvds->driver_private; 446 447 pPriv->scale = G80_SCALE_ASPECT; 448 449 if(pNv->lvds.i2cPort != -1) { 450 I2CBusPtr i2c; 451 char i2cName[16]; 452 453 snprintf(i2cName, sizeof(i2cName), "I2C%i (LVDS)", pNv->lvds.i2cPort); 454 pPriv->i2c = G80I2CInit(pScrn, i2cName, pNv->lvds.i2cPort); 455 if(!pPriv->i2c) { 456 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 457 "Failed to initialize I2C for port %i (LVDS)!\n", 458 pNv->lvds.i2cPort); 459 } 460 } 461 } 462 463 /* For each output, set the crtc and clone masks */ 464 for(i = 0; i < xf86_config->num_output; i++) { 465 xf86OutputPtr output = xf86_config->output[i]; 466 467 /* Any output can connect to any head */ 468 output->possible_crtcs = 0x3; 469 output->possible_clones = 0; 470 } 471 472 return TRUE; 473} 474