1fa225cbcSrjs/* 2fa225cbcSrjs * Copyright © 2007 Intel Corporation 3fa225cbcSrjs * 4fa225cbcSrjs * Permission is hereby granted, free of charge, to any person obtaining a 5fa225cbcSrjs * copy of this software and associated documentation files (the "Software"), 6fa225cbcSrjs * to deal in the Software without restriction, including without limitation 7fa225cbcSrjs * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8fa225cbcSrjs * and/or sell copies of the Software, and to permit persons to whom the 9fa225cbcSrjs * Software is furnished to do so, subject to the following conditions: 10fa225cbcSrjs * 11fa225cbcSrjs * The above copyright notice and this permission notice (including the next 12fa225cbcSrjs * paragraph) shall be included in all copies or substantial portions of the 13fa225cbcSrjs * Software. 14fa225cbcSrjs * 15fa225cbcSrjs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16fa225cbcSrjs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17fa225cbcSrjs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18fa225cbcSrjs * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19fa225cbcSrjs * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20fa225cbcSrjs * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21fa225cbcSrjs * IN THE SOFTWARE. 22fa225cbcSrjs * 23fa225cbcSrjs * Authors: 24fa225cbcSrjs * Eric Anholt <eric@anholt.net> 25fa225cbcSrjs * 26fa225cbcSrjs */ 27fa225cbcSrjs 28fa225cbcSrjs#ifdef HAVE_CONFIG_H 29fa225cbcSrjs#include "config.h" 30fa225cbcSrjs#endif 31fa225cbcSrjs 32fa225cbcSrjs#include "xf86.h" 33fa225cbcSrjs#include "i830.h" 34fa225cbcSrjs#include "xf86Modes.h" 35fa225cbcSrjs#include "i830_display.h" 36fa225cbcSrjs#include "X11/Xatom.h" 37fa225cbcSrjs 38fa225cbcSrjsstruct i830_hdmi_priv { 39fa225cbcSrjs uint32_t output_reg; 40fa225cbcSrjs 41fa225cbcSrjs uint32_t save_SDVO; 42fa225cbcSrjs 43fa225cbcSrjs Bool has_hdmi_sink; 44fa225cbcSrjs /* Default 0 for full RGB range 0-255, 1 is for RGB range 16-235 */ 45fa225cbcSrjs uint32_t broadcast_rgb; 46fa225cbcSrjs}; 47fa225cbcSrjs 48fa225cbcSrjsstatic Atom broadcast_atom; 49fa225cbcSrjs 50fa225cbcSrjsstatic int 51fa225cbcSrjsi830_hdmi_mode_valid(xf86OutputPtr output, DisplayModePtr mode) 52fa225cbcSrjs{ 53fa225cbcSrjs if (mode->Clock > 165000) 54fa225cbcSrjs return MODE_CLOCK_HIGH; 55fa225cbcSrjs 56fa225cbcSrjs if (mode->Clock < 20000) 57fa225cbcSrjs return MODE_CLOCK_LOW; 58fa225cbcSrjs 59fa225cbcSrjs return MODE_OK; 60fa225cbcSrjs} 61fa225cbcSrjs 62fa225cbcSrjsstatic Bool 63fa225cbcSrjsi830_hdmi_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, 64fa225cbcSrjs DisplayModePtr adjusted_mode) 65fa225cbcSrjs{ 66fa225cbcSrjs /* The HDMI output doesn't need the pixel multiplication that SDVO does, 67fa225cbcSrjs * so no fixup. 68fa225cbcSrjs */ 69fa225cbcSrjs return TRUE; 70fa225cbcSrjs} 71fa225cbcSrjs 72fa225cbcSrjsstatic void 73fa225cbcSrjsi830_hdmi_mode_set(xf86OutputPtr output, DisplayModePtr mode, 74fa225cbcSrjs DisplayModePtr adjusted_mode) 75fa225cbcSrjs{ 76fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 77fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 78fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 79fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 80fa225cbcSrjs xf86CrtcPtr crtc = output->crtc; 81fa225cbcSrjs I830CrtcPrivatePtr intel_crtc = crtc->driver_private; 82fa225cbcSrjs uint32_t sdvox; 83fa225cbcSrjs 84fa225cbcSrjs sdvox = SDVO_ENCODING_HDMI | 85fa225cbcSrjs SDVO_BORDER_ENABLE | 86fa225cbcSrjs SDVO_VSYNC_ACTIVE_HIGH | 87fa225cbcSrjs SDVO_HSYNC_ACTIVE_HIGH; 88fa225cbcSrjs 89fa225cbcSrjs if (dev_priv->has_hdmi_sink) 90fa225cbcSrjs sdvox |= SDVO_AUDIO_ENABLE; 91fa225cbcSrjs 92fa225cbcSrjs if (intel_crtc->pipe == 1) 93fa225cbcSrjs sdvox |= SDVO_PIPE_B_SELECT; 94fa225cbcSrjs 95fa225cbcSrjs OUTREG(dev_priv->output_reg, sdvox); 96fa225cbcSrjs POSTING_READ(dev_priv->output_reg); 97fa225cbcSrjs} 98fa225cbcSrjs 99fa225cbcSrjsstatic void 100fa225cbcSrjsi830_hdmi_dpms(xf86OutputPtr output, int mode) 101fa225cbcSrjs{ 102fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 103fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 104fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 105fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 106fa225cbcSrjs uint32_t temp; 107fa225cbcSrjs 108fa225cbcSrjs if (mode == DPMSModeOff) { 109fa225cbcSrjs temp = INREG(dev_priv->output_reg); 110fa225cbcSrjs OUTREG(dev_priv->output_reg, temp & ~SDVO_ENABLE); 111fa225cbcSrjs } else { 112fa225cbcSrjs temp = INREG(dev_priv->output_reg); 113fa225cbcSrjs OUTREG(dev_priv->output_reg, temp | SDVO_ENABLE); 114fa225cbcSrjs } 115fa225cbcSrjs} 116fa225cbcSrjs 117fa225cbcSrjsstatic void 118fa225cbcSrjsi830_hdmi_save(xf86OutputPtr output) 119fa225cbcSrjs{ 120fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 121fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 122fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 123fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 124fa225cbcSrjs 125fa225cbcSrjs dev_priv->save_SDVO = INREG(dev_priv->output_reg); 126fa225cbcSrjs} 127fa225cbcSrjs 128fa225cbcSrjsstatic void 129fa225cbcSrjsi830_hdmi_restore(xf86OutputPtr output) 130fa225cbcSrjs{ 131fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 132fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 133fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 134fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 135fa225cbcSrjs 136fa225cbcSrjs OUTREG(dev_priv->output_reg, dev_priv->save_SDVO); 137fa225cbcSrjs} 138fa225cbcSrjs 139fa225cbcSrjs/** 140fa225cbcSrjs * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect HDMI connection. 141fa225cbcSrjs * 142fa225cbcSrjs * \return TRUE if HDMI port is connected. 143fa225cbcSrjs * \return FALSE if HDMI port is disconnected. 144fa225cbcSrjs */ 145fa225cbcSrjsstatic xf86OutputStatus 146fa225cbcSrjsi830_hdmi_detect(xf86OutputPtr output) 147fa225cbcSrjs{ 148fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 149fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 150fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 151fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 152fa225cbcSrjs uint32_t temp, bit; 153fa225cbcSrjs xf86OutputStatus status; 154fa225cbcSrjs xf86MonPtr edid_mon; 155fa225cbcSrjs 156fa225cbcSrjs dev_priv->has_hdmi_sink = FALSE; 157fa225cbcSrjs 158fa225cbcSrjs /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written 0xd. 159fa225cbcSrjs * Failure to do so will result in spurious interrupts being 160fa225cbcSrjs * generated on the port when a cable is not attached. 161fa225cbcSrjs */ 162fa225cbcSrjs if (IS_G4X(pI830) && !IS_GM45(pI830)) { 163fa225cbcSrjs temp = INREG(PEG_BAND_GAP_DATA); 164fa225cbcSrjs OUTREG(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); 165fa225cbcSrjs } 166fa225cbcSrjs 167fa225cbcSrjs temp = INREG(PORT_HOTPLUG_EN); 168fa225cbcSrjs 169fa225cbcSrjs switch (dev_priv->output_reg) { 170fa225cbcSrjs case SDVOB: 171fa225cbcSrjs temp |= HDMIB_HOTPLUG_INT_EN; 172fa225cbcSrjs break; 173fa225cbcSrjs case SDVOC: 174fa225cbcSrjs temp |= HDMIC_HOTPLUG_INT_EN; 175fa225cbcSrjs break; 176fa225cbcSrjs default: 177fa225cbcSrjs return XF86OutputStatusUnknown; 178fa225cbcSrjs } 179fa225cbcSrjs 180fa225cbcSrjs OUTREG(PORT_HOTPLUG_EN, temp); 181fa225cbcSrjs 182fa225cbcSrjs POSTING_READ(PORT_HOTPLUG_EN); 183fa225cbcSrjs 184fa225cbcSrjs i830WaitForVblank(pScrn); 185fa225cbcSrjs switch (dev_priv->output_reg) { 186fa225cbcSrjs case SDVOB: 187fa225cbcSrjs bit = HDMIB_HOTPLUG_INT_STATUS; 188fa225cbcSrjs break; 189fa225cbcSrjs case SDVOC: 190fa225cbcSrjs bit = HDMIC_HOTPLUG_INT_STATUS; 191fa225cbcSrjs break; 192fa225cbcSrjs default: 193fa225cbcSrjs return XF86OutputStatusUnknown; 194fa225cbcSrjs } 195fa225cbcSrjs 196fa225cbcSrjs if ((INREG(PORT_HOTPLUG_STAT) & bit) != 0) 197fa225cbcSrjs status = XF86OutputStatusConnected; 198fa225cbcSrjs else 199fa225cbcSrjs return XF86OutputStatusDisconnected; 200fa225cbcSrjs 201fa225cbcSrjs edid_mon = xf86OutputGetEDID (output, intel_output->pDDCBus); 202fa225cbcSrjs if (!edid_mon || !DIGITAL(edid_mon->features.input_type)) 203fa225cbcSrjs status = XF86OutputStatusDisconnected; 204fa225cbcSrjs 205fa225cbcSrjs if (xf86LoaderCheckSymbol("xf86MonitorIsHDMI") && 206fa225cbcSrjs xf86MonitorIsHDMI(edid_mon)) 207fa225cbcSrjs dev_priv->has_hdmi_sink = TRUE; 208fa225cbcSrjs 209fa225cbcSrjs if (pI830->debug_modes) 210fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_INFO, 211fa225cbcSrjs "%s monitor detected on HDMI-%d\n", 212fa225cbcSrjs dev_priv->has_hdmi_sink ? "HDMI" : "DVI", 213fa225cbcSrjs (dev_priv->output_reg == SDVOB) ? 1 : 2); 214fa225cbcSrjs 215fa225cbcSrjs xfree(edid_mon); 216fa225cbcSrjs return status; 217fa225cbcSrjs} 218fa225cbcSrjs 219fa225cbcSrjsstatic void 220fa225cbcSrjsi830_hdmi_destroy (xf86OutputPtr output) 221fa225cbcSrjs{ 222fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 223fa225cbcSrjs 224fa225cbcSrjs if (intel_output != NULL) { 225fa225cbcSrjs xf86DestroyI2CBusRec(intel_output->pDDCBus, FALSE, FALSE); 226fa225cbcSrjs xfree(intel_output); 227fa225cbcSrjs } 228fa225cbcSrjs} 229fa225cbcSrjs 230fa225cbcSrjsstatic void 231fa225cbcSrjsi830_hdmi_create_resources(xf86OutputPtr output) 232fa225cbcSrjs{ 233fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 234fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 235fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 236fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 237fa225cbcSrjs INT32 broadcast_range[2]; 238fa225cbcSrjs int err; 239fa225cbcSrjs 240fa225cbcSrjs /* only R G B are 8bit color mode */ 241fa225cbcSrjs if (pScrn->depth != 24 || 242fa225cbcSrjs /* only 965G and G4X platform */ 243fa225cbcSrjs !(IS_I965G(pI830) || IS_G4X(pI830))) 244fa225cbcSrjs return; 245fa225cbcSrjs 246fa225cbcSrjs broadcast_atom = 247fa225cbcSrjs MakeAtom("BROADCAST_RGB", sizeof("BROADCAST_RGB") - 1, TRUE); 248fa225cbcSrjs 249fa225cbcSrjs broadcast_range[0] = 0; 250fa225cbcSrjs broadcast_range[1] = 1; 251fa225cbcSrjs err = RRConfigureOutputProperty(output->randr_output, 252fa225cbcSrjs broadcast_atom, 253fa225cbcSrjs FALSE, TRUE, FALSE, 2, broadcast_range); 254fa225cbcSrjs if (err != 0) { 255fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 256fa225cbcSrjs "RRConfigureOutputProperty error, %d\n", err); 257fa225cbcSrjs return; 258fa225cbcSrjs } 259fa225cbcSrjs /* Set the current value of the broadcast property as full range */ 260fa225cbcSrjs dev_priv->broadcast_rgb = 0; 261fa225cbcSrjs err = RRChangeOutputProperty(output->randr_output, 262fa225cbcSrjs broadcast_atom, 263fa225cbcSrjs XA_INTEGER, 32, PropModeReplace, 264fa225cbcSrjs 1, &dev_priv->broadcast_rgb, 265fa225cbcSrjs FALSE, TRUE); 266fa225cbcSrjs if (err != 0) { 267fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 268fa225cbcSrjs "RRChangeOutputProperty error, %d\n", err); 269fa225cbcSrjs return; 270fa225cbcSrjs } 271fa225cbcSrjs} 272fa225cbcSrjs 273fa225cbcSrjsstatic Bool 274fa225cbcSrjsi830_hdmi_set_property(xf86OutputPtr output, Atom property, 275fa225cbcSrjs RRPropertyValuePtr value) 276fa225cbcSrjs{ 277fa225cbcSrjs ScrnInfoPtr pScrn = output->scrn; 278fa225cbcSrjs I830Ptr pI830 = I830PTR(pScrn); 279fa225cbcSrjs I830OutputPrivatePtr intel_output = output->driver_private; 280fa225cbcSrjs struct i830_hdmi_priv *dev_priv = intel_output->dev_priv; 281fa225cbcSrjs uint32_t temp; 282fa225cbcSrjs 283fa225cbcSrjs if (property == broadcast_atom) { 284fa225cbcSrjs uint32_t val; 285fa225cbcSrjs 286fa225cbcSrjs if (value->type != XA_INTEGER || value->format != 32 || 287fa225cbcSrjs value->size != 1) 288fa225cbcSrjs { 289fa225cbcSrjs return FALSE; 290fa225cbcSrjs } 291fa225cbcSrjs 292fa225cbcSrjs val = *(INT32 *)value->data; 293fa225cbcSrjs if (val < 0 || val > 1) 294fa225cbcSrjs { 295fa225cbcSrjs return FALSE; 296fa225cbcSrjs } 297fa225cbcSrjs if (val == dev_priv->broadcast_rgb) 298fa225cbcSrjs return TRUE; 299fa225cbcSrjs 300fa225cbcSrjs temp = INREG(dev_priv->output_reg); 301fa225cbcSrjs 302fa225cbcSrjs if (val == 1) 303fa225cbcSrjs temp |= SDVO_COLOR_NOT_FULL_RANGE; 304fa225cbcSrjs else if (val == 0) 305fa225cbcSrjs temp &= ~SDVO_COLOR_NOT_FULL_RANGE; 306fa225cbcSrjs 307fa225cbcSrjs OUTREG(dev_priv->output_reg, temp); 308fa225cbcSrjs dev_priv->broadcast_rgb = val; 309fa225cbcSrjs } 310fa225cbcSrjs return TRUE; 311fa225cbcSrjs} 312fa225cbcSrjs 313fa225cbcSrjsstatic const xf86OutputFuncsRec i830_hdmi_output_funcs = { 314fa225cbcSrjs .create_resources = i830_hdmi_create_resources, 315fa225cbcSrjs .dpms = i830_hdmi_dpms, 316fa225cbcSrjs .save = i830_hdmi_save, 317fa225cbcSrjs .restore = i830_hdmi_restore, 318fa225cbcSrjs .mode_valid = i830_hdmi_mode_valid, 319fa225cbcSrjs .mode_fixup = i830_hdmi_mode_fixup, 320fa225cbcSrjs .prepare = i830_output_prepare, 321fa225cbcSrjs .mode_set = i830_hdmi_mode_set, 322fa225cbcSrjs .commit = i830_output_commit, 323fa225cbcSrjs .detect = i830_hdmi_detect, 324fa225cbcSrjs .get_modes = i830_ddc_get_modes, 325fa225cbcSrjs .set_property = i830_hdmi_set_property, 326fa225cbcSrjs .destroy = i830_hdmi_destroy 327fa225cbcSrjs}; 328fa225cbcSrjs 329fa225cbcSrjsvoid 330fa225cbcSrjsi830_hdmi_init(ScrnInfoPtr pScrn, int output_reg) 331fa225cbcSrjs{ 332fa225cbcSrjs xf86OutputPtr output; 333fa225cbcSrjs I830OutputPrivatePtr intel_output; 334fa225cbcSrjs struct i830_hdmi_priv *dev_priv; 335fa225cbcSrjs 336fa225cbcSrjs output = xf86OutputCreate(pScrn, &i830_hdmi_output_funcs, 337fa225cbcSrjs (output_reg == SDVOB) ? "HDMI-1" : "HDMI-2"); 338fa225cbcSrjs if (!output) 339fa225cbcSrjs return; 340fa225cbcSrjs intel_output = xnfcalloc(sizeof (I830OutputPrivateRec) + 341fa225cbcSrjs sizeof (struct i830_hdmi_priv), 1); 342fa225cbcSrjs if (intel_output == NULL) { 343fa225cbcSrjs xf86OutputDestroy(output); 344fa225cbcSrjs return; 345fa225cbcSrjs } 346fa225cbcSrjs output->driver_private = intel_output; 347fa225cbcSrjs output->interlaceAllowed = FALSE; 348fa225cbcSrjs output->doubleScanAllowed = FALSE; 349fa225cbcSrjs 350fa225cbcSrjs dev_priv = (struct i830_hdmi_priv *)(intel_output + 1); 351fa225cbcSrjs dev_priv->output_reg = output_reg; 352fa225cbcSrjs dev_priv->has_hdmi_sink = FALSE; 353fa225cbcSrjs 354fa225cbcSrjs intel_output->dev_priv = dev_priv; 355fa225cbcSrjs intel_output->type = I830_OUTPUT_HDMI; 356fa225cbcSrjs intel_output->pipe_mask = ((1 << 0) | (1 << 1)); 357fa225cbcSrjs intel_output->clone_mask = (1 << I830_OUTPUT_HDMI); 358fa225cbcSrjs 359fa225cbcSrjs /* Set up the DDC bus. */ 360fa225cbcSrjs if (output_reg == SDVOB) 361fa225cbcSrjs I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOE, "HDMIDDC_B"); 362fa225cbcSrjs else 363fa225cbcSrjs I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOD, "HDMIDDC_C"); 364fa225cbcSrjs 365fa225cbcSrjs xf86DrvMsg(pScrn->scrnIndex, X_INFO, 366fa225cbcSrjs "HDMI output %d detected\n", 367fa225cbcSrjs (output_reg == SDVOB) ? 1 : 2); 368fa225cbcSrjs} 369