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