intel_hdmi.c revision 9dff6af860d6b7f661d4360eb859837afaca0a1b
17d57382e65994ab7d01741373bd1c420370aed9fEric Anholt/*
27d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * Copyright 2006 Dave Airlie <airlied@linux.ie>
37d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * Copyright © 2006-2009 Intel Corporation
47d57382e65994ab7d01741373bd1c420370aed9fEric Anholt *
57d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * Permission is hereby granted, free of charge, to any person obtaining a
67d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * copy of this software and associated documentation files (the "Software"),
77d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * to deal in the Software without restriction, including without limitation
87d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * the rights to use, copy, modify, merge, publish, distribute, sublicense,
97d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * and/or sell copies of the Software, and to permit persons to whom the
107d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * Software is furnished to do so, subject to the following conditions:
117d57382e65994ab7d01741373bd1c420370aed9fEric Anholt *
127d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * The above copyright notice and this permission notice (including the next
137d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * paragraph) shall be included in all copies or substantial portions of the
147d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * Software.
157d57382e65994ab7d01741373bd1c420370aed9fEric Anholt *
167d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
197d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
207d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
217d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
227d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * DEALINGS IN THE SOFTWARE.
237d57382e65994ab7d01741373bd1c420370aed9fEric Anholt *
247d57382e65994ab7d01741373bd1c420370aed9fEric Anholt * Authors:
257d57382e65994ab7d01741373bd1c420370aed9fEric Anholt *	Eric Anholt <eric@anholt.net>
267d57382e65994ab7d01741373bd1c420370aed9fEric Anholt *	Jesse Barnes <jesse.barnes@intel.com>
277d57382e65994ab7d01741373bd1c420370aed9fEric Anholt */
287d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
297d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include <linux/i2c.h>
307d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include <linux/delay.h>
317d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "drmP.h"
327d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "drm.h"
337d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "drm_crtc.h"
347d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "intel_drv.h"
357d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "i915_drm.h"
367d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "i915_drv.h"
377d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
387d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstruct intel_hdmi_priv {
397d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 sdvox_reg;
407d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 save_SDVOX;
419dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	bool has_hdmi_sink;
427d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
437d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
447d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_mode_set(struct drm_encoder *encoder,
457d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				struct drm_display_mode *mode,
467d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				struct drm_display_mode *adjusted_mode)
477d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
487d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = encoder->dev;
497d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
507d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_crtc *crtc = encoder->crtc;
517d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
527d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = enc_to_intel_output(encoder);
537d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
547d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 sdvox;
557d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
567d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	sdvox = SDVO_ENCODING_HDMI |
577d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		SDVO_BORDER_ENABLE |
587d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		SDVO_VSYNC_ACTIVE_HIGH |
597d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		SDVO_HSYNC_ACTIVE_HIGH;
607d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
617d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (hdmi_priv->has_hdmi_sink)
627d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		sdvox |= SDVO_AUDIO_ENABLE;
637d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
647d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (intel_crtc->pipe == 1)
657d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		sdvox |= SDVO_PIPE_B_SELECT;
667d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	I915_WRITE(hdmi_priv->sdvox_reg, sdvox);
687d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	POSTING_READ(hdmi_priv->sdvox_reg);
697d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
707d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
717d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
727d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
737d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = encoder->dev;
747d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
757d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = enc_to_intel_output(encoder);
767d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
777d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 temp;
787d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
797d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode != DRM_MODE_DPMS_ON) {
807d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		temp = I915_READ(hdmi_priv->sdvox_reg);
817d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE);
827d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	} else {
837d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		temp = I915_READ(hdmi_priv->sdvox_reg);
847d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		I915_WRITE(hdmi_priv->sdvox_reg, temp | SDVO_ENABLE);
857d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	}
867d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	POSTING_READ(hdmi_priv->sdvox_reg);
877d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
887d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
897d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_save(struct drm_connector *connector)
907d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
917d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = connector->dev;
927d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
937d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = to_intel_output(connector);
947d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
957d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
967d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	hdmi_priv->save_SDVOX = I915_READ(hdmi_priv->sdvox_reg);
977d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
987d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
997d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_restore(struct drm_connector *connector)
1007d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
1017d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = connector->dev;
1027d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
1037d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = to_intel_output(connector);
1047d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
1057d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1067d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	I915_WRITE(hdmi_priv->sdvox_reg, hdmi_priv->save_SDVOX);
1077d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	POSTING_READ(hdmi_priv->sdvox_reg);
1087d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
1097d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1107d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic int intel_hdmi_mode_valid(struct drm_connector *connector,
1117d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				 struct drm_display_mode *mode)
1127d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
1137d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode->clock > 165000)
1147d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return MODE_CLOCK_HIGH;
1157d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode->clock < 20000)
1167d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return MODE_CLOCK_HIGH;
1177d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1187d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
1197d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return MODE_NO_DBLESCAN;
1207d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1217d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return MODE_OK;
1227d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
1237d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1247d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
1257d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				  struct drm_display_mode *mode,
1267d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				  struct drm_display_mode *adjusted_mode)
1277d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
1287d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return true;
1297d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
1307d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1319dff6af860d6b7f661d4360eb859837afaca0a1bMa Lingstatic void
1329dff6af860d6b7f661d4360eb859837afaca0a1bMa Lingintel_hdmi_sink_detect(struct drm_connector *connector)
1339dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling{
1349dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	struct intel_output *intel_output = to_intel_output(connector);
1359dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
1369dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	struct edid *edid = NULL;
1379dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling
1389dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	edid = drm_get_edid(&intel_output->base,
1399dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling			    &intel_output->ddc_bus->adapter);
1409dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	if (edid != NULL) {
1419dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling		hdmi_priv->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
1429dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling		kfree(edid);
1439dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling		intel_output->base.display_info.raw_edid = NULL;
1449dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	}
1459dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling}
1469dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling
1477d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic enum drm_connector_status
1487d57382e65994ab7d01741373bd1c420370aed9fEric Anholtintel_hdmi_detect(struct drm_connector *connector)
1497d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
1507d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = connector->dev;
1517d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
1527d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = to_intel_output(connector);
1537d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
1547d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 temp, bit;
1557d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1567d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	temp = I915_READ(PORT_HOTPLUG_EN);
1577d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1587d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	I915_WRITE(PORT_HOTPLUG_EN,
1597d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		   temp |
1607d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		   HDMIB_HOTPLUG_INT_EN |
1617d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		   HDMIC_HOTPLUG_INT_EN |
1627d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		   HDMID_HOTPLUG_INT_EN);
1637d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1647d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	POSTING_READ(PORT_HOTPLUG_EN);
1657d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1667d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	switch (hdmi_priv->sdvox_reg) {
1677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	case SDVOB:
1687d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		bit = HDMIB_HOTPLUG_INT_STATUS;
1697d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		break;
1707d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	case SDVOC:
1717d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		bit = HDMIC_HOTPLUG_INT_STATUS;
1727d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		break;
1737d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	default:
1747d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return connector_status_unknown;
1757d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	}
1767d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1779dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	if ((I915_READ(PORT_HOTPLUG_STAT) & bit) != 0) {
1789dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling		intel_hdmi_sink_detect(connector);
1797d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return connector_status_connected;
1809dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	} else
1817d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return connector_status_disconnected;
1827d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
1837d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1847d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic int intel_hdmi_get_modes(struct drm_connector *connector)
1857d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
1867d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = to_intel_output(connector);
1877d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1887d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	/* We should parse the EDID data and find out if it's an HDMI sink so
1897d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 * we can send audio to it.
1907d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 */
1917d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1927d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return intel_ddc_get_modes(intel_output);
1937d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
1947d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1957d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_destroy(struct drm_connector *connector)
1967d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
1977d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output = to_intel_output(connector);
1987d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
1997d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (intel_output->i2c_bus)
2007d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		intel_i2c_destroy(intel_output->i2c_bus);
2017d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_sysfs_connector_remove(connector);
2027d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_cleanup(connector);
2037d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	kfree(intel_output);
2047d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
2057d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2067d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
2077d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.dpms = intel_hdmi_dpms,
2087d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.mode_fixup = intel_hdmi_mode_fixup,
2097d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.prepare = intel_encoder_prepare,
2107d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.mode_set = intel_hdmi_mode_set,
2117d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.commit = intel_encoder_commit,
2127d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
2137d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2147d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_connector_funcs intel_hdmi_connector_funcs = {
2157d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.save = intel_hdmi_save,
2167d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.restore = intel_hdmi_restore,
2177d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.detect = intel_hdmi_detect,
2187d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.fill_modes = drm_helper_probe_single_connector_modes,
2197d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.destroy = intel_hdmi_destroy,
2207d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
2217d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2227d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
2237d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.get_modes = intel_hdmi_get_modes,
2247d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.mode_valid = intel_hdmi_mode_valid,
2257d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.best_encoder = intel_best_encoder,
2267d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
2277d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2287d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_enc_destroy(struct drm_encoder *encoder)
2297d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
2307d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_encoder_cleanup(encoder);
2317d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
2327d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2337d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
2347d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.destroy = intel_hdmi_enc_destroy,
2357d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
2367d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2377d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2387d57382e65994ab7d01741373bd1c420370aed9fEric Anholtvoid intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
2397d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
2407d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
2417d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_connector *connector;
2427d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_output *intel_output;
2437d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_hdmi_priv *hdmi_priv;
2447d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2457d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	intel_output = kcalloc(sizeof(struct intel_output) +
2467d57382e65994ab7d01741373bd1c420370aed9fEric Anholt			       sizeof(struct intel_hdmi_priv), 1, GFP_KERNEL);
2477d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (!intel_output)
2487d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return;
2497d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	hdmi_priv = (struct intel_hdmi_priv *)(intel_output + 1);
2507d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2517d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	connector = &intel_output->base;
2527d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
2537d57382e65994ab7d01741373bd1c420370aed9fEric Anholt			   DRM_MODE_CONNECTOR_DVID);
2547d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
2557d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2567d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	intel_output->type = INTEL_OUTPUT_HDMI;
2577d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2587d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	connector->interlace_allowed = 0;
2597d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	connector->doublescan_allowed = 0;
2607d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2617d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	/* Set up the DDC bus. */
2627d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (sdvox_reg == SDVOB)
2637d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB");
2647d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	else
2657d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC");
2667d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (!intel_output->ddc_bus)
2687d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		goto err_connector;
2697d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2707d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	hdmi_priv->sdvox_reg = sdvox_reg;
2717d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	intel_output->dev_priv = hdmi_priv;
2727d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2737d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_encoder_init(dev, &intel_output->enc, &intel_hdmi_enc_funcs,
2747d57382e65994ab7d01741373bd1c420370aed9fEric Anholt			 DRM_MODE_ENCODER_TMDS);
2757d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_encoder_helper_add(&intel_output->enc, &intel_hdmi_helper_funcs);
2767d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2777d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_mode_connector_attach_encoder(&intel_output->base,
2787d57382e65994ab7d01741373bd1c420370aed9fEric Anholt					  &intel_output->enc);
2797d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_sysfs_connector_add(connector);
2807d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2817d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
2827d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 * 0xd.  Failure to do so will result in spurious interrupts being
2837d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 * generated on the port when a cable is not attached.
2847d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 */
2857d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (IS_G4X(dev) && !IS_GM45(dev)) {
2867d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		u32 temp = I915_READ(PEG_BAND_GAP_DATA);
2877d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
2887d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	}
2897d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2907d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return;
2917d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2927d57382e65994ab7d01741373bd1c420370aed9fEric Anholterr_connector:
2937d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_cleanup(connector);
2947d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	kfree(intel_output);
2957d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2967d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return;
2977d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
298