1/* 2 * Copyright © 2006 Intel 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 "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: 24 * Eric Anholt <eric@anholt.net> 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 "xf86Modes.h" 35#include "i830_display.h" 36 37static void 38i830_crt_dpms(xf86OutputPtr output, int mode) 39{ 40 ScrnInfoPtr pScrn = output->scrn; 41 I830Ptr pI830 = I830PTR(pScrn); 42 uint32_t temp; 43 44 temp = INREG(ADPA); 45 temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); 46 temp &= ~ADPA_DAC_ENABLE; 47 48 switch(mode) { 49 case DPMSModeOn: 50 temp |= ADPA_DAC_ENABLE; 51 break; 52 case DPMSModeStandby: 53 temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; 54 break; 55 case DPMSModeSuspend: 56 temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; 57 break; 58 case DPMSModeOff: 59 temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; 60 break; 61 } 62 63 OUTREG(ADPA, temp); 64} 65 66static void 67i830_crt_save (xf86OutputPtr output) 68{ 69 ScrnInfoPtr pScrn = output->scrn; 70 I830Ptr pI830 = I830PTR(pScrn); 71 72 pI830->saveADPA = INREG(ADPA); 73} 74 75static void 76i830_crt_restore (xf86OutputPtr output) 77{ 78 ScrnInfoPtr pScrn = output->scrn; 79 I830Ptr pI830 = I830PTR(pScrn); 80 81 OUTREG(ADPA, pI830->saveADPA); 82} 83 84static int 85i830_crt_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) 86{ 87 ScrnInfoPtr pScrn = output->scrn; 88 I830Ptr pI830 = I830PTR(pScrn); 89 int maxclock; 90 91 if (pMode->Flags & V_DBLSCAN) 92 return MODE_NO_DBLESCAN; 93 94 if (pMode->Clock < 25000) 95 return MODE_CLOCK_LOW; 96 97 if (!IS_I9XX(pI830)) 98 maxclock = 350000; 99 else 100 maxclock = 400000; 101 102 if (pMode->Clock > maxclock) 103 return MODE_CLOCK_HIGH; 104 105 return MODE_OK; 106} 107 108static Bool 109i830_crt_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 110 DisplayModePtr adjusted_mode) 111{ 112 return TRUE; 113} 114 115static void 116i830_crt_mode_set(xf86OutputPtr output, DisplayModePtr mode, 117 DisplayModePtr adjusted_mode) 118{ 119 ScrnInfoPtr pScrn = output->scrn; 120 I830Ptr pI830 = I830PTR(pScrn); 121 xf86CrtcPtr crtc = output->crtc; 122 I830CrtcPrivatePtr i830_crtc = crtc->driver_private; 123 int dpll_md_reg; 124 uint32_t adpa, dpll_md; 125 126 if (i830_crtc->pipe == 0) 127 dpll_md_reg = DPLL_A_MD; 128 else 129 dpll_md_reg = DPLL_B_MD; 130 /* 131 * Disable separate mode multiplier used when cloning SDVO to CRT 132 * XXX this needs to be adjusted when we really are cloning 133 */ 134 if (IS_I965G(pI830)) 135 { 136 dpll_md = INREG(dpll_md_reg); 137 OUTREG(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); 138 } 139 140 adpa = 0; 141 if (adjusted_mode->Flags & V_PHSYNC) 142 adpa |= ADPA_HSYNC_ACTIVE_HIGH; 143 if (adjusted_mode->Flags & V_PVSYNC) 144 adpa |= ADPA_VSYNC_ACTIVE_HIGH; 145 146 if (i830_crtc->pipe == 0) 147 { 148 adpa |= ADPA_PIPE_A_SELECT; 149 OUTREG(BCLRPAT_A, 0); 150 } 151 else 152 { 153 adpa |= ADPA_PIPE_B_SELECT; 154 OUTREG(BCLRPAT_B, 0); 155 } 156 157 OUTREG(ADPA, adpa); 158} 159 160/** 161 * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. 162 * 163 * Only for I945G/GM. 164 * 165 * \return TRUE if CRT is connected. 166 * \return FALSE if CRT is disconnected. 167 */ 168static Bool 169i830_crt_detect_hotplug(xf86OutputPtr output) 170{ 171 ScrnInfoPtr pScrn = output->scrn; 172 I830Ptr pI830 = I830PTR(pScrn); 173 uint32_t hotplug_en, temp; 174 const int timeout_ms = 1000; 175 int starttime, curtime; 176 int tries = 1; 177 int try; 178 179 /* On 4 series desktop, CRT detect sequence need to be done twice 180 * to get a reliable result. */ 181 if (IS_G4X(pI830) && !IS_GM45(pI830)) 182 tries = 2; 183 else 184 tries = 1; 185 186 hotplug_en = INREG(PORT_HOTPLUG_EN); 187 188 hotplug_en &= CRT_FORCE_HOTPLUG_MASK; 189 190 /* This starts the detection sequence */ 191 hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; 192 193 /* GM45 requires a longer activation period to reliably 194 * detect CRT 195 */ 196 if (IS_G4X(pI830)) 197 hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; 198 199 /* Use the default voltage value */ 200 hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; 201 202 for (try = 0; try < tries; try++) { 203 /* turn FORCE_DETECT on */ 204 OUTREG(PORT_HOTPLUG_EN, hotplug_en); 205 206 /* wait for FORCE_DETECT to go off */ 207 for (curtime = starttime = GetTimeInMillis(); 208 (curtime - starttime) < timeout_ms; 209 curtime = GetTimeInMillis()) 210 { 211 temp = INREG(PORT_HOTPLUG_EN); 212 213 if ((temp & CRT_HOTPLUG_FORCE_DETECT) == 0) 214 break; 215 } 216 } 217 218 /* Check the status to see if both blue and green are on now */ 219 temp = INREG(PORT_HOTPLUG_STAT); 220 return ((temp & CRT_HOTPLUG_MONITOR_MASK) == 221 CRT_HOTPLUG_MONITOR_COLOR); 222} 223 224/** 225 * Detects CRT presence by checking for load. 226 * 227 * Requires that the current pipe's DPLL is active. This will cause flicker 228 * on the CRT, so it should not be used while the display is being used. Only 229 * color (not monochrome) displays are detected. 230 * 231 * \return TRUE if CRT is connected. 232 * \return FALSE if CRT is disconnected. 233 */ 234static Bool 235i830_crt_detect_load (xf86CrtcPtr crtc, 236 xf86OutputPtr output) 237{ 238 ScrnInfoPtr pScrn = output->scrn; 239 I830Ptr pI830 = I830PTR(pScrn); 240 I830CrtcPrivatePtr i830_crtc = I830CrtcPrivate(crtc); 241 uint32_t save_bclrpat; 242 uint32_t save_vtotal; 243 uint32_t vtotal, vactive; 244 uint32_t vsample; 245 uint32_t vblank, vblank_start, vblank_end; 246 uint32_t dsl; 247 uint8_t st00; 248 int bclrpat_reg, pipeconf_reg, pipe_dsl_reg; 249 int vtotal_reg, vblank_reg, vsync_reg; 250 int pipe = i830_crtc->pipe; 251 Bool present; 252 253 if (pipe == 0) 254 { 255 bclrpat_reg = BCLRPAT_A; 256 vtotal_reg = VTOTAL_A; 257 vblank_reg = VBLANK_A; 258 vsync_reg = VSYNC_A; 259 pipeconf_reg = PIPEACONF; 260 pipe_dsl_reg = PIPEA_DSL; 261 } 262 else 263 { 264 bclrpat_reg = BCLRPAT_B; 265 vtotal_reg = VTOTAL_B; 266 vblank_reg = VBLANK_B; 267 vsync_reg = VSYNC_B; 268 pipeconf_reg = PIPEBCONF; 269 pipe_dsl_reg = PIPEB_DSL; 270 } 271 272 save_bclrpat = INREG(bclrpat_reg); 273 save_vtotal = INREG(vtotal_reg); 274 vblank = INREG(vblank_reg); 275 276 vtotal = ((save_vtotal >> 16) & 0xfff) + 1; 277 vactive = (save_vtotal & 0x7ff) + 1; 278 279 vblank_start = (vblank & 0xfff) + 1; 280 vblank_end = ((vblank >> 16) & 0xfff) + 1; 281 282 /* Set the border color to purple. */ 283 OUTREG(bclrpat_reg, 0x500050); 284 285 if (IS_I9XX (pI830)) 286 { 287 uint32_t pipeconf = INREG(pipeconf_reg); 288 OUTREG(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); 289 /* Wait for next Vblank to substitue border color for Color info */ 290 i830WaitForVblank (pScrn); 291 st00 = pI830->readStandard (pI830, 0x3c2); 292 present = (st00 & (1 << 4)) != 0; 293 OUTREG(pipeconf_reg, pipeconf); 294 } 295 else 296 { 297 Bool restore_vblank = FALSE; 298 int count, detect; 299 300 /* 301 * If there isn't any border, add some. 302 * Yes, this will flicker 303 */ 304 if (vblank_start <= vactive && vblank_end >= vtotal) 305 { 306 uint32_t vsync = INREG(vsync_reg); 307 uint32_t vsync_start = (vsync & 0xffff) + 1; 308 309 vblank_start = vsync_start; 310 OUTREG(vblank_reg, (vblank_start - 1) | ((vblank_end - 1) << 16)); 311 restore_vblank = TRUE; 312 } 313 314 /* sample in the vertical border, selecting the larger one */ 315 if (vblank_start - vactive >= vtotal - vblank_end) 316 vsample = (vblank_start + vactive) >> 1; 317 else 318 vsample = (vtotal + vblank_end) >> 1; 319 320 /* 321 * Wait for the border to be displayed 322 */ 323 while (INREG(pipe_dsl_reg) >= vactive) 324 ; 325 while ((dsl = INREG(pipe_dsl_reg)) <= vsample) 326 ; 327 /* 328 * Watch ST00 for an entire scanline 329 */ 330 detect = 0; 331 count = 0; 332 do { 333 count++; 334 /* Read the ST00 VGA status register */ 335 st00 = pI830->readStandard(pI830, 0x3c2); 336 if (st00 & (1 << 4)) 337 detect++; 338 } while ((INREG(pipe_dsl_reg) == dsl)); 339 340 /* restore vblank if necessary */ 341 if (restore_vblank) 342 OUTREG(vblank_reg, vblank); 343 /* 344 * If more than 3/4 of the scanline detected a monitor, 345 * then it is assumed to be present. This works even on i830, 346 * where there isn't any way to force the border color across 347 * the screen 348 */ 349 present = detect * 4 > count * 3; 350 } 351 352 /* Restore previous settings */ 353 OUTREG(bclrpat_reg, save_bclrpat); 354 355 return present; 356} 357 358/** 359 * Detects CRT presence by probing for a response on the DDC address. 360 * 361 * This takes approximately 5ms in testing on an i915GM, with CRT connected or 362 * not. 363 * 364 * \return TRUE if the CRT is connected and responded to DDC. 365 * \return FALSE if no DDC response was detected. 366 */ 367static Bool 368i830_crt_detect_ddc(xf86OutputPtr output) 369{ 370 ScrnInfoPtr pScrn = output->scrn; 371 I830OutputPrivatePtr i830_output = output->driver_private; 372 Bool detect; 373 374 /* CRT should always be at 0, but check anyway */ 375 if (i830_output->type != I830_OUTPUT_ANALOG) 376 return FALSE; 377 378 I830I2CInit(pScrn, &i830_output->pDDCBus, GPIOA, "CRTDDC_A"); 379 detect = xf86I2CProbeAddress(i830_output->pDDCBus, 0x00A0); 380 xf86DestroyI2CBusRec(i830_output->pDDCBus, TRUE, TRUE); 381 382 return detect; 383} 384 385/** 386 * Attempts to detect CRT presence through any method available. 387 * 388 * @param allow_disturb enables detection methods that may cause flickering 389 * on active displays. 390 */ 391static xf86OutputStatus 392i830_crt_detect(xf86OutputPtr output) 393{ 394 ScrnInfoPtr pScrn = output->scrn; 395 I830Ptr pI830 = I830PTR(pScrn); 396 xf86CrtcPtr crtc; 397 int dpms_mode; 398 xf86OutputStatus status; 399 Bool connected; 400 401 /* 402 * Try hotplug detection where supported 403 */ 404 if (IS_I945G(pI830) || IS_I945GM(pI830) || IS_I965G(pI830) || 405 IS_G33CLASS(pI830)) { 406 if (i830_crt_detect_hotplug(output)) 407 status = XF86OutputStatusConnected; 408 else 409 status = XF86OutputStatusDisconnected; 410 411 goto done; 412 } 413 414 /* 415 * DDC is next best, no flicker 416 */ 417 crtc = i830GetLoadDetectPipe (output, NULL, &dpms_mode); 418 if (!crtc) 419 return XF86OutputStatusUnknown; 420 421 if (i830_crt_detect_ddc(output)) { 422 status = XF86OutputStatusConnected; 423 goto out_release_pipe; 424 } 425 426 /* Use the load-detect method if we have no other way of telling. */ 427 connected = i830_crt_detect_load (crtc, output); 428 if (connected) 429 status = XF86OutputStatusConnected; 430 else 431 status = XF86OutputStatusDisconnected; 432 433out_release_pipe: 434 i830ReleaseLoadDetectPipe (output, dpms_mode); 435 436done: 437 return status; 438} 439 440static void 441i830_crt_destroy (xf86OutputPtr output) 442{ 443 if (output->driver_private) 444 xfree (output->driver_private); 445} 446 447#ifdef RANDR_GET_CRTC_INTERFACE 448static xf86CrtcPtr 449i830_crt_get_crtc(xf86OutputPtr output) 450{ 451 ScrnInfoPtr pScrn = output->scrn; 452 I830Ptr pI830 = I830PTR(pScrn); 453 int pipe = !!(INREG(ADPA) & ADPA_PIPE_SELECT_MASK); 454 455 return i830_pipe_to_crtc(pScrn, pipe); 456} 457#endif 458 459static xf86MonPtr 460i830_get_edid(xf86OutputPtr output, int gpio_reg, char *gpio_str) 461{ 462 I830OutputPrivatePtr intel_output = output->driver_private; 463 xf86MonPtr edid_mon = NULL; 464 465 /* Set up the DDC bus. */ 466 I830I2CInit(output->scrn, &intel_output->pDDCBus, gpio_reg, gpio_str); 467 468 edid_mon = xf86OutputGetEDID (output, intel_output->pDDCBus); 469 470 if (!edid_mon || DIGITAL(edid_mon->features.input_type)) { 471 xf86DestroyI2CBusRec(intel_output->pDDCBus, TRUE, TRUE); 472 intel_output->pDDCBus = NULL; 473 if (edid_mon) { 474 xfree(edid_mon); 475 edid_mon = NULL; 476 } 477 } 478 479 return edid_mon; 480} 481 482static DisplayModePtr 483i830_crt_get_modes (xf86OutputPtr output) 484{ 485 DisplayModePtr modes; 486 xf86MonPtr edid_mon = NULL; 487 I830OutputPrivatePtr intel_output = output->driver_private; 488 489 /* Try to probe normal CRT port, and also digital port for output 490 in DVI-I mode. */ 491 if ((edid_mon = i830_get_edid(output, GPIOA, "CRTDDC_A"))) 492 goto found; 493 if ((edid_mon = i830_get_edid(output, GPIOD, "CRTDDC_D"))) 494 goto found; 495 if ((edid_mon = i830_get_edid(output, GPIOE, "CRTDDC_E"))) 496 goto found; 497found: 498 /* Destroy DDC bus after probe, so every other new probe will 499 scan all ports again */ 500 if (intel_output->pDDCBus) 501 xf86DestroyI2CBusRec(intel_output->pDDCBus, TRUE, TRUE); 502 503 xf86OutputSetEDID (output, edid_mon); 504 505 modes = xf86OutputGetEDIDModes (output); 506 return modes; 507} 508 509static const xf86OutputFuncsRec i830_crt_output_funcs = { 510 .dpms = i830_crt_dpms, 511 .save = i830_crt_save, 512 .restore = i830_crt_restore, 513 .mode_valid = i830_crt_mode_valid, 514 .mode_fixup = i830_crt_mode_fixup, 515 .prepare = i830_output_prepare, 516 .mode_set = i830_crt_mode_set, 517 .commit = i830_output_commit, 518 .detect = i830_crt_detect, 519 .get_modes = i830_crt_get_modes, 520 .destroy = i830_crt_destroy, 521#ifdef RANDR_GET_CRTC_INTERFACE 522 .get_crtc = i830_crt_get_crtc, 523#endif 524}; 525 526void 527i830_crt_init(ScrnInfoPtr pScrn) 528{ 529 xf86OutputPtr output; 530 I830OutputPrivatePtr i830_output; 531 I830Ptr pI830 = I830PTR(pScrn); 532 533 if (pI830->quirk_flag & QUIRK_IGNORE_CRT) 534 return; 535 536 output = xf86OutputCreate (pScrn, &i830_crt_output_funcs, "VGA"); 537 if (!output) 538 return; 539 i830_output = xnfcalloc (sizeof (I830OutputPrivateRec), 1); 540 if (!i830_output) 541 { 542 xf86OutputDestroy (output); 543 return; 544 } 545 i830_output->type = I830_OUTPUT_ANALOG; 546 /* i830 (almador) cannot place the analog adaptor on pipe B */ 547 if (IS_I830(pI830)) 548 i830_output->pipe_mask = (1 << 0); 549 else 550 i830_output->pipe_mask = ((1 << 0) | (1 << 1)); 551 i830_output->clone_mask = ((1 << I830_OUTPUT_ANALOG) | 552 (1 << I830_OUTPUT_DVO_TMDS)); 553 554 output->driver_private = i830_output; 555 output->interlaceAllowed = FALSE; 556 output->doubleScanAllowed = FALSE; 557} 558