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