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