i830_dvo.c revision fa225cbc
1/************************************************************************** 2 3Copyright 2006 Dave Airlie <airlied@linux.ie> 4 5All Rights Reserved. 6 7Permission is hereby granted, free of charge, to any person obtaining a 8copy of this software and associated documentation files (the "Software"), 9to deal in the Software without restriction, including without limitation 10on the rights to use, copy, modify, merge, publish, distribute, sub 11license, and/or sell copies of the Software, and to permit persons to whom 12the Software is furnished to do so, subject to the following conditions: 13The above copyright notice and this permission notice (including the next 14paragraph) shall be included in all copies or substantial portions of the 15Software. 16 17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 21DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25****** 26********************************************************************/ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include "xf86.h" 33#include "i830.h" 34#include "i830_display.h" 35#include "i810_reg.h" 36 37#include "sil164/sil164.h" 38#include "ch7xxx/ch7xxx.h" 39#include "tfp410/tfp410.h" 40 41/* driver list */ 42static struct _I830DVODriver i830_dvo_drivers[] = 43{ 44 { 45 .type = I830_OUTPUT_DVO_TMDS, 46 .modulename = "sil164", 47 .fntablename = "SIL164VidOutput", 48 .dvo_reg = DVOC, 49 .address = (SIL164_ADDR_1<<1), 50 }, 51 { 52 .type = I830_OUTPUT_DVO_TMDS, 53 .modulename = "ch7xxx", 54 .fntablename = "CH7xxxVidOutput", 55 .dvo_reg = DVOC, 56 .address = (CH7xxx_ADDR_1<<1), 57 }, 58 { 59 .type = I830_OUTPUT_DVO_LVDS, 60 .modulename = "ivch", 61 .fntablename = "ivch_methods", 62 .dvo_reg = DVOA, 63 .address = 0x04, /* Might also be 0x44, 0x84, 0xc4 */ 64 }, 65 { 66 .type = I830_OUTPUT_DVO_TMDS, 67 .modulename = "tfp410", 68 .fntablename = "TFP410VidOutput", 69 .dvo_reg = DVOC, 70 .address = (TFP410_ADDR_1<<1), 71 }, 72 { 73 .type = I830_OUTPUT_DVO_LVDS, 74 .modulename = "ch7017", 75 .fntablename = "ch7017_methods", 76 .dvo_reg = DVOC, 77 .address = 0xea, 78 .gpio = GPIOE, 79 } 80}; 81 82#define I830_NUM_DVO_DRIVERS (sizeof(i830_dvo_drivers)/sizeof(struct _I830DVODriver)) 83 84static void 85i830_dvo_dpms(xf86OutputPtr output, int mode) 86{ 87 ScrnInfoPtr pScrn = output->scrn; 88 I830Ptr pI830 = I830PTR(pScrn); 89 I830OutputPrivatePtr intel_output = output->driver_private; 90 struct _I830DVODriver *drv = intel_output->i2c_drv; 91 void * dev_priv = drv->dev_priv; 92 unsigned int dvo_reg = drv->dvo_reg; 93 94 if (mode == DPMSModeOn) { 95 OUTREG(dvo_reg, INREG(dvo_reg) | DVO_ENABLE); 96 POSTING_READ(dvo_reg); 97 (*intel_output->i2c_drv->vid_rec->dpms)(dev_priv, mode); 98 } else { 99 (*intel_output->i2c_drv->vid_rec->dpms)(dev_priv, mode); 100 OUTREG(dvo_reg, INREG(dvo_reg) & ~DVO_ENABLE); 101 POSTING_READ(dvo_reg); 102 } 103} 104 105static void 106i830_dvo_save(xf86OutputPtr output) 107{ 108 ScrnInfoPtr pScrn = output->scrn; 109 I830Ptr pI830 = I830PTR(pScrn); 110 I830OutputPrivatePtr intel_output = output->driver_private; 111 void * dev_priv = intel_output->i2c_drv->dev_priv; 112 113 /* Each output should probably just save the registers it touches, but for 114 * now, use more overkill. 115 */ 116 pI830->saveDVOA = INREG(DVOA); 117 pI830->saveDVOB = INREG(DVOB); 118 pI830->saveDVOC = INREG(DVOC); 119 120 (*intel_output->i2c_drv->vid_rec->save)(dev_priv); 121} 122 123static void 124i830_dvo_restore(xf86OutputPtr output) 125{ 126 ScrnInfoPtr pScrn = output->scrn; 127 I830Ptr pI830 = I830PTR(pScrn); 128 I830OutputPrivatePtr intel_output = output->driver_private; 129 void * dev_priv = intel_output->i2c_drv->dev_priv; 130 131 (*intel_output->i2c_drv->vid_rec->restore)(dev_priv); 132 133 OUTREG(DVOA, pI830->saveDVOA); 134 OUTREG(DVOB, pI830->saveDVOB); 135 OUTREG(DVOC, pI830->saveDVOC); 136} 137 138static int 139i830_dvo_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) 140{ 141 ScrnInfoPtr pScrn = output->scrn; 142 I830Ptr pI830 = I830PTR(pScrn); 143 I830OutputPrivatePtr intel_output = output->driver_private; 144 void *dev_priv = intel_output->i2c_drv->dev_priv; 145 146 if (pMode->Flags & V_DBLSCAN) 147 return MODE_NO_DBLESCAN; 148 149 /* XXX: Validate clock range */ 150 151 if (pI830->lvds_fixed_mode) { 152 if (pMode->HDisplay > pI830->lvds_fixed_mode->HDisplay) 153 return MODE_PANEL; 154 if (pMode->VDisplay > pI830->lvds_fixed_mode->VDisplay) 155 return MODE_PANEL; 156 } 157 158 return intel_output->i2c_drv->vid_rec->mode_valid(dev_priv, pMode); 159} 160 161static Bool 162i830_dvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 163 DisplayModePtr adjusted_mode) 164{ 165 ScrnInfoPtr pScrn = output->scrn; 166 I830Ptr pI830 = I830PTR(pScrn); 167 I830OutputPrivatePtr intel_output = output->driver_private; 168 169 /* If we have timings from the BIOS for the panel, put them in 170 * to the adjusted mode. The CRTC will be set up for this mode, 171 * with the panel scaling set up to source from the H/VDisplay 172 * of the original mode. 173 */ 174 if (pI830->lvds_fixed_mode != NULL) { 175 adjusted_mode->HDisplay = pI830->lvds_fixed_mode->HDisplay; 176 adjusted_mode->HSyncStart = pI830->lvds_fixed_mode->HSyncStart; 177 adjusted_mode->HSyncEnd = pI830->lvds_fixed_mode->HSyncEnd; 178 adjusted_mode->HTotal = pI830->lvds_fixed_mode->HTotal; 179 adjusted_mode->VDisplay = pI830->lvds_fixed_mode->VDisplay; 180 adjusted_mode->VSyncStart = pI830->lvds_fixed_mode->VSyncStart; 181 adjusted_mode->VSyncEnd = pI830->lvds_fixed_mode->VSyncEnd; 182 adjusted_mode->VTotal = pI830->lvds_fixed_mode->VTotal; 183 adjusted_mode->Clock = pI830->lvds_fixed_mode->Clock; 184 xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); 185 } 186 187 if (intel_output->i2c_drv->vid_rec->mode_fixup) 188 return intel_output->i2c_drv->vid_rec->mode_fixup (intel_output->i2c_drv->dev_priv, 189 mode, adjusted_mode); 190 return TRUE; 191} 192 193static void 194i830_dvo_mode_set(xf86OutputPtr output, DisplayModePtr mode, 195 DisplayModePtr adjusted_mode) 196{ 197 ScrnInfoPtr pScrn = output->scrn; 198 I830Ptr pI830 = I830PTR(pScrn); 199 xf86CrtcPtr crtc = output->crtc; 200 I830CrtcPrivatePtr intel_crtc = crtc->driver_private; 201 I830OutputPrivatePtr intel_output = output->driver_private; 202 struct _I830DVODriver *drv = intel_output->i2c_drv; 203 int pipe = intel_crtc->pipe; 204 uint32_t dvo; 205 unsigned int dvo_reg = drv->dvo_reg, dvo_srcdim_reg; 206 int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; 207 208 switch (dvo_reg) { 209 case DVOA: 210 default: 211 dvo_srcdim_reg = DVOA_SRCDIM; 212 break; 213 case DVOB: 214 dvo_srcdim_reg = DVOB_SRCDIM; 215 break; 216 case DVOC: 217 dvo_srcdim_reg = DVOC_SRCDIM; 218 break; 219 } 220 221 intel_output->i2c_drv->vid_rec->mode_set(intel_output->i2c_drv->dev_priv, 222 mode, adjusted_mode); 223 224 /* Save the data order, since I don't know what it should be set to. */ 225 dvo = INREG(dvo_reg) & (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); 226 dvo |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | DVO_BLANK_ACTIVE_HIGH; 227 228 if (pipe == 1) 229 dvo |= DVO_PIPE_B_SELECT; 230 dvo |= DVO_PIPE_STALL; 231 if (adjusted_mode->Flags & V_PHSYNC) 232 dvo |= DVO_HSYNC_ACTIVE_HIGH; 233 if (adjusted_mode->Flags & V_PVSYNC) 234 dvo |= DVO_VSYNC_ACTIVE_HIGH; 235 236 OUTREG(dpll_reg, INREG(dpll_reg) | DPLL_DVO_HIGH_SPEED); 237 238 /*OUTREG(DVOB_SRCDIM, 239 (adjusted_mode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | 240 (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ 241 OUTREG(dvo_srcdim_reg, 242 (adjusted_mode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | 243 (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT)); 244 /*OUTREG(DVOB, dvo);*/ 245 OUTREG(dvo_reg, dvo); 246} 247 248/** 249 * Detect the output connection on our DVO device. 250 * 251 * Unimplemented. 252 */ 253static xf86OutputStatus 254i830_dvo_detect(xf86OutputPtr output) 255{ 256 I830OutputPrivatePtr intel_output = output->driver_private; 257 void *dev_priv = intel_output->i2c_drv->dev_priv; 258 259 return intel_output->i2c_drv->vid_rec->detect(dev_priv); 260} 261 262static DisplayModePtr 263i830_dvo_get_modes(xf86OutputPtr output) 264{ 265 ScrnInfoPtr pScrn = output->scrn; 266 I830Ptr pI830 = I830PTR(pScrn); 267 I830OutputPrivatePtr intel_output = output->driver_private; 268 DisplayModePtr modes; 269 270 /* We should probably have an i2c driver get_modes function for those 271 * devices which will have a fixed set of modes determined by the chip 272 * (TV-out, for example), but for now with just TMDS and LVDS, that's not 273 * the case. 274 */ 275 modes = i830_ddc_get_modes(output); 276 if (modes != NULL) 277 return modes; 278 279 if (intel_output->i2c_drv->vid_rec->get_modes) 280 { 281 modes = intel_output->i2c_drv->vid_rec->get_modes (intel_output->i2c_drv->dev_priv); 282 if (modes != NULL) 283 return modes; 284 } 285 286 if (pI830->lvds_fixed_mode != NULL) 287 return xf86DuplicateMode(pI830->lvds_fixed_mode); 288 289 return NULL; 290} 291 292static void 293i830_dvo_destroy (xf86OutputPtr output) 294{ 295 I830OutputPrivatePtr intel_output = output->driver_private; 296 297 if (intel_output) 298 { 299 if (intel_output->i2c_drv->vid_rec->destroy) 300 intel_output->i2c_drv->vid_rec->destroy (intel_output->i2c_drv->dev_priv); 301 if (intel_output->pI2CBus) 302 xf86DestroyI2CBusRec (intel_output->pI2CBus, TRUE, TRUE); 303 if (intel_output->pDDCBus) 304 xf86DestroyI2CBusRec (intel_output->pDDCBus, TRUE, TRUE); 305 xfree (intel_output); 306 } 307} 308 309#ifdef RANDR_GET_CRTC_INTERFACE 310static xf86CrtcPtr 311i830_dvo_get_crtc(xf86OutputPtr output) 312{ 313 ScrnInfoPtr pScrn = output->scrn; 314 I830Ptr pI830 = I830PTR(pScrn); 315 I830OutputPrivatePtr intel_output = output->driver_private; 316 struct _I830DVODriver *drv = intel_output->i2c_drv; 317 int pipe = !!(INREG(drv->dvo_reg) & SDVO_PIPE_B_SELECT); 318 319 return i830_pipe_to_crtc(pScrn, pipe); 320} 321#endif 322 323static const xf86OutputFuncsRec i830_dvo_output_funcs = { 324 .dpms = i830_dvo_dpms, 325 .save = i830_dvo_save, 326 .restore = i830_dvo_restore, 327 .mode_valid = i830_dvo_mode_valid, 328 .mode_fixup = i830_dvo_mode_fixup, 329 .prepare = i830_output_prepare, 330 .mode_set = i830_dvo_mode_set, 331 .commit = i830_output_commit, 332 .detect = i830_dvo_detect, 333 .get_modes = i830_dvo_get_modes, 334 .destroy = i830_dvo_destroy, 335#ifdef RANDR_GET_CRTC_INTERFACE 336 .get_crtc = i830_dvo_get_crtc, 337#endif 338}; 339 340/** 341 * Attempts to get a fixed panel timing for LVDS (currently only the i830). 342 * 343 * Other chips with DVO LVDS will need to extend this to deal with the LVDS 344 * chip being on DVOB/C and having multiple pipes. 345 */ 346static DisplayModePtr 347i830_dvo_get_current_mode (xf86OutputPtr output) 348{ 349 ScrnInfoPtr pScrn = output->scrn; 350 I830OutputPrivatePtr intel_output = output->driver_private; 351 I830Ptr pI830 = I830PTR(pScrn); 352 struct _I830DVODriver *drv = intel_output->i2c_drv; 353 unsigned int dvo_reg = drv->dvo_reg; 354 uint32_t dvo = INREG(dvo_reg); 355 DisplayModePtr mode = NULL; 356 357 /* If the DVO port is active, that'll be the LVDS, so we can pull out 358 * its timings to get how the BIOS set up the panel. 359 */ 360 if (dvo & DVO_ENABLE) 361 { 362 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); 363 int pipe = (dvo & DVO_PIPE_B_SELECT) ? 1 : 0; 364 int c; 365 366 for (c = 0; c < xf86_config->num_crtc; c++) 367 { 368 xf86CrtcPtr crtc = xf86_config->crtc[c]; 369 I830CrtcPrivatePtr intel_crtc = crtc->driver_private; 370 371 if (intel_crtc->pipe == pipe) 372 { 373 mode = i830_crtc_mode_get(pScrn, crtc); 374 375 if (mode) 376 { 377 mode->type |= M_T_PREFERRED; 378 379 if (dvo & DVO_HSYNC_ACTIVE_HIGH) 380 mode->Flags |= V_PHSYNC; 381 if (dvo & DVO_VSYNC_ACTIVE_HIGH) 382 mode->Flags |= V_PVSYNC; 383 } 384 break; 385 } 386 } 387 } 388 return mode; 389} 390 391void 392i830_dvo_init(ScrnInfoPtr pScrn) 393{ 394 I830Ptr pI830 = I830PTR(pScrn); 395 I830OutputPrivatePtr intel_output; 396 int ret; 397 int i; 398 void *ret_ptr; 399 struct _I830DVODriver *drv; 400 int gpio_inited = 0; 401 I2CBusPtr pI2CBus = NULL; 402 403 intel_output = xnfcalloc (sizeof (I830OutputPrivateRec), 1); 404 if (!intel_output) 405 return; 406 407 /* Set up the DDC bus */ 408 ret = I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOD, "DVODDC_D"); 409 if (!ret) { 410 xfree(intel_output); 411 return; 412 } 413 414 /* Now, try to find a controller */ 415 for (i = 0; i < I830_NUM_DVO_DRIVERS; i++) { 416 int gpio; 417 418 drv = &i830_dvo_drivers[i]; 419 drv->modhandle = xf86LoadSubModule(pScrn, drv->modulename); 420 if (drv->modhandle == NULL) 421 continue; 422 423 ret_ptr = NULL; 424 drv->vid_rec = LoaderSymbol(drv->fntablename); 425 426 if (!strcmp(drv->modulename, "ivch") && 427 pI830->quirk_flag & QUIRK_IVCH_NEED_DVOB) { 428 drv->dvo_reg = DVOB; 429 } 430 431 /* Allow the I2C driver info to specify the GPIO to be used in 432 * special cases, but otherwise default to what's defined in the spec. 433 */ 434 if (drv->gpio != 0) 435 gpio = drv->gpio; 436 else if (drv->type == I830_OUTPUT_DVO_LVDS) 437 gpio = GPIOB; 438 else 439 gpio = GPIOE; 440 441 /* Set up the I2C bus necessary for the chip we're probing. It appears 442 * that everything is on GPIOE except for panels on i830 laptops, which 443 * are on GPIOB (DVOA). 444 */ 445 if (gpio_inited != gpio) { 446 if (pI2CBus != NULL) 447 xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE); 448 if (!I830I2CInit(pScrn, &pI2CBus, gpio, 449 gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E")) { 450 continue; 451 } 452 } 453 454 if (drv->vid_rec != NULL) 455 ret_ptr = drv->vid_rec->init(pI2CBus, drv->address); 456 457 if (ret_ptr != NULL) { 458 xf86OutputPtr output = NULL; 459 460 intel_output->type = drv->type; 461 switch (drv->type) { 462 case I830_OUTPUT_DVO_TMDS: 463 intel_output->pipe_mask = ((1 << 0) | (1 << 1)); 464 intel_output->clone_mask = ((1 << I830_OUTPUT_ANALOG) | 465 (1 << I830_OUTPUT_DVO_TMDS)); 466 output = xf86OutputCreate(pScrn, &i830_dvo_output_funcs, 467 "TMDS"); 468 break; 469 case I830_OUTPUT_DVO_LVDS: 470 intel_output->pipe_mask = ((1 << 0) | (1 << 1)); 471 intel_output->clone_mask = (1 << I830_OUTPUT_DVO_LVDS); 472 output = xf86OutputCreate(pScrn, &i830_dvo_output_funcs, 473 "LVDS"); 474 break; 475 case I830_OUTPUT_DVO_TVOUT: 476 intel_output->pipe_mask = ((1 << 0) | (1 << 1)); 477 intel_output->clone_mask = (1 << I830_OUTPUT_DVO_TVOUT); 478 output = xf86OutputCreate(pScrn, &i830_dvo_output_funcs, 479 "TV"); 480 break; 481 } 482 if (output == NULL) { 483 xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE); 484 xf86DestroyI2CBusRec(intel_output->pDDCBus, TRUE, TRUE); 485 xfree(intel_output); 486 xf86UnloadSubModule(drv->modhandle); 487 return; 488 } 489 490 output->driver_private = intel_output; 491 output->subpixel_order = SubPixelHorizontalRGB; 492 output->interlaceAllowed = FALSE; 493 output->doubleScanAllowed = FALSE; 494 495 drv->dev_priv = ret_ptr; 496 intel_output->i2c_drv = drv; 497 intel_output->pI2CBus = pI2CBus; 498 499 if (intel_output->type == I830_OUTPUT_DVO_LVDS) { 500 /* For our LVDS chipsets, we should hopefully be able to 501 * dig the fixed panel mode out of the BIOS data. However, 502 * it's in a different format from the BIOS data on chipsets 503 * with integrated LVDS (stored in AIM headers, liekly), 504 * so for now, just get the current mode being output through 505 * DVO. 506 */ 507 pI830->lvds_fixed_mode = i830_dvo_get_current_mode(output); 508 pI830->lvds_dither = TRUE; 509 } 510 511 return; 512 } 513 xf86UnloadSubModule(drv->modhandle); 514 } 515 516 /* Didn't find a chip, so tear down. */ 517 if (pI2CBus != NULL) 518 xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE); 519 xf86DestroyI2CBusRec(intel_output->pDDCBus, TRUE, TRUE); 520 xfree(intel_output); 521} 522