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