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>
305a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
317d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include <linux/delay.h>
327d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "drmP.h"
337d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "drm.h"
347d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "drm_crtc.h"
35aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packard#include "drm_edid.h"
367d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "intel_drv.h"
377d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "i915_drm.h"
387d57382e65994ab7d01741373bd1c420370aed9fEric Anholt#include "i915_drv.h"
397d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
40ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilsonstruct intel_hdmi {
41ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	struct intel_encoder base;
427d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 sdvox_reg;
43f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson	int ddc_bus;
44e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson	uint32_t color_range;
459dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	bool has_hdmi_sink;
462e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang	bool has_audio;
47b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang	enum hdmi_force_audio force_audio;
4845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	void (*write_infoframe)(struct drm_encoder *encoder,
4945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes				struct dip_infoframe *frame);
507d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
517d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
52ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilsonstatic struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
53ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson{
544ef69c7a64b78d477d1666eba258ca049e8bac91Chris Wilson	return container_of(encoder, struct intel_hdmi, base.base);
55ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson}
56ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson
57df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilsonstatic struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
58df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson{
59df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson	return container_of(intel_attached_encoder(connector),
60df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson			    struct intel_hdmi, base);
61df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson}
62df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson
6345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesvoid intel_dip_infoframe_csum(struct dip_infoframe *frame)
643c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman{
6545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	uint8_t *data = (uint8_t *)frame;
663c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	uint8_t sum = 0;
673c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	unsigned i;
683c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
6945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	frame->checksum = 0;
7045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	frame->ecc = 0;
713c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
7264a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++)
733c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		sum += data[i];
743c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
7545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	frame->checksum = 0x100 - sum;
763c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman}
773c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
7845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesstatic u32 intel_infoframe_index(struct dip_infoframe *frame)
793c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman{
8045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	u32 flags = 0;
8145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
8245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	switch (frame->type) {
8345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	case DIP_TYPE_AVI:
8445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		flags |= VIDEO_DIP_SELECT_AVI;
8545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		break;
8645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	case DIP_TYPE_SPD:
8745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		flags |= VIDEO_DIP_SELECT_SPD;
8845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		break;
8945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	default:
9045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
9145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		break;
9245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	}
9345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
9445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	return flags;
9545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes}
9645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
9745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesstatic u32 intel_infoframe_flags(struct dip_infoframe *frame)
9845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes{
9945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	u32 flags = 0;
10045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
10145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	switch (frame->type) {
10245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	case DIP_TYPE_AVI:
10345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
10445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		break;
10545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	case DIP_TYPE_SPD:
10664a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes		flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC;
10745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		break;
10845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	default:
10945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
11045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		break;
11145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	}
11245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
11345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	return flags;
11445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes}
11545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
11645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesstatic void i9xx_write_infoframe(struct drm_encoder *encoder,
11745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes				 struct dip_infoframe *frame)
11845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes{
11945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	uint32_t *data = (uint32_t *)frame;
1203c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	struct drm_device *dev = encoder->dev;
1213c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	struct drm_i915_private *dev_priv = dev->dev_private;
1223c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
12345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
12445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	unsigned i, len = DIP_HEADER_SIZE + frame->len;
1253c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
1263c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
1273c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	/* XXX first guess at handling video port, is this corrent? */
1283c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	if (intel_hdmi->sdvox_reg == SDVOB)
1293c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		port = VIDEO_DIP_PORT_B;
1303c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	else if (intel_hdmi->sdvox_reg == SDVOC)
1313c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		port = VIDEO_DIP_PORT_C;
1323c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	else
1333c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		return;
1343c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
13545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	flags = intel_infoframe_index(frame);
13645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
13745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	val &= ~VIDEO_DIP_SELECT_MASK;
13845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
139c1230df7e19e0f27655c0eb9d966c7e03be7cc50Paulo Zanoni	I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
1403c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
14145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	for (i = 0; i < len; i += 4) {
1423c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		I915_WRITE(VIDEO_DIP_DATA, *data);
1433c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		data++;
1443c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	}
1453c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
14645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	flags |= intel_infoframe_flags(frame);
14745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
14845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
1493c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman}
1503c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
15145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesstatic void ironlake_write_infoframe(struct drm_encoder *encoder,
15245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes				     struct dip_infoframe *frame)
153b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes{
15445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	uint32_t *data = (uint32_t *)frame;
155b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	struct drm_device *dev = encoder->dev;
156b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	struct drm_i915_private *dev_priv = dev->dev_private;
157b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	struct drm_crtc *crtc = encoder->crtc;
158b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
159b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
16045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	unsigned i, len = DIP_HEADER_SIZE + frame->len;
16145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	u32 flags, val = I915_READ(reg);
162b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes
163b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	intel_wait_for_vblank(dev, intel_crtc->pipe);
164b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes
16545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	flags = intel_infoframe_index(frame);
166b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes
16764a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
16845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
16964a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
17045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
17145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	for (i = 0; i < len; i += 4) {
172b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
173b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes		data++;
174b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes	}
175b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes
17645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	flags |= intel_infoframe_flags(frame);
17745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
17845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
17945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes}
18045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesstatic void intel_set_infoframe(struct drm_encoder *encoder,
18145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes				struct dip_infoframe *frame)
18245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes{
18345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
18445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
18545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	if (!intel_hdmi->has_hdmi_sink)
18645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		return;
18745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
18845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	intel_dip_infoframe_csum(frame);
18945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	intel_hdmi->write_infoframe(encoder, frame);
19045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes}
19145187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
19245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnesstatic void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
19345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes{
19445187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	struct dip_infoframe avi_if = {
19545187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		.type = DIP_TYPE_AVI,
19645187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		.ver = DIP_VERSION_AVI,
19745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		.len = DIP_LEN_AVI,
19845187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	};
19945187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
20045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	intel_set_infoframe(encoder, &avi_if);
201b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes}
202b055c8f3ef9f7bc6ba415d900f298d7801a9d1d4Jesse Barnes
203c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnesstatic void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
204c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes{
205c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	struct dip_infoframe spd_if;
206c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes
207c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	memset(&spd_if, 0, sizeof(spd_if));
208c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	spd_if.type = DIP_TYPE_SPD;
209c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	spd_if.ver = DIP_VERSION_SPD;
210c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	spd_if.len = DIP_LEN_SPD;
211c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	strcpy(spd_if.body.spd.vn, "Intel");
212c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	strcpy(spd_if.body.spd.pd, "Integrated gfx");
213c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	spd_if.body.spd.sdi = DIP_SPD_PC;
214c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes
215c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	intel_set_infoframe(encoder, &spd_if);
216c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes}
217c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes
2187d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_mode_set(struct drm_encoder *encoder,
2197d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				struct drm_display_mode *mode,
2207d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				struct drm_display_mode *adjusted_mode)
2217d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
2227d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = encoder->dev;
2237d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
2247d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_crtc *crtc = encoder->crtc;
2257d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
226ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
2277d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 sdvox;
2287d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
229b599c0bca1e08a89a7fc4305bc84f4be30ada368Adam Jackson	sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
2305d4fac9716988dc7a26dddcd994f4dc7ee651e3cJesse Barnes	if (!HAS_PCH_SPLIT(dev))
2315d4fac9716988dc7a26dddcd994f4dc7ee651e3cJesse Barnes		sdvox |= intel_hdmi->color_range;
232b599c0bca1e08a89a7fc4305bc84f4be30ada368Adam Jackson	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
233b599c0bca1e08a89a7fc4305bc84f4be30ada368Adam Jackson		sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
234b599c0bca1e08a89a7fc4305bc84f4be30ada368Adam Jackson	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
235b599c0bca1e08a89a7fc4305bc84f4be30ada368Adam Jackson		sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
2367d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
237020f6704b5fbf687534ce53aeedc0364a995ae8aJesse Barnes	if (intel_crtc->bpp > 24)
238020f6704b5fbf687534ce53aeedc0364a995ae8aJesse Barnes		sdvox |= COLOR_FORMAT_12bpc;
239020f6704b5fbf687534ce53aeedc0364a995ae8aJesse Barnes	else
240020f6704b5fbf687534ce53aeedc0364a995ae8aJesse Barnes		sdvox |= COLOR_FORMAT_8bpc;
241020f6704b5fbf687534ce53aeedc0364a995ae8aJesse Barnes
2422e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang	/* Required on CPT */
2432e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang	if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
2442e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang		sdvox |= HDMI_MODE_SELECT;
2452e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang
2463c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	if (intel_hdmi->has_audio) {
247e0dac65ed45e72fe34cc7ccc76de0ba220bd38bbWu Fengguang		DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
248e0dac65ed45e72fe34cc7ccc76de0ba220bd38bbWu Fengguang				 pipe_name(intel_crtc->pipe));
2497d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		sdvox |= SDVO_AUDIO_ENABLE;
2503c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman		sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
251e0dac65ed45e72fe34cc7ccc76de0ba220bd38bbWu Fengguang		intel_write_eld(encoder, adjusted_mode);
2523c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman	}
2537d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
25475770564c90c45618003267f4cdde4bbc090f1bdJesse Barnes	if (HAS_PCH_CPT(dev))
25575770564c90c45618003267f4cdde4bbc090f1bdJesse Barnes		sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
25675770564c90c45618003267f4cdde4bbc090f1bdJesse Barnes	else if (intel_crtc->pipe == 1)
25775770564c90c45618003267f4cdde4bbc090f1bdJesse Barnes		sdvox |= SDVO_PIPE_B_SELECT;
2587d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
259ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
260ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	POSTING_READ(intel_hdmi->sdvox_reg);
2613c17fe4b8f40a112a85758a9ab2aebf772bdd647David Härdeman
26245187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes	intel_hdmi_set_avi_infoframe(encoder);
263c0864cb39c68696e80657360eba63da5e743b7aaJesse Barnes	intel_hdmi_set_spd_infoframe(encoder);
2647d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
2657d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
2667d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
2677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
2687d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_device *dev = encoder->dev;
2697d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
270ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
2717d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	u32 temp;
2722deed761188d7480eb5f7efbfe7aa77f09322ed8Wu Fengguang	u32 enable_bits = SDVO_ENABLE;
2732deed761188d7480eb5f7efbfe7aa77f09322ed8Wu Fengguang
2742deed761188d7480eb5f7efbfe7aa77f09322ed8Wu Fengguang	if (intel_hdmi->has_audio)
2752deed761188d7480eb5f7efbfe7aa77f09322ed8Wu Fengguang		enable_bits |= SDVO_AUDIO_ENABLE;
2767d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
277ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	temp = I915_READ(intel_hdmi->sdvox_reg);
278d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang
279d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
280d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	 * we do this anyway which shows more stable in testing.
281d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	 */
282c619eed4b2ee1b2bde3e02464eb81632a08bb976Eric Anholt	if (HAS_PCH_SPLIT(dev)) {
283ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson		I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
284ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson		POSTING_READ(intel_hdmi->sdvox_reg);
285d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	}
286d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang
287d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	if (mode != DRM_MODE_DPMS_ON) {
2882deed761188d7480eb5f7efbfe7aa77f09322ed8Wu Fengguang		temp &= ~enable_bits;
2897d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	} else {
2902deed761188d7480eb5f7efbfe7aa77f09322ed8Wu Fengguang		temp |= enable_bits;
2917d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	}
292d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang
293ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	I915_WRITE(intel_hdmi->sdvox_reg, temp);
294ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	POSTING_READ(intel_hdmi->sdvox_reg);
295d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang
296d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	/* HW workaround, need to write this twice for issue that may result
297d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	 * in first write getting masked.
298d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	 */
299c619eed4b2ee1b2bde3e02464eb81632a08bb976Eric Anholt	if (HAS_PCH_SPLIT(dev)) {
300ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson		I915_WRITE(intel_hdmi->sdvox_reg, temp);
301ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson		POSTING_READ(intel_hdmi->sdvox_reg);
302d8a2d0e00c0d5a0d55e14b884bff034205015e51Zhenyu Wang	}
3037d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
3047d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3057d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic int intel_hdmi_mode_valid(struct drm_connector *connector,
3067d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				 struct drm_display_mode *mode)
3077d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
3087d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode->clock > 165000)
3097d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return MODE_CLOCK_HIGH;
3107d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode->clock < 20000)
3115cbba41d2857477bd9a008f2d82c0622db1a8deeNicolas Kaiser		return MODE_CLOCK_LOW;
3127d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3137d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
3147d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return MODE_NO_DBLESCAN;
3157d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3167d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return MODE_OK;
3177d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
3187d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3197d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
3207d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				  struct drm_display_mode *mode,
3217d57382e65994ab7d01741373bd1c420370aed9fEric Anholt				  struct drm_display_mode *adjusted_mode)
3227d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
3237d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	return true;
3247d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
3257d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
326aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packardstatic enum drm_connector_status
327930a9e283516a3a3595c0c515113f1b78d07f695Chris Wilsonintel_hdmi_detect(struct drm_connector *connector, bool force)
3289dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling{
329df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
330f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson	struct drm_i915_private *dev_priv = connector->dev->dev_private;
331f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson	struct edid *edid;
332aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packard	enum drm_connector_status status = connector_status_disconnected;
3339dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling
334ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	intel_hdmi->has_hdmi_sink = false;
3352e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang	intel_hdmi->has_audio = false;
336f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson	edid = drm_get_edid(connector,
337f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson			    &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
3382ded9e2747d0a390d281bb5b16ff7f640ec85f78ling.ma@intel.com
339aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packard	if (edid) {
340be9f1c4f738a715abbd8ea742f3ec60a1ce73f4bEric Anholt		if (edid->input & DRM_EDID_INPUT_DIGITAL) {
341aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packard			status = connector_status_connected;
342b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang			if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
343b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang				intel_hdmi->has_hdmi_sink =
344b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang						drm_detect_hdmi_monitor(edid);
3452e3d6006aca163db3eeb931cec631974aaa3c293Zhenyu Wang			intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
346aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packard		}
347674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang		connector->display_info.raw_edid = NULL;
348aa93d632c496184e5b779dbcf961bf1c6ececf0bKeith Packard		kfree(edid);
3499dff6af860d6b7f661d4360eb859837afaca0a1bMa Ling	}
35030ad48b7334a2eb2edf22f6c91f7b3f22a22a837Zhenyu Wang
35155b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	if (status == connector_status_connected) {
352b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang		if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
353b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang			intel_hdmi->has_audio =
354b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang				(intel_hdmi->force_audio == HDMI_AUDIO_ON);
35555b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	}
35655b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
3572ded9e2747d0a390d281bb5b16ff7f640ec85f78ling.ma@intel.com	return status;
3587d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
3597d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3607d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic int intel_hdmi_get_modes(struct drm_connector *connector)
3617d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
362df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
363f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson	struct drm_i915_private *dev_priv = connector->dev->dev_private;
3647d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3657d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	/* We should parse the EDID data and find out if it's an HDMI sink so
3667d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 * we can send audio to it.
3677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 */
3687d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
369f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson	return intel_ddc_get_modes(connector,
370f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson				   &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
3717d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
3727d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
3731aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilsonstatic bool
3741aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilsonintel_hdmi_detect_audio(struct drm_connector *connector)
3751aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson{
3761aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
3771aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	struct drm_i915_private *dev_priv = connector->dev->dev_private;
3781aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	struct edid *edid;
3791aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	bool has_audio = false;
3801aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson
3811aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	edid = drm_get_edid(connector,
3821aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson			    &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
3831aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	if (edid) {
3841aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		if (edid->input & DRM_EDID_INPUT_DIGITAL)
3851aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson			has_audio = drm_detect_monitor_audio(edid);
3861aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson
3871aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		connector->display_info.raw_edid = NULL;
3881aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		kfree(edid);
3891aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	}
3901aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson
3911aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson	return has_audio;
3921aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson}
3931aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson
39455b7d6e8c4690047ac001026cb75a47f747db816Chris Wilsonstatic int
39555b7d6e8c4690047ac001026cb75a47f747db816Chris Wilsonintel_hdmi_set_property(struct drm_connector *connector,
39655b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson		      struct drm_property *property,
39755b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson		      uint64_t val)
39855b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson{
39955b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
400e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson	struct drm_i915_private *dev_priv = connector->dev->dev_private;
40155b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	int ret;
40255b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
40355b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	ret = drm_connector_property_set_value(connector, property, val);
40455b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	if (ret)
40555b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson		return ret;
40655b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
4073f43c48d333777e815ae68d66396cb6dfbc2dd79Chris Wilson	if (property == dev_priv->force_audio_property) {
408b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang		enum hdmi_force_audio i = val;
4091aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		bool has_audio;
4101aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson
4111aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		if (i == intel_hdmi->force_audio)
41255b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson			return 0;
41355b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
4141aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		intel_hdmi->force_audio = i;
41555b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
416b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang		if (i == HDMI_AUDIO_AUTO)
4171aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson			has_audio = intel_hdmi_detect_audio(connector);
4181aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		else
419b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang			has_audio = (i == HDMI_AUDIO_ON);
4201aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson
421b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang		if (i == HDMI_AUDIO_OFF_DVI)
422b1d7e4b41fd0f72ea8149056778db5d737739305Wu Fengguang			intel_hdmi->has_hdmi_sink = 0;
42355b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
4241aad7ac0458f40e2d0365d488620084f3965f6e7Chris Wilson		intel_hdmi->has_audio = has_audio;
42555b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson		goto done;
42655b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	}
42755b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
428e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson	if (property == dev_priv->broadcast_rgb_property) {
429e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson		if (val == !!intel_hdmi->color_range)
430e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson			return 0;
431e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson
432e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson		intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0;
433e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson		goto done;
434e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson	}
435e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson
43655b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	return -EINVAL;
43755b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
43855b7d6e8c4690047ac001026cb75a47f747db816Chris Wilsondone:
43955b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	if (intel_hdmi->base.base.crtc) {
44055b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson		struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
44155b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson		drm_crtc_helper_set_mode(crtc, &crtc->mode,
44255b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson					 crtc->x, crtc->y,
44355b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson					 crtc->fb);
44455b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	}
44555b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
44655b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	return 0;
44755b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson}
44855b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
4497d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic void intel_hdmi_destroy(struct drm_connector *connector)
4507d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
4517d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_sysfs_connector_remove(connector);
4527d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_cleanup(connector);
453674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang	kfree(connector);
4547d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
4557d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
4567d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
4577d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.dpms = intel_hdmi_dpms,
4587d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.mode_fixup = intel_hdmi_mode_fixup,
4597d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.prepare = intel_encoder_prepare,
4607d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.mode_set = intel_hdmi_mode_set,
4617d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.commit = intel_encoder_commit,
4627d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
4637d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
4647d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_connector_funcs intel_hdmi_connector_funcs = {
465c9fb15f60eb517c958dec64dca9357bf62bf2201Keith Packard	.dpms = drm_helper_connector_dpms,
4667d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.detect = intel_hdmi_detect,
4677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.fill_modes = drm_helper_probe_single_connector_modes,
46855b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	.set_property = intel_hdmi_set_property,
4697d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.destroy = intel_hdmi_destroy,
4707d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
4717d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
4727d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
4737d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.get_modes = intel_hdmi_get_modes,
4747d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	.mode_valid = intel_hdmi_mode_valid,
475df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson	.best_encoder = intel_best_encoder,
4767d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
4777d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
4787d57382e65994ab7d01741373bd1c420370aed9fEric Anholtstatic const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
479ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	.destroy = intel_encoder_destroy,
4807d57382e65994ab7d01741373bd1c420370aed9fEric Anholt};
4817d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
48255b7d6e8c4690047ac001026cb75a47f747db816Chris Wilsonstatic void
48355b7d6e8c4690047ac001026cb75a47f747db816Chris Wilsonintel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
48455b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson{
4853f43c48d333777e815ae68d66396cb6dfbc2dd79Chris Wilson	intel_attach_force_audio_property(connector);
486e953fd7bb32f55309a96abd5ceba9cf68d221434Chris Wilson	intel_attach_broadcast_rgb_property(connector);
48755b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson}
48855b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
4897d57382e65994ab7d01741373bd1c420370aed9fEric Anholtvoid intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
4907d57382e65994ab7d01741373bd1c420370aed9fEric Anholt{
4917d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_i915_private *dev_priv = dev->dev_private;
4927d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	struct drm_connector *connector;
49321d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt	struct intel_encoder *intel_encoder;
494674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang	struct intel_connector *intel_connector;
495ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	struct intel_hdmi *intel_hdmi;
49664a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	int i;
4977d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
498ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL);
499ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	if (!intel_hdmi)
5007d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		return;
501674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang
502674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang	intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
503674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang	if (!intel_connector) {
504ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson		kfree(intel_hdmi);
505674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang		return;
506674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang	}
507674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang
508ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	intel_encoder = &intel_hdmi->base;
509373a3cf744c774478f44921c50011b896ab08f9dChris Wilson	drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
510373a3cf744c774478f44921c50011b896ab08f9dChris Wilson			 DRM_MODE_ENCODER_TMDS);
511373a3cf744c774478f44921c50011b896ab08f9dChris Wilson
512674e2d0885e009c078d89f789f28f63374a4f337Zhenyu Wang	connector = &intel_connector->base;
5137d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
5148d91104aac6e21e6ca2a56124e2e47b0db043ea8Adam Jackson			   DRM_MODE_CONNECTOR_HDMIA);
5157d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
5167d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
51721d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt	intel_encoder->type = INTEL_OUTPUT_HDMI;
5187d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
519eb1f8e4f3be898df808e2dfc131099f5831d491dDave Airlie	connector->polled = DRM_CONNECTOR_POLL_HPD;
520c3febcc438ba0878b164c74310bd77c50dbb0ba8Peter Ross	connector->interlace_allowed = 1;
5217d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	connector->doublescan_allowed = 0;
52227f8227b1e2b326a9a0995dd9c1f14893c61ee01Jesse Barnes	intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
5237d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
5247d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	/* Set up the DDC bus. */
525f8aed700c6ec46ddade6570004ce25332283b306Ma Ling	if (sdvox_reg == SDVOB) {
52621d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt		intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
527f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson		intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
528b01f2c3a4a37d09a47ad73ccbb46d554d21cfeb0Jesse Barnes		dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
529f8aed700c6ec46ddade6570004ce25332283b306Ma Ling	} else if (sdvox_reg == SDVOC) {
53021d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt		intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
531f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson		intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
532b01f2c3a4a37d09a47ad73ccbb46d554d21cfeb0Jesse Barnes		dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
533f8aed700c6ec46ddade6570004ce25332283b306Ma Ling	} else if (sdvox_reg == HDMIB) {
53421d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt		intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
535f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson		intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
536b01f2c3a4a37d09a47ad73ccbb46d554d21cfeb0Jesse Barnes		dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
537f8aed700c6ec46ddade6570004ce25332283b306Ma Ling	} else if (sdvox_reg == HDMIC) {
53821d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt		intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
539f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson		intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
540b01f2c3a4a37d09a47ad73ccbb46d554d21cfeb0Jesse Barnes		dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
541f8aed700c6ec46ddade6570004ce25332283b306Ma Ling	} else if (sdvox_reg == HDMID) {
54221d40d37eca86872f2bf0af995809ebdef25c9d9Eric Anholt		intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
543f899fc64cda8569d0529452aafc0da31c042df2eChris Wilson		intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
544b01f2c3a4a37d09a47ad73ccbb46d554d21cfeb0Jesse Barnes		dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
545f8aed700c6ec46ddade6570004ce25332283b306Ma Ling	}
5467d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
547ea5b213ad4b161463e76b63dbb115ea20e2200f0Chris Wilson	intel_hdmi->sdvox_reg = sdvox_reg;
5487d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
54964a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	if (!HAS_PCH_SPLIT(dev)) {
55045187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		intel_hdmi->write_infoframe = i9xx_write_infoframe;
55164a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes		I915_WRITE(VIDEO_DIP_CTL, 0);
55264a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	} else {
55345187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes		intel_hdmi->write_infoframe = ironlake_write_infoframe;
55464a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes		for_each_pipe(i)
55564a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes			I915_WRITE(TVIDEO_DIP_CTL(i), 0);
55664a8fc0145a1d0fdc25fc9367c2e6c621955fb3bJesse Barnes	}
55745187ace97f7b3deb559b25348ccb7e301c158c9Jesse Barnes
5584ef69c7a64b78d477d1666eba258ca049e8bac91Chris Wilson	drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
5597d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
56055b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson	intel_hdmi_add_properties(intel_hdmi, connector);
56155b7d6e8c4690047ac001026cb75a47f747db816Chris Wilson
562df0e924883d029a8651a2a0c7b8da67a07611ed2Chris Wilson	intel_connector_attach_encoder(intel_connector, intel_encoder);
5637d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	drm_sysfs_connector_add(connector);
5647d57382e65994ab7d01741373bd1c420370aed9fEric Anholt
5657d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
5667d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 * 0xd.  Failure to do so will result in spurious interrupts being
5677d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 * generated on the port when a cable is not attached.
5687d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	 */
5697d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	if (IS_G4X(dev) && !IS_GM45(dev)) {
5707d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		u32 temp = I915_READ(PEG_BAND_GAP_DATA);
5717d57382e65994ab7d01741373bd1c420370aed9fEric Anholt		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
5727d57382e65994ab7d01741373bd1c420370aed9fEric Anholt	}
5737d57382e65994ab7d01741373bd1c420370aed9fEric Anholt}
574