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