1fa225cbcSrjs/**************************************************************************
2fa225cbcSrjs
3fa225cbcSrjs Copyright 2006 Dave Airlie <airlied@linux.ie>
4fa225cbcSrjs
5fa225cbcSrjsPermission is hereby granted, free of charge, to any person obtaining a
6fa225cbcSrjscopy of this software and associated documentation files (the "Software"),
7fa225cbcSrjsto deal in the Software without restriction, including without limitation
8fa225cbcSrjson the rights to use, copy, modify, merge, publish, distribute, sub
9fa225cbcSrjslicense, and/or sell copies of the Software, and to permit persons to whom
10fa225cbcSrjsthe Software is furnished to do so, subject to the following conditions:
11fa225cbcSrjs
12fa225cbcSrjsThe above copyright notice and this permission notice (including the next
13fa225cbcSrjsparagraph) shall be included in all copies or substantial portions of the
14fa225cbcSrjsSoftware.
15fa225cbcSrjs
16fa225cbcSrjsTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17fa225cbcSrjsIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18fa225cbcSrjsFITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19fa225cbcSrjsTHE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
20fa225cbcSrjsDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21fa225cbcSrjsOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22fa225cbcSrjsUSE OR OTHER DEALINGS IN THE SOFTWARE.
23fa225cbcSrjs
24fa225cbcSrjs**************************************************************************/
25fa225cbcSrjs
26fa225cbcSrjs/** @file
27fa225cbcSrjs * SDVO support for i915 and newer chipsets.
28fa225cbcSrjs *
29fa225cbcSrjs * The SDVO outputs send digital display data out over the PCIE bus to display
30fa225cbcSrjs * cards implementing a defined interface.  These cards may have DVI, TV, CRT,
31fa225cbcSrjs * or other outputs on them.
32fa225cbcSrjs *
33fa225cbcSrjs * The system has two SDVO channels, which may be used for SDVO chips on the
34fa225cbcSrjs * motherboard, or in the external cards.  The two channels may also be used
35fa225cbcSrjs * in a ganged mode to provide higher bandwidth to a single output.  Currently,
36fa225cbcSrjs * this code doesn't deal with either ganged mode or more than one SDVO output.
37fa225cbcSrjs */
38fa225cbcSrjs
39fa225cbcSrjs#ifdef HAVE_CONFIG_H
40fa225cbcSrjs#include "config.h"
41fa225cbcSrjs#endif
42fa225cbcSrjs
43fa225cbcSrjs#include <string.h>
44fa225cbcSrjs
45fa225cbcSrjs#include "xf86.h"
46fa225cbcSrjs#include "xf86_OSproc.h"
47fa225cbcSrjs#include "compiler.h"
48fa225cbcSrjs#include "i830.h"
49fa225cbcSrjs#include "i830_display.h"
50fa225cbcSrjs#include "i810_reg.h"
51fa225cbcSrjs#include "i830_sdvo_regs.h"
52fa225cbcSrjs#include "X11/Xatom.h"
53fa225cbcSrjs
54fa225cbcSrjs/** SDVO driver private structure. */
55fa225cbcSrjsstruct i830_sdvo_priv {
56fa225cbcSrjs    /** SDVO device on SDVO I2C bus. */
57fa225cbcSrjs    I2CDevRec d;
58fa225cbcSrjs
59fa225cbcSrjs    /** Register for the SDVO device: SDVOB or SDVOC */
60fa225cbcSrjs    int output_device;
61fa225cbcSrjs
62fa225cbcSrjs    /** Active outputs controlled by this SDVO output */
63fa225cbcSrjs    uint16_t controlled_output;
64fa225cbcSrjs
65fa225cbcSrjs    /**
66fa225cbcSrjs     * Capabilities of the SDVO device returned by i830_sdvo_get_capabilities()
67fa225cbcSrjs     */
68fa225cbcSrjs    struct i830_sdvo_caps caps;
69fa225cbcSrjs
70fa225cbcSrjs    /**
71fa225cbcSrjs     * For multiple function SDVO device, this is for current attached outputs.
72fa225cbcSrjs     */
73fa225cbcSrjs    uint16_t attached_output;
74fa225cbcSrjs
75fa225cbcSrjs    /* Current output type name */
76fa225cbcSrjs    char *name;
77fa225cbcSrjs
78fa225cbcSrjs    /** Pixel clock limitations reported by the SDVO device, in kHz */
79fa225cbcSrjs    int pixel_clock_min, pixel_clock_max;
80fa225cbcSrjs
81fa225cbcSrjs    /**
82fa225cbcSrjs     * This is set if we're going to treat the device as TV-out.
83fa225cbcSrjs     *
84fa225cbcSrjs     * While we have these nice friendly flags for output types that ought to
85fa225cbcSrjs     * decide this for us, the S-Video output on our HDMI+S-Video card shows
86fa225cbcSrjs     * up as RGB1 (VGA).
87fa225cbcSrjs     */
88fa225cbcSrjs    Bool is_tv;
89fa225cbcSrjs
90fa225cbcSrjs    /**
91fa225cbcSrjs     * This is set if we treat the device as HDMI, instead of DVI.
92fa225cbcSrjs     */
93fa225cbcSrjs    Bool is_hdmi;
94fa225cbcSrjs    /**
95fa225cbcSrjs     * This is set if we detect output of sdvo device as LVDS.
96fa225cbcSrjs     */
97fa225cbcSrjs    Bool is_lvds;
98fa225cbcSrjs    DisplayModePtr sdvo_lvds_fixed_mode;
99fa225cbcSrjs    /**
100fa225cbcSrjs     *This is set if output is LVDS or TV.
101fa225cbcSrjs     */
102fa225cbcSrjs    uint8_t sdvo_flags;
103fa225cbcSrjs
104fa225cbcSrjs    /**
105fa225cbcSrjs     * Returned SDTV resolutions allowed for the current format, if the
106fa225cbcSrjs     * device reported it.
107fa225cbcSrjs     */
108fa225cbcSrjs    struct i830_sdvo_sdtv_resolution_reply sdtv_resolutions;
109fa225cbcSrjs
110fa225cbcSrjs    /**
111fa225cbcSrjs     * Current selected TV format.
112fa225cbcSrjs     *
113fa225cbcSrjs     * This is stored in the same structure that's passed to the device, for
114fa225cbcSrjs     * convenience.
115fa225cbcSrjs     */
116fa225cbcSrjs    struct i830_sdvo_tv_format tv_format;
117fa225cbcSrjs
118fa225cbcSrjs    /** supported encoding mode, used to determine whether HDMI is supported */
119fa225cbcSrjs    struct i830_sdvo_encode encode;
120fa225cbcSrjs
121fa225cbcSrjs    /** DDC bus used by this SDVO output */
122fa225cbcSrjs    uint8_t ddc_bus;
123fa225cbcSrjs    /* Default 0 for full RGB range 0-255, 1 is for RGB range 16-235 */
124fa225cbcSrjs    uint32_t broadcast_rgb;
125fa225cbcSrjs
126fa225cbcSrjs    /** This flag means if we should switch ddc bus before next i2c Start */
127fa225cbcSrjs    Bool ddc_bus_switch;
128fa225cbcSrjs
129fa225cbcSrjs    /** State for save/restore */
130fa225cbcSrjs    /** @{ */
131fa225cbcSrjs    int save_sdvo_mult;
132fa225cbcSrjs    uint16_t save_active_outputs;
133fa225cbcSrjs    struct i830_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
134fa225cbcSrjs    struct i830_sdvo_dtd save_output_dtd[16];
135fa225cbcSrjs    uint32_t save_SDVOX;
136fa225cbcSrjs    /** @} */
137fa225cbcSrjs};
138fa225cbcSrjs
139fa225cbcSrjsstatic Atom broadcast_atom;
140fa225cbcSrjs
141fa225cbcSrjsstatic void
142fa225cbcSrjsi830_sdvo_dump(ScrnInfoPtr pScrn);
143fa225cbcSrjs
144fa225cbcSrjs/**
145fa225cbcSrjs * Writes the SDVOB or SDVOC with the given value, but always writes both
146fa225cbcSrjs * SDVOB and SDVOC to work around apparent hardware issues (according to
147fa225cbcSrjs * comments in the BIOS).
148fa225cbcSrjs */
149fa225cbcSrjsstatic void i830_sdvo_write_sdvox(xf86OutputPtr output, uint32_t val)
150fa225cbcSrjs{
151fa225cbcSrjs    ScrnInfoPtr		    pScrn = output->scrn;
152fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
153fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
154fa225cbcSrjs    I830Ptr		    pI830 = I830PTR(pScrn);
155fa225cbcSrjs    uint32_t		    bval = val, cval = val;
156fa225cbcSrjs    int			    i;
157fa225cbcSrjs
158fa225cbcSrjs    if (dev_priv->output_device == SDVOB)
159fa225cbcSrjs	cval = INREG(SDVOC);
160fa225cbcSrjs    else
161fa225cbcSrjs	bval = INREG(SDVOB);
162fa225cbcSrjs
163fa225cbcSrjs    /*
164fa225cbcSrjs     * Write the registers twice for luck. Sometimes,
165fa225cbcSrjs     * writing them only once doesn't appear to 'stick'.
166fa225cbcSrjs     * The BIOS does this too. Yay, magic
167fa225cbcSrjs     */
168fa225cbcSrjs    for (i = 0; i < 2; i++)
169fa225cbcSrjs    {
170fa225cbcSrjs	OUTREG(SDVOB, bval);
171fa225cbcSrjs	POSTING_READ(SDVOB);
172fa225cbcSrjs	OUTREG(SDVOC, cval);
173fa225cbcSrjs	POSTING_READ(SDVOC);
174fa225cbcSrjs    }
175fa225cbcSrjs}
176fa225cbcSrjs
177fa225cbcSrjs/** Read a single byte from the given address on the SDVO device. */
178fa225cbcSrjsstatic Bool i830_sdvo_read_byte(xf86OutputPtr output, int addr,
179fa225cbcSrjs				unsigned char *ch)
180fa225cbcSrjs{
181fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
182fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
183fa225cbcSrjs
184fa225cbcSrjs    if (!xf86I2CReadByte(&dev_priv->d, addr, ch)) {
185fa225cbcSrjs	xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_ERROR,
186fa225cbcSrjs		   "Unable to read from %s slave 0x%02x.\n",
187fa225cbcSrjs		   intel_output->pI2CBus->BusName, dev_priv->d.SlaveAddr);
188fa225cbcSrjs	return FALSE;
189fa225cbcSrjs    }
190fa225cbcSrjs    return TRUE;
191fa225cbcSrjs}
192fa225cbcSrjs
193fa225cbcSrjs/** Read a single byte from the given address on the SDVO device. */
194fa225cbcSrjsstatic Bool i830_sdvo_read_byte_quiet(xf86OutputPtr output, int addr,
195fa225cbcSrjs				      unsigned char *ch)
196fa225cbcSrjs{
197fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
198fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
199fa225cbcSrjs
200fa225cbcSrjs    return xf86I2CReadByte(&dev_priv->d, addr, ch);
201fa225cbcSrjs}
202fa225cbcSrjs
203fa225cbcSrjs/** Write a single byte to the given address on the SDVO device. */
204fa225cbcSrjsstatic Bool i830_sdvo_write_byte(xf86OutputPtr output,
205fa225cbcSrjs				 int addr, unsigned char ch)
206fa225cbcSrjs{
207fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
208fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
209fa225cbcSrjs
210fa225cbcSrjs    if (!xf86I2CWriteByte(&dev_priv->d, addr, ch)) {
211fa225cbcSrjs	xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_ERROR,
212fa225cbcSrjs		   "Unable to write to %s Slave 0x%02x.\n",
213fa225cbcSrjs		   intel_output->pI2CBus->BusName, dev_priv->d.SlaveAddr);
214fa225cbcSrjs	return FALSE;
215fa225cbcSrjs    }
216fa225cbcSrjs    return TRUE;
217fa225cbcSrjs}
218fa225cbcSrjs
219fa225cbcSrjs
220fa225cbcSrjs#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
221fa225cbcSrjs/** Mapping of command numbers to names, for debug output */
222fa225cbcSrjsconst static struct _sdvo_cmd_name {
223fa225cbcSrjs    uint8_t cmd;
224fa225cbcSrjs    char *name;
225fa225cbcSrjs} sdvo_cmd_names[] = {
226fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
227fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
228fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
229fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
230fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
231fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
232fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
233fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
234fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
235fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
236fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
237fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
238fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
239fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
240fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
241fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
242fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
243fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
244fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
245fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
246fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
247fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
248fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
249fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
250fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
251fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
252fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
253fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
254fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
255fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
256fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
257fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
258fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
259fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
260fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
261fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES),
262fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
263fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
264fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
265fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
266fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
267fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
268fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
269fa225cbcSrjs    /* HDMI op code */
270fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
271fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
272fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
273fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
274fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
275fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
276fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
277fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
278fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
279fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
280fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
281fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
282fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
283fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
284fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
285fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
286fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
287fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
288fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
289fa225cbcSrjs    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
290fa225cbcSrjs};
291fa225cbcSrjs
292fa225cbcSrjsstatic I2CSlaveAddr slaveAddr;
293fa225cbcSrjs
294fa225cbcSrjs#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
295fa225cbcSrjs#define SDVO_PRIV(output)   ((struct i830_sdvo_priv *) (output)->dev_priv)
296fa225cbcSrjs
297fa225cbcSrjs/**
298fa225cbcSrjs * Writes out the data given in args (up to 8 bytes), followed by the opcode.
299fa225cbcSrjs */
300fa225cbcSrjsstatic void
301fa225cbcSrjsi830_sdvo_write_cmd(xf86OutputPtr output, uint8_t cmd, void *args,
302fa225cbcSrjs		    int args_len)
303fa225cbcSrjs{
304fa225cbcSrjs    I830Ptr pI830 = I830PTR(output->scrn);
305fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
306fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
307fa225cbcSrjs    int			    i;
308fa225cbcSrjs
309fa225cbcSrjs    if (slaveAddr && slaveAddr != dev_priv->d.SlaveAddr)
310fa225cbcSrjs	ErrorF ("Mismatch slave addr %x != %x\n", slaveAddr, dev_priv->d.SlaveAddr);
311fa225cbcSrjs
312fa225cbcSrjs    /* Write the SDVO command logging */
313fa225cbcSrjs    if (pI830->debug_modes) {
314fa225cbcSrjs	xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_INFO, "%s: W: %02X ",
315fa225cbcSrjs		   SDVO_NAME(dev_priv), cmd);
316fa225cbcSrjs	for (i = 0; i < args_len; i++)
317fa225cbcSrjs	    LogWrite(1, "%02X ", ((uint8_t *)args)[i]);
318fa225cbcSrjs	for (; i < 8; i++)
319fa225cbcSrjs	    LogWrite(1, "   ");
320fa225cbcSrjs	for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]);
321fa225cbcSrjs	     i++)
322fa225cbcSrjs	{
323fa225cbcSrjs	    if (cmd == sdvo_cmd_names[i].cmd) {
324fa225cbcSrjs		LogWrite(1, "(%s)", sdvo_cmd_names[i].name);
325fa225cbcSrjs		break;
326fa225cbcSrjs	    }
327fa225cbcSrjs	}
328fa225cbcSrjs	if (i == sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]))
329fa225cbcSrjs	    LogWrite(1, "(%02X)", cmd);
330fa225cbcSrjs	LogWrite(1, "\n");
331fa225cbcSrjs    }
332fa225cbcSrjs
333fa225cbcSrjs    /* send the output regs */
334fa225cbcSrjs    for (i = 0; i < args_len; i++) {
335fa225cbcSrjs	i830_sdvo_write_byte(output, SDVO_I2C_ARG_0 - i, ((uint8_t *)args)[i]);
336fa225cbcSrjs    }
337fa225cbcSrjs    /* blast the command reg */
338fa225cbcSrjs    i830_sdvo_write_byte(output, SDVO_I2C_OPCODE, cmd);
339fa225cbcSrjs}
340fa225cbcSrjs
341fa225cbcSrjsstatic const char *cmd_status_names[] = {
342fa225cbcSrjs	"Power on",
343fa225cbcSrjs	"Success",
344fa225cbcSrjs	"Not supported",
345fa225cbcSrjs	"Invalid arg",
346fa225cbcSrjs	"Pending",
347fa225cbcSrjs	"Target not specified",
348fa225cbcSrjs	"Scaling not supported"
349fa225cbcSrjs};
350fa225cbcSrjs
351fa225cbcSrjs/**
352fa225cbcSrjs * Reads back response_len bytes from the SDVO device, and returns the status.
353fa225cbcSrjs */
354fa225cbcSrjsstatic uint8_t
355fa225cbcSrjsi830_sdvo_read_response(xf86OutputPtr output, void *response, int response_len)
356fa225cbcSrjs{
357fa225cbcSrjs    I830Ptr pI830 = I830PTR(output->scrn);
358fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
359fa225cbcSrjs    int			    i;
360fa225cbcSrjs    uint8_t		    status;
361fa225cbcSrjs    uint8_t		    retry = 50;
362fa225cbcSrjs
363fa225cbcSrjs    while (retry--) {
364fa225cbcSrjs    	/* Read the command response */
365fa225cbcSrjs    	for (i = 0; i < response_len; i++) {
366fa225cbcSrjs	    i830_sdvo_read_byte(output, SDVO_I2C_RETURN_0 + i,
367fa225cbcSrjs			    &((uint8_t *)response)[i]);
368fa225cbcSrjs    	}
369fa225cbcSrjs
370fa225cbcSrjs    	/* Read the return status */
371fa225cbcSrjs    	i830_sdvo_read_byte(output, SDVO_I2C_CMD_STATUS, &status);
372fa225cbcSrjs
373fa225cbcSrjs    	/* Write the SDVO command logging */
374fa225cbcSrjs    	if (pI830->debug_modes) {
375fa225cbcSrjs	    xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_INFO,
376fa225cbcSrjs		   "%s: R: ", SDVO_NAME(SDVO_PRIV(intel_output)));
377fa225cbcSrjs	    for (i = 0; i < response_len; i++)
378fa225cbcSrjs	    	LogWrite(1, "%02X ", ((uint8_t *)response)[i]);
379fa225cbcSrjs	    for (; i < 8; i++)
380fa225cbcSrjs	    	LogWrite(1, "   ");
381fa225cbcSrjs	    if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) {
382fa225cbcSrjs	    	LogWrite(1, "(%s)", cmd_status_names[status]);
383fa225cbcSrjs	    } else {
384fa225cbcSrjs	    	LogWrite(1, "(??? %d)", status);
385fa225cbcSrjs	    }
386fa225cbcSrjs	    LogWrite(1, "\n");
387fa225cbcSrjs    	}
388fa225cbcSrjs
389fa225cbcSrjs	if (status != SDVO_CMD_STATUS_PENDING)
390fa225cbcSrjs	    return status;
391fa225cbcSrjs
392fa225cbcSrjs        intel_output->pI2CBus->I2CUDelay(intel_output->pI2CBus, 50);
393fa225cbcSrjs    }
394fa225cbcSrjs
395fa225cbcSrjs    return status;
396fa225cbcSrjs}
397fa225cbcSrjs
398fa225cbcSrjsstatic int
399fa225cbcSrjsi830_sdvo_get_pixel_multiplier(DisplayModePtr pMode)
400fa225cbcSrjs{
401fa225cbcSrjs    if (pMode->Clock >= 100000)
402fa225cbcSrjs	return 1;
403fa225cbcSrjs    else if (pMode->Clock >= 50000)
404fa225cbcSrjs	return 2;
405fa225cbcSrjs    else
406fa225cbcSrjs	return 4;
407fa225cbcSrjs}
408fa225cbcSrjs
409fa225cbcSrjs/* Sets the control bus switch to either point at one of the DDC buses or the
410fa225cbcSrjs * PROM.  It resets from the DDC bus back to internal registers at the next I2C
411fa225cbcSrjs * STOP.  PROM access is terminated by accessing an internal register.
412fa225cbcSrjs */
413fa225cbcSrjsstatic void
414fa225cbcSrjsi830_sdvo_set_control_bus_switch(xf86OutputPtr output, uint8_t target)
415fa225cbcSrjs{
416fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
417fa225cbcSrjs}
418fa225cbcSrjs
419fa225cbcSrjsstatic Bool
420fa225cbcSrjsi830_sdvo_set_target_input(xf86OutputPtr output, Bool target_0, Bool target_1)
421fa225cbcSrjs{
422fa225cbcSrjs    struct i830_sdvo_set_target_input_args targets = {0};
423fa225cbcSrjs    uint8_t status;
424fa225cbcSrjs
425fa225cbcSrjs    if (target_0 && target_1)
426fa225cbcSrjs	return SDVO_CMD_STATUS_NOTSUPP;
427fa225cbcSrjs
428fa225cbcSrjs    if (target_1)
429fa225cbcSrjs	targets.target_1 = 1;
430fa225cbcSrjs
431fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_INPUT, &targets,
432fa225cbcSrjs			sizeof(targets));
433fa225cbcSrjs
434fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
435fa225cbcSrjs
436fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
437fa225cbcSrjs}
438fa225cbcSrjs
439fa225cbcSrjs/**
440fa225cbcSrjs * Return whether each input is trained.
441fa225cbcSrjs *
442fa225cbcSrjs * This function is making an assumption about the layout of the response,
443fa225cbcSrjs * which should be checked against the docs.
444fa225cbcSrjs */
445fa225cbcSrjsstatic Bool
446fa225cbcSrjsi830_sdvo_get_trained_inputs(xf86OutputPtr output, Bool *input_1, Bool *input_2)
447fa225cbcSrjs{
448fa225cbcSrjs    struct i830_sdvo_get_trained_inputs_response response;
449fa225cbcSrjs    uint8_t status;
450fa225cbcSrjs
451fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0);
452fa225cbcSrjs
453fa225cbcSrjs    status = i830_sdvo_read_response(output, &response, sizeof(response));
454fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
455fa225cbcSrjs	return FALSE;
456fa225cbcSrjs
457fa225cbcSrjs    *input_1 = response.input0_trained;
458fa225cbcSrjs    *input_2 = response.input1_trained;
459fa225cbcSrjs
460fa225cbcSrjs    return TRUE;
461fa225cbcSrjs}
462fa225cbcSrjs
463fa225cbcSrjsstatic Bool
464fa225cbcSrjsi830_sdvo_get_active_outputs(xf86OutputPtr output,
465fa225cbcSrjs			     uint16_t *outputs)
466fa225cbcSrjs{
467fa225cbcSrjs    uint8_t status;
468fa225cbcSrjs
469fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0);
470fa225cbcSrjs    status = i830_sdvo_read_response(output, outputs, sizeof(*outputs));
471fa225cbcSrjs
472fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
473fa225cbcSrjs}
474fa225cbcSrjs
475fa225cbcSrjsstatic Bool
476fa225cbcSrjsi830_sdvo_set_active_outputs(xf86OutputPtr output,
477fa225cbcSrjs			     uint16_t outputs)
478fa225cbcSrjs{
479fa225cbcSrjs    uint8_t status;
480fa225cbcSrjs
481fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs,
482fa225cbcSrjs			sizeof(outputs));
483fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
484fa225cbcSrjs
485fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
486fa225cbcSrjs}
487fa225cbcSrjs
488fa225cbcSrjsstatic Bool
489fa225cbcSrjsi830_sdvo_set_encoder_power_state(xf86OutputPtr output, int mode)
490fa225cbcSrjs{
491fa225cbcSrjs    uint8_t status;
492fa225cbcSrjs    uint8_t state;
493fa225cbcSrjs
494fa225cbcSrjs    switch (mode) {
495fa225cbcSrjs    case DPMSModeOn:
496fa225cbcSrjs	state = SDVO_ENCODER_STATE_ON;
497fa225cbcSrjs	break;
498fa225cbcSrjs    case DPMSModeStandby:
499fa225cbcSrjs	state = SDVO_ENCODER_STATE_STANDBY;
500fa225cbcSrjs	break;
501fa225cbcSrjs    case DPMSModeSuspend:
502fa225cbcSrjs	state = SDVO_ENCODER_STATE_SUSPEND;
503fa225cbcSrjs	break;
504fa225cbcSrjs    case DPMSModeOff:
505fa225cbcSrjs	state = SDVO_ENCODER_STATE_OFF;
506fa225cbcSrjs	break;
507fa225cbcSrjs    }
508fa225cbcSrjs
509fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
510fa225cbcSrjs			sizeof(state));
511fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
512fa225cbcSrjs
513fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
514fa225cbcSrjs}
515fa225cbcSrjs
516fa225cbcSrjs/**
517fa225cbcSrjs * Returns the pixel clock range limits of the current target input in kHz.
518fa225cbcSrjs */
519fa225cbcSrjsstatic Bool
520fa225cbcSrjsi830_sdvo_get_input_pixel_clock_range(xf86OutputPtr output, int *clock_min,
521fa225cbcSrjs				      int *clock_max)
522fa225cbcSrjs{
523fa225cbcSrjs    struct i830_sdvo_pixel_clock_range clocks;
524fa225cbcSrjs    uint8_t status;
525fa225cbcSrjs
526fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, NULL, 0);
527fa225cbcSrjs
528fa225cbcSrjs    status = i830_sdvo_read_response(output, &clocks, sizeof(clocks));
529fa225cbcSrjs
530fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
531fa225cbcSrjs	return FALSE;
532fa225cbcSrjs
533fa225cbcSrjs    /* Convert the values from units of 10 kHz to kHz. */
534fa225cbcSrjs    *clock_min = clocks.min * 10;
535fa225cbcSrjs    *clock_max = clocks.max * 10;
536fa225cbcSrjs
537fa225cbcSrjs    return TRUE;
538fa225cbcSrjs}
539fa225cbcSrjs
540fa225cbcSrjsstatic Bool
541fa225cbcSrjsi830_sdvo_set_target_output(xf86OutputPtr output, uint16_t outputs)
542fa225cbcSrjs{
543fa225cbcSrjs    uint8_t status;
544fa225cbcSrjs
545fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs,
546fa225cbcSrjs			sizeof(outputs));
547fa225cbcSrjs
548fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
549fa225cbcSrjs
550fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
551fa225cbcSrjs}
552fa225cbcSrjs
553fa225cbcSrjs/** Fetches either input or output timings to *dtd, depending on cmd. */
554fa225cbcSrjsstatic Bool
555fa225cbcSrjsi830_sdvo_get_timing(xf86OutputPtr output, uint8_t cmd, struct i830_sdvo_dtd *dtd)
556fa225cbcSrjs{
557fa225cbcSrjs    uint8_t status;
558fa225cbcSrjs
559fa225cbcSrjs    i830_sdvo_write_cmd(output, cmd, NULL, 0);
560fa225cbcSrjs
561fa225cbcSrjs    status = i830_sdvo_read_response(output, &dtd->part1, sizeof(dtd->part1));
562fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
563fa225cbcSrjs	return FALSE;
564fa225cbcSrjs
565fa225cbcSrjs    i830_sdvo_write_cmd(output, cmd + 1, NULL, 0);
566fa225cbcSrjs
567fa225cbcSrjs    status = i830_sdvo_read_response(output, &dtd->part2, sizeof(dtd->part2));
568fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
569fa225cbcSrjs	return FALSE;
570fa225cbcSrjs
571fa225cbcSrjs    return TRUE;
572fa225cbcSrjs}
573fa225cbcSrjs
574fa225cbcSrjsstatic Bool
575fa225cbcSrjsi830_sdvo_get_input_timing(xf86OutputPtr output, struct i830_sdvo_dtd *dtd)
576fa225cbcSrjs{
577fa225cbcSrjs    return i830_sdvo_get_timing(output, SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
578fa225cbcSrjs}
579fa225cbcSrjs
580fa225cbcSrjsstatic Bool
581fa225cbcSrjsi830_sdvo_get_output_timing(xf86OutputPtr output, struct i830_sdvo_dtd *dtd)
582fa225cbcSrjs{
583fa225cbcSrjs    return i830_sdvo_get_timing(output, SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd);
584fa225cbcSrjs}
585fa225cbcSrjs
586fa225cbcSrjs/** Sets either input or output timings from *dtd, depending on cmd. */
587fa225cbcSrjsstatic Bool
588fa225cbcSrjsi830_sdvo_set_timing(xf86OutputPtr output, uint8_t cmd,
589fa225cbcSrjs		     struct i830_sdvo_dtd *dtd)
590fa225cbcSrjs{
591fa225cbcSrjs    uint8_t status;
592fa225cbcSrjs
593fa225cbcSrjs    i830_sdvo_write_cmd(output, cmd, &dtd->part1, sizeof(dtd->part1));
594fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
595fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
596fa225cbcSrjs	return FALSE;
597fa225cbcSrjs
598fa225cbcSrjs    i830_sdvo_write_cmd(output, cmd + 1, &dtd->part2, sizeof(dtd->part2));
599fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
600fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
601fa225cbcSrjs	return FALSE;
602fa225cbcSrjs
603fa225cbcSrjs    return TRUE;
604fa225cbcSrjs}
605fa225cbcSrjs
606fa225cbcSrjsstatic Bool
607fa225cbcSrjsi830_sdvo_set_input_timing(xf86OutputPtr output, struct i830_sdvo_dtd *dtd)
608fa225cbcSrjs{
609fa225cbcSrjs    return i830_sdvo_set_timing(output, SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
610fa225cbcSrjs}
611fa225cbcSrjs
612fa225cbcSrjsstatic Bool
613fa225cbcSrjsi830_sdvo_set_output_timing(xf86OutputPtr output, struct i830_sdvo_dtd *dtd)
614fa225cbcSrjs{
615fa225cbcSrjs    return i830_sdvo_set_timing(output, SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
616fa225cbcSrjs}
617fa225cbcSrjs
618fa225cbcSrjsstatic Bool
619fa225cbcSrjsi830_sdvo_create_preferred_input_timing(xf86OutputPtr output, uint16_t clock,
620fa225cbcSrjs					uint16_t width, uint16_t height)
621fa225cbcSrjs{
622fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
623fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
624fa225cbcSrjs    struct i830_sdvo_preferred_input_timing_args args;
625fa225cbcSrjs    uint8_t status;
626fa225cbcSrjs
627fa225cbcSrjs    memset(&args, 0, sizeof(args));
628fa225cbcSrjs    args.clock = clock;
629fa225cbcSrjs    args.width = width;
630fa225cbcSrjs    args.height = height;
631fa225cbcSrjs    args.interlace = 0;
632fa225cbcSrjs    if (dev_priv->is_lvds &&
633fa225cbcSrjs        (dev_priv->sdvo_lvds_fixed_mode->HDisplay != width ||
634fa225cbcSrjs        (dev_priv->sdvo_lvds_fixed_mode->VDisplay != height)))
635fa225cbcSrjs        args.scaled = 1;
636fa225cbcSrjs
637fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
638fa225cbcSrjs			&args, sizeof(args));
639fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
640fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
641fa225cbcSrjs	return FALSE;
642fa225cbcSrjs
643fa225cbcSrjs    return TRUE;
644fa225cbcSrjs}
645fa225cbcSrjs
646fa225cbcSrjsstatic Bool
647fa225cbcSrjsi830_sdvo_get_preferred_input_timing(xf86OutputPtr output,
648fa225cbcSrjs				     struct i830_sdvo_dtd *dtd)
649fa225cbcSrjs{
650fa225cbcSrjs    Bool status;
651fa225cbcSrjs
652fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
653fa225cbcSrjs			NULL, 0);
654fa225cbcSrjs
655fa225cbcSrjs    status = i830_sdvo_read_response(output, &dtd->part1, sizeof(dtd->part1));
656fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
657fa225cbcSrjs	return FALSE;
658fa225cbcSrjs
659fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
660fa225cbcSrjs			NULL, 0);
661fa225cbcSrjs
662fa225cbcSrjs    status = i830_sdvo_read_response(output, &dtd->part2, sizeof(dtd->part2));
663fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
664fa225cbcSrjs	return FALSE;
665fa225cbcSrjs
666fa225cbcSrjs    return TRUE;
667fa225cbcSrjs}
668fa225cbcSrjs
669fa225cbcSrjs/** Returns the SDVO_CLOCK_RATE_MULT_* for the current clock multiplier */
670fa225cbcSrjsstatic int
671fa225cbcSrjsi830_sdvo_get_clock_rate_mult(xf86OutputPtr output)
672fa225cbcSrjs{
673fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
674fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
675fa225cbcSrjs    uint8_t response;
676fa225cbcSrjs    uint8_t status;
677fa225cbcSrjs
678fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0);
679fa225cbcSrjs    status = i830_sdvo_read_response(output, &response, 1);
680fa225cbcSrjs
681fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS) {
682fa225cbcSrjs	xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_ERROR,
683fa225cbcSrjs		   "Couldn't get SDVO clock rate multiplier\n");
684fa225cbcSrjs	return SDVO_CLOCK_RATE_MULT_1X;
685fa225cbcSrjs    } else {
686fa225cbcSrjs	xf86DrvMsg(dev_priv->d.pI2CBus->scrnIndex, X_INFO,
687fa225cbcSrjs		   "Current clock rate multiplier: %d\n", response);
688fa225cbcSrjs    }
689fa225cbcSrjs
690fa225cbcSrjs    return response;
691fa225cbcSrjs}
692fa225cbcSrjs
693fa225cbcSrjs/**
694fa225cbcSrjs * Sets the current clock multiplier.
695fa225cbcSrjs *
696fa225cbcSrjs * This has to match with the settings in the DPLL/SDVO reg when the output
697fa225cbcSrjs * is actually turned on.
698fa225cbcSrjs */
699fa225cbcSrjsstatic Bool
700fa225cbcSrjsi830_sdvo_set_clock_rate_mult(xf86OutputPtr output, uint8_t val)
701fa225cbcSrjs{
702fa225cbcSrjs    uint8_t status;
703fa225cbcSrjs
704fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
705fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
706fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
707fa225cbcSrjs	return FALSE;
708fa225cbcSrjs
709fa225cbcSrjs    return TRUE;
710fa225cbcSrjs}
711fa225cbcSrjs
712fa225cbcSrjsstatic void
713fa225cbcSrjsi830_sdvo_get_dtd_from_mode(struct i830_sdvo_dtd *dtd, DisplayModePtr mode)
714fa225cbcSrjs{
715fa225cbcSrjs    uint16_t width, height;
716fa225cbcSrjs    uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
717fa225cbcSrjs    uint16_t h_sync_offset, v_sync_offset;
718fa225cbcSrjs
719fa225cbcSrjs    width = mode->CrtcHDisplay;
720fa225cbcSrjs    height = mode->CrtcVDisplay;
721fa225cbcSrjs
722fa225cbcSrjs    /* do some mode translations */
723fa225cbcSrjs    h_blank_len = mode->CrtcHBlankEnd - mode->CrtcHBlankStart;
724fa225cbcSrjs    h_sync_len = mode->CrtcHSyncEnd - mode->CrtcHSyncStart;
725fa225cbcSrjs
726fa225cbcSrjs    v_blank_len = mode->CrtcVBlankEnd - mode->CrtcVBlankStart;
727fa225cbcSrjs    v_sync_len = mode->CrtcVSyncEnd - mode->CrtcVSyncStart;
728fa225cbcSrjs
729fa225cbcSrjs    h_sync_offset = mode->CrtcHSyncStart - mode->CrtcHBlankStart;
730fa225cbcSrjs    v_sync_offset = mode->CrtcVSyncStart - mode->CrtcVBlankStart;
731fa225cbcSrjs
732fa225cbcSrjs    dtd->part1.clock = mode->Clock / 10;
733fa225cbcSrjs    dtd->part1.h_active = width & 0xff;
734fa225cbcSrjs    dtd->part1.h_blank = h_blank_len & 0xff;
735fa225cbcSrjs    dtd->part1.h_high = (((width >> 8) & 0xf) << 4) |
736fa225cbcSrjs	((h_blank_len >> 8) & 0xf);
737fa225cbcSrjs    dtd->part1.v_active = height & 0xff;
738fa225cbcSrjs    dtd->part1.v_blank = v_blank_len & 0xff;
739fa225cbcSrjs    dtd->part1.v_high = (((height >> 8) & 0xf) << 4) |
740fa225cbcSrjs	((v_blank_len >> 8) & 0xf);
741fa225cbcSrjs
742fa225cbcSrjs    dtd->part2.h_sync_off = h_sync_offset & 0xff;
743fa225cbcSrjs    dtd->part2.h_sync_width = h_sync_len & 0xff;
744fa225cbcSrjs    dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
745fa225cbcSrjs	(v_sync_len & 0xf);
746fa225cbcSrjs    dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
747fa225cbcSrjs	((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
748fa225cbcSrjs	((v_sync_len & 0x30) >> 4);
749fa225cbcSrjs
750fa225cbcSrjs    dtd->part2.dtd_flags = 0x18;
751fa225cbcSrjs    if (mode->Flags & V_PHSYNC)
752fa225cbcSrjs	dtd->part2.dtd_flags |= 0x2;
753fa225cbcSrjs    if (mode->Flags & V_PVSYNC)
754fa225cbcSrjs	dtd->part2.dtd_flags |= 0x4;
755fa225cbcSrjs
756fa225cbcSrjs    dtd->part2.sdvo_flags = 0;
757fa225cbcSrjs    dtd->part2.v_sync_off_high = v_sync_offset & 0xc0;
758fa225cbcSrjs    dtd->part2.reserved = 0;
759fa225cbcSrjs}
760fa225cbcSrjs
761fa225cbcSrjsstatic void
762fa225cbcSrjsi830_sdvo_get_mode_from_dtd(DisplayModePtr mode, struct i830_sdvo_dtd *dtd)
763fa225cbcSrjs{
764fa225cbcSrjs    mode->HDisplay = dtd->part1.h_active;
765fa225cbcSrjs    mode->HDisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
766fa225cbcSrjs    mode->HSyncStart = mode->HDisplay + dtd->part2.h_sync_off;
767fa225cbcSrjs    mode->HSyncStart += (dtd->part2.sync_off_width_high & 0xc0) << 2;
768fa225cbcSrjs    mode->HSyncEnd = mode->HSyncStart + dtd->part2.h_sync_width;
769fa225cbcSrjs    mode->HSyncEnd += (dtd->part2.sync_off_width_high & 0x30) << 4;
770fa225cbcSrjs    mode->HTotal = mode->HDisplay + dtd->part1.h_blank;
771fa225cbcSrjs    mode->HTotal += (dtd->part1.h_high & 0xf) << 8;
772fa225cbcSrjs
773fa225cbcSrjs    mode->VDisplay = dtd->part1.v_active;
774fa225cbcSrjs    mode->VDisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
775fa225cbcSrjs    mode->VSyncStart = mode->VDisplay;
776fa225cbcSrjs    mode->VSyncStart += (dtd->part2.v_sync_off_width >> 4) & 0xf;
777fa225cbcSrjs    mode->VSyncStart += (dtd->part2.sync_off_width_high & 0x0c) << 2;
778fa225cbcSrjs    mode->VSyncStart += dtd->part2.v_sync_off_high & 0xc0;
779fa225cbcSrjs    mode->VSyncEnd = mode->VSyncStart + (dtd->part2.v_sync_off_width & 0xf);
780fa225cbcSrjs    mode->VSyncEnd += (dtd->part2.sync_off_width_high & 0x3) << 4;
781fa225cbcSrjs    mode->VTotal = mode->VDisplay + dtd->part1.v_blank;
782fa225cbcSrjs    mode->VTotal += (dtd->part1.v_high & 0xf) << 8;
783fa225cbcSrjs
784fa225cbcSrjs    mode->Clock = dtd->part1.clock * 10;
785fa225cbcSrjs
786fa225cbcSrjs    mode->Flags &= ~(V_PHSYNC | V_PVSYNC);
787fa225cbcSrjs    if (dtd->part2.dtd_flags & 0x2)
788fa225cbcSrjs	mode->Flags |= V_PHSYNC;
789fa225cbcSrjs    if (dtd->part2.dtd_flags & 0x4)
790fa225cbcSrjs	mode->Flags |= V_PVSYNC;
791fa225cbcSrjs}
792fa225cbcSrjs
793fa225cbcSrjsstatic Bool
794fa225cbcSrjsi830_sdvo_get_supp_encode(xf86OutputPtr output, struct i830_sdvo_encode *encode)
795fa225cbcSrjs{
796fa225cbcSrjs    uint8_t status;
797fa225cbcSrjs
798fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_SUPP_ENCODE, NULL, 0);
799fa225cbcSrjs    status = i830_sdvo_read_response(output, encode, sizeof(*encode));
800fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS) { /* non-support means DVI */
801fa225cbcSrjs	memset(encode, 0, sizeof(*encode));
802fa225cbcSrjs	return FALSE;
803fa225cbcSrjs    }
804fa225cbcSrjs
805fa225cbcSrjs    return TRUE;
806fa225cbcSrjs}
807fa225cbcSrjs
808fa225cbcSrjsstatic Bool
809fa225cbcSrjsi830_sdvo_get_digital_encoding_mode(xf86OutputPtr output)
810fa225cbcSrjs{
811fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
812fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
813fa225cbcSrjs    uint8_t status;
814fa225cbcSrjs
815fa225cbcSrjs    i830_sdvo_set_target_output(output, dev_priv->controlled_output);
816fa225cbcSrjs
817fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_ENCODE, NULL, 0);
818fa225cbcSrjs    status = i830_sdvo_read_response(output, &dev_priv->is_hdmi, 1);
819fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS) {
820fa225cbcSrjs	dev_priv->is_hdmi = FALSE;
821fa225cbcSrjs	return FALSE;
822fa225cbcSrjs    }
823fa225cbcSrjs    return TRUE;
824fa225cbcSrjs}
825fa225cbcSrjs
826fa225cbcSrjsstatic Bool
827fa225cbcSrjsi830_sdvo_set_encode(xf86OutputPtr output, uint8_t mode)
828fa225cbcSrjs{
829fa225cbcSrjs    uint8_t status;
830fa225cbcSrjs
831fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODE, &mode, 1);
832fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
833fa225cbcSrjs
834fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
835fa225cbcSrjs}
836fa225cbcSrjs
837fa225cbcSrjsstatic Bool
838fa225cbcSrjsi830_sdvo_set_colorimetry(xf86OutputPtr output, uint8_t mode)
839fa225cbcSrjs{
840fa225cbcSrjs    uint8_t status;
841fa225cbcSrjs
842fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_COLORIMETRY, &mode, 1);
843fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
844fa225cbcSrjs
845fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
846fa225cbcSrjs}
847fa225cbcSrjs
848fa225cbcSrjs#if 0
849fa225cbcSrjsstatic Bool
850fa225cbcSrjsi830_sdvo_set_pixel_repli(xf86OutputPtr output, uint8_t repli)
851fa225cbcSrjs{
852fa225cbcSrjs    uint8_t status;
853fa225cbcSrjs
854fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_PIXEL_REPLI, &repli, 1);
855fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
856fa225cbcSrjs
857fa225cbcSrjs    return (status == SDVO_CMD_STATUS_SUCCESS);
858fa225cbcSrjs}
859fa225cbcSrjs#endif
860fa225cbcSrjs
861fa225cbcSrjsstatic void i830_sdvo_dump_hdmi_buf(xf86OutputPtr output)
862fa225cbcSrjs{
863fa225cbcSrjs    int i, j;
864fa225cbcSrjs    uint8_t set_buf_index[2];
865fa225cbcSrjs    uint8_t av_split;
866fa225cbcSrjs    uint8_t buf_size;
867fa225cbcSrjs    uint8_t buf[48];
868fa225cbcSrjs    uint8_t *pos;
869fa225cbcSrjs
870fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_AV_SPLIT, NULL, 0);
871fa225cbcSrjs    i830_sdvo_read_response(output, &av_split, 1);
872fa225cbcSrjs
873fa225cbcSrjs    for (i = 0; i <= av_split; i++) {
874fa225cbcSrjs	set_buf_index[0] = i; set_buf_index[1] = 0;
875fa225cbcSrjs	i830_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX,
876fa225cbcSrjs				set_buf_index, 2);
877fa225cbcSrjs	i830_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_INFO, NULL, 0);
878fa225cbcSrjs	i830_sdvo_read_response(output, &buf_size, 1);
879fa225cbcSrjs
880fa225cbcSrjs	pos = buf;
881fa225cbcSrjs	for (j = 0; j <= buf_size; j += 8) {
882fa225cbcSrjs	    i830_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_DATA, NULL, 0);
883fa225cbcSrjs	    i830_sdvo_read_response(output, pos, 8);
884fa225cbcSrjs	    pos += 8;
885fa225cbcSrjs	}
886fa225cbcSrjs    }
887fa225cbcSrjs}
888fa225cbcSrjs
889fa225cbcSrjsstatic void i830_sdvo_set_hdmi_buf(xf86OutputPtr output, int index,
890fa225cbcSrjs				uint8_t *data, int8_t size, uint8_t tx_rate)
891fa225cbcSrjs{
892fa225cbcSrjs    uint8_t set_buf_index[2];
893fa225cbcSrjs
894fa225cbcSrjs    set_buf_index[0] = index;
895fa225cbcSrjs    set_buf_index[1] = 0;
896fa225cbcSrjs
897fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2);
898fa225cbcSrjs
899fa225cbcSrjs    for (; size > 0; size -= 8) {
900fa225cbcSrjs	i830_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_DATA, data, 8);
901fa225cbcSrjs	data += 8;
902fa225cbcSrjs    }
903fa225cbcSrjs
904fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1);
905fa225cbcSrjs}
906fa225cbcSrjs
907fa225cbcSrjsstatic uint8_t i830_sdvo_calc_hbuf_csum(uint8_t *data, uint8_t size)
908fa225cbcSrjs{
909fa225cbcSrjs    uint8_t csum = 0;
910fa225cbcSrjs    int i;
911fa225cbcSrjs
912fa225cbcSrjs    for (i = 0; i < size; i++)
913fa225cbcSrjs	csum += data[i];
914fa225cbcSrjs
915fa225cbcSrjs    return 0x100 - csum;
916fa225cbcSrjs}
917fa225cbcSrjs
918fa225cbcSrjs#define DIP_TYPE_AVI	0x82
919fa225cbcSrjs#define DIP_VERSION_AVI	0x2
920fa225cbcSrjs#define DIP_LEN_AVI	13
921fa225cbcSrjs
922fa225cbcSrjsstruct dip_infoframe {
923fa225cbcSrjs    uint8_t type;
924fa225cbcSrjs    uint8_t version;
925fa225cbcSrjs    uint8_t len;
926fa225cbcSrjs    uint8_t checksum;
927fa225cbcSrjs    union {
928fa225cbcSrjs	struct {
929fa225cbcSrjs	    /* Packet Byte #1 */
930fa225cbcSrjs	    uint8_t S:2;
931fa225cbcSrjs	    uint8_t B:2;
932fa225cbcSrjs	    uint8_t A:1;
933fa225cbcSrjs	    uint8_t Y:2;
934fa225cbcSrjs	    uint8_t rsvd1:1;
935fa225cbcSrjs	    /* Packet Byte #2 */
936fa225cbcSrjs	    uint8_t R:4;
937fa225cbcSrjs	    uint8_t M:2;
938fa225cbcSrjs	    uint8_t C:2;
939fa225cbcSrjs	    /* Packet Byte #3 */
940fa225cbcSrjs	    uint8_t SC:2;
941fa225cbcSrjs	    uint8_t Q:2;
942fa225cbcSrjs	    uint8_t EC:3;
943fa225cbcSrjs	    uint8_t ITC:1;
944fa225cbcSrjs	    /* Packet Byte #4 */
945fa225cbcSrjs	    uint8_t VIC:7;
946fa225cbcSrjs	    uint8_t rsvd2:1;
947fa225cbcSrjs	    /* Packet Byte #5 */
948fa225cbcSrjs	    uint8_t PR:4;
949fa225cbcSrjs	    uint8_t rsvd3:4;
950fa225cbcSrjs	    /* Packet Byte #6~13 */
951fa225cbcSrjs	    uint16_t top_bar_end;
952fa225cbcSrjs	    uint16_t bottom_bar_start;
953fa225cbcSrjs	    uint16_t left_bar_end;
954fa225cbcSrjs	    uint16_t right_bar_start;
955fa225cbcSrjs	} avi;
956fa225cbcSrjs	struct {
957fa225cbcSrjs	    /* Packet Byte #1 */
958fa225cbcSrjs	    uint8_t CC:3;
959fa225cbcSrjs	    uint8_t rsvd1:1;
960fa225cbcSrjs	    uint8_t CT:4;
961fa225cbcSrjs	    /* Packet Byte #2 */
962fa225cbcSrjs	    uint8_t SS:2;
963fa225cbcSrjs	    uint8_t SF:3;
964fa225cbcSrjs	    uint8_t rsvd2:3;
965fa225cbcSrjs	    /* Packet Byte #3 */
966fa225cbcSrjs	    uint8_t CXT:5;
967fa225cbcSrjs	    uint8_t rsvd3:3;
968fa225cbcSrjs	    /* Packet Byte #4 */
969fa225cbcSrjs	    uint8_t CA;
970fa225cbcSrjs	    /* Packet Byte #5 */
971fa225cbcSrjs	    uint8_t rsvd4:3;
972fa225cbcSrjs	    uint8_t LSV:4;
973fa225cbcSrjs	    uint8_t DM_INH:1;
974fa225cbcSrjs	} audio;
975fa225cbcSrjs	uint8_t payload[28];
976fa225cbcSrjs    } __attribute__ ((packed)) u;
977fa225cbcSrjs} __attribute__((packed));
978fa225cbcSrjs
979fa225cbcSrjsstatic void i830_sdvo_set_avi_infoframe(xf86OutputPtr output,
980fa225cbcSrjs					DisplayModePtr mode)
981fa225cbcSrjs{
982fa225cbcSrjs    struct dip_infoframe avi_if = {
983fa225cbcSrjs	.type = DIP_TYPE_AVI,
984fa225cbcSrjs	.version = DIP_VERSION_AVI,
985fa225cbcSrjs	.len = DIP_LEN_AVI,
986fa225cbcSrjs    };
987fa225cbcSrjs
988fa225cbcSrjs    avi_if.checksum = i830_sdvo_calc_hbuf_csum((uint8_t *)&avi_if,
989fa225cbcSrjs					4 + avi_if.len);
990fa225cbcSrjs    i830_sdvo_set_hdmi_buf(output, 1, (uint8_t *)&avi_if, 4 + avi_if.len,
991fa225cbcSrjs			SDVO_HBUF_TX_VSYNC);
992fa225cbcSrjs}
993fa225cbcSrjs
994fa225cbcSrjsstatic void
995fa225cbcSrjsi830_sdvo_set_tv_format(xf86OutputPtr output)
996fa225cbcSrjs{
997fa225cbcSrjs    ScrnInfoPtr pScrn = output->scrn;
998fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
999fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1000fa225cbcSrjs    struct i830_sdvo_tv_format *format, unset;
1001fa225cbcSrjs    uint8_t status;
1002fa225cbcSrjs
1003fa225cbcSrjs    format = &dev_priv->tv_format;
1004fa225cbcSrjs    memset(&unset, 0, sizeof(unset));
1005fa225cbcSrjs    if (memcmp(format, &unset, sizeof(*format))) {
1006fa225cbcSrjs	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1007fa225cbcSrjs		   "%s: Choosing default TV format of NTSC-M\n",
1008fa225cbcSrjs		   SDVO_NAME(dev_priv));
1009fa225cbcSrjs	format->ntsc_m = 1;
1010fa225cbcSrjs	i830_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, format,
1011fa225cbcSrjs		sizeof(*format));
1012fa225cbcSrjs	status = i830_sdvo_read_response(output, NULL, 0);
1013fa225cbcSrjs	if (status != SDVO_CMD_STATUS_SUCCESS)
1014fa225cbcSrjs	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1015fa225cbcSrjs		    "%s: Fail to set TV format\n", SDVO_NAME(dev_priv));
1016fa225cbcSrjs    }
1017fa225cbcSrjs}
1018fa225cbcSrjs
1019fa225cbcSrjsstatic Bool
1020fa225cbcSrjsi830_sdvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
1021fa225cbcSrjs		     DisplayModePtr adjusted_mode)
1022fa225cbcSrjs{
1023fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1024fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1025fa225cbcSrjs
1026fa225cbcSrjs    if (dev_priv->is_tv) {
1027fa225cbcSrjs	struct i830_sdvo_dtd output_dtd;
1028fa225cbcSrjs	Bool success;
1029fa225cbcSrjs
1030fa225cbcSrjs	/* We need to construct preferred input timings based on our output
1031fa225cbcSrjs	 * timings.  To do that, we have to set the output timings, even
1032fa225cbcSrjs	 * though this isn't really the right place in the sequence to do it.
1033fa225cbcSrjs	 * Oh well.
1034fa225cbcSrjs	 */
1035fa225cbcSrjs
1036fa225cbcSrjs	ErrorF("output modeline:\n");
1037fa225cbcSrjs	xf86PrintModeline(0, mode);
1038fa225cbcSrjs
1039fa225cbcSrjs	/* Set output timings */
1040fa225cbcSrjs	i830_sdvo_get_dtd_from_mode(&output_dtd, mode);
1041fa225cbcSrjs	i830_sdvo_set_target_output(output, dev_priv->controlled_output);
1042fa225cbcSrjs	i830_sdvo_set_output_timing(output, &output_dtd);
1043fa225cbcSrjs
1044fa225cbcSrjs	/* Set the input timing to the screen. Assume always input 0. */
1045fa225cbcSrjs	i830_sdvo_set_target_input(output, TRUE, FALSE);
1046fa225cbcSrjs
1047fa225cbcSrjs	success = i830_sdvo_create_preferred_input_timing(output,
1048fa225cbcSrjs							  mode->Clock / 10,
1049fa225cbcSrjs							  mode->HDisplay,
1050fa225cbcSrjs							  mode->VDisplay);
1051fa225cbcSrjs	if (success) {
1052fa225cbcSrjs	    struct i830_sdvo_dtd input_dtd;
1053fa225cbcSrjs
1054fa225cbcSrjs	    i830_sdvo_get_preferred_input_timing(output, &input_dtd);
1055fa225cbcSrjs
1056fa225cbcSrjs	    i830_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
1057fa225cbcSrjs	    dev_priv->sdvo_flags = input_dtd.part2.sdvo_flags;
1058fa225cbcSrjs
1059fa225cbcSrjs	    xf86SetModeCrtc(adjusted_mode, 0);
1060fa225cbcSrjs
1061fa225cbcSrjs	    ErrorF("input modeline:\n");
1062fa225cbcSrjs	    xf86PrintModeline(0, adjusted_mode);
1063fa225cbcSrjs
1064fa225cbcSrjs	    /* adjust origin mode's clock for current input,
1065fa225cbcSrjs	       for correct pixel mulitiplier setting. */
1066fa225cbcSrjs	    mode->Clock = adjusted_mode->Clock;
1067fa225cbcSrjs
1068fa225cbcSrjs	    /* Clock range is required to be in 100-200Mhz */
1069fa225cbcSrjs	    adjusted_mode->Clock *= i830_sdvo_get_pixel_multiplier(mode);
1070fa225cbcSrjs	} else {
1071fa225cbcSrjs	    return FALSE;
1072fa225cbcSrjs	}
1073fa225cbcSrjs    } else if (dev_priv->is_lvds) {
1074fa225cbcSrjs        struct i830_sdvo_dtd output_dtd;
1075fa225cbcSrjs        Bool success;
1076fa225cbcSrjs
1077fa225cbcSrjs        /* Set output timings */
1078fa225cbcSrjs        i830_sdvo_get_dtd_from_mode(&output_dtd,
1079fa225cbcSrjs                                    dev_priv->sdvo_lvds_fixed_mode);
1080fa225cbcSrjs        i830_sdvo_set_target_output(output, dev_priv->controlled_output);
1081fa225cbcSrjs        i830_sdvo_set_output_timing(output, &output_dtd);
1082fa225cbcSrjs
1083fa225cbcSrjs        /* Set the input timing to the screen. Assume always input 0. */
1084fa225cbcSrjs        i830_sdvo_set_target_input(output, TRUE, FALSE);
1085fa225cbcSrjs
1086fa225cbcSrjs
1087fa225cbcSrjs        success = i830_sdvo_create_preferred_input_timing(output,
1088fa225cbcSrjs                                                          mode->Clock / 10,
1089fa225cbcSrjs                                                          mode->HDisplay,
1090fa225cbcSrjs                                                          mode->VDisplay);
1091fa225cbcSrjs        if (success) {
1092fa225cbcSrjs            struct i830_sdvo_dtd input_dtd;
1093fa225cbcSrjs
1094fa225cbcSrjs            i830_sdvo_get_preferred_input_timing(output, &input_dtd);
1095fa225cbcSrjs
1096fa225cbcSrjs            i830_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
1097fa225cbcSrjs            dev_priv->sdvo_flags = input_dtd.part2.sdvo_flags;
1098fa225cbcSrjs
1099fa225cbcSrjs            xf86SetModeCrtc(adjusted_mode, 0);
1100fa225cbcSrjs
1101fa225cbcSrjs            /* adjust origin mode's clock for current input,
1102fa225cbcSrjs              for correct pixel mulitiplier setting. */
1103fa225cbcSrjs            mode->Clock = adjusted_mode->Clock;
1104fa225cbcSrjs
1105fa225cbcSrjs            /* Clock range is required to be in 100-200Mhz */
1106fa225cbcSrjs            adjusted_mode->Clock *= i830_sdvo_get_pixel_multiplier(mode);
1107fa225cbcSrjs        } else
1108fa225cbcSrjs            return FALSE;
1109fa225cbcSrjs    } else
1110fa225cbcSrjs	/* Make the CRTC code factor in the SDVO pixel multiplier.  The SDVO
1111fa225cbcSrjs	 * device will be told of the multiplier during mode_set.
1112fa225cbcSrjs	 */
1113fa225cbcSrjs	adjusted_mode->Clock *= i830_sdvo_get_pixel_multiplier(mode);
1114fa225cbcSrjs
1115fa225cbcSrjs    return TRUE;
1116fa225cbcSrjs}
1117fa225cbcSrjs
1118fa225cbcSrjsstatic void
1119fa225cbcSrjsi830_sdvo_mode_set(xf86OutputPtr output, DisplayModePtr mode,
1120fa225cbcSrjs		   DisplayModePtr adjusted_mode)
1121fa225cbcSrjs{
1122fa225cbcSrjs    ScrnInfoPtr pScrn = output->scrn;
1123fa225cbcSrjs    I830Ptr pI830 = I830PTR(pScrn);
1124fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1125fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1126fa225cbcSrjs    xf86CrtcPtr	    crtc = output->crtc;
1127fa225cbcSrjs    I830CrtcPrivatePtr	    intel_crtc = crtc->driver_private;
1128fa225cbcSrjs    uint32_t sdvox = 0;
1129fa225cbcSrjs    int sdvo_pixel_multiply;
1130fa225cbcSrjs    struct i830_sdvo_in_out_map in_out;
1131fa225cbcSrjs    struct i830_sdvo_dtd input_dtd;
1132fa225cbcSrjs    uint8_t status;
1133fa225cbcSrjs
1134fa225cbcSrjs    if (!mode)
1135fa225cbcSrjs	return;
1136fa225cbcSrjs
1137fa225cbcSrjs    /* First, set the input mapping for the first input to our controlled
1138fa225cbcSrjs     * output. This is only correct if we're a single-input device, in
1139fa225cbcSrjs     * which case the first input is the output from the appropriate SDVO
1140fa225cbcSrjs     * channel on the motherboard.  In a two-input device, the first input
1141fa225cbcSrjs     * will be SDVOB and the second SDVOC.
1142fa225cbcSrjs     */
1143fa225cbcSrjs    in_out.in0 = dev_priv->controlled_output;
1144fa225cbcSrjs    in_out.in1 = 0;
1145fa225cbcSrjs
1146fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP,
1147fa225cbcSrjs			&in_out, sizeof(in_out));
1148fa225cbcSrjs    status = i830_sdvo_read_response(output, NULL, 0);
1149fa225cbcSrjs
1150fa225cbcSrjs    if (dev_priv->is_hdmi) {
1151fa225cbcSrjs	i830_sdvo_set_avi_infoframe(output, mode);
1152fa225cbcSrjs	sdvox |= SDVO_AUDIO_ENABLE;
1153fa225cbcSrjs    }
1154fa225cbcSrjs
1155fa225cbcSrjs    /* We have tried to get input timing in mode_fixup, and filled into
1156fa225cbcSrjs       adjusted_mode */
1157fa225cbcSrjs    if (dev_priv->is_tv || dev_priv->is_lvds) {
1158fa225cbcSrjs	i830_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
1159fa225cbcSrjs        input_dtd.part2.sdvo_flags = dev_priv->sdvo_flags;
1160fa225cbcSrjs    } else
1161fa225cbcSrjs	i830_sdvo_get_dtd_from_mode(&input_dtd, mode);
1162fa225cbcSrjs
1163fa225cbcSrjs    /* If it's a TV, we already set the output timing in mode_fixup.
1164fa225cbcSrjs     * Otherwise, the output timing is equal to the input timing.
1165fa225cbcSrjs     */
1166fa225cbcSrjs    i830_sdvo_set_target_output(output, dev_priv->controlled_output);
1167fa225cbcSrjs    /* Set the input timing to the screen. Assume always input 0. */
1168fa225cbcSrjs    i830_sdvo_set_target_input(output, TRUE, FALSE);
1169fa225cbcSrjs
1170fa225cbcSrjs    if (dev_priv->is_tv)
1171fa225cbcSrjs	i830_sdvo_set_tv_format(output);
1172fa225cbcSrjs
1173fa225cbcSrjs    if (!dev_priv->is_tv && !dev_priv->is_lvds) {
1174fa225cbcSrjs	/* Set the output timing to the screen */
1175fa225cbcSrjs	i830_sdvo_set_output_timing(output, &input_dtd);
1176fa225cbcSrjs    }
1177fa225cbcSrjs
1178fa225cbcSrjs    /* We would like to use i830_sdvo_create_preferred_input_timing() to
1179fa225cbcSrjs     * provide the device with a timing it can support, if it supports that
1180fa225cbcSrjs     * feature.  However, presumably we would need to adjust the CRTC to output
1181fa225cbcSrjs     * the preferred timing, and we don't support that currently.
1182fa225cbcSrjs     */
1183fa225cbcSrjs#if 0
1184fa225cbcSrjs    success = i830_sdvo_create_preferred_input_timing(output, clock,
1185fa225cbcSrjs						      width, height);
1186fa225cbcSrjs    if (success) {
1187fa225cbcSrjs	struct i830_sdvo_dtd *input_dtd;
1188fa225cbcSrjs
1189fa225cbcSrjs	i830_sdvo_get_preferred_input_timing(output, &input_dtd);
1190fa225cbcSrjs	i830_sdvo_set_input_timing(output, &input_dtd);
1191fa225cbcSrjs    }
1192fa225cbcSrjs#else
1193fa225cbcSrjs    i830_sdvo_set_input_timing(output, &input_dtd);
1194fa225cbcSrjs#endif
1195fa225cbcSrjs
1196fa225cbcSrjs    switch (i830_sdvo_get_pixel_multiplier(mode)) {
1197fa225cbcSrjs    case 1:
1198fa225cbcSrjs	i830_sdvo_set_clock_rate_mult(output, SDVO_CLOCK_RATE_MULT_1X);
1199fa225cbcSrjs	break;
1200fa225cbcSrjs    case 2:
1201fa225cbcSrjs	i830_sdvo_set_clock_rate_mult(output, SDVO_CLOCK_RATE_MULT_2X);
1202fa225cbcSrjs	break;
1203fa225cbcSrjs    case 4:
1204fa225cbcSrjs	i830_sdvo_set_clock_rate_mult(output, SDVO_CLOCK_RATE_MULT_4X);
1205fa225cbcSrjs	break;
1206fa225cbcSrjs    }
1207fa225cbcSrjs
1208fa225cbcSrjs    /* Set the SDVO control regs. */
1209fa225cbcSrjs    if (IS_I965G(pI830)) {
1210fa225cbcSrjs	sdvox |= SDVO_BORDER_ENABLE |
1211fa225cbcSrjs		SDVO_VSYNC_ACTIVE_HIGH |
1212fa225cbcSrjs		SDVO_HSYNC_ACTIVE_HIGH;
1213fa225cbcSrjs    } else {
1214fa225cbcSrjs	sdvox |= INREG(dev_priv->output_device);
1215fa225cbcSrjs	switch (dev_priv->output_device) {
1216fa225cbcSrjs	case SDVOB:
1217fa225cbcSrjs	    sdvox &= SDVOB_PRESERVE_MASK;
1218fa225cbcSrjs	    break;
1219fa225cbcSrjs	case SDVOC:
1220fa225cbcSrjs	    sdvox &= SDVOC_PRESERVE_MASK;
1221fa225cbcSrjs	    break;
1222fa225cbcSrjs	}
1223fa225cbcSrjs	sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
1224fa225cbcSrjs    }
1225fa225cbcSrjs    if (intel_crtc->pipe == 1)
1226fa225cbcSrjs	sdvox |= SDVO_PIPE_B_SELECT;
1227fa225cbcSrjs
1228fa225cbcSrjs    sdvo_pixel_multiply = i830_sdvo_get_pixel_multiplier(mode);
1229fa225cbcSrjs    if (IS_I965G(pI830)) {
1230fa225cbcSrjs	/* done in crtc_mode_set as the dpll_md reg must be written early */
1231fa225cbcSrjs    } else if (IS_I945G(pI830) || IS_I945GM(pI830) || IS_G33CLASS(pI830)) {
1232fa225cbcSrjs	/* done in crtc_mode_set as it lives inside the dpll register */
1233fa225cbcSrjs    } else {
1234fa225cbcSrjs	sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
1235fa225cbcSrjs    }
1236fa225cbcSrjs    if (dev_priv->sdvo_flags & SDVO_STALL_FLAG)
1237fa225cbcSrjs        sdvox |= SDVO_STALL_SELECT;
1238fa225cbcSrjs
1239fa225cbcSrjs    i830_sdvo_write_sdvox(output, sdvox);
1240fa225cbcSrjs
1241fa225cbcSrjs    if (0)
1242fa225cbcSrjs	i830_sdvo_dump(pScrn);
1243fa225cbcSrjs}
1244fa225cbcSrjs
1245fa225cbcSrjsstatic void
1246fa225cbcSrjsi830_sdvo_dpms(xf86OutputPtr output, int mode)
1247fa225cbcSrjs{
1248fa225cbcSrjs    ScrnInfoPtr		    pScrn = output->scrn;
1249fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1250fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1251fa225cbcSrjs    I830Ptr pI830 = I830PTR(pScrn);
1252fa225cbcSrjs    uint32_t  temp;
1253fa225cbcSrjs
1254fa225cbcSrjs    if (mode != DPMSModeOn) {
1255fa225cbcSrjs	i830_sdvo_set_active_outputs(output, 0);
1256fa225cbcSrjs	if (0)
1257fa225cbcSrjs	    i830_sdvo_set_encoder_power_state(output, mode);
1258fa225cbcSrjs
1259fa225cbcSrjs	if (mode == DPMSModeOff) {
1260fa225cbcSrjs	    temp = INREG(dev_priv->output_device);
1261fa225cbcSrjs	    if ((temp & SDVO_ENABLE) != 0) {
1262fa225cbcSrjs		i830_sdvo_write_sdvox(output, temp & ~SDVO_ENABLE);
1263fa225cbcSrjs	    }
1264fa225cbcSrjs	}
1265fa225cbcSrjs    } else {
1266fa225cbcSrjs	Bool input1, input2;
1267fa225cbcSrjs	int i;
1268fa225cbcSrjs	uint8_t status;
1269fa225cbcSrjs
1270fa225cbcSrjs	temp = INREG(dev_priv->output_device);
1271fa225cbcSrjs	if ((temp & SDVO_ENABLE) == 0)
1272fa225cbcSrjs	    i830_sdvo_write_sdvox(output, temp | SDVO_ENABLE);
1273fa225cbcSrjs	for (i = 0; i < 2; i++)
1274fa225cbcSrjs	    i830WaitForVblank(pScrn);
1275fa225cbcSrjs
1276fa225cbcSrjs	status = i830_sdvo_get_trained_inputs(output, &input1, &input2);
1277fa225cbcSrjs
1278fa225cbcSrjs	/* Warn if the device reported failure to sync. */
1279fa225cbcSrjs	if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
1280fa225cbcSrjs	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1281fa225cbcSrjs		       "First %s output reported failure to sync\n",
1282fa225cbcSrjs		       SDVO_NAME(dev_priv));
1283fa225cbcSrjs	}
1284fa225cbcSrjs
1285fa225cbcSrjs	if (0)
1286fa225cbcSrjs	    i830_sdvo_set_encoder_power_state(output, mode);
1287fa225cbcSrjs	i830_sdvo_set_active_outputs(output, dev_priv->controlled_output);
1288fa225cbcSrjs    }
1289fa225cbcSrjs}
1290fa225cbcSrjs
1291fa225cbcSrjsstatic void
1292fa225cbcSrjsi830_sdvo_save(xf86OutputPtr output)
1293fa225cbcSrjs{
1294fa225cbcSrjs    ScrnInfoPtr		    pScrn = output->scrn;
1295fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1296fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1297fa225cbcSrjs    I830Ptr		    pI830 = I830PTR(pScrn);
1298fa225cbcSrjs    int			    o;
1299fa225cbcSrjs
1300fa225cbcSrjs    /* XXX: We should save the in/out mapping. */
1301fa225cbcSrjs
1302fa225cbcSrjs    dev_priv->save_sdvo_mult = i830_sdvo_get_clock_rate_mult(output);
1303fa225cbcSrjs    i830_sdvo_get_active_outputs(output, &dev_priv->save_active_outputs);
1304fa225cbcSrjs
1305fa225cbcSrjs    i830_sdvo_set_target_input(output, TRUE, FALSE);
1306fa225cbcSrjs    i830_sdvo_get_input_timing(output, &dev_priv->save_input_dtd_1);
1307fa225cbcSrjs
1308fa225cbcSrjs    if (dev_priv->caps.sdvo_input_count >= 2) {
1309fa225cbcSrjs       i830_sdvo_set_target_input(output, FALSE, TRUE);
1310fa225cbcSrjs       i830_sdvo_get_input_timing(output, &dev_priv->save_input_dtd_2);
1311fa225cbcSrjs    }
1312fa225cbcSrjs
1313fa225cbcSrjs    for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
1314fa225cbcSrjs    {
1315fa225cbcSrjs	uint16_t this_output = (1 << o);
1316fa225cbcSrjs	if (dev_priv->caps.output_flags & this_output)
1317fa225cbcSrjs	{
1318fa225cbcSrjs	    i830_sdvo_set_target_output(output, this_output);
1319fa225cbcSrjs	    i830_sdvo_get_output_timing(output, &dev_priv->save_output_dtd[o]);
1320fa225cbcSrjs	}
1321fa225cbcSrjs    }
1322fa225cbcSrjs    if (dev_priv->is_tv) {
1323fa225cbcSrjs	/* XXX: Save TV format/enhancements. */
1324fa225cbcSrjs    }
1325fa225cbcSrjs
1326fa225cbcSrjs    dev_priv->save_SDVOX = INREG(dev_priv->output_device);
1327fa225cbcSrjs}
1328fa225cbcSrjs
1329fa225cbcSrjsstatic void
1330fa225cbcSrjsi830_sdvo_restore(xf86OutputPtr output)
1331fa225cbcSrjs{
1332fa225cbcSrjs    ScrnInfoPtr		    pScrn = output->scrn;
1333fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1334fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1335fa225cbcSrjs    int			    o;
1336fa225cbcSrjs    int			    i;
1337fa225cbcSrjs    Bool		    input1, input2;
1338fa225cbcSrjs    uint8_t		    status;
1339fa225cbcSrjs
1340fa225cbcSrjs    i830_sdvo_set_active_outputs(output, 0);
1341fa225cbcSrjs
1342fa225cbcSrjs    for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
1343fa225cbcSrjs    {
1344fa225cbcSrjs	uint16_t this_output = (1 << o);
1345fa225cbcSrjs	if (dev_priv->caps.output_flags & this_output)
1346fa225cbcSrjs	{
1347fa225cbcSrjs	    i830_sdvo_set_target_output(output, this_output);
1348fa225cbcSrjs	    i830_sdvo_set_output_timing(output, &dev_priv->save_output_dtd[o]);
1349fa225cbcSrjs	}
1350fa225cbcSrjs    }
1351fa225cbcSrjs
1352fa225cbcSrjs    i830_sdvo_set_target_input(output, TRUE, FALSE);
1353fa225cbcSrjs    i830_sdvo_set_input_timing(output, &dev_priv->save_input_dtd_1);
1354fa225cbcSrjs
1355fa225cbcSrjs    if (dev_priv->caps.sdvo_input_count >= 2) {
1356fa225cbcSrjs       i830_sdvo_set_target_input(output, FALSE, TRUE);
1357fa225cbcSrjs       i830_sdvo_set_input_timing(output, &dev_priv->save_input_dtd_2);
1358fa225cbcSrjs    }
1359fa225cbcSrjs
1360fa225cbcSrjs    i830_sdvo_set_clock_rate_mult(output, dev_priv->save_sdvo_mult);
1361fa225cbcSrjs
1362fa225cbcSrjs    if (dev_priv->is_tv) {
1363fa225cbcSrjs	/* XXX: Restore TV format/enhancements. */
1364fa225cbcSrjs    }
1365fa225cbcSrjs
1366fa225cbcSrjs    i830_sdvo_write_sdvox(output, dev_priv->save_SDVOX);
1367fa225cbcSrjs
1368fa225cbcSrjs    if (dev_priv->save_SDVOX & SDVO_ENABLE)
1369fa225cbcSrjs    {
1370fa225cbcSrjs	for (i = 0; i < 2; i++)
1371fa225cbcSrjs	    i830WaitForVblank(pScrn);
1372fa225cbcSrjs	status = i830_sdvo_get_trained_inputs(output, &input1, &input2);
1373fa225cbcSrjs	if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
1374fa225cbcSrjs	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1375fa225cbcSrjs		       "First %s output reported failure to sync\n",
1376fa225cbcSrjs		       SDVO_NAME(dev_priv));
1377fa225cbcSrjs    }
1378fa225cbcSrjs
1379fa225cbcSrjs    i830_sdvo_set_active_outputs(output, dev_priv->save_active_outputs);
1380fa225cbcSrjs}
1381fa225cbcSrjs
1382fa225cbcSrjsstatic int
1383fa225cbcSrjsi830_sdvo_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
1384fa225cbcSrjs{
1385fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1386fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1387fa225cbcSrjs
1388fa225cbcSrjs    if (pMode->Flags & V_DBLSCAN)
1389fa225cbcSrjs	return MODE_NO_DBLESCAN;
1390fa225cbcSrjs
1391fa225cbcSrjs    if (dev_priv->pixel_clock_min > pMode->Clock)
1392fa225cbcSrjs	return MODE_CLOCK_LOW;
1393fa225cbcSrjs
1394fa225cbcSrjs    if (dev_priv->pixel_clock_max < pMode->Clock)
1395fa225cbcSrjs	return MODE_CLOCK_HIGH;
1396fa225cbcSrjs    if (dev_priv->is_lvds) {
1397fa225cbcSrjs        if (dev_priv->sdvo_lvds_fixed_mode == NULL)
1398fa225cbcSrjs            return MODE_PANEL;
1399fa225cbcSrjs
1400fa225cbcSrjs        if (pMode->HDisplay > dev_priv->sdvo_lvds_fixed_mode->HDisplay)
1401fa225cbcSrjs            return MODE_PANEL;
1402fa225cbcSrjs
1403fa225cbcSrjs        if (pMode->VDisplay > dev_priv->sdvo_lvds_fixed_mode->VDisplay)
1404fa225cbcSrjs            return MODE_PANEL;
1405fa225cbcSrjs    }
1406fa225cbcSrjs
1407fa225cbcSrjs    return MODE_OK;
1408fa225cbcSrjs}
1409fa225cbcSrjs
1410fa225cbcSrjsstatic Bool
1411fa225cbcSrjsi830_sdvo_get_capabilities(xf86OutputPtr output, struct i830_sdvo_caps *caps)
1412fa225cbcSrjs{
1413fa225cbcSrjs    uint8_t status;
1414fa225cbcSrjs
1415fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0);
1416fa225cbcSrjs    status = i830_sdvo_read_response(output, caps, sizeof(*caps));
1417fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
1418fa225cbcSrjs	return FALSE;
1419fa225cbcSrjs
1420fa225cbcSrjs    return TRUE;
1421fa225cbcSrjs}
1422fa225cbcSrjs
1423fa225cbcSrjs/** Forces the device over to the real I2C bus and uses its GetByte */
1424fa225cbcSrjsstatic Bool
1425fa225cbcSrjsi830_sdvo_ddc_i2c_get_byte(I2CDevPtr d, I2CByte *data, Bool last)
1426fa225cbcSrjs{
1427fa225cbcSrjs    xf86OutputPtr	    output = d->pI2CBus->DriverPrivate.ptr;
1428fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1429fa225cbcSrjs    I2CBusPtr		    i2cbus = intel_output->pI2CBus, savebus;
1430fa225cbcSrjs    Bool		    ret;
1431fa225cbcSrjs
1432fa225cbcSrjs    savebus = d->pI2CBus;
1433fa225cbcSrjs    d->pI2CBus = i2cbus;
1434fa225cbcSrjs    ret = i2cbus->I2CGetByte(d, data, last);
1435fa225cbcSrjs    d->pI2CBus = savebus;
1436fa225cbcSrjs
1437fa225cbcSrjs    return ret;
1438fa225cbcSrjs}
1439fa225cbcSrjs
1440fa225cbcSrjs/** Forces the device over to the real I2C bus and uses its PutByte */
1441fa225cbcSrjsstatic Bool
1442fa225cbcSrjsi830_sdvo_ddc_i2c_put_byte(I2CDevPtr d, I2CByte c)
1443fa225cbcSrjs{
1444fa225cbcSrjs    xf86OutputPtr	    output = d->pI2CBus->DriverPrivate.ptr;
1445fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1446fa225cbcSrjs    I2CBusPtr		    i2cbus = intel_output->pI2CBus, savebus;
1447fa225cbcSrjs    Bool		    ret;
1448fa225cbcSrjs
1449fa225cbcSrjs    savebus = d->pI2CBus;
1450fa225cbcSrjs    d->pI2CBus = i2cbus;
1451fa225cbcSrjs    ret = i2cbus->I2CPutByte(d, c);
1452fa225cbcSrjs    d->pI2CBus = savebus;
1453fa225cbcSrjs
1454fa225cbcSrjs    return ret;
1455fa225cbcSrjs}
1456fa225cbcSrjs
1457fa225cbcSrjs/**
1458fa225cbcSrjs * Sets the control bus over to DDC before sending the start on the real I2C
1459fa225cbcSrjs * bus.
1460fa225cbcSrjs *
1461fa225cbcSrjs * The control bus will flip back at the stop following the start executed
1462fa225cbcSrjs * here.
1463fa225cbcSrjs */
1464fa225cbcSrjsstatic Bool
1465fa225cbcSrjsi830_sdvo_ddc_i2c_start(I2CBusPtr b, int timeout)
1466fa225cbcSrjs{
1467fa225cbcSrjs    xf86OutputPtr	    output = b->DriverPrivate.ptr;
1468fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1469fa225cbcSrjs    I2CBusPtr		    i2cbus = intel_output->pI2CBus;
1470fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1471fa225cbcSrjs
1472fa225cbcSrjs    if (dev_priv->ddc_bus_switch) {
1473fa225cbcSrjs        i830_sdvo_set_control_bus_switch(output, dev_priv->ddc_bus);
1474fa225cbcSrjs        dev_priv->ddc_bus_switch = FALSE;
1475fa225cbcSrjs    }
1476fa225cbcSrjs    return i2cbus->I2CStart(i2cbus, timeout);
1477fa225cbcSrjs}
1478fa225cbcSrjs
1479fa225cbcSrjs/** Forces the device over to the real SDVO bus and sends a stop to it. */
1480fa225cbcSrjsstatic void
1481fa225cbcSrjsi830_sdvo_ddc_i2c_stop(I2CDevPtr d)
1482fa225cbcSrjs{
1483fa225cbcSrjs    xf86OutputPtr	    output = d->pI2CBus->DriverPrivate.ptr;
1484fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1485fa225cbcSrjs    I2CBusPtr		    i2cbus = intel_output->pI2CBus, savebus;
1486fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1487fa225cbcSrjs
1488fa225cbcSrjs    savebus = d->pI2CBus;
1489fa225cbcSrjs    d->pI2CBus = i2cbus;
1490fa225cbcSrjs    i2cbus->I2CStop(d);
1491fa225cbcSrjs    d->pI2CBus = savebus;
1492fa225cbcSrjs    dev_priv->ddc_bus_switch = TRUE;
1493fa225cbcSrjs}
1494fa225cbcSrjs
1495fa225cbcSrjs/**
1496fa225cbcSrjs * Mirrors xf86i2c I2CAddress, using the bus's (wrapped) methods rather than
1497fa225cbcSrjs * the default methods.
1498fa225cbcSrjs *
1499fa225cbcSrjs * This ensures that our start commands always get wrapped with control bus
1500fa225cbcSrjs * switches.  xf86i2c should probably be fixed to do this.
1501fa225cbcSrjs */
1502fa225cbcSrjsstatic Bool
1503fa225cbcSrjsi830_sdvo_ddc_i2c_address(I2CDevPtr d, I2CSlaveAddr addr)
1504fa225cbcSrjs{
1505fa225cbcSrjs    if (d->pI2CBus->I2CStart(d->pI2CBus, d->StartTimeout)) {
1506fa225cbcSrjs	if (d->pI2CBus->I2CPutByte(d, addr & 0xFF)) {
1507fa225cbcSrjs	    if ((addr & 0xF8) != 0xF0 &&
1508fa225cbcSrjs		(addr & 0xFE) != 0x00)
1509fa225cbcSrjs		return TRUE;
1510fa225cbcSrjs
1511fa225cbcSrjs	    if (d->pI2CBus->I2CPutByte(d, (addr >> 8) & 0xFF))
1512fa225cbcSrjs		return TRUE;
1513fa225cbcSrjs	}
1514fa225cbcSrjs
1515fa225cbcSrjs	d->pI2CBus->I2CStop(d);
1516fa225cbcSrjs    }
1517fa225cbcSrjs
1518fa225cbcSrjs    return FALSE;
1519fa225cbcSrjs}
1520fa225cbcSrjs
1521fa225cbcSrjsstatic void
1522fa225cbcSrjsi830_sdvo_dump_cmd(xf86OutputPtr output, int opcode)
1523fa225cbcSrjs{
1524fa225cbcSrjs    uint8_t response[8];
1525fa225cbcSrjs
1526fa225cbcSrjs    i830_sdvo_write_cmd(output, opcode, NULL, 0);
1527fa225cbcSrjs    i830_sdvo_read_response(output, response, 8);
1528fa225cbcSrjs}
1529fa225cbcSrjs
1530fa225cbcSrjsstatic void
1531fa225cbcSrjsi830_sdvo_dump_device(xf86OutputPtr output)
1532fa225cbcSrjs{
1533fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
1534fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
1535fa225cbcSrjs
1536fa225cbcSrjs    ErrorF("Dump %s\n", dev_priv->d.DevName);
1537fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_DEVICE_CAPS);
1538fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_FIRMWARE_REV);
1539fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_TRAINED_INPUTS);
1540fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_ACTIVE_OUTPUTS);
1541fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_IN_OUT_MAP);
1542fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS);
1543fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_HOT_PLUG_SUPPORT);
1544fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_ACTIVE_HOT_PLUG);
1545fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE);
1546fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_INPUT_TIMINGS_PART1);
1547fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_INPUT_TIMINGS_PART2);
1548fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_OUTPUT_TIMINGS_PART1);
1549fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_OUTPUT_TIMINGS_PART2);
1550fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1);
1551fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2);
1552fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE);
1553fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE);
1554fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS);
1555fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT);
1556fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_SUPPORTED_TV_FORMATS);
1557fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_TV_FORMAT);
1558fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT);
1559fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT);
1560fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS);
1561fa225cbcSrjs
1562fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_SUPP_ENCODE);
1563fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_ENCODE);
1564fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_PIXEL_REPLI);
1565fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_COLORIMETRY_CAP);
1566fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_COLORIMETRY);
1567fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER);
1568fa225cbcSrjs    i830_sdvo_dump_cmd(output, SDVO_CMD_GET_AUDIO_STAT);
1569fa225cbcSrjs    i830_sdvo_dump_hdmi_buf(output);
1570fa225cbcSrjs}
1571fa225cbcSrjs
1572fa225cbcSrjsstatic void
1573fa225cbcSrjsi830_sdvo_dump(ScrnInfoPtr pScrn)
1574fa225cbcSrjs{
1575fa225cbcSrjs    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
1576fa225cbcSrjs    int	i;
1577fa225cbcSrjs
1578fa225cbcSrjs    for (i = 0; i < xf86_config->num_output; i++)
1579fa225cbcSrjs    {
1580fa225cbcSrjs	xf86OutputPtr	output = xf86_config->output[i];
1581fa225cbcSrjs	I830OutputPrivatePtr	intel_output = output->driver_private;
1582fa225cbcSrjs
1583fa225cbcSrjs	if (intel_output->type == I830_OUTPUT_SDVO)
1584fa225cbcSrjs	    i830_sdvo_dump_device(output);
1585fa225cbcSrjs    }
1586fa225cbcSrjs}
1587fa225cbcSrjs
1588fa225cbcSrjsstatic void
1589fa225cbcSrjsi830_sdvo_set_hdmi_encode (xf86OutputPtr output)
1590fa225cbcSrjs{
1591fa225cbcSrjs    /* enable hdmi encoding mode if supported */
1592fa225cbcSrjs    i830_sdvo_set_encode(output, SDVO_ENCODE_HDMI);
1593fa225cbcSrjs    i830_sdvo_set_colorimetry(output, SDVO_COLORIMETRY_RGB256);
1594fa225cbcSrjs}
1595fa225cbcSrjs
1596fa225cbcSrjs/**
1597fa225cbcSrjs * Determine if current TMDS encoding is HDMI.
1598fa225cbcSrjs * Return TRUE if found HDMI encoding is used, otherwise return FALSE.
1599fa225cbcSrjs */
1600fa225cbcSrjsstatic Bool
1601fa225cbcSrjsi830_sdvo_check_hdmi_encode (xf86OutputPtr output)
1602fa225cbcSrjs{
1603fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
1604fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1605fa225cbcSrjs
1606fa225cbcSrjs    if (i830_sdvo_get_supp_encode(output, &dev_priv->encode) &&
1607fa225cbcSrjs	    i830_sdvo_get_digital_encoding_mode(output) &&
1608fa225cbcSrjs	    dev_priv->is_hdmi)
1609fa225cbcSrjs    {
1610fa225cbcSrjs	i830_sdvo_set_hdmi_encode(output);
1611fa225cbcSrjs	return TRUE;
1612fa225cbcSrjs    } else
1613fa225cbcSrjs	return FALSE;
1614fa225cbcSrjs}
1615fa225cbcSrjs
1616fa225cbcSrjs/* This function will try to fetch native modes for sdvo lvds output*/
1617fa225cbcSrjsstatic DisplayModePtr i830_sdvo_lvds_fetch_modes(xf86OutputPtr  output)
1618fa225cbcSrjs{
1619fa225cbcSrjs    I830OutputPrivatePtr intel_output =output->driver_private;
1620fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1621fa225cbcSrjs    I830Ptr                 pI830 = I830PTR(output->scrn);
1622fa225cbcSrjs    DisplayModePtr          modes;
1623fa225cbcSrjs
1624fa225cbcSrjs    /*
1625fa225cbcSrjs     * Attempt to get the mode list from DDC.
1626fa225cbcSrjs     * Assume that the preferred modes are
1627fa225cbcSrjs     * arranged in priority order,
1628fa225cbcSrjs     */
1629fa225cbcSrjs    modes = i830_ddc_get_modes(output);
1630fa225cbcSrjs    if (modes != NULL)
1631fa225cbcSrjs        goto end;
1632fa225cbcSrjs
1633fa225cbcSrjs    if (pI830->sdvo_lvds_fixed_mode != NULL)
1634fa225cbcSrjs        modes = xf86DuplicateModes(output->scrn, pI830->sdvo_lvds_fixed_mode);
1635fa225cbcSrjs
1636fa225cbcSrjsend:
1637fa225cbcSrjs    /* Guarantee the the first preferred mode is chosen by xserver */
1638fa225cbcSrjs    if (modes != NULL) {
1639fa225cbcSrjs        dev_priv->sdvo_lvds_fixed_mode = xf86DuplicateMode(modes);
1640fa225cbcSrjs        modes->type |= (M_T_DRIVER | M_T_PREFERRED);
1641fa225cbcSrjs        xf86SetModeCrtc(dev_priv->sdvo_lvds_fixed_mode, 0);
1642fa225cbcSrjs    }
1643fa225cbcSrjs    return modes;
1644fa225cbcSrjs}
1645fa225cbcSrjs
1646fa225cbcSrjsstatic void i830_sdvo_select_ddc_bus(struct i830_sdvo_priv *dev_priv);
1647fa225cbcSrjs
1648fa225cbcSrjsstatic Bool
1649fa225cbcSrjsi830_sdvo_output_setup (xf86OutputPtr output, uint16_t flag)
1650fa225cbcSrjs{
1651fa225cbcSrjs    I830OutputPrivatePtr  intel_output = output->driver_private;
1652fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1653fa225cbcSrjs    char                  *name_prefix;
1654fa225cbcSrjs    char                  *name_suffix;
1655fa225cbcSrjs
1656fa225cbcSrjs    if (dev_priv->output_device == SDVOB)
1657fa225cbcSrjs	name_suffix = "-1";
1658fa225cbcSrjs    else
1659fa225cbcSrjs	name_suffix = "-2";
1660fa225cbcSrjs
1661fa225cbcSrjs    /* clear up privates */
1662fa225cbcSrjs    dev_priv->is_tv = FALSE;
1663fa225cbcSrjs    intel_output->needs_tv_clock = FALSE;
1664fa225cbcSrjs    dev_priv->is_lvds = FALSE;
1665fa225cbcSrjs
1666fa225cbcSrjs    if (flag & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
1667fa225cbcSrjs    {
1668fa225cbcSrjs	if (flag & SDVO_OUTPUT_TMDS0)
1669fa225cbcSrjs	    dev_priv->controlled_output = SDVO_OUTPUT_TMDS0;
1670fa225cbcSrjs	else
1671fa225cbcSrjs	    dev_priv->controlled_output = SDVO_OUTPUT_TMDS1;
1672fa225cbcSrjs	output->subpixel_order = SubPixelHorizontalRGB;
1673fa225cbcSrjs	name_prefix="TMDS";
1674fa225cbcSrjs
1675fa225cbcSrjs	if (i830_sdvo_check_hdmi_encode (output))
1676fa225cbcSrjs	    name_prefix = "HDMI";
1677fa225cbcSrjs    }
1678fa225cbcSrjs    else if (flag & SDVO_OUTPUT_SVID0)
1679fa225cbcSrjs    {
1680fa225cbcSrjs	dev_priv->controlled_output = SDVO_OUTPUT_SVID0;
1681fa225cbcSrjs	output->subpixel_order = SubPixelHorizontalRGB; /* XXX */
1682fa225cbcSrjs	name_prefix="TV";
1683fa225cbcSrjs	dev_priv->is_tv = TRUE;
1684fa225cbcSrjs	intel_output->needs_tv_clock = TRUE;
1685fa225cbcSrjs    }
1686fa225cbcSrjs    else if (flag & SDVO_OUTPUT_CVBS0)
1687fa225cbcSrjs    {
1688fa225cbcSrjs	dev_priv->controlled_output = SDVO_OUTPUT_CVBS0;
1689fa225cbcSrjs	output->subpixel_order = SubPixelHorizontalRGB; /* XXX */
1690fa225cbcSrjs	name_prefix="TV";
1691fa225cbcSrjs	dev_priv->is_tv = TRUE;
1692fa225cbcSrjs	intel_output->needs_tv_clock = TRUE;
1693fa225cbcSrjs    }
1694fa225cbcSrjs    else if (flag & SDVO_OUTPUT_RGB0)
1695fa225cbcSrjs    {
1696fa225cbcSrjs	dev_priv->controlled_output = SDVO_OUTPUT_RGB0;
1697fa225cbcSrjs	output->subpixel_order = SubPixelHorizontalRGB;
1698fa225cbcSrjs	name_prefix="VGA";
1699fa225cbcSrjs    }
1700fa225cbcSrjs    else if (flag & SDVO_OUTPUT_RGB1)
1701fa225cbcSrjs    {
1702fa225cbcSrjs	dev_priv->controlled_output = SDVO_OUTPUT_RGB1;
1703fa225cbcSrjs	output->subpixel_order = SubPixelHorizontalRGB;
1704fa225cbcSrjs	name_prefix="VGA";
1705fa225cbcSrjs    } else if (flag & (SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1)) {
1706fa225cbcSrjs        if (flag & SDVO_OUTPUT_LVDS0)
1707fa225cbcSrjs            dev_priv->controlled_output = SDVO_OUTPUT_LVDS0;
1708fa225cbcSrjs        else
1709fa225cbcSrjs            dev_priv->controlled_output = SDVO_OUTPUT_LVDS1;
1710fa225cbcSrjs        output->subpixel_order = SubPixelHorizontalRGB;
1711fa225cbcSrjs        name_prefix="LVDS";
1712fa225cbcSrjs        dev_priv->is_lvds = TRUE;
1713fa225cbcSrjs    } else {
1714fa225cbcSrjs	unsigned char	bytes[2];
1715fa225cbcSrjs
1716fa225cbcSrjs	dev_priv->controlled_output = 0;
1717fa225cbcSrjs	memcpy (bytes, &flag, 2);
1718fa225cbcSrjs	xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_WARNING,
1719fa225cbcSrjs		   "%s: Unknown SDVO output type (0x%02x%02x)\n",
1720fa225cbcSrjs		   SDVO_NAME(dev_priv),
1721fa225cbcSrjs		   bytes[1], bytes[0]);
1722fa225cbcSrjs	name_prefix="Unknown";
1723fa225cbcSrjs    }
1724fa225cbcSrjs
1725fa225cbcSrjs    /* if exist origin name it will be freed in xf86OutputRename() */
1726fa225cbcSrjs    dev_priv->name = xalloc(strlen(name_prefix) + strlen(name_suffix) + 1);
1727fa225cbcSrjs    strcpy (dev_priv->name, name_prefix);
1728fa225cbcSrjs    strcat (dev_priv->name, name_suffix);
1729fa225cbcSrjs
1730fa225cbcSrjs    if (!xf86OutputRename (output, dev_priv->name))
1731fa225cbcSrjs    {
1732fa225cbcSrjs	xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_WARNING,
1733fa225cbcSrjs		"%s: Failed to rename output to %s\n",
1734fa225cbcSrjs		SDVO_NAME(dev_priv), dev_priv->name);
1735fa225cbcSrjs	xf86OutputDestroy (output);
1736fa225cbcSrjs	return FALSE;
1737fa225cbcSrjs    }
1738fa225cbcSrjs
1739fa225cbcSrjs    /* update randr_output's name */
1740fa225cbcSrjs    if (output->randr_output) {
1741fa225cbcSrjs	int nameLength = strlen(dev_priv->name);
1742fa225cbcSrjs	RROutputPtr randr_output = output->randr_output;
1743fa225cbcSrjs	char *name = xalloc(nameLength + 1);
1744fa225cbcSrjs
1745fa225cbcSrjs	if (name) {
1746fa225cbcSrjs	    if (randr_output->name != (char *) (randr_output + 1))
1747fa225cbcSrjs		xfree(randr_output->name);
1748fa225cbcSrjs	    randr_output->name = name;
1749fa225cbcSrjs	    randr_output->nameLength = nameLength;
1750fa225cbcSrjs	    memcpy(randr_output->name, dev_priv->name, nameLength);
1751fa225cbcSrjs	    randr_output->name[nameLength] = '\0';
1752fa225cbcSrjs	} else
1753fa225cbcSrjs	    xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_WARNING,
1754fa225cbcSrjs		   "%s: Failed to update RandR output name to %s\n",
1755fa225cbcSrjs		   SDVO_NAME(dev_priv), dev_priv->name);
1756fa225cbcSrjs    }
1757fa225cbcSrjs
1758fa225cbcSrjs    i830_sdvo_select_ddc_bus(dev_priv);
1759fa225cbcSrjs
1760fa225cbcSrjs    return TRUE;
1761fa225cbcSrjs}
1762fa225cbcSrjs
1763fa225cbcSrjsstatic Bool
1764fa225cbcSrjsi830_sdvo_multifunc_encoder(xf86OutputPtr output)
1765fa225cbcSrjs{
1766fa225cbcSrjs    I830OutputPrivatePtr  intel_output = output->driver_private;
1767fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1768fa225cbcSrjs    int caps = 0;
1769fa225cbcSrjs
1770fa225cbcSrjs    if (dev_priv->caps.output_flags & (SDVO_OUTPUT_TMDS0 |
1771fa225cbcSrjs		SDVO_OUTPUT_TMDS1))
1772fa225cbcSrjs	caps++;
1773fa225cbcSrjs    if (dev_priv->caps.output_flags & (SDVO_OUTPUT_RGB0 |
1774fa225cbcSrjs		SDVO_OUTPUT_RGB1))
1775fa225cbcSrjs	caps++;
1776fa225cbcSrjs    if (dev_priv->caps.output_flags & (SDVO_OUTPUT_CVBS0 |
1777fa225cbcSrjs		SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_YPRPB0 |
1778fa225cbcSrjs		SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_CVBS1 |
1779fa225cbcSrjs		SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_YPRPB1 |
1780fa225cbcSrjs		SDVO_OUTPUT_SCART1))
1781fa225cbcSrjs	caps++;
1782fa225cbcSrjs    if (dev_priv->caps.output_flags & (SDVO_OUTPUT_LVDS0 |
1783fa225cbcSrjs		SDVO_OUTPUT_LVDS1))
1784fa225cbcSrjs	caps++;
1785fa225cbcSrjs    return (caps > 1);
1786fa225cbcSrjs}
1787fa225cbcSrjs
1788fa225cbcSrjs/**
1789fa225cbcSrjs * Asks the SDVO device if any displays are currently connected.
1790fa225cbcSrjs *
1791fa225cbcSrjs * This interface will need to be augmented, since we could potentially have
1792fa225cbcSrjs * multiple displays connected, and the caller will also probably want to know
1793fa225cbcSrjs * what type of display is connected.  But this is enough for the moment.
1794fa225cbcSrjs *
1795fa225cbcSrjs * Takes 14ms on average on my i945G.
1796fa225cbcSrjs */
1797fa225cbcSrjsstatic xf86OutputStatus
1798fa225cbcSrjsi830_sdvo_detect(xf86OutputPtr output)
1799fa225cbcSrjs{
1800fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
1801fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1802fa225cbcSrjs    uint16_t response;
1803fa225cbcSrjs    uint8_t status;
1804fa225cbcSrjs
1805fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
1806fa225cbcSrjs    status = i830_sdvo_read_response(output, &response, 2);
1807fa225cbcSrjs
1808fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
1809fa225cbcSrjs	return XF86OutputStatusUnknown;
1810fa225cbcSrjs
1811fa225cbcSrjs    if (response == 0)
1812fa225cbcSrjs	return XF86OutputStatusDisconnected;
1813fa225cbcSrjs
1814fa225cbcSrjs    if (i830_sdvo_multifunc_encoder(output)) {
1815fa225cbcSrjs	if (dev_priv->attached_output != response) {
1816fa225cbcSrjs	    if (!i830_sdvo_output_setup(output, response))
1817fa225cbcSrjs		return XF86OutputStatusUnknown;
1818fa225cbcSrjs	    dev_priv->attached_output = response;
1819fa225cbcSrjs	}
1820fa225cbcSrjs    }
1821fa225cbcSrjs
1822fa225cbcSrjs    if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
1823fa225cbcSrjs    {
1824fa225cbcSrjs	xf86MonPtr edid_mon;
1825fa225cbcSrjs	/* Check EDID in DVI-I case */
1826fa225cbcSrjs	edid_mon = xf86OutputGetEDID (output, intel_output->pDDCBus);
1827fa225cbcSrjs	if (edid_mon && !DIGITAL(edid_mon->features.input_type)) {
1828fa225cbcSrjs	    xfree(edid_mon);
1829fa225cbcSrjs	    return XF86OutputStatusDisconnected;
1830fa225cbcSrjs	}
1831fa225cbcSrjs	xfree(edid_mon);
1832fa225cbcSrjs    }
1833fa225cbcSrjs    return XF86OutputStatusConnected;
1834fa225cbcSrjs}
1835fa225cbcSrjs
1836fa225cbcSrjsstatic DisplayModePtr
1837fa225cbcSrjsi830_sdvo_get_ddc_modes(xf86OutputPtr output)
1838fa225cbcSrjs{
1839fa225cbcSrjs    ScrnInfoPtr pScrn = output->scrn;
1840fa225cbcSrjs    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
1841fa225cbcSrjs    DisplayModePtr modes = NULL;
1842fa225cbcSrjs    xf86OutputPtr crt;
1843fa225cbcSrjs    I830OutputPrivatePtr intel_output =output->driver_private;
1844fa225cbcSrjs    xf86MonPtr edid_mon = NULL;
1845fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1846fa225cbcSrjs
1847fa225cbcSrjs    if (dev_priv->is_lvds)
1848fa225cbcSrjs        modes = i830_sdvo_lvds_fetch_modes(output);
1849fa225cbcSrjs    else
1850fa225cbcSrjs        modes = i830_ddc_get_modes(output);
1851fa225cbcSrjs
1852fa225cbcSrjs    if (modes != NULL)
1853fa225cbcSrjs	goto check_hdmi;
1854fa225cbcSrjs
1855fa225cbcSrjs    /* Mac mini hack.  On this device, I get DDC through the analog, which
1856fa225cbcSrjs     * load-detects as disconnected.  I fail to DDC through the SDVO DDC,
1857fa225cbcSrjs     * but it does load-detect as connected.  So, just steal the DDC bits from
1858fa225cbcSrjs     * analog when we fail at finding it the right way.
1859fa225cbcSrjs     */
1860fa225cbcSrjs    crt = xf86_config->output[0];
1861fa225cbcSrjs    intel_output = crt->driver_private;
1862fa225cbcSrjs    if (intel_output->type == I830_OUTPUT_ANALOG &&
1863fa225cbcSrjs	crt->funcs->detect(crt) == XF86OutputStatusDisconnected) {
1864fa225cbcSrjs	I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOA, "CRTDDC_A");
1865fa225cbcSrjs	edid_mon = xf86OutputGetEDID(crt, intel_output->pDDCBus);
1866fa225cbcSrjs	xf86DestroyI2CBusRec(intel_output->pDDCBus, TRUE, TRUE);
1867fa225cbcSrjs    }
1868fa225cbcSrjs    if (edid_mon) {
1869fa225cbcSrjs	xf86OutputSetEDID(output, edid_mon);
1870fa225cbcSrjs	modes = xf86OutputGetEDIDModes(output);
1871fa225cbcSrjs    }
1872fa225cbcSrjs
1873fa225cbcSrjscheck_hdmi:
1874fa225cbcSrjs    /* Check if HDMI encode, setup it and set the flag for HDMI audio */
1875fa225cbcSrjs    if (dev_priv->caps.output_flags & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
1876fa225cbcSrjs    {
1877fa225cbcSrjs	if (!i830_sdvo_check_hdmi_encode(output)) {
1878fa225cbcSrjs	    /* check EDID HDMI info for monitor */
1879fa225cbcSrjs	    if (output->MonInfo && xf86LoaderCheckSymbol("xf86MonitorIsHDMI")
1880fa225cbcSrjs		    && xf86MonitorIsHDMI(output->MonInfo)) {
1881fa225cbcSrjs		dev_priv->is_hdmi = TRUE;
1882fa225cbcSrjs		i830_sdvo_set_hdmi_encode (output);
1883fa225cbcSrjs	    } else
1884fa225cbcSrjs		dev_priv->is_hdmi = FALSE;
1885fa225cbcSrjs	}
1886fa225cbcSrjs    }
1887fa225cbcSrjs    return modes;
1888fa225cbcSrjs}
1889fa225cbcSrjs
1890fa225cbcSrjs/**
1891fa225cbcSrjs * Constructs a DisplayModeRec for the given widht/height/refresh, which will
1892fa225cbcSrjs * be programmed into the display pipe.  The TV encoder's scaler will filter
1893fa225cbcSrjs * this to the format actually required by the display.
1894fa225cbcSrjs */
1895fa225cbcSrjsstatic void
1896fa225cbcSrjsi830_sdvo_get_tv_mode(DisplayModePtr *head, int width, int height,
1897fa225cbcSrjs		      float refresh)
1898fa225cbcSrjs{
1899fa225cbcSrjs    DisplayModePtr mode;
1900fa225cbcSrjs
1901fa225cbcSrjs    mode = xcalloc(1, sizeof(*mode));
1902fa225cbcSrjs    if (mode == NULL)
1903fa225cbcSrjs	return;
1904fa225cbcSrjs
1905fa225cbcSrjs    mode->name = XNFprintf("%dx%d@%.2f", width, height, refresh);
1906fa225cbcSrjs    mode->HDisplay = width;
1907fa225cbcSrjs    mode->HSyncStart = width + 1;
1908fa225cbcSrjs    mode->HSyncEnd = width + 64;
1909fa225cbcSrjs    mode->HTotal = width + 96;
1910fa225cbcSrjs
1911fa225cbcSrjs    mode->VDisplay = height;
1912fa225cbcSrjs    mode->VSyncStart = height + 1;
1913fa225cbcSrjs    mode->VSyncEnd = height + 32;
1914fa225cbcSrjs    mode->VTotal = height + 33;
1915fa225cbcSrjs
1916fa225cbcSrjs    mode->Clock = (int) (refresh * mode->VTotal * mode->HTotal / 1000.0);
1917fa225cbcSrjs    mode->type = M_T_DRIVER;
1918fa225cbcSrjs    mode->next = NULL;
1919fa225cbcSrjs    mode->prev = NULL;
1920fa225cbcSrjs
1921fa225cbcSrjs    mode->next = *head;
1922fa225cbcSrjs    mode->prev = NULL;
1923fa225cbcSrjs    if (*head != NULL)
1924fa225cbcSrjs	(*head)->prev = mode;
1925fa225cbcSrjs    *head = mode;
1926fa225cbcSrjs}
1927fa225cbcSrjs
1928fa225cbcSrjs/**
1929fa225cbcSrjs * This function checks the current TV format, and chooses a default if
1930fa225cbcSrjs * it hasn't been set.
1931fa225cbcSrjs */
1932fa225cbcSrjsstatic void
1933fa225cbcSrjsi830_sdvo_check_tv_format(xf86OutputPtr output)
1934fa225cbcSrjs{
1935fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
1936fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1937fa225cbcSrjs    struct i830_sdvo_tv_format format;
1938fa225cbcSrjs    uint8_t status;
1939fa225cbcSrjs
1940fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0);
1941fa225cbcSrjs    status = i830_sdvo_read_response(output, &format, sizeof(format));
1942fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
1943fa225cbcSrjs	return;
1944fa225cbcSrjs
1945fa225cbcSrjs    memcpy(&dev_priv->tv_format, &format, sizeof(format));
1946fa225cbcSrjs}
1947fa225cbcSrjs
1948fa225cbcSrjsstatic DisplayModePtr
1949fa225cbcSrjsi830_sdvo_get_tv_modes(xf86OutputPtr output)
1950fa225cbcSrjs{
1951fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
1952fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1953fa225cbcSrjs    DisplayModePtr modes = NULL;
1954fa225cbcSrjs    struct i830_sdvo_sdtv_resolution_reply *res = &dev_priv->sdtv_resolutions;
1955fa225cbcSrjs    struct i830_sdvo_sdtv_resolution_request tv_res;
1956fa225cbcSrjs    uint8_t status;
1957fa225cbcSrjs    float refresh = 60; /* XXX */
1958fa225cbcSrjs
1959fa225cbcSrjs    i830_sdvo_check_tv_format(output);
1960fa225cbcSrjs
1961fa225cbcSrjs    /* Read the list of supported input resolutions for the selected TV format.
1962fa225cbcSrjs     */
1963fa225cbcSrjs    memset(&tv_res, 0, sizeof(tv_res));
1964fa225cbcSrjs    memcpy(&tv_res, &dev_priv->tv_format, sizeof(tv_res));
1965fa225cbcSrjs    i830_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
1966fa225cbcSrjs	    &tv_res, sizeof(tv_res));
1967fa225cbcSrjs    status = i830_sdvo_read_response(output, res, sizeof(*res));
1968fa225cbcSrjs    if (status != SDVO_CMD_STATUS_SUCCESS)
1969fa225cbcSrjs	return NULL;
1970fa225cbcSrjs
1971fa225cbcSrjs    if (res->res_320x200) i830_sdvo_get_tv_mode(&modes, 320, 200, refresh);
1972fa225cbcSrjs    if (res->res_320x240) i830_sdvo_get_tv_mode(&modes, 320, 240, refresh);
1973fa225cbcSrjs    if (res->res_400x300) i830_sdvo_get_tv_mode(&modes, 400, 300, refresh);
1974fa225cbcSrjs    if (res->res_640x350) i830_sdvo_get_tv_mode(&modes, 640, 350, refresh);
1975fa225cbcSrjs    if (res->res_640x400) i830_sdvo_get_tv_mode(&modes, 640, 400, refresh);
1976fa225cbcSrjs    if (res->res_640x480) i830_sdvo_get_tv_mode(&modes, 640, 480, refresh);
1977fa225cbcSrjs    if (res->res_704x480) i830_sdvo_get_tv_mode(&modes, 704, 480, refresh);
1978fa225cbcSrjs    if (res->res_704x576) i830_sdvo_get_tv_mode(&modes, 704, 576, refresh);
1979fa225cbcSrjs    if (res->res_720x350) i830_sdvo_get_tv_mode(&modes, 720, 350, refresh);
1980fa225cbcSrjs    if (res->res_720x400) i830_sdvo_get_tv_mode(&modes, 720, 400, refresh);
1981fa225cbcSrjs    if (res->res_720x480) i830_sdvo_get_tv_mode(&modes, 720, 480, refresh);
1982fa225cbcSrjs    if (res->res_720x540) i830_sdvo_get_tv_mode(&modes, 720, 540, refresh);
1983fa225cbcSrjs    if (res->res_720x576) i830_sdvo_get_tv_mode(&modes, 720, 576, refresh);
1984fa225cbcSrjs    if (res->res_800x600) i830_sdvo_get_tv_mode(&modes, 800, 600, refresh);
1985fa225cbcSrjs    if (res->res_832x624) i830_sdvo_get_tv_mode(&modes, 832, 624, refresh);
1986fa225cbcSrjs    if (res->res_920x766) i830_sdvo_get_tv_mode(&modes, 920, 766, refresh);
1987fa225cbcSrjs    if (res->res_1024x768) i830_sdvo_get_tv_mode(&modes, 1024, 768, refresh);
1988fa225cbcSrjs    if (res->res_1280x1024) i830_sdvo_get_tv_mode(&modes, 1280, 1024, refresh);
1989fa225cbcSrjs
1990fa225cbcSrjs    return modes;
1991fa225cbcSrjs}
1992fa225cbcSrjs
1993fa225cbcSrjsstatic DisplayModePtr
1994fa225cbcSrjsi830_sdvo_get_modes(xf86OutputPtr output)
1995fa225cbcSrjs{
1996fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
1997fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
1998fa225cbcSrjs
1999fa225cbcSrjs    if (dev_priv->is_tv)
2000fa225cbcSrjs	return i830_sdvo_get_tv_modes(output);
2001fa225cbcSrjs    else
2002fa225cbcSrjs	return i830_sdvo_get_ddc_modes(output);
2003fa225cbcSrjs}
2004fa225cbcSrjs
2005fa225cbcSrjsstatic void
2006fa225cbcSrjsi830_sdvo_destroy (xf86OutputPtr output)
2007fa225cbcSrjs{
2008fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
2009fa225cbcSrjs
2010fa225cbcSrjs    if (intel_output)
2011fa225cbcSrjs    {
2012fa225cbcSrjs	struct i830_sdvo_priv	*dev_priv = intel_output->dev_priv;
2013fa225cbcSrjs
2014fa225cbcSrjs	xf86DestroyI2CBusRec (intel_output->pDDCBus, FALSE, FALSE);
2015fa225cbcSrjs	xf86DestroyI2CDevRec (&dev_priv->d, FALSE);
2016fa225cbcSrjs	xf86DestroyI2CBusRec (dev_priv->d.pI2CBus, TRUE, TRUE);
2017fa225cbcSrjs	free(dev_priv->name);
2018fa225cbcSrjs
2019fa225cbcSrjs	if (output->randr_output) {
2020fa225cbcSrjs	    RROutputPtr	randr_output = output->randr_output;
2021fa225cbcSrjs	    if (randr_output->name &&
2022fa225cbcSrjs		    randr_output->name != (char *) (randr_output + 1))
2023fa225cbcSrjs		xfree(randr_output->name);
2024fa225cbcSrjs	}
2025fa225cbcSrjs
2026fa225cbcSrjs	if (dev_priv->sdvo_lvds_fixed_mode)
2027fa225cbcSrjs		xf86DeleteMode(&dev_priv->sdvo_lvds_fixed_mode,
2028fa225cbcSrjs			dev_priv->sdvo_lvds_fixed_mode);
2029fa225cbcSrjs
2030fa225cbcSrjs	xfree (intel_output);
2031fa225cbcSrjs    }
2032fa225cbcSrjs}
2033fa225cbcSrjs
2034fa225cbcSrjs#ifdef RANDR_GET_CRTC_INTERFACE
2035fa225cbcSrjsstatic xf86CrtcPtr
2036fa225cbcSrjsi830_sdvo_get_crtc(xf86OutputPtr output)
2037fa225cbcSrjs{
2038fa225cbcSrjs    ScrnInfoPtr	pScrn = output->scrn;
2039fa225cbcSrjs    I830Ptr pI830 = I830PTR(pScrn);
2040fa225cbcSrjs    I830OutputPrivatePtr intel_output = output->driver_private;
2041fa225cbcSrjs    struct i830_sdvo_priv *dev_priv = intel_output->dev_priv;
2042fa225cbcSrjs    int pipe = !!(INREG(dev_priv->output_device) & SDVO_PIPE_B_SELECT);
2043fa225cbcSrjs
2044fa225cbcSrjs    return i830_pipe_to_crtc(pScrn, pipe);
2045fa225cbcSrjs}
2046fa225cbcSrjs#endif
2047fa225cbcSrjs
2048fa225cbcSrjsstatic void
2049fa225cbcSrjsi830_sdvo_create_resources(xf86OutputPtr output)
2050fa225cbcSrjs{
2051fa225cbcSrjs    ScrnInfoPtr                 pScrn = output->scrn;
2052fa225cbcSrjs    I830Ptr                     pI830 = I830PTR(pScrn);
2053fa225cbcSrjs    I830OutputPrivatePtr        intel_output = output->driver_private;
2054fa225cbcSrjs    struct i830_sdvo_priv       *dev_priv = intel_output->dev_priv;
2055fa225cbcSrjs    INT32			broadcast_range[2];
2056fa225cbcSrjs    int                         err;
2057fa225cbcSrjs
2058fa225cbcSrjs    /* only R G B are 8bit color mode */
2059fa225cbcSrjs    if (pScrn->depth != 24 ||
2060fa225cbcSrjs        /* only 965G and G4X platform */
2061fa225cbcSrjs        !(IS_I965G(pI830) || IS_G4X(pI830)) ||
2062fa225cbcSrjs        /* only TMDS encoding */
2063fa225cbcSrjs        !(strstr(output->name, "TMDS") || strstr(output->name, "HDMI")))
2064fa225cbcSrjs        return;
2065fa225cbcSrjs
2066fa225cbcSrjs    broadcast_atom =
2067fa225cbcSrjs        MakeAtom("BROADCAST_RGB", sizeof("BROADCAST_RGB") - 1, TRUE);
2068fa225cbcSrjs
2069fa225cbcSrjs    broadcast_range[0] = 0;
2070fa225cbcSrjs    broadcast_range[1] = 1;
2071fa225cbcSrjs    err = RRConfigureOutputProperty(output->randr_output,
2072fa225cbcSrjs                                    broadcast_atom,
2073fa225cbcSrjs                                    FALSE, TRUE, FALSE, 2, broadcast_range);
2074fa225cbcSrjs    if (err != 0) {
2075fa225cbcSrjs        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
2076fa225cbcSrjs                   "RRConfigureOutputProperty error, %d\n", err);
2077fa225cbcSrjs        return;
2078fa225cbcSrjs    }
2079fa225cbcSrjs    /* Set the current value of the broadcast property as full range */
2080fa225cbcSrjs    dev_priv->broadcast_rgb = 0;
2081fa225cbcSrjs    err = RRChangeOutputProperty(output->randr_output,
2082fa225cbcSrjs                                 broadcast_atom,
2083fa225cbcSrjs                                 XA_INTEGER, 32, PropModeReplace,
2084fa225cbcSrjs                                 1, &dev_priv->broadcast_rgb,
2085fa225cbcSrjs                                 FALSE, TRUE);
2086fa225cbcSrjs    if (err != 0) {
2087fa225cbcSrjs        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
2088fa225cbcSrjs                   "RRChangeOutputProperty error, %d\n", err);
2089fa225cbcSrjs        return;
2090fa225cbcSrjs    }
2091fa225cbcSrjs}
2092fa225cbcSrjs
2093fa225cbcSrjsstatic Bool
2094fa225cbcSrjsi830_sdvo_set_property(xf86OutputPtr output, Atom property,
2095fa225cbcSrjs		       RRPropertyValuePtr value)
2096fa225cbcSrjs{
2097fa225cbcSrjs    ScrnInfoPtr             pScrn = output->scrn;
2098fa225cbcSrjs    I830Ptr                 pI830 = I830PTR(pScrn);
2099fa225cbcSrjs    I830OutputPrivatePtr    intel_output = output->driver_private;
2100fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv = intel_output->dev_priv;
2101fa225cbcSrjs    uint32_t temp;
2102fa225cbcSrjs
2103fa225cbcSrjs    if (property == broadcast_atom) {
2104fa225cbcSrjs        uint32_t val;
2105fa225cbcSrjs
2106fa225cbcSrjs        if (value->type != XA_INTEGER || value->format != 32 ||
2107fa225cbcSrjs            value->size != 1)
2108fa225cbcSrjs        {
2109fa225cbcSrjs            return FALSE;
2110fa225cbcSrjs        }
2111fa225cbcSrjs
2112fa225cbcSrjs        val = *(INT32 *)value->data;
2113fa225cbcSrjs        if (val < 0 || val > 1)
2114fa225cbcSrjs        {
2115fa225cbcSrjs            return FALSE;
2116fa225cbcSrjs        }
2117fa225cbcSrjs        if (val == dev_priv->broadcast_rgb)
2118fa225cbcSrjs            return TRUE;
2119fa225cbcSrjs
2120fa225cbcSrjs        temp = INREG(dev_priv->output_device);
2121fa225cbcSrjs
2122fa225cbcSrjs        if (val == 1)
2123fa225cbcSrjs            temp |= SDVO_COLOR_NOT_FULL_RANGE;
2124fa225cbcSrjs        else if (val == 0)
2125fa225cbcSrjs            temp &= ~SDVO_COLOR_NOT_FULL_RANGE;
2126fa225cbcSrjs
2127fa225cbcSrjs        i830_sdvo_write_sdvox(output, temp);
2128fa225cbcSrjs
2129fa225cbcSrjs        dev_priv->broadcast_rgb = val;
2130fa225cbcSrjs    }
2131fa225cbcSrjs    return TRUE;
2132fa225cbcSrjs}
2133fa225cbcSrjs
2134fa225cbcSrjsstatic const xf86OutputFuncsRec i830_sdvo_output_funcs = {
2135fa225cbcSrjs    .create_resources = i830_sdvo_create_resources,
2136fa225cbcSrjs    .dpms = i830_sdvo_dpms,
2137fa225cbcSrjs    .save = i830_sdvo_save,
2138fa225cbcSrjs    .restore = i830_sdvo_restore,
2139fa225cbcSrjs    .mode_valid = i830_sdvo_mode_valid,
2140fa225cbcSrjs    .mode_fixup = i830_sdvo_mode_fixup,
2141fa225cbcSrjs    .prepare = i830_output_prepare,
2142fa225cbcSrjs    .mode_set = i830_sdvo_mode_set,
2143fa225cbcSrjs    .commit = i830_output_commit,
2144fa225cbcSrjs    .detect = i830_sdvo_detect,
2145fa225cbcSrjs    .get_modes = i830_sdvo_get_modes,
2146fa225cbcSrjs    .set_property = i830_sdvo_set_property,
2147fa225cbcSrjs    .destroy = i830_sdvo_destroy,
2148fa225cbcSrjs#ifdef RANDR_GET_CRTC_INTERFACE
2149fa225cbcSrjs    .get_crtc = i830_sdvo_get_crtc,
2150fa225cbcSrjs#endif
2151fa225cbcSrjs};
2152fa225cbcSrjs
2153fa225cbcSrjsstatic unsigned int count_bits(uint32_t mask)
2154fa225cbcSrjs{
2155fa225cbcSrjs    unsigned int n;
2156fa225cbcSrjs
2157fa225cbcSrjs    for (n = 0; mask; n++)
2158fa225cbcSrjs	mask &= mask - 1;
2159fa225cbcSrjs
2160fa225cbcSrjs    return n;
2161fa225cbcSrjs}
2162fa225cbcSrjs
2163fa225cbcSrjs/**
2164fa225cbcSrjs * Choose the appropriate DDC bus for control bus switch command for this
2165fa225cbcSrjs * SDVO output based on the controlled output.
2166fa225cbcSrjs *
2167fa225cbcSrjs * DDC bus number assignment is in a priority order of RGB outputs, then TMDS
2168fa225cbcSrjs * outputs, then LVDS outputs.
2169fa225cbcSrjs */
2170fa225cbcSrjsstatic void
2171fa225cbcSrjsi830_sdvo_select_ddc_bus(struct i830_sdvo_priv *dev_priv)
2172fa225cbcSrjs{
2173fa225cbcSrjs    uint16_t mask = 0;
2174fa225cbcSrjs    unsigned int num_bits;
2175fa225cbcSrjs
2176fa225cbcSrjs    /* Make a mask of outputs less than or equal to our own priority in the
2177fa225cbcSrjs     * list.
2178fa225cbcSrjs     */
2179fa225cbcSrjs    switch (dev_priv->controlled_output) {
2180fa225cbcSrjs    case SDVO_OUTPUT_LVDS1:
2181fa225cbcSrjs	mask |= SDVO_OUTPUT_LVDS1;
2182fa225cbcSrjs    case SDVO_OUTPUT_LVDS0:
2183fa225cbcSrjs	mask |= SDVO_OUTPUT_LVDS0;
2184fa225cbcSrjs    case SDVO_OUTPUT_TMDS1:
2185fa225cbcSrjs	mask |= SDVO_OUTPUT_TMDS1;
2186fa225cbcSrjs    case SDVO_OUTPUT_TMDS0:
2187fa225cbcSrjs	mask |= SDVO_OUTPUT_TMDS0;
2188fa225cbcSrjs    case SDVO_OUTPUT_RGB1:
2189fa225cbcSrjs	mask |= SDVO_OUTPUT_RGB1;
2190fa225cbcSrjs    case SDVO_OUTPUT_RGB0:
2191fa225cbcSrjs	mask |= SDVO_OUTPUT_RGB0;
2192fa225cbcSrjs	break;
2193fa225cbcSrjs    }
2194fa225cbcSrjs
2195fa225cbcSrjs    /* Count bits to find what number we are in the priority list. */
2196fa225cbcSrjs    mask &= dev_priv->caps.output_flags;
2197fa225cbcSrjs    num_bits = count_bits(mask);
2198fa225cbcSrjs    if (num_bits > 3) {
2199fa225cbcSrjs	/* if more than 3 outputs, default to DDC bus 3 for now */
2200fa225cbcSrjs	num_bits = 3;
2201fa225cbcSrjs    }
2202fa225cbcSrjs
2203fa225cbcSrjs    /* Corresponds to SDVO_CONTROL_BUS_DDCx */
2204fa225cbcSrjs    dev_priv->ddc_bus = 1 << num_bits;
2205fa225cbcSrjs}
2206fa225cbcSrjs/**
2207fa225cbcSrjs * find the slave address for the given SDVO port based on the info
2208fa225cbcSrjs * parsed in general definition blocks
2209fa225cbcSrjs * If the slave address is found in the SDVO device info parsed from
2210fa225cbcSrjs * VBT,it will be returned. Otherwise it will return the slave address
2211fa225cbcSrjs * by the following steps.
2212fa225cbcSrjs * and 0x72 for SDVOC port.
2213fa225cbcSrjs * a. If one SDVO device info is found in another DVO port, it will return
2214fa225cbcSrjs * the slave address that is not used. For example: if 0x70 is used,
2215fa225cbcSrjs * then 0x72 is returned.
2216fa225cbcSrjs * b. If no SDVO device info is found in another DVO port, it will return
2217fa225cbcSrjs * 0x70 for SDVOB and 0x72 for SDVOC port.
2218fa225cbcSrjs */
2219fa225cbcSrjsstatic
2220fa225cbcSrjsvoid i830_find_sdvo_slave(ScrnInfoPtr pScrn, int output_device,
2221fa225cbcSrjs			  uint8_t *slave_addr)
2222fa225cbcSrjs{
2223fa225cbcSrjs    uint8_t temp_slave_addr;
2224fa225cbcSrjs    I830Ptr pI830 = I830PTR(pScrn);
2225fa225cbcSrjs    uint8_t dvo_port, dvo2_port;
2226fa225cbcSrjs    struct sdvo_device_mapping *p_mapping;
2227fa225cbcSrjs
2228fa225cbcSrjs    if (output_device == SDVOB) {
2229fa225cbcSrjs	/* DEVICE_PORT_DVOB */
2230fa225cbcSrjs	dvo_port = 0;
2231fa225cbcSrjs	dvo2_port = 1;
2232fa225cbcSrjs    } else {
2233fa225cbcSrjs	/* DEVICE_POTR_DVOC */
2234fa225cbcSrjs	dvo_port = 1;
2235fa225cbcSrjs	dvo2_port = 0;
2236fa225cbcSrjs    }
2237fa225cbcSrjs
2238fa225cbcSrjs    p_mapping = &(pI830->sdvo_mappings[dvo_port]);
2239fa225cbcSrjs    temp_slave_addr = p_mapping->slave_addr;
2240fa225cbcSrjs    if (temp_slave_addr) {
2241fa225cbcSrjs	/* slave address is found . return it */
2242fa225cbcSrjs	*slave_addr = temp_slave_addr;
2243fa225cbcSrjs	return ;
2244fa225cbcSrjs    }
2245fa225cbcSrjs    /* Check whether the SDVO device info is found in another dvo port */
2246fa225cbcSrjs    p_mapping = &(pI830->sdvo_mappings[dvo2_port]);
2247fa225cbcSrjs    temp_slave_addr = p_mapping->slave_addr;
2248fa225cbcSrjs    if (!temp_slave_addr) {
2249fa225cbcSrjs	/* no SDVO device is found in another DVO port */
2250fa225cbcSrjs	/* it will return 0x70 for SDVOB and 0x72 for SDVOC */
2251fa225cbcSrjs	if (output_device == SDVOB)
2252fa225cbcSrjs		temp_slave_addr = 0x70;
2253fa225cbcSrjs	else
2254fa225cbcSrjs		temp_slave_addr = 0x72;
2255fa225cbcSrjs	*slave_addr = temp_slave_addr;
2256fa225cbcSrjs	return ;
2257fa225cbcSrjs    }
2258fa225cbcSrjs    /* return the slave address that is not used.
2259fa225cbcSrjs     * If the 0x70 is used, then 0x72 is returned.
2260fa225cbcSrjs     * If the 0x72 is used, then 0x70 is returned.
2261fa225cbcSrjs     */
2262fa225cbcSrjs    if (temp_slave_addr == 0x70)
2263fa225cbcSrjs	temp_slave_addr = 0x72;
2264fa225cbcSrjs    else
2265fa225cbcSrjs	temp_slave_addr = 0x70;
2266fa225cbcSrjs
2267fa225cbcSrjs    *slave_addr = temp_slave_addr;
2268fa225cbcSrjs    return ;
2269fa225cbcSrjs}
2270fa225cbcSrjsBool
2271fa225cbcSrjsi830_sdvo_init(ScrnInfoPtr pScrn, int output_device)
2272fa225cbcSrjs{
2273fa225cbcSrjs    xf86OutputPtr	    output;
2274fa225cbcSrjs    I830OutputPrivatePtr    intel_output;
2275fa225cbcSrjs    struct i830_sdvo_priv   *dev_priv;
2276fa225cbcSrjs    int			    i;
2277fa225cbcSrjs    unsigned char	    ch[0x40];
2278fa225cbcSrjs    I2CBusPtr		    i2cbus = NULL, ddcbus;
2279fa225cbcSrjs    uint8_t slave_addr;
2280fa225cbcSrjs
2281fa225cbcSrjs    slave_addr = 0;
2282fa225cbcSrjs    i830_find_sdvo_slave(pScrn, output_device, &slave_addr);
2283fa225cbcSrjs
2284fa225cbcSrjs    output = xf86OutputCreate (pScrn, &i830_sdvo_output_funcs,NULL);
2285fa225cbcSrjs    if (!output)
2286fa225cbcSrjs	return FALSE;
2287fa225cbcSrjs    intel_output = xnfcalloc (sizeof (I830OutputPrivateRec) +
2288fa225cbcSrjs			      sizeof (struct i830_sdvo_priv), 1);
2289fa225cbcSrjs    if (!intel_output)
2290fa225cbcSrjs    {
2291fa225cbcSrjs	xf86OutputDestroy (output);
2292fa225cbcSrjs	return FALSE;
2293fa225cbcSrjs    }
2294fa225cbcSrjs    output->driver_private = intel_output;
2295fa225cbcSrjs    dev_priv = (struct i830_sdvo_priv *) (intel_output + 1);
2296fa225cbcSrjs    intel_output->dev_priv = dev_priv;
2297fa225cbcSrjs
2298fa225cbcSrjs    output->interlaceAllowed = FALSE;
2299fa225cbcSrjs    output->doubleScanAllowed = FALSE;
2300fa225cbcSrjs
2301fa225cbcSrjs    intel_output->type = I830_OUTPUT_SDVO;
2302fa225cbcSrjs    intel_output->pipe_mask = ((1 << 0) | (1 << 1));
2303fa225cbcSrjs    intel_output->clone_mask = (1 << I830_OUTPUT_SDVO);
2304fa225cbcSrjs
2305fa225cbcSrjs    /* While it's the same bus, we just initialize a new copy to avoid trouble
2306fa225cbcSrjs     * with tracking refcounting ourselves, since the XFree86 DDX bits don't.
2307fa225cbcSrjs     */
2308fa225cbcSrjs    if (output_device == SDVOB)
2309fa225cbcSrjs	I830I2CInit(pScrn, &i2cbus, GPIOE, "SDVOCTRL_E for SDVOB");
2310fa225cbcSrjs    else
2311fa225cbcSrjs	I830I2CInit(pScrn, &i2cbus, GPIOE, "SDVOCTRL_E for SDVOC");
2312fa225cbcSrjs
2313fa225cbcSrjs    if (i2cbus == NULL)
2314fa225cbcSrjs    {
2315fa225cbcSrjs	xf86OutputDestroy (output);
2316fa225cbcSrjs	return FALSE;
2317fa225cbcSrjs    }
2318fa225cbcSrjs    if (output_device == SDVOB) {
2319fa225cbcSrjs	dev_priv->d.DevName = "SDVO Controller B";
2320fa225cbcSrjs    } else {
2321fa225cbcSrjs	dev_priv->d.DevName = "SDVO Controller C";
2322fa225cbcSrjs    }
2323fa225cbcSrjs    dev_priv->d.SlaveAddr = slave_addr;
2324fa225cbcSrjs    dev_priv->d.pI2CBus = i2cbus;
2325fa225cbcSrjs    dev_priv->d.DriverPrivate.ptr = output;
2326fa225cbcSrjs    dev_priv->output_device = output_device;
2327fa225cbcSrjs
2328fa225cbcSrjs    if (!xf86I2CDevInit(&dev_priv->d))
2329fa225cbcSrjs    {
2330fa225cbcSrjs	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
2331fa225cbcSrjs		   "Failed to initialize %s I2C device\n",
2332fa225cbcSrjs		   SDVO_NAME(dev_priv));
2333fa225cbcSrjs	xf86OutputDestroy (output);
2334fa225cbcSrjs	return FALSE;
2335fa225cbcSrjs    }
2336fa225cbcSrjs
2337fa225cbcSrjs    intel_output->pI2CBus = i2cbus;
2338fa225cbcSrjs
2339fa225cbcSrjs    /* Read the regs to test if we can talk to the device */
2340fa225cbcSrjs    for (i = 0; i < 0x40; i++) {
2341fa225cbcSrjs	if (!i830_sdvo_read_byte_quiet(output, i, &ch[i])) {
2342fa225cbcSrjs	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
2343fa225cbcSrjs		       "No SDVO device found on SDVO%c\n",
2344fa225cbcSrjs		       output_device == SDVOB ? 'B' : 'C');
2345fa225cbcSrjs	    xf86OutputDestroy (output);
2346fa225cbcSrjs	    return FALSE;
2347fa225cbcSrjs	}
2348fa225cbcSrjs    }
2349fa225cbcSrjs
2350fa225cbcSrjs    /* Set up our wrapper I2C bus for DDC.  It acts just like the regular I2C
2351fa225cbcSrjs     * bus, except that it does the control bus switch to DDC mode before every
2352fa225cbcSrjs     * Start.  While we only need to do it at Start after every Stop after a
2353fa225cbcSrjs     * Start, extra attempts should be harmless.
2354fa225cbcSrjs     */
2355fa225cbcSrjs    ddcbus = xf86CreateI2CBusRec();
2356fa225cbcSrjs    if (ddcbus == NULL)
2357fa225cbcSrjs    {
2358fa225cbcSrjs	xf86OutputDestroy (output);
2359fa225cbcSrjs	return FALSE;
2360fa225cbcSrjs    }
2361fa225cbcSrjs    if (output_device == SDVOB)
2362fa225cbcSrjs        ddcbus->BusName = "SDVOB DDC Bus";
2363fa225cbcSrjs    else
2364fa225cbcSrjs        ddcbus->BusName = "SDVOC DDC Bus";
2365fa225cbcSrjs    ddcbus->scrnIndex = i2cbus->scrnIndex;
2366fa225cbcSrjs    ddcbus->I2CGetByte = i830_sdvo_ddc_i2c_get_byte;
2367fa225cbcSrjs    ddcbus->I2CPutByte = i830_sdvo_ddc_i2c_put_byte;
2368fa225cbcSrjs    ddcbus->I2CStart = i830_sdvo_ddc_i2c_start;
2369fa225cbcSrjs    ddcbus->I2CStop = i830_sdvo_ddc_i2c_stop;
2370fa225cbcSrjs    ddcbus->I2CAddress = i830_sdvo_ddc_i2c_address;
2371fa225cbcSrjs    ddcbus->DriverPrivate.ptr = output;
2372fa225cbcSrjs    dev_priv->ddc_bus_switch = TRUE;
2373fa225cbcSrjs
2374fa225cbcSrjs    if (!xf86I2CBusInit(ddcbus))
2375fa225cbcSrjs    {
2376fa225cbcSrjs	xf86OutputDestroy (output);
2377fa225cbcSrjs	return FALSE;
2378fa225cbcSrjs    }
2379fa225cbcSrjs
2380fa225cbcSrjs    intel_output->pI2CBus = i2cbus;
2381fa225cbcSrjs    intel_output->pDDCBus = ddcbus;
2382fa225cbcSrjs    intel_output->dev_priv = dev_priv;
2383fa225cbcSrjs
2384fa225cbcSrjs    if (!i830_sdvo_get_capabilities(output, &dev_priv->caps))
2385fa225cbcSrjs    {
2386fa225cbcSrjs	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
2387fa225cbcSrjs		   "Failed to get %s capabilities\n",
2388fa225cbcSrjs		   SDVO_NAME(dev_priv));
2389fa225cbcSrjs	xf86OutputDestroy (output);
2390fa225cbcSrjs	return FALSE;
2391fa225cbcSrjs    }
2392fa225cbcSrjs
2393fa225cbcSrjs    if (!i830_sdvo_output_setup (output, dev_priv->caps.output_flags))
2394fa225cbcSrjs	return FALSE;
2395fa225cbcSrjs
2396fa225cbcSrjs    /* Set the input timing to the screen. Assume always input 0. */
2397fa225cbcSrjs    i830_sdvo_set_target_input(output, TRUE, FALSE);
2398fa225cbcSrjs
2399fa225cbcSrjs    i830_sdvo_get_input_pixel_clock_range(output, &dev_priv->pixel_clock_min,
2400fa225cbcSrjs					  &dev_priv->pixel_clock_max);
2401fa225cbcSrjs
2402fa225cbcSrjs    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
2403fa225cbcSrjs	       "%s: device VID/DID: %02X:%02X.%02X, "
2404fa225cbcSrjs	       "clock range %.1fMHz - %.1fMHz\n",
2405fa225cbcSrjs	       SDVO_NAME(dev_priv),
2406fa225cbcSrjs	       dev_priv->caps.vendor_id, dev_priv->caps.device_id,
2407fa225cbcSrjs	       dev_priv->caps.device_rev_id,
2408fa225cbcSrjs	       dev_priv->pixel_clock_min / 1000.0,
2409fa225cbcSrjs	       dev_priv->pixel_clock_max / 1000.0);
2410fa225cbcSrjs
2411fa225cbcSrjs    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
2412fa225cbcSrjs	       "%s: %d input channel%s\n",
2413fa225cbcSrjs	       SDVO_NAME(dev_priv), dev_priv->caps.sdvo_input_count,
2414fa225cbcSrjs	       dev_priv->caps.sdvo_input_count >= 2 ? "s" : "");
2415fa225cbcSrjs
2416fa225cbcSrjs#define REPORT_OUTPUT_FLAG(flag, name) do {				\
2417fa225cbcSrjs    if (dev_priv->caps.output_flags & flag) {				\
2418fa225cbcSrjs	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s: %s output reported\n", \
2419fa225cbcSrjs		   SDVO_NAME(dev_priv), name);				\
2420fa225cbcSrjs    }									\
2421fa225cbcSrjs} while (0)
2422fa225cbcSrjs
2423fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_TMDS0, "TMDS0");
2424fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_RGB0, "RGB0");
2425fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_CVBS0, "CVBS0");
2426fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_SVID0, "SVID0");
2427fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_YPRPB0, "YPRPB0");
2428fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_SCART0, "SCART0");
2429fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_LVDS0, "LVDS0");
2430fa225cbcSrjs
2431fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_TMDS1, "TMDS1");
2432fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_RGB1, "RGB1");
2433fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_CVBS1, "CVBS1");
2434fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_SVID1, "SVID1");
2435fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_YPRPB1, "YPRPB1");
2436fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_SCART1, "SCART1");
2437fa225cbcSrjs    REPORT_OUTPUT_FLAG(SDVO_OUTPUT_LVDS1, "LVDS1");
2438fa225cbcSrjs
2439fa225cbcSrjs    return TRUE;
2440fa225cbcSrjs}
2441