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