xref: /dflybsd-src/sys/dev/drm/i915/intel_hdmi.c (revision ba55f2f542af67c1331fd80f611891b0a29f57bc)
1e3adcf8fSFrançois Tigeot /*
2e3adcf8fSFrançois Tigeot  * Copyright 2006 Dave Airlie <airlied@linux.ie>
3e3adcf8fSFrançois Tigeot  * Copyright © 2006-2009 Intel Corporation
4e3adcf8fSFrançois Tigeot  *
5e3adcf8fSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
6e3adcf8fSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
7e3adcf8fSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
8e3adcf8fSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9e3adcf8fSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
10e3adcf8fSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
11e3adcf8fSFrançois Tigeot  *
12e3adcf8fSFrançois Tigeot  * The above copyright notice and this permission notice (including the next
13e3adcf8fSFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
14e3adcf8fSFrançois Tigeot  * Software.
15e3adcf8fSFrançois Tigeot  *
16e3adcf8fSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17e3adcf8fSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18e3adcf8fSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19e3adcf8fSFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20e3adcf8fSFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21e3adcf8fSFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22e3adcf8fSFrançois Tigeot  * DEALINGS IN THE SOFTWARE.
23e3adcf8fSFrançois Tigeot  *
24e3adcf8fSFrançois Tigeot  * Authors:
25e3adcf8fSFrançois Tigeot  *	Eric Anholt <eric@anholt.net>
26e3adcf8fSFrançois Tigeot  *	Jesse Barnes <jesse.barnes@intel.com>
27e3adcf8fSFrançois Tigeot  */
28e3adcf8fSFrançois Tigeot 
29a2fdbec6SFrançois Tigeot #include <linux/i2c.h>
30a2fdbec6SFrançois Tigeot #include <linux/delay.h>
319edbd4a0SFrançois Tigeot #include <linux/hdmi.h>
3218e26a6dSFrançois Tigeot #include <drm/drmP.h>
3318e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3418e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
3518e26a6dSFrançois Tigeot #include "intel_drv.h"
365c6c6f23SFrançois Tigeot #include <drm/i915_drm.h>
37e3adcf8fSFrançois Tigeot #include "i915_drv.h"
38e3adcf8fSFrançois Tigeot 
3919df918dSFrançois Tigeot static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi)
40e3adcf8fSFrançois Tigeot {
4119df918dSFrançois Tigeot 	return hdmi_to_dig_port(intel_hdmi)->base.base.dev;
4219df918dSFrançois Tigeot }
4319df918dSFrançois Tigeot 
4419df918dSFrançois Tigeot static void
4519df918dSFrançois Tigeot assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi)
4619df918dSFrançois Tigeot {
4719df918dSFrançois Tigeot 	struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi);
4819df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
4919df918dSFrançois Tigeot 	uint32_t enabled_bits;
5019df918dSFrançois Tigeot 
51a2fdbec6SFrançois Tigeot 	enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE;
5219df918dSFrançois Tigeot 
538e26cdf6SFrançois Tigeot 	WARN(I915_READ(intel_hdmi->hdmi_reg) & enabled_bits,
5419df918dSFrançois Tigeot 	     "HDMI port enabled, expecting disabled\n");
5519df918dSFrançois Tigeot }
5619df918dSFrançois Tigeot 
5719df918dSFrançois Tigeot struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
5819df918dSFrançois Tigeot {
5919df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
6019df918dSFrançois Tigeot 		container_of(encoder, struct intel_digital_port, base.base);
6119df918dSFrançois Tigeot 	return &intel_dig_port->hdmi;
62e3adcf8fSFrançois Tigeot }
63e3adcf8fSFrançois Tigeot 
64e3adcf8fSFrançois Tigeot static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
65e3adcf8fSFrançois Tigeot {
6619df918dSFrançois Tigeot 	return enc_to_intel_hdmi(&intel_attached_encoder(connector)->base);
67e3adcf8fSFrançois Tigeot }
68e3adcf8fSFrançois Tigeot 
699edbd4a0SFrançois Tigeot static u32 g4x_infoframe_index(enum hdmi_infoframe_type type)
70e3adcf8fSFrançois Tigeot {
719edbd4a0SFrançois Tigeot 	switch (type) {
729edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
7319df918dSFrançois Tigeot 		return VIDEO_DIP_SELECT_AVI;
749edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
7519df918dSFrançois Tigeot 		return VIDEO_DIP_SELECT_SPD;
769edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
779edbd4a0SFrançois Tigeot 		return VIDEO_DIP_SELECT_VENDOR;
78e3adcf8fSFrançois Tigeot 	default:
799edbd4a0SFrançois Tigeot 		DRM_DEBUG_DRIVER("unknown info frame type %d\n", type);
8019df918dSFrançois Tigeot 		return 0;
8119df918dSFrançois Tigeot 	}
82e3adcf8fSFrançois Tigeot }
83e3adcf8fSFrançois Tigeot 
849edbd4a0SFrançois Tigeot static u32 g4x_infoframe_enable(enum hdmi_infoframe_type type)
85e3adcf8fSFrançois Tigeot {
869edbd4a0SFrançois Tigeot 	switch (type) {
879edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
8819df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_AVI;
899edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
9019df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_SPD;
919edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
929edbd4a0SFrançois Tigeot 		return VIDEO_DIP_ENABLE_VENDOR;
93e3adcf8fSFrançois Tigeot 	default:
949edbd4a0SFrançois Tigeot 		DRM_DEBUG_DRIVER("unknown info frame type %d\n", type);
9519df918dSFrançois Tigeot 		return 0;
9619df918dSFrançois Tigeot 	}
97e3adcf8fSFrançois Tigeot }
98e3adcf8fSFrançois Tigeot 
999edbd4a0SFrançois Tigeot static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type)
10019df918dSFrançois Tigeot {
1019edbd4a0SFrançois Tigeot 	switch (type) {
1029edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
10319df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_AVI_HSW;
1049edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
10519df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_SPD_HSW;
1069edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
1079edbd4a0SFrançois Tigeot 		return VIDEO_DIP_ENABLE_VS_HSW;
10819df918dSFrançois Tigeot 	default:
1099edbd4a0SFrançois Tigeot 		DRM_DEBUG_DRIVER("unknown info frame type %d\n", type);
11019df918dSFrançois Tigeot 		return 0;
11119df918dSFrançois Tigeot 	}
112e3adcf8fSFrançois Tigeot }
113e3adcf8fSFrançois Tigeot 
1149edbd4a0SFrançois Tigeot static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type,
115*ba55f2f5SFrançois Tigeot 				  enum transcoder cpu_transcoder,
116*ba55f2f5SFrançois Tigeot 				  struct drm_i915_private *dev_priv)
11719df918dSFrançois Tigeot {
1189edbd4a0SFrançois Tigeot 	switch (type) {
1199edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
1208e26cdf6SFrançois Tigeot 		return HSW_TVIDEO_DIP_AVI_DATA(cpu_transcoder);
1219edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
1228e26cdf6SFrançois Tigeot 		return HSW_TVIDEO_DIP_SPD_DATA(cpu_transcoder);
1239edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
1249edbd4a0SFrançois Tigeot 		return HSW_TVIDEO_DIP_VS_DATA(cpu_transcoder);
12519df918dSFrançois Tigeot 	default:
1269edbd4a0SFrançois Tigeot 		DRM_DEBUG_DRIVER("unknown info frame type %d\n", type);
12719df918dSFrançois Tigeot 		return 0;
12819df918dSFrançois Tigeot 	}
12919df918dSFrançois Tigeot }
13019df918dSFrançois Tigeot 
13119df918dSFrançois Tigeot static void g4x_write_infoframe(struct drm_encoder *encoder,
1329edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
1339edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
134e3adcf8fSFrançois Tigeot {
1359edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
136e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
137e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
13819df918dSFrançois Tigeot 	u32 val = I915_READ(VIDEO_DIP_CTL);
1399edbd4a0SFrançois Tigeot 	int i;
140e3adcf8fSFrançois Tigeot 
14119df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
142e3adcf8fSFrançois Tigeot 
14319df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
1449edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
145e3adcf8fSFrançois Tigeot 
1469edbd4a0SFrançois Tigeot 	val &= ~g4x_infoframe_enable(type);
147e3adcf8fSFrançois Tigeot 
14819df918dSFrançois Tigeot 	I915_WRITE(VIDEO_DIP_CTL, val);
149e3adcf8fSFrançois Tigeot 
15019df918dSFrançois Tigeot 	mmiowb();
151e3adcf8fSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
152e3adcf8fSFrançois Tigeot 		I915_WRITE(VIDEO_DIP_DATA, *data);
153e3adcf8fSFrançois Tigeot 		data++;
154e3adcf8fSFrançois Tigeot 	}
15519df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
15619df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
15719df918dSFrançois Tigeot 		I915_WRITE(VIDEO_DIP_DATA, 0);
15819df918dSFrançois Tigeot 	mmiowb();
159e3adcf8fSFrançois Tigeot 
1609edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
16119df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
16219df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
163e3adcf8fSFrançois Tigeot 
16419df918dSFrançois Tigeot 	I915_WRITE(VIDEO_DIP_CTL, val);
16519df918dSFrançois Tigeot 	POSTING_READ(VIDEO_DIP_CTL);
166e3adcf8fSFrançois Tigeot }
167e3adcf8fSFrançois Tigeot 
16819df918dSFrançois Tigeot static void ibx_write_infoframe(struct drm_encoder *encoder,
1699edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
1709edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
171e3adcf8fSFrançois Tigeot {
1729edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
173e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
174e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
17519df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
1769edbd4a0SFrançois Tigeot 	int i, reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
17719df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
178e3adcf8fSFrançois Tigeot 
17919df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
180e3adcf8fSFrançois Tigeot 
181e3adcf8fSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
1829edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
183e3adcf8fSFrançois Tigeot 
1849edbd4a0SFrançois Tigeot 	val &= ~g4x_infoframe_enable(type);
185e3adcf8fSFrançois Tigeot 
18619df918dSFrançois Tigeot 	I915_WRITE(reg, val);
18719df918dSFrançois Tigeot 
18819df918dSFrançois Tigeot 	mmiowb();
189e3adcf8fSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
190e3adcf8fSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
191e3adcf8fSFrançois Tigeot 		data++;
192e3adcf8fSFrançois Tigeot 	}
19319df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
19419df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
19519df918dSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
19619df918dSFrançois Tigeot 	mmiowb();
197e3adcf8fSFrançois Tigeot 
1989edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
19919df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
20019df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
201e3adcf8fSFrançois Tigeot 
20219df918dSFrançois Tigeot 	I915_WRITE(reg, val);
20319df918dSFrançois Tigeot 	POSTING_READ(reg);
20419df918dSFrançois Tigeot }
20519df918dSFrançois Tigeot 
20619df918dSFrançois Tigeot static void cpt_write_infoframe(struct drm_encoder *encoder,
2079edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
2089edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
20919df918dSFrançois Tigeot {
2109edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
21119df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
21219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
21319df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
2149edbd4a0SFrançois Tigeot 	int i, reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
21519df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
21619df918dSFrançois Tigeot 
21719df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
21819df918dSFrançois Tigeot 
21919df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
2209edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
22119df918dSFrançois Tigeot 
22219df918dSFrançois Tigeot 	/* The DIP control register spec says that we need to update the AVI
22319df918dSFrançois Tigeot 	 * infoframe without clearing its enable bit */
2249edbd4a0SFrançois Tigeot 	if (type != HDMI_INFOFRAME_TYPE_AVI)
2259edbd4a0SFrançois Tigeot 		val &= ~g4x_infoframe_enable(type);
22619df918dSFrançois Tigeot 
22719df918dSFrançois Tigeot 	I915_WRITE(reg, val);
22819df918dSFrançois Tigeot 
22919df918dSFrançois Tigeot 	mmiowb();
23019df918dSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
23119df918dSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
23219df918dSFrançois Tigeot 		data++;
23319df918dSFrançois Tigeot 	}
23419df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
23519df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
23619df918dSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
23719df918dSFrançois Tigeot 	mmiowb();
23819df918dSFrançois Tigeot 
2399edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
24019df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
24119df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
24219df918dSFrançois Tigeot 
24319df918dSFrançois Tigeot 	I915_WRITE(reg, val);
24419df918dSFrançois Tigeot 	POSTING_READ(reg);
24519df918dSFrançois Tigeot }
24619df918dSFrançois Tigeot 
24719df918dSFrançois Tigeot static void vlv_write_infoframe(struct drm_encoder *encoder,
2489edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
2499edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
25019df918dSFrançois Tigeot {
2519edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
25219df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
25319df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
25419df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
2559edbd4a0SFrançois Tigeot 	int i, reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
25619df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
25719df918dSFrançois Tigeot 
25819df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
25919df918dSFrançois Tigeot 
26019df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
2619edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
26219df918dSFrançois Tigeot 
2639edbd4a0SFrançois Tigeot 	val &= ~g4x_infoframe_enable(type);
26419df918dSFrançois Tigeot 
26519df918dSFrançois Tigeot 	I915_WRITE(reg, val);
26619df918dSFrançois Tigeot 
26719df918dSFrançois Tigeot 	mmiowb();
26819df918dSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
26919df918dSFrançois Tigeot 		I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
27019df918dSFrançois Tigeot 		data++;
27119df918dSFrançois Tigeot 	}
27219df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
27319df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
27419df918dSFrançois Tigeot 		I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
27519df918dSFrançois Tigeot 	mmiowb();
27619df918dSFrançois Tigeot 
2779edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
27819df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
27919df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
28019df918dSFrançois Tigeot 
28119df918dSFrançois Tigeot 	I915_WRITE(reg, val);
28219df918dSFrançois Tigeot 	POSTING_READ(reg);
28319df918dSFrançois Tigeot }
28419df918dSFrançois Tigeot 
28519df918dSFrançois Tigeot static void hsw_write_infoframe(struct drm_encoder *encoder,
2869edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
2879edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
28819df918dSFrançois Tigeot {
2899edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
29019df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
29119df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
29219df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
2938e26cdf6SFrançois Tigeot 	u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
2949edbd4a0SFrançois Tigeot 	u32 data_reg;
2959edbd4a0SFrançois Tigeot 	int i;
29619df918dSFrançois Tigeot 	u32 val = I915_READ(ctl_reg);
29719df918dSFrançois Tigeot 
2989edbd4a0SFrançois Tigeot 	data_reg = hsw_infoframe_data_reg(type,
299*ba55f2f5SFrançois Tigeot 					  intel_crtc->config.cpu_transcoder,
300*ba55f2f5SFrançois Tigeot 					  dev_priv);
30119df918dSFrançois Tigeot 	if (data_reg == 0)
30219df918dSFrançois Tigeot 		return;
30319df918dSFrançois Tigeot 
3049edbd4a0SFrançois Tigeot 	val &= ~hsw_infoframe_enable(type);
30519df918dSFrançois Tigeot 	I915_WRITE(ctl_reg, val);
30619df918dSFrançois Tigeot 
30719df918dSFrançois Tigeot 	mmiowb();
30819df918dSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
30919df918dSFrançois Tigeot 		I915_WRITE(data_reg + i, *data);
31019df918dSFrançois Tigeot 		data++;
31119df918dSFrançois Tigeot 	}
31219df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
31319df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
31419df918dSFrançois Tigeot 		I915_WRITE(data_reg + i, 0);
31519df918dSFrançois Tigeot 	mmiowb();
31619df918dSFrançois Tigeot 
3179edbd4a0SFrançois Tigeot 	val |= hsw_infoframe_enable(type);
31819df918dSFrançois Tigeot 	I915_WRITE(ctl_reg, val);
31919df918dSFrançois Tigeot 	POSTING_READ(ctl_reg);
320e3adcf8fSFrançois Tigeot }
321e3adcf8fSFrançois Tigeot 
3229edbd4a0SFrançois Tigeot /*
3239edbd4a0SFrançois Tigeot  * The data we write to the DIP data buffer registers is 1 byte bigger than the
3249edbd4a0SFrançois Tigeot  * HDMI infoframe size because of an ECC/reserved byte at position 3 (starting
3259edbd4a0SFrançois Tigeot  * at 0). It's also a byte used by DisplayPort so the same DIP registers can be
3269edbd4a0SFrançois Tigeot  * used for both technologies.
3279edbd4a0SFrançois Tigeot  *
3289edbd4a0SFrançois Tigeot  * DW0: Reserved/ECC/DP | HB2 | HB1 | HB0
3299edbd4a0SFrançois Tigeot  * DW1:       DB3       | DB2 | DB1 | DB0
3309edbd4a0SFrançois Tigeot  * DW2:       DB7       | DB6 | DB5 | DB4
3319edbd4a0SFrançois Tigeot  * DW3: ...
3329edbd4a0SFrançois Tigeot  *
3339edbd4a0SFrançois Tigeot  * (HB is Header Byte, DB is Data Byte)
3349edbd4a0SFrançois Tigeot  *
3359edbd4a0SFrançois Tigeot  * The hdmi pack() functions don't know about that hardware specific hole so we
3369edbd4a0SFrançois Tigeot  * trick them by giving an offset into the buffer and moving back the header
3379edbd4a0SFrançois Tigeot  * bytes by one.
3389edbd4a0SFrançois Tigeot  */
3399edbd4a0SFrançois Tigeot static void intel_write_infoframe(struct drm_encoder *encoder,
3409edbd4a0SFrançois Tigeot 				  union hdmi_infoframe *frame)
341e3adcf8fSFrançois Tigeot {
342e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
3439edbd4a0SFrançois Tigeot 	uint8_t buffer[VIDEO_DIP_DATA_SIZE];
3449edbd4a0SFrançois Tigeot 	ssize_t len;
345e3adcf8fSFrançois Tigeot 
3469edbd4a0SFrançois Tigeot 	/* see comment above for the reason for this offset */
3479edbd4a0SFrançois Tigeot 	len = hdmi_infoframe_pack(frame, buffer + 1, sizeof(buffer) - 1);
3489edbd4a0SFrançois Tigeot 	if (len < 0)
3499edbd4a0SFrançois Tigeot 		return;
3509edbd4a0SFrançois Tigeot 
3519edbd4a0SFrançois Tigeot 	/* Insert the 'hole' (see big comment above) at position 3 */
3529edbd4a0SFrançois Tigeot 	buffer[0] = buffer[1];
3539edbd4a0SFrançois Tigeot 	buffer[1] = buffer[2];
3549edbd4a0SFrançois Tigeot 	buffer[2] = buffer[3];
3559edbd4a0SFrançois Tigeot 	buffer[3] = 0;
3569edbd4a0SFrançois Tigeot 	len++;
3579edbd4a0SFrançois Tigeot 
3589edbd4a0SFrançois Tigeot 	intel_hdmi->write_infoframe(encoder, frame->any.type, buffer, len);
359e3adcf8fSFrançois Tigeot }
360e3adcf8fSFrançois Tigeot 
36119df918dSFrançois Tigeot static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
36219df918dSFrançois Tigeot 					 struct drm_display_mode *adjusted_mode)
363e3adcf8fSFrançois Tigeot {
364a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
3658e26cdf6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
3669edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
3679edbd4a0SFrançois Tigeot 	int ret;
368e3adcf8fSFrançois Tigeot 
3699edbd4a0SFrançois Tigeot 	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
3709edbd4a0SFrançois Tigeot 						       adjusted_mode);
3719edbd4a0SFrançois Tigeot 	if (ret < 0) {
3729edbd4a0SFrançois Tigeot 		DRM_ERROR("couldn't fill AVI infoframe\n");
3739edbd4a0SFrançois Tigeot 		return;
3749edbd4a0SFrançois Tigeot 	}
37519df918dSFrançois Tigeot 
376a2fdbec6SFrançois Tigeot 	if (intel_hdmi->rgb_quant_range_selectable) {
3778e26cdf6SFrançois Tigeot 		if (intel_crtc->config.limited_color_range)
3789edbd4a0SFrançois Tigeot 			frame.avi.quantization_range =
3799edbd4a0SFrançois Tigeot 				HDMI_QUANTIZATION_RANGE_LIMITED;
380a2fdbec6SFrançois Tigeot 		else
3819edbd4a0SFrançois Tigeot 			frame.avi.quantization_range =
3829edbd4a0SFrançois Tigeot 				HDMI_QUANTIZATION_RANGE_FULL;
383a2fdbec6SFrançois Tigeot 	}
384a2fdbec6SFrançois Tigeot 
3859edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
386e3adcf8fSFrançois Tigeot }
387e3adcf8fSFrançois Tigeot 
388e3adcf8fSFrançois Tigeot static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
389e3adcf8fSFrançois Tigeot {
3909edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
3919edbd4a0SFrançois Tigeot 	int ret;
392e3adcf8fSFrançois Tigeot 
3939edbd4a0SFrançois Tigeot 	ret = hdmi_spd_infoframe_init(&frame.spd, "Intel", "Integrated gfx");
3949edbd4a0SFrançois Tigeot 	if (ret < 0) {
3959edbd4a0SFrançois Tigeot 		DRM_ERROR("couldn't fill SPD infoframe\n");
3969edbd4a0SFrançois Tigeot 		return;
3979edbd4a0SFrançois Tigeot 	}
398e3adcf8fSFrançois Tigeot 
3999edbd4a0SFrançois Tigeot 	frame.spd.sdi = HDMI_SPD_SDI_PC;
4009edbd4a0SFrançois Tigeot 
4019edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
4029edbd4a0SFrançois Tigeot }
4039edbd4a0SFrançois Tigeot 
4049edbd4a0SFrançois Tigeot static void
4059edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder,
4069edbd4a0SFrançois Tigeot 			      struct drm_display_mode *adjusted_mode)
4079edbd4a0SFrançois Tigeot {
4089edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
4099edbd4a0SFrançois Tigeot 	int ret;
4109edbd4a0SFrançois Tigeot 
4119edbd4a0SFrançois Tigeot 	ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
4129edbd4a0SFrançois Tigeot 							  adjusted_mode);
4139edbd4a0SFrançois Tigeot 	if (ret < 0)
4149edbd4a0SFrançois Tigeot 		return;
4159edbd4a0SFrançois Tigeot 
4169edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
417e3adcf8fSFrançois Tigeot }
418e3adcf8fSFrançois Tigeot 
41919df918dSFrançois Tigeot static void g4x_set_infoframes(struct drm_encoder *encoder,
420*ba55f2f5SFrançois Tigeot 			       bool enable,
42119df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
42219df918dSFrançois Tigeot {
42319df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
424a2fdbec6SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
425a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
42619df918dSFrançois Tigeot 	u32 reg = VIDEO_DIP_CTL;
42719df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
428*ba55f2f5SFrançois Tigeot 	u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
42919df918dSFrançois Tigeot 
43019df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
43119df918dSFrançois Tigeot 
43219df918dSFrançois Tigeot 	/* If the registers were not initialized yet, they might be zeroes,
43319df918dSFrançois Tigeot 	 * which means we're selecting the AVI DIP and we're setting its
43419df918dSFrançois Tigeot 	 * frequency to once. This seems to really confuse the HW and make
43519df918dSFrançois Tigeot 	 * things stop working (the register spec says the AVI always needs to
43619df918dSFrançois Tigeot 	 * be sent every VSync). So here we avoid writing to the register more
43719df918dSFrançois Tigeot 	 * than we need and also explicitly select the AVI DIP and explicitly
43819df918dSFrançois Tigeot 	 * set its frequency to every VSync. Avoiding to write it twice seems to
43919df918dSFrançois Tigeot 	 * be enough to solve the problem, but being defensive shouldn't hurt us
44019df918dSFrançois Tigeot 	 * either. */
44119df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
44219df918dSFrançois Tigeot 
443*ba55f2f5SFrançois Tigeot 	if (!enable) {
44419df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
44519df918dSFrançois Tigeot 			return;
44619df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
44719df918dSFrançois Tigeot 		I915_WRITE(reg, val);
44819df918dSFrançois Tigeot 		POSTING_READ(reg);
44919df918dSFrançois Tigeot 		return;
45019df918dSFrançois Tigeot 	}
45119df918dSFrançois Tigeot 
45219df918dSFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
45319df918dSFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
45419df918dSFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
45519df918dSFrançois Tigeot 			I915_WRITE(reg, val);
45619df918dSFrançois Tigeot 			POSTING_READ(reg);
45719df918dSFrançois Tigeot 		}
45819df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
45919df918dSFrançois Tigeot 		val |= port;
46019df918dSFrançois Tigeot 	}
46119df918dSFrançois Tigeot 
46219df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
46319df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_ENABLE_VENDOR;
46419df918dSFrançois Tigeot 
46519df918dSFrançois Tigeot 	I915_WRITE(reg, val);
46619df918dSFrançois Tigeot 	POSTING_READ(reg);
46719df918dSFrançois Tigeot 
46819df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
46919df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
4709edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
47119df918dSFrançois Tigeot }
47219df918dSFrançois Tigeot 
47319df918dSFrançois Tigeot static void ibx_set_infoframes(struct drm_encoder *encoder,
474*ba55f2f5SFrançois Tigeot 			       bool enable,
47519df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
47619df918dSFrançois Tigeot {
47719df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
47819df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
479a2fdbec6SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
480a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
48119df918dSFrançois Tigeot 	u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
48219df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
483*ba55f2f5SFrançois Tigeot 	u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
48419df918dSFrançois Tigeot 
48519df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
48619df918dSFrançois Tigeot 
48719df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
48819df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
48919df918dSFrançois Tigeot 
490*ba55f2f5SFrançois Tigeot 	if (!enable) {
49119df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
49219df918dSFrançois Tigeot 			return;
49319df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
49419df918dSFrançois Tigeot 		I915_WRITE(reg, val);
49519df918dSFrançois Tigeot 		POSTING_READ(reg);
49619df918dSFrançois Tigeot 		return;
49719df918dSFrançois Tigeot 	}
49819df918dSFrançois Tigeot 
49919df918dSFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
50019df918dSFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
50119df918dSFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
50219df918dSFrançois Tigeot 			I915_WRITE(reg, val);
50319df918dSFrançois Tigeot 			POSTING_READ(reg);
50419df918dSFrançois Tigeot 		}
50519df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
50619df918dSFrançois Tigeot 		val |= port;
50719df918dSFrançois Tigeot 	}
50819df918dSFrançois Tigeot 
50919df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
51019df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
51119df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
51219df918dSFrançois Tigeot 
51319df918dSFrançois Tigeot 	I915_WRITE(reg, val);
51419df918dSFrançois Tigeot 	POSTING_READ(reg);
51519df918dSFrançois Tigeot 
51619df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
51719df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
5189edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
51919df918dSFrançois Tigeot }
52019df918dSFrançois Tigeot 
52119df918dSFrançois Tigeot static void cpt_set_infoframes(struct drm_encoder *encoder,
522*ba55f2f5SFrançois Tigeot 			       bool enable,
52319df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
52419df918dSFrançois Tigeot {
52519df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
52619df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
52719df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
52819df918dSFrançois Tigeot 	u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
52919df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
53019df918dSFrançois Tigeot 
53119df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
53219df918dSFrançois Tigeot 
53319df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
53419df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
53519df918dSFrançois Tigeot 
536*ba55f2f5SFrançois Tigeot 	if (!enable) {
53719df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
53819df918dSFrançois Tigeot 			return;
53919df918dSFrançois Tigeot 		val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
54019df918dSFrançois Tigeot 		I915_WRITE(reg, val);
54119df918dSFrançois Tigeot 		POSTING_READ(reg);
54219df918dSFrançois Tigeot 		return;
54319df918dSFrançois Tigeot 	}
54419df918dSFrançois Tigeot 
54519df918dSFrançois Tigeot 	/* Set both together, unset both together: see the spec. */
54619df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI;
54719df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
54819df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
54919df918dSFrançois Tigeot 
55019df918dSFrançois Tigeot 	I915_WRITE(reg, val);
55119df918dSFrançois Tigeot 	POSTING_READ(reg);
55219df918dSFrançois Tigeot 
55319df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
55419df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
5559edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
55619df918dSFrançois Tigeot }
55719df918dSFrançois Tigeot 
55819df918dSFrançois Tigeot static void vlv_set_infoframes(struct drm_encoder *encoder,
559*ba55f2f5SFrançois Tigeot 			       bool enable,
56019df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
56119df918dSFrançois Tigeot {
56219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
563*ba55f2f5SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
56419df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
56519df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
56619df918dSFrançois Tigeot 	u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
56719df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
568*ba55f2f5SFrançois Tigeot 	u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
56919df918dSFrançois Tigeot 
57019df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
57119df918dSFrançois Tigeot 
57219df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
57319df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
57419df918dSFrançois Tigeot 
575*ba55f2f5SFrançois Tigeot 	if (!enable) {
57619df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
57719df918dSFrançois Tigeot 			return;
57819df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
57919df918dSFrançois Tigeot 		I915_WRITE(reg, val);
58019df918dSFrançois Tigeot 		POSTING_READ(reg);
58119df918dSFrançois Tigeot 		return;
58219df918dSFrançois Tigeot 	}
58319df918dSFrançois Tigeot 
584*ba55f2f5SFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
585*ba55f2f5SFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
586*ba55f2f5SFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
587*ba55f2f5SFrançois Tigeot 			I915_WRITE(reg, val);
588*ba55f2f5SFrançois Tigeot 			POSTING_READ(reg);
589*ba55f2f5SFrançois Tigeot 		}
590*ba55f2f5SFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
591*ba55f2f5SFrançois Tigeot 		val |= port;
592*ba55f2f5SFrançois Tigeot 	}
593*ba55f2f5SFrançois Tigeot 
59419df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
595*ba55f2f5SFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_ENABLE_VENDOR |
596*ba55f2f5SFrançois Tigeot 		 VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_GCP);
59719df918dSFrançois Tigeot 
59819df918dSFrançois Tigeot 	I915_WRITE(reg, val);
59919df918dSFrançois Tigeot 	POSTING_READ(reg);
60019df918dSFrançois Tigeot 
60119df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
60219df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
6039edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
60419df918dSFrançois Tigeot }
60519df918dSFrançois Tigeot 
60619df918dSFrançois Tigeot static void hsw_set_infoframes(struct drm_encoder *encoder,
607*ba55f2f5SFrançois Tigeot 			       bool enable,
60819df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
60919df918dSFrançois Tigeot {
61019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
61119df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
61219df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
6138e26cdf6SFrançois Tigeot 	u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
61419df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
61519df918dSFrançois Tigeot 
61619df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
61719df918dSFrançois Tigeot 
618*ba55f2f5SFrançois Tigeot 	if (!enable) {
61919df918dSFrançois Tigeot 		I915_WRITE(reg, 0);
62019df918dSFrançois Tigeot 		POSTING_READ(reg);
62119df918dSFrançois Tigeot 		return;
62219df918dSFrançois Tigeot 	}
62319df918dSFrançois Tigeot 
62419df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW |
62519df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW);
62619df918dSFrançois Tigeot 
62719df918dSFrançois Tigeot 	I915_WRITE(reg, val);
62819df918dSFrançois Tigeot 	POSTING_READ(reg);
62919df918dSFrançois Tigeot 
63019df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
63119df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
6329edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
63319df918dSFrançois Tigeot }
63419df918dSFrançois Tigeot 
635*ba55f2f5SFrançois Tigeot static void intel_hdmi_prepare(struct intel_encoder *encoder)
636e3adcf8fSFrançois Tigeot {
6379edbd4a0SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
638e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
6399edbd4a0SFrançois Tigeot 	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
6409edbd4a0SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
6419edbd4a0SFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
6428e26cdf6SFrançois Tigeot 	u32 hdmi_val;
643e3adcf8fSFrançois Tigeot 
6448e26cdf6SFrançois Tigeot 	hdmi_val = SDVO_ENCODING_HDMI;
6455d0b1887SFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev))
6468e26cdf6SFrançois Tigeot 		hdmi_val |= intel_hdmi->color_range;
647e3adcf8fSFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
6488e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH;
649e3adcf8fSFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
6508e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH;
651e3adcf8fSFrançois Tigeot 
6529edbd4a0SFrançois Tigeot 	if (crtc->config.pipe_bpp > 24)
6538e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_COLOR_FORMAT_12bpc;
654e3adcf8fSFrançois Tigeot 	else
6558e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_COLOR_FORMAT_8bpc;
656e3adcf8fSFrançois Tigeot 
657*ba55f2f5SFrançois Tigeot 	if (crtc->config.has_hdmi_sink)
6588e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_MODE_SELECT_HDMI;
659e3adcf8fSFrançois Tigeot 
660*ba55f2f5SFrançois Tigeot 	if (crtc->config.has_audio) {
661*ba55f2f5SFrançois Tigeot 		WARN_ON(!crtc->config.has_hdmi_sink);
66219df918dSFrançois Tigeot 		DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
6639edbd4a0SFrançois Tigeot 				 pipe_name(crtc->pipe));
6648e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_AUDIO_ENABLE;
6659edbd4a0SFrançois Tigeot 		intel_write_eld(&encoder->base, adjusted_mode);
666e3adcf8fSFrançois Tigeot 	}
667e3adcf8fSFrançois Tigeot 
668e3adcf8fSFrançois Tigeot 	if (HAS_PCH_CPT(dev))
6699edbd4a0SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe);
670*ba55f2f5SFrançois Tigeot 	else if (IS_CHERRYVIEW(dev))
671*ba55f2f5SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL_CHV(crtc->pipe);
6728e26cdf6SFrançois Tigeot 	else
6739edbd4a0SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL(crtc->pipe);
674e3adcf8fSFrançois Tigeot 
6758e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val);
6768e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
677e3adcf8fSFrançois Tigeot }
678e3adcf8fSFrançois Tigeot 
67919df918dSFrançois Tigeot static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
68019df918dSFrançois Tigeot 				    enum i915_pipe *pipe)
681e3adcf8fSFrançois Tigeot {
68219df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
683e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
68419df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
685*ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
68619df918dSFrançois Tigeot 	u32 tmp;
68719df918dSFrançois Tigeot 
688*ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(encoder);
689*ba55f2f5SFrançois Tigeot 	if (!intel_display_power_enabled(dev_priv, power_domain))
690*ba55f2f5SFrançois Tigeot 		return false;
691*ba55f2f5SFrançois Tigeot 
6928e26cdf6SFrançois Tigeot 	tmp = I915_READ(intel_hdmi->hdmi_reg);
69319df918dSFrançois Tigeot 
69419df918dSFrançois Tigeot 	if (!(tmp & SDVO_ENABLE))
69519df918dSFrançois Tigeot 		return false;
69619df918dSFrançois Tigeot 
69719df918dSFrançois Tigeot 	if (HAS_PCH_CPT(dev))
69819df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE_CPT(tmp);
699*ba55f2f5SFrançois Tigeot 	else if (IS_CHERRYVIEW(dev))
700*ba55f2f5SFrançois Tigeot 		*pipe = SDVO_PORT_TO_PIPE_CHV(tmp);
70119df918dSFrançois Tigeot 	else
70219df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE(tmp);
70319df918dSFrançois Tigeot 
70419df918dSFrançois Tigeot 	return true;
70519df918dSFrançois Tigeot }
70619df918dSFrançois Tigeot 
7075d0b1887SFrançois Tigeot static void intel_hdmi_get_config(struct intel_encoder *encoder,
7085d0b1887SFrançois Tigeot 				  struct intel_crtc_config *pipe_config)
7095d0b1887SFrançois Tigeot {
7105d0b1887SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
7115d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
7125d0b1887SFrançois Tigeot 	u32 tmp, flags = 0;
7139edbd4a0SFrançois Tigeot 	int dotclock;
7145d0b1887SFrançois Tigeot 
7155d0b1887SFrançois Tigeot 	tmp = I915_READ(intel_hdmi->hdmi_reg);
7165d0b1887SFrançois Tigeot 
7175d0b1887SFrançois Tigeot 	if (tmp & SDVO_HSYNC_ACTIVE_HIGH)
7185d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PHSYNC;
7195d0b1887SFrançois Tigeot 	else
7205d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NHSYNC;
7215d0b1887SFrançois Tigeot 
7225d0b1887SFrançois Tigeot 	if (tmp & SDVO_VSYNC_ACTIVE_HIGH)
7235d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PVSYNC;
7245d0b1887SFrançois Tigeot 	else
7255d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NVSYNC;
7265d0b1887SFrançois Tigeot 
727*ba55f2f5SFrançois Tigeot 	if (tmp & HDMI_MODE_SELECT_HDMI)
728*ba55f2f5SFrançois Tigeot 		pipe_config->has_hdmi_sink = true;
729*ba55f2f5SFrançois Tigeot 
730*ba55f2f5SFrançois Tigeot 	if (tmp & HDMI_MODE_SELECT_HDMI)
731*ba55f2f5SFrançois Tigeot 		pipe_config->has_audio = true;
732*ba55f2f5SFrançois Tigeot 
7335d0b1887SFrançois Tigeot 	pipe_config->adjusted_mode.flags |= flags;
7349edbd4a0SFrançois Tigeot 
7359edbd4a0SFrançois Tigeot 	if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
7369edbd4a0SFrançois Tigeot 		dotclock = pipe_config->port_clock * 2 / 3;
7379edbd4a0SFrançois Tigeot 	else
7389edbd4a0SFrançois Tigeot 		dotclock = pipe_config->port_clock;
7399edbd4a0SFrançois Tigeot 
7409edbd4a0SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv->dev))
7419edbd4a0SFrançois Tigeot 		ironlake_check_encoder_dotclock(pipe_config, dotclock);
7429edbd4a0SFrançois Tigeot 
7439edbd4a0SFrançois Tigeot 	pipe_config->adjusted_mode.crtc_clock = dotclock;
7445d0b1887SFrançois Tigeot }
7455d0b1887SFrançois Tigeot 
74619df918dSFrançois Tigeot static void intel_enable_hdmi(struct intel_encoder *encoder)
74719df918dSFrançois Tigeot {
74819df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
74919df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
7508e26cdf6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
75119df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
752e3adcf8fSFrançois Tigeot 	u32 temp;
753e3adcf8fSFrançois Tigeot 	u32 enable_bits = SDVO_ENABLE;
754e3adcf8fSFrançois Tigeot 
755*ba55f2f5SFrançois Tigeot 	if (intel_crtc->config.has_audio)
756e3adcf8fSFrançois Tigeot 		enable_bits |= SDVO_AUDIO_ENABLE;
757e3adcf8fSFrançois Tigeot 
7588e26cdf6SFrançois Tigeot 	temp = I915_READ(intel_hdmi->hdmi_reg);
759e3adcf8fSFrançois Tigeot 
76019df918dSFrançois Tigeot 	/* HW workaround for IBX, we need to move the port to transcoder A
7618e26cdf6SFrançois Tigeot 	 * before disabling it, so restore the transcoder select bit here. */
7628e26cdf6SFrançois Tigeot 	if (HAS_PCH_IBX(dev))
7638e26cdf6SFrançois Tigeot 		enable_bits |= SDVO_PIPE_SEL(intel_crtc->pipe);
76419df918dSFrançois Tigeot 
765e3adcf8fSFrançois Tigeot 	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
766e3adcf8fSFrançois Tigeot 	 * we do this anyway which shows more stable in testing.
767e3adcf8fSFrançois Tigeot 	 */
768e3adcf8fSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
7698e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE);
7708e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
771e3adcf8fSFrançois Tigeot 	}
772e3adcf8fSFrançois Tigeot 
773e3adcf8fSFrançois Tigeot 	temp |= enable_bits;
77419df918dSFrançois Tigeot 
7758e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, temp);
7768e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
77719df918dSFrançois Tigeot 
77819df918dSFrançois Tigeot 	/* HW workaround, need to write this twice for issue that may result
77919df918dSFrançois Tigeot 	 * in first write getting masked.
78019df918dSFrançois Tigeot 	 */
78119df918dSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
7828e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp);
7838e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
784e3adcf8fSFrançois Tigeot 	}
7855d0b1887SFrançois Tigeot }
7869edbd4a0SFrançois Tigeot 
7879edbd4a0SFrançois Tigeot static void vlv_enable_hdmi(struct intel_encoder *encoder)
7889edbd4a0SFrançois Tigeot {
78919df918dSFrançois Tigeot }
79019df918dSFrançois Tigeot 
79119df918dSFrançois Tigeot static void intel_disable_hdmi(struct intel_encoder *encoder)
79219df918dSFrançois Tigeot {
79319df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
79419df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
79519df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
79619df918dSFrançois Tigeot 	u32 temp;
79719df918dSFrançois Tigeot 	u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
79819df918dSFrançois Tigeot 
7998e26cdf6SFrançois Tigeot 	temp = I915_READ(intel_hdmi->hdmi_reg);
80019df918dSFrançois Tigeot 
80119df918dSFrançois Tigeot 	/* HW workaround for IBX, we need to move the port to transcoder A
80219df918dSFrançois Tigeot 	 * before disabling it. */
80319df918dSFrançois Tigeot 	if (HAS_PCH_IBX(dev)) {
80419df918dSFrançois Tigeot 		struct drm_crtc *crtc = encoder->base.crtc;
80519df918dSFrançois Tigeot 		int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
80619df918dSFrançois Tigeot 
80719df918dSFrançois Tigeot 		if (temp & SDVO_PIPE_B_SELECT) {
80819df918dSFrançois Tigeot 			temp &= ~SDVO_PIPE_B_SELECT;
8098e26cdf6SFrançois Tigeot 			I915_WRITE(intel_hdmi->hdmi_reg, temp);
8108e26cdf6SFrançois Tigeot 			POSTING_READ(intel_hdmi->hdmi_reg);
81119df918dSFrançois Tigeot 
81219df918dSFrançois Tigeot 			/* Again we need to write this twice. */
8138e26cdf6SFrançois Tigeot 			I915_WRITE(intel_hdmi->hdmi_reg, temp);
8148e26cdf6SFrançois Tigeot 			POSTING_READ(intel_hdmi->hdmi_reg);
81519df918dSFrançois Tigeot 
81619df918dSFrançois Tigeot 			/* Transcoder selection bits only update
81719df918dSFrançois Tigeot 			 * effectively on vblank. */
81819df918dSFrançois Tigeot 			if (crtc)
81919df918dSFrançois Tigeot 				intel_wait_for_vblank(dev, pipe);
82019df918dSFrançois Tigeot 			else
82119df918dSFrançois Tigeot 				msleep(50);
82219df918dSFrançois Tigeot 		}
82319df918dSFrançois Tigeot 	}
82419df918dSFrançois Tigeot 
82519df918dSFrançois Tigeot 	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
82619df918dSFrançois Tigeot 	 * we do this anyway which shows more stable in testing.
82719df918dSFrançois Tigeot 	 */
82819df918dSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
8298e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE);
8308e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
83119df918dSFrançois Tigeot 	}
83219df918dSFrançois Tigeot 
83319df918dSFrançois Tigeot 	temp &= ~enable_bits;
834e3adcf8fSFrançois Tigeot 
8358e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, temp);
8368e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
837e3adcf8fSFrançois Tigeot 
838e3adcf8fSFrançois Tigeot 	/* HW workaround, need to write this twice for issue that may result
839e3adcf8fSFrançois Tigeot 	 * in first write getting masked.
840e3adcf8fSFrançois Tigeot 	 */
841e3adcf8fSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
8428e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp);
8438e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
844e3adcf8fSFrançois Tigeot 	}
845e3adcf8fSFrançois Tigeot }
846e3adcf8fSFrançois Tigeot 
847*ba55f2f5SFrançois Tigeot static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit)
8485d0b1887SFrançois Tigeot {
8495d0b1887SFrançois Tigeot 	struct drm_device *dev = intel_hdmi_to_dev(hdmi);
8505d0b1887SFrançois Tigeot 
851*ba55f2f5SFrançois Tigeot 	if ((respect_dvi_limit && !hdmi->has_hdmi_sink) || IS_G4X(dev))
8525d0b1887SFrançois Tigeot 		return 165000;
8539edbd4a0SFrançois Tigeot 	else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8)
8545d0b1887SFrançois Tigeot 		return 300000;
8555d0b1887SFrançois Tigeot 	else
8565d0b1887SFrançois Tigeot 		return 225000;
8575d0b1887SFrançois Tigeot }
8585d0b1887SFrançois Tigeot 
8599edbd4a0SFrançois Tigeot static enum drm_mode_status
8609edbd4a0SFrançois Tigeot intel_hdmi_mode_valid(struct drm_connector *connector,
861e3adcf8fSFrançois Tigeot 		      struct drm_display_mode *mode)
862e3adcf8fSFrançois Tigeot {
863*ba55f2f5SFrançois Tigeot 	if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector),
864*ba55f2f5SFrançois Tigeot 					       true))
865e3adcf8fSFrançois Tigeot 		return MODE_CLOCK_HIGH;
866e3adcf8fSFrançois Tigeot 	if (mode->clock < 20000)
867e3adcf8fSFrançois Tigeot 		return MODE_CLOCK_LOW;
868e3adcf8fSFrançois Tigeot 
869e3adcf8fSFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
870e3adcf8fSFrançois Tigeot 		return MODE_NO_DBLESCAN;
871e3adcf8fSFrançois Tigeot 
872e3adcf8fSFrançois Tigeot 	return MODE_OK;
873e3adcf8fSFrançois Tigeot }
874e3adcf8fSFrançois Tigeot 
875*ba55f2f5SFrançois Tigeot static bool hdmi_12bpc_possible(struct intel_crtc *crtc)
876*ba55f2f5SFrançois Tigeot {
877*ba55f2f5SFrançois Tigeot 	struct drm_device *dev = crtc->base.dev;
878*ba55f2f5SFrançois Tigeot 	struct intel_encoder *encoder;
879*ba55f2f5SFrançois Tigeot 	int count = 0, count_hdmi = 0;
880*ba55f2f5SFrançois Tigeot 
881*ba55f2f5SFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev))
882*ba55f2f5SFrançois Tigeot 		return false;
883*ba55f2f5SFrançois Tigeot 
884*ba55f2f5SFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
885*ba55f2f5SFrançois Tigeot 		if (encoder->new_crtc != crtc)
886*ba55f2f5SFrançois Tigeot 			continue;
887*ba55f2f5SFrançois Tigeot 
888*ba55f2f5SFrançois Tigeot 		count_hdmi += encoder->type == INTEL_OUTPUT_HDMI;
889*ba55f2f5SFrançois Tigeot 		count++;
890*ba55f2f5SFrançois Tigeot 	}
891*ba55f2f5SFrançois Tigeot 
892*ba55f2f5SFrançois Tigeot 	/*
893*ba55f2f5SFrançois Tigeot 	 * HDMI 12bpc affects the clocks, so it's only possible
894*ba55f2f5SFrançois Tigeot 	 * when not cloning with other encoder types.
895*ba55f2f5SFrançois Tigeot 	 */
896*ba55f2f5SFrançois Tigeot 	return count_hdmi > 0 && count_hdmi == count;
897*ba55f2f5SFrançois Tigeot }
898*ba55f2f5SFrançois Tigeot 
8998e26cdf6SFrançois Tigeot bool intel_hdmi_compute_config(struct intel_encoder *encoder,
9008e26cdf6SFrançois Tigeot 			       struct intel_crtc_config *pipe_config)
901e3adcf8fSFrançois Tigeot {
9028e26cdf6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
9038e26cdf6SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
9048e26cdf6SFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
9059edbd4a0SFrançois Tigeot 	int clock_12bpc = pipe_config->adjusted_mode.crtc_clock * 3 / 2;
906*ba55f2f5SFrançois Tigeot 	int portclock_limit = hdmi_portclock_limit(intel_hdmi, false);
9075d0b1887SFrançois Tigeot 	int desired_bpp;
908a2fdbec6SFrançois Tigeot 
909*ba55f2f5SFrançois Tigeot 	pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink;
910*ba55f2f5SFrançois Tigeot 
911a2fdbec6SFrançois Tigeot 	if (intel_hdmi->color_range_auto) {
912a2fdbec6SFrançois Tigeot 		/* See CEA-861-E - 5.1 Default Encoding Parameters */
913*ba55f2f5SFrançois Tigeot 		if (pipe_config->has_hdmi_sink &&
914a2fdbec6SFrançois Tigeot 		    drm_match_cea_mode(adjusted_mode) > 1)
9158e26cdf6SFrançois Tigeot 			intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
916a2fdbec6SFrançois Tigeot 		else
917a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range = 0;
918a2fdbec6SFrançois Tigeot 	}
919a2fdbec6SFrançois Tigeot 
920a2fdbec6SFrançois Tigeot 	if (intel_hdmi->color_range)
9218e26cdf6SFrançois Tigeot 		pipe_config->limited_color_range = true;
9228e26cdf6SFrançois Tigeot 
9238e26cdf6SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev))
9248e26cdf6SFrançois Tigeot 		pipe_config->has_pch_encoder = true;
9258e26cdf6SFrançois Tigeot 
926*ba55f2f5SFrançois Tigeot 	if (pipe_config->has_hdmi_sink && intel_hdmi->has_audio)
927*ba55f2f5SFrançois Tigeot 		pipe_config->has_audio = true;
928*ba55f2f5SFrançois Tigeot 
9298e26cdf6SFrançois Tigeot 	/*
9308e26cdf6SFrançois Tigeot 	 * HDMI is either 12 or 8, so if the display lets 10bpc sneak
9318e26cdf6SFrançois Tigeot 	 * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi
9325d0b1887SFrançois Tigeot 	 * outputs. We also need to check that the higher clock still fits
9335d0b1887SFrançois Tigeot 	 * within limits.
9348e26cdf6SFrançois Tigeot 	 */
935*ba55f2f5SFrançois Tigeot 	if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
936*ba55f2f5SFrançois Tigeot 	    clock_12bpc <= portclock_limit &&
937*ba55f2f5SFrançois Tigeot 	    hdmi_12bpc_possible(encoder->new_crtc)) {
9385d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
9395d0b1887SFrançois Tigeot 		desired_bpp = 12*3;
9405d0b1887SFrançois Tigeot 
9415d0b1887SFrançois Tigeot 		/* Need to adjust the port link by 1.5x for 12bpc. */
9425d0b1887SFrançois Tigeot 		pipe_config->port_clock = clock_12bpc;
9438e26cdf6SFrançois Tigeot 	} else {
9445d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n");
9455d0b1887SFrançois Tigeot 		desired_bpp = 8*3;
9465d0b1887SFrançois Tigeot 	}
9475d0b1887SFrançois Tigeot 
9485d0b1887SFrançois Tigeot 	if (!pipe_config->bw_constrained) {
9495d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp);
9505d0b1887SFrançois Tigeot 		pipe_config->pipe_bpp = desired_bpp;
9515d0b1887SFrançois Tigeot 	}
9525d0b1887SFrançois Tigeot 
9539edbd4a0SFrançois Tigeot 	if (adjusted_mode->crtc_clock > portclock_limit) {
9545d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n");
9555d0b1887SFrançois Tigeot 		return false;
9568e26cdf6SFrançois Tigeot 	}
957a2fdbec6SFrançois Tigeot 
958e3adcf8fSFrançois Tigeot 	return true;
959e3adcf8fSFrançois Tigeot }
960e3adcf8fSFrançois Tigeot 
961e3adcf8fSFrançois Tigeot static enum drm_connector_status
962e3adcf8fSFrançois Tigeot intel_hdmi_detect(struct drm_connector *connector, bool force)
963e3adcf8fSFrançois Tigeot {
964a2fdbec6SFrançois Tigeot 	struct drm_device *dev = connector->dev;
965e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
96619df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
96719df918dSFrançois Tigeot 		hdmi_to_dig_port(intel_hdmi);
96819df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = &intel_dig_port->base;
969a2fdbec6SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
970e3adcf8fSFrançois Tigeot 	struct edid *edid;
971*ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
972e3adcf8fSFrançois Tigeot 	enum drm_connector_status status = connector_status_disconnected;
973e3adcf8fSFrançois Tigeot 
9749edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
975*ba55f2f5SFrançois Tigeot 		      connector->base.id, "connector->name");
976*ba55f2f5SFrançois Tigeot 
977*ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(intel_encoder);
978*ba55f2f5SFrançois Tigeot 	intel_display_power_get(dev_priv, power_domain);
9799edbd4a0SFrançois Tigeot 
980e3adcf8fSFrançois Tigeot 	intel_hdmi->has_hdmi_sink = false;
981e3adcf8fSFrançois Tigeot 	intel_hdmi->has_audio = false;
982a2fdbec6SFrançois Tigeot 	intel_hdmi->rgb_quant_range_selectable = false;
98319df918dSFrançois Tigeot 	edid = drm_get_edid(connector,
98419df918dSFrançois Tigeot 			    intel_gmbus_get_adapter(dev_priv,
98519df918dSFrançois Tigeot 						    intel_hdmi->ddc_bus));
986e3adcf8fSFrançois Tigeot 
987e3adcf8fSFrançois Tigeot 	if (edid) {
988e3adcf8fSFrançois Tigeot 		if (edid->input & DRM_EDID_INPUT_DIGITAL) {
989e3adcf8fSFrançois Tigeot 			status = connector_status_connected;
990e3adcf8fSFrançois Tigeot 			if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
991e3adcf8fSFrançois Tigeot 				intel_hdmi->has_hdmi_sink =
992e3adcf8fSFrançois Tigeot 						drm_detect_hdmi_monitor(edid);
993e3adcf8fSFrançois Tigeot 			intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
994a2fdbec6SFrançois Tigeot 			intel_hdmi->rgb_quant_range_selectable =
995a2fdbec6SFrançois Tigeot 				drm_rgb_quant_range_selectable(edid);
996e3adcf8fSFrançois Tigeot 		}
9978e26cdf6SFrançois Tigeot 		kfree(edid);
998e3adcf8fSFrançois Tigeot 	}
999e3adcf8fSFrançois Tigeot 
1000e3adcf8fSFrançois Tigeot 	if (status == connector_status_connected) {
1001e3adcf8fSFrançois Tigeot 		if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
1002e3adcf8fSFrançois Tigeot 			intel_hdmi->has_audio =
1003e3adcf8fSFrançois Tigeot 				(intel_hdmi->force_audio == HDMI_AUDIO_ON);
100419df918dSFrançois Tigeot 		intel_encoder->type = INTEL_OUTPUT_HDMI;
1005e3adcf8fSFrançois Tigeot 	}
1006e3adcf8fSFrançois Tigeot 
1007*ba55f2f5SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
1008*ba55f2f5SFrançois Tigeot 
1009e3adcf8fSFrançois Tigeot 	return status;
1010e3adcf8fSFrançois Tigeot }
1011e3adcf8fSFrançois Tigeot 
1012e3adcf8fSFrançois Tigeot static int intel_hdmi_get_modes(struct drm_connector *connector)
1013e3adcf8fSFrançois Tigeot {
1014*ba55f2f5SFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
1015*ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
1016e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1017*ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
1018*ba55f2f5SFrançois Tigeot 	int ret;
1019e3adcf8fSFrançois Tigeot 
1020e3adcf8fSFrançois Tigeot 	/* We should parse the EDID data and find out if it's an HDMI sink so
1021e3adcf8fSFrançois Tigeot 	 * we can send audio to it.
1022e3adcf8fSFrançois Tigeot 	 */
1023e3adcf8fSFrançois Tigeot 
1024*ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(intel_encoder);
1025*ba55f2f5SFrançois Tigeot 	intel_display_power_get(dev_priv, power_domain);
1026*ba55f2f5SFrançois Tigeot 
1027*ba55f2f5SFrançois Tigeot 	ret = intel_ddc_get_modes(connector,
102819df918dSFrançois Tigeot 				   intel_gmbus_get_adapter(dev_priv,
102919df918dSFrançois Tigeot 							   intel_hdmi->ddc_bus));
1030*ba55f2f5SFrançois Tigeot 
1031*ba55f2f5SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
1032*ba55f2f5SFrançois Tigeot 
1033*ba55f2f5SFrançois Tigeot 	return ret;
1034e3adcf8fSFrançois Tigeot }
1035e3adcf8fSFrançois Tigeot 
1036e3adcf8fSFrançois Tigeot static bool
1037e3adcf8fSFrançois Tigeot intel_hdmi_detect_audio(struct drm_connector *connector)
1038e3adcf8fSFrançois Tigeot {
1039*ba55f2f5SFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
1040*ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
1041e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1042*ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
1043e3adcf8fSFrançois Tigeot 	struct edid *edid;
1044e3adcf8fSFrançois Tigeot 	bool has_audio = false;
1045e3adcf8fSFrançois Tigeot 
1046*ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(intel_encoder);
1047*ba55f2f5SFrançois Tigeot 	intel_display_power_get(dev_priv, power_domain);
1048*ba55f2f5SFrançois Tigeot 
104919df918dSFrançois Tigeot 	edid = drm_get_edid(connector,
105019df918dSFrançois Tigeot 			    intel_gmbus_get_adapter(dev_priv,
105119df918dSFrançois Tigeot 						    intel_hdmi->ddc_bus));
1052e3adcf8fSFrançois Tigeot 	if (edid) {
1053e3adcf8fSFrançois Tigeot 		if (edid->input & DRM_EDID_INPUT_DIGITAL)
1054e3adcf8fSFrançois Tigeot 			has_audio = drm_detect_monitor_audio(edid);
10558e26cdf6SFrançois Tigeot 		kfree(edid);
1056e3adcf8fSFrançois Tigeot 	}
1057e3adcf8fSFrançois Tigeot 
1058*ba55f2f5SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
1059*ba55f2f5SFrançois Tigeot 
1060e3adcf8fSFrançois Tigeot 	return has_audio;
1061e3adcf8fSFrançois Tigeot }
1062e3adcf8fSFrançois Tigeot 
1063e3adcf8fSFrançois Tigeot static int
1064e3adcf8fSFrançois Tigeot intel_hdmi_set_property(struct drm_connector *connector,
1065e3adcf8fSFrançois Tigeot 			struct drm_property *property,
1066e3adcf8fSFrançois Tigeot 			uint64_t val)
1067e3adcf8fSFrançois Tigeot {
1068e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
106919df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
107019df918dSFrançois Tigeot 		hdmi_to_dig_port(intel_hdmi);
1071e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1072e3adcf8fSFrançois Tigeot 	int ret;
1073e3adcf8fSFrançois Tigeot 
1074b5162e19SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base, property, val);
1075e3adcf8fSFrançois Tigeot 	if (ret)
1076e3adcf8fSFrançois Tigeot 		return ret;
1077e3adcf8fSFrançois Tigeot 
1078e3adcf8fSFrançois Tigeot 	if (property == dev_priv->force_audio_property) {
1079e3adcf8fSFrançois Tigeot 		enum hdmi_force_audio i = val;
1080e3adcf8fSFrançois Tigeot 		bool has_audio;
1081e3adcf8fSFrançois Tigeot 
1082e3adcf8fSFrançois Tigeot 		if (i == intel_hdmi->force_audio)
1083e3adcf8fSFrançois Tigeot 			return 0;
1084e3adcf8fSFrançois Tigeot 
1085e3adcf8fSFrançois Tigeot 		intel_hdmi->force_audio = i;
1086e3adcf8fSFrançois Tigeot 
1087e3adcf8fSFrançois Tigeot 		if (i == HDMI_AUDIO_AUTO)
1088e3adcf8fSFrançois Tigeot 			has_audio = intel_hdmi_detect_audio(connector);
1089e3adcf8fSFrançois Tigeot 		else
1090e3adcf8fSFrançois Tigeot 			has_audio = (i == HDMI_AUDIO_ON);
1091e3adcf8fSFrançois Tigeot 
1092e3adcf8fSFrançois Tigeot 		if (i == HDMI_AUDIO_OFF_DVI)
1093e3adcf8fSFrançois Tigeot 			intel_hdmi->has_hdmi_sink = 0;
1094e3adcf8fSFrançois Tigeot 
1095e3adcf8fSFrançois Tigeot 		intel_hdmi->has_audio = has_audio;
1096e3adcf8fSFrançois Tigeot 		goto done;
1097e3adcf8fSFrançois Tigeot 	}
1098e3adcf8fSFrançois Tigeot 
1099e3adcf8fSFrançois Tigeot 	if (property == dev_priv->broadcast_rgb_property) {
11008e26cdf6SFrançois Tigeot 		bool old_auto = intel_hdmi->color_range_auto;
11018e26cdf6SFrançois Tigeot 		uint32_t old_range = intel_hdmi->color_range;
11028e26cdf6SFrançois Tigeot 
1103a2fdbec6SFrançois Tigeot 		switch (val) {
1104a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_AUTO:
1105a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = true;
1106a2fdbec6SFrançois Tigeot 			break;
1107a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_FULL:
1108a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = false;
1109a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range = 0;
1110a2fdbec6SFrançois Tigeot 			break;
1111a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_LIMITED:
1112a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = false;
11138e26cdf6SFrançois Tigeot 			intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
1114a2fdbec6SFrançois Tigeot 			break;
1115a2fdbec6SFrançois Tigeot 		default:
1116a2fdbec6SFrançois Tigeot 			return -EINVAL;
1117a2fdbec6SFrançois Tigeot 		}
11188e26cdf6SFrançois Tigeot 
11198e26cdf6SFrançois Tigeot 		if (old_auto == intel_hdmi->color_range_auto &&
11208e26cdf6SFrançois Tigeot 		    old_range == intel_hdmi->color_range)
11218e26cdf6SFrançois Tigeot 			return 0;
11228e26cdf6SFrançois Tigeot 
1123e3adcf8fSFrançois Tigeot 		goto done;
1124e3adcf8fSFrançois Tigeot 	}
1125e3adcf8fSFrançois Tigeot 
1126e3adcf8fSFrançois Tigeot 	return -EINVAL;
1127e3adcf8fSFrançois Tigeot 
1128e3adcf8fSFrançois Tigeot done:
1129a2fdbec6SFrançois Tigeot 	if (intel_dig_port->base.base.crtc)
1130a2fdbec6SFrançois Tigeot 		intel_crtc_restore_mode(intel_dig_port->base.base.crtc);
1131e3adcf8fSFrançois Tigeot 
1132e3adcf8fSFrançois Tigeot 	return 0;
1133e3adcf8fSFrançois Tigeot }
1134e3adcf8fSFrançois Tigeot 
1135*ba55f2f5SFrançois Tigeot static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
1136*ba55f2f5SFrançois Tigeot {
1137*ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
1138*ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
1139*ba55f2f5SFrançois Tigeot 	struct drm_display_mode *adjusted_mode =
1140*ba55f2f5SFrançois Tigeot 		&intel_crtc->config.adjusted_mode;
1141*ba55f2f5SFrançois Tigeot 
1142*ba55f2f5SFrançois Tigeot 	intel_hdmi_prepare(encoder);
1143*ba55f2f5SFrançois Tigeot 
1144*ba55f2f5SFrançois Tigeot 	intel_hdmi->set_infoframes(&encoder->base,
1145*ba55f2f5SFrançois Tigeot 				   intel_crtc->config.has_hdmi_sink,
1146*ba55f2f5SFrançois Tigeot 				   adjusted_mode);
1147*ba55f2f5SFrançois Tigeot }
1148*ba55f2f5SFrançois Tigeot 
11499edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
11505d0b1887SFrançois Tigeot {
11515d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1152*ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &dport->hdmi;
11535d0b1887SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
11545d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
11555d0b1887SFrançois Tigeot 	struct intel_crtc *intel_crtc =
11565d0b1887SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1157*ba55f2f5SFrançois Tigeot 	struct drm_display_mode *adjusted_mode =
1158*ba55f2f5SFrançois Tigeot 		&intel_crtc->config.adjusted_mode;
11599edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
11605d0b1887SFrançois Tigeot 	int pipe = intel_crtc->pipe;
11615d0b1887SFrançois Tigeot 	u32 val;
11625d0b1887SFrançois Tigeot 
11635d0b1887SFrançois Tigeot 	/* Enable clock channels for this port */
11649edbd4a0SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
11659edbd4a0SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port));
11665d0b1887SFrançois Tigeot 	val = 0;
11675d0b1887SFrançois Tigeot 	if (pipe)
11685d0b1887SFrançois Tigeot 		val |= (1<<21);
11695d0b1887SFrançois Tigeot 	else
11705d0b1887SFrançois Tigeot 		val &= ~(1<<21);
11715d0b1887SFrançois Tigeot 	val |= 0x001000c4;
11729edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW8(port), val);
11735d0b1887SFrançois Tigeot 
11745d0b1887SFrançois Tigeot 	/* HDMI 1.0V-2dB */
11759edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), 0);
11769edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW4(port), 0x2b245f5f);
11779edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(port), 0x5578b83a);
11789edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(port), 0x0c782040);
11799edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX3_DW4(port), 0x2b247878);
11809edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW11(port), 0x00030000);
11819edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000);
11829edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN);
11835d0b1887SFrançois Tigeot 
11845d0b1887SFrançois Tigeot 	/* Program lane clock */
11859edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW14(port), 0x00760018);
11869edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888);
11879edbd4a0SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
11889edbd4a0SFrançois Tigeot 
1189*ba55f2f5SFrançois Tigeot 	intel_hdmi->set_infoframes(&encoder->base,
1190*ba55f2f5SFrançois Tigeot 				   intel_crtc->config.has_hdmi_sink,
1191*ba55f2f5SFrançois Tigeot 				   adjusted_mode);
1192*ba55f2f5SFrançois Tigeot 
11939edbd4a0SFrançois Tigeot 	intel_enable_hdmi(encoder);
11949edbd4a0SFrançois Tigeot 
11959edbd4a0SFrançois Tigeot 	vlv_wait_port_ready(dev_priv, dport);
11965d0b1887SFrançois Tigeot }
11975d0b1887SFrançois Tigeot 
11989edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
11995d0b1887SFrançois Tigeot {
12005d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
12015d0b1887SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
12025d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
12039edbd4a0SFrançois Tigeot 	struct intel_crtc *intel_crtc =
12049edbd4a0SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
12059edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
12069edbd4a0SFrançois Tigeot 	int pipe = intel_crtc->pipe;
12075d0b1887SFrançois Tigeot 
1208*ba55f2f5SFrançois Tigeot 	intel_hdmi_prepare(encoder);
12095d0b1887SFrançois Tigeot 
12105d0b1887SFrançois Tigeot 	/* Program Tx lane resets to default */
12119edbd4a0SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
12129edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port),
12135d0b1887SFrançois Tigeot 			 DPIO_PCS_TX_LANE2_RESET |
12145d0b1887SFrançois Tigeot 			 DPIO_PCS_TX_LANE1_RESET);
12159edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port),
12165d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_CRI_RXEB_EIOS_EN |
12175d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN |
12185d0b1887SFrançois Tigeot 			 (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) |
12195d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_SOFT_RESET);
12205d0b1887SFrançois Tigeot 
12215d0b1887SFrançois Tigeot 	/* Fix up inter-pair skew failure */
12229edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW12(port), 0x00750f00);
12239edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW11(port), 0x00001500);
12249edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW14(port), 0x40400000);
12255d0b1887SFrançois Tigeot 
12269edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000);
12279edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN);
12289edbd4a0SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
12295d0b1887SFrançois Tigeot }
12305d0b1887SFrançois Tigeot 
12319edbd4a0SFrançois Tigeot static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
12325d0b1887SFrançois Tigeot {
12335d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
12345d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
12359edbd4a0SFrançois Tigeot 	struct intel_crtc *intel_crtc =
12369edbd4a0SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
12379edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
12389edbd4a0SFrançois Tigeot 	int pipe = intel_crtc->pipe;
12395d0b1887SFrançois Tigeot 
12405d0b1887SFrançois Tigeot 	/* Reset lanes to avoid HDMI flicker (VLV w/a) */
12415d0b1887SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
12429edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 0x00000000);
12439edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 0x00e00060);
12445d0b1887SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
12455d0b1887SFrançois Tigeot }
12465d0b1887SFrançois Tigeot 
1247*ba55f2f5SFrançois Tigeot static void chv_hdmi_post_disable(struct intel_encoder *encoder)
1248*ba55f2f5SFrançois Tigeot {
1249*ba55f2f5SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1250*ba55f2f5SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
1251*ba55f2f5SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1252*ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1253*ba55f2f5SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1254*ba55f2f5SFrançois Tigeot 	enum dpio_channel ch = vlv_dport_to_channel(dport);
1255*ba55f2f5SFrançois Tigeot 	enum i915_pipe pipe = intel_crtc->pipe;
1256*ba55f2f5SFrançois Tigeot 	u32 val;
1257*ba55f2f5SFrançois Tigeot 
1258*ba55f2f5SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1259*ba55f2f5SFrançois Tigeot 
1260*ba55f2f5SFrançois Tigeot 	/* Propagate soft reset to data lane reset */
1261*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
1262*ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1263*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
1264*ba55f2f5SFrançois Tigeot 
1265*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
1266*ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1267*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
1268*ba55f2f5SFrançois Tigeot 
1269*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
1270*ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1271*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
1272*ba55f2f5SFrançois Tigeot 
1273*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
1274*ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1275*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
1276*ba55f2f5SFrançois Tigeot 
1277*ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
1278*ba55f2f5SFrançois Tigeot }
1279*ba55f2f5SFrançois Tigeot 
1280*ba55f2f5SFrançois Tigeot static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
1281*ba55f2f5SFrançois Tigeot {
1282*ba55f2f5SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1283*ba55f2f5SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
1284*ba55f2f5SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1285*ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1286*ba55f2f5SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1287*ba55f2f5SFrançois Tigeot 	enum dpio_channel ch = vlv_dport_to_channel(dport);
1288*ba55f2f5SFrançois Tigeot 	int pipe = intel_crtc->pipe;
1289*ba55f2f5SFrançois Tigeot 	int data, i;
1290*ba55f2f5SFrançois Tigeot 	u32 val;
1291*ba55f2f5SFrançois Tigeot 
1292*ba55f2f5SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1293*ba55f2f5SFrançois Tigeot 
1294*ba55f2f5SFrançois Tigeot 	/* Deassert soft data lane reset*/
1295*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
1296*ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1297*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
1298*ba55f2f5SFrançois Tigeot 
1299*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
1300*ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1301*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
1302*ba55f2f5SFrançois Tigeot 
1303*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
1304*ba55f2f5SFrançois Tigeot 	val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1305*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
1306*ba55f2f5SFrançois Tigeot 
1307*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
1308*ba55f2f5SFrançois Tigeot 	val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1309*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
1310*ba55f2f5SFrançois Tigeot 
1311*ba55f2f5SFrançois Tigeot 	/* Program Tx latency optimal setting */
1312*ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1313*ba55f2f5SFrançois Tigeot 		/* Set the latency optimal bit */
1314*ba55f2f5SFrançois Tigeot 		data = (i == 1) ? 0x0 : 0x6;
1315*ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i),
1316*ba55f2f5SFrançois Tigeot 				data << DPIO_FRC_LATENCY_SHFIT);
1317*ba55f2f5SFrançois Tigeot 
1318*ba55f2f5SFrançois Tigeot 		/* Set the upar bit */
1319*ba55f2f5SFrançois Tigeot 		data = (i == 1) ? 0x0 : 0x1;
1320*ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
1321*ba55f2f5SFrançois Tigeot 				data << DPIO_UPAR_SHIFT);
1322*ba55f2f5SFrançois Tigeot 	}
1323*ba55f2f5SFrançois Tigeot 
1324*ba55f2f5SFrançois Tigeot 	/* Data lane stagger programming */
1325*ba55f2f5SFrançois Tigeot 	/* FIXME: Fix up value only after power analysis */
1326*ba55f2f5SFrançois Tigeot 
1327*ba55f2f5SFrançois Tigeot 	/* Clear calc init */
1328*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
1329*ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
1330*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
1331*ba55f2f5SFrançois Tigeot 
1332*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
1333*ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
1334*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
1335*ba55f2f5SFrançois Tigeot 
1336*ba55f2f5SFrançois Tigeot 	/* FIXME: Program the support xxx V-dB */
1337*ba55f2f5SFrançois Tigeot 	/* Use 800mV-0dB */
1338*ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1339*ba55f2f5SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
1340*ba55f2f5SFrançois Tigeot 		val &= ~DPIO_SWING_DEEMPH9P5_MASK;
1341*ba55f2f5SFrançois Tigeot 		val |= 128 << DPIO_SWING_DEEMPH9P5_SHIFT;
1342*ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val);
1343*ba55f2f5SFrançois Tigeot 	}
1344*ba55f2f5SFrançois Tigeot 
1345*ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1346*ba55f2f5SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
1347*ba55f2f5SFrançois Tigeot 		val &= ~DPIO_SWING_MARGIN_MASK;
1348*ba55f2f5SFrançois Tigeot 		val |= 102 << DPIO_SWING_MARGIN_SHIFT;
1349*ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
1350*ba55f2f5SFrançois Tigeot 	}
1351*ba55f2f5SFrançois Tigeot 
1352*ba55f2f5SFrançois Tigeot 	/* Disable unique transition scale */
1353*ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1354*ba55f2f5SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
1355*ba55f2f5SFrançois Tigeot 		val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
1356*ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
1357*ba55f2f5SFrançois Tigeot 	}
1358*ba55f2f5SFrançois Tigeot 
1359*ba55f2f5SFrançois Tigeot 	/* Additional steps for 1200mV-0dB */
1360*ba55f2f5SFrançois Tigeot #if 0
1361*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_TX_DW3(ch));
1362*ba55f2f5SFrançois Tigeot 	if (ch)
1363*ba55f2f5SFrançois Tigeot 		val |= DPIO_TX_UNIQ_TRANS_SCALE_CH1;
1364*ba55f2f5SFrançois Tigeot 	else
1365*ba55f2f5SFrançois Tigeot 		val |= DPIO_TX_UNIQ_TRANS_SCALE_CH0;
1366*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(ch), val);
1367*ba55f2f5SFrançois Tigeot 
1368*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(ch),
1369*ba55f2f5SFrançois Tigeot 			vlv_dpio_read(dev_priv, pipe, VLV_TX_DW2(ch)) |
1370*ba55f2f5SFrançois Tigeot 				(0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT));
1371*ba55f2f5SFrançois Tigeot #endif
1372*ba55f2f5SFrançois Tigeot 	/* Start swing calculation */
1373*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
1374*ba55f2f5SFrançois Tigeot 	val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
1375*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
1376*ba55f2f5SFrançois Tigeot 
1377*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
1378*ba55f2f5SFrançois Tigeot 	val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
1379*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
1380*ba55f2f5SFrançois Tigeot 
1381*ba55f2f5SFrançois Tigeot 	/* LRC Bypass */
1382*ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
1383*ba55f2f5SFrançois Tigeot 	val |= DPIO_LRC_BYPASS;
1384*ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val);
1385*ba55f2f5SFrançois Tigeot 
1386*ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
1387*ba55f2f5SFrançois Tigeot 
1388*ba55f2f5SFrançois Tigeot 	intel_enable_hdmi(encoder);
1389*ba55f2f5SFrançois Tigeot 
1390*ba55f2f5SFrançois Tigeot 	vlv_wait_port_ready(dev_priv, dport);
1391*ba55f2f5SFrançois Tigeot }
1392*ba55f2f5SFrançois Tigeot 
1393e3adcf8fSFrançois Tigeot static void intel_hdmi_destroy(struct drm_connector *connector)
1394e3adcf8fSFrançois Tigeot {
1395e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
13968e26cdf6SFrançois Tigeot 	kfree(connector);
1397e3adcf8fSFrançois Tigeot }
1398e3adcf8fSFrançois Tigeot 
1399e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
140019df918dSFrançois Tigeot 	.dpms = intel_connector_dpms,
1401e3adcf8fSFrançois Tigeot 	.detect = intel_hdmi_detect,
1402e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
1403e3adcf8fSFrançois Tigeot 	.set_property = intel_hdmi_set_property,
1404e3adcf8fSFrançois Tigeot 	.destroy = intel_hdmi_destroy,
1405e3adcf8fSFrançois Tigeot };
1406e3adcf8fSFrançois Tigeot 
1407e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
1408e3adcf8fSFrançois Tigeot 	.get_modes = intel_hdmi_get_modes,
1409e3adcf8fSFrançois Tigeot 	.mode_valid = intel_hdmi_mode_valid,
1410e3adcf8fSFrançois Tigeot 	.best_encoder = intel_best_encoder,
1411e3adcf8fSFrançois Tigeot };
1412e3adcf8fSFrançois Tigeot 
1413e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
1414e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
1415e3adcf8fSFrançois Tigeot };
1416e3adcf8fSFrançois Tigeot 
1417e3adcf8fSFrançois Tigeot static void
1418e3adcf8fSFrançois Tigeot intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
1419e3adcf8fSFrançois Tigeot {
1420e3adcf8fSFrançois Tigeot 	intel_attach_force_audio_property(connector);
1421e3adcf8fSFrançois Tigeot 	intel_attach_broadcast_rgb_property(connector);
1422a2fdbec6SFrançois Tigeot 	intel_hdmi->color_range_auto = true;
1423e3adcf8fSFrançois Tigeot }
1424e3adcf8fSFrançois Tigeot 
142519df918dSFrançois Tigeot void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
142619df918dSFrançois Tigeot 			       struct intel_connector *intel_connector)
1427e3adcf8fSFrançois Tigeot {
142819df918dSFrançois Tigeot 	struct drm_connector *connector = &intel_connector->base;
142919df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
143019df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = &intel_dig_port->base;
143119df918dSFrançois Tigeot 	struct drm_device *dev = intel_encoder->base.dev;
1432e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
143319df918dSFrançois Tigeot 	enum port port = intel_dig_port->port;
1434e3adcf8fSFrançois Tigeot 
1435e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
1436e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_HDMIA);
1437e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
1438e3adcf8fSFrançois Tigeot 
1439e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = 1;
1440e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = 0;
14419edbd4a0SFrançois Tigeot 	connector->stereo_allowed = 1;
1442e3adcf8fSFrançois Tigeot 
144319df918dSFrançois Tigeot 	switch (port) {
144419df918dSFrançois Tigeot 	case PORT_B:
1445e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
14468e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_B;
144719df918dSFrançois Tigeot 		break;
144819df918dSFrançois Tigeot 	case PORT_C:
1449e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
14508e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_C;
145119df918dSFrançois Tigeot 		break;
145219df918dSFrançois Tigeot 	case PORT_D:
1453*ba55f2f5SFrançois Tigeot 		if (IS_CHERRYVIEW(dev))
1454*ba55f2f5SFrançois Tigeot 			intel_hdmi->ddc_bus = GMBUS_PORT_DPD_CHV;
1455*ba55f2f5SFrançois Tigeot 		else
1456e3adcf8fSFrançois Tigeot 			intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
14578e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_D;
145819df918dSFrançois Tigeot 		break;
145919df918dSFrançois Tigeot 	case PORT_A:
14608e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_A;
146119df918dSFrançois Tigeot 		/* Internal port only for eDP. */
146219df918dSFrançois Tigeot 	default:
146319df918dSFrançois Tigeot 		BUG();
1464e3adcf8fSFrançois Tigeot 	}
1465e3adcf8fSFrançois Tigeot 
14668e26cdf6SFrançois Tigeot 	if (IS_VALLEYVIEW(dev)) {
146719df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = vlv_write_infoframe;
146819df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = vlv_set_infoframes;
14698e26cdf6SFrançois Tigeot 	} else if (!HAS_PCH_SPLIT(dev)) {
14708e26cdf6SFrançois Tigeot 		intel_hdmi->write_infoframe = g4x_write_infoframe;
14718e26cdf6SFrançois Tigeot 		intel_hdmi->set_infoframes = g4x_set_infoframes;
14728e26cdf6SFrançois Tigeot 	} else if (HAS_DDI(dev)) {
147319df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = hsw_write_infoframe;
147419df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = hsw_set_infoframes;
147519df918dSFrançois Tigeot 	} else if (HAS_PCH_IBX(dev)) {
147619df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = ibx_write_infoframe;
147719df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = ibx_set_infoframes;
1478e3adcf8fSFrançois Tigeot 	} else {
147919df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = cpt_write_infoframe;
148019df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = cpt_set_infoframes;
1481e3adcf8fSFrançois Tigeot 	}
1482e3adcf8fSFrançois Tigeot 
1483a2fdbec6SFrançois Tigeot 	if (HAS_DDI(dev))
148419df918dSFrançois Tigeot 		intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
148519df918dSFrançois Tigeot 	else
148619df918dSFrançois Tigeot 		intel_connector->get_hw_state = intel_connector_get_hw_state;
1487*ba55f2f5SFrançois Tigeot 	intel_connector->unregister = intel_connector_unregister;
1488e3adcf8fSFrançois Tigeot 
1489e3adcf8fSFrançois Tigeot 	intel_hdmi_add_properties(intel_hdmi, connector);
1490e3adcf8fSFrançois Tigeot 
1491e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1492e3adcf8fSFrançois Tigeot 	drm_sysfs_connector_add(connector);
1493e3adcf8fSFrançois Tigeot 
1494e3adcf8fSFrançois Tigeot 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
1495e3adcf8fSFrançois Tigeot 	 * 0xd.  Failure to do so will result in spurious interrupts being
1496e3adcf8fSFrançois Tigeot 	 * generated on the port when a cable is not attached.
1497e3adcf8fSFrançois Tigeot 	 */
1498e3adcf8fSFrançois Tigeot 	if (IS_G4X(dev) && !IS_GM45(dev)) {
1499e3adcf8fSFrançois Tigeot 		u32 temp = I915_READ(PEG_BAND_GAP_DATA);
1500e3adcf8fSFrançois Tigeot 		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
1501e3adcf8fSFrançois Tigeot 	}
1502e3adcf8fSFrançois Tigeot }
150319df918dSFrançois Tigeot 
15048e26cdf6SFrançois Tigeot void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
150519df918dSFrançois Tigeot {
150619df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port;
150719df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder;
150819df918dSFrançois Tigeot 	struct intel_connector *intel_connector;
150919df918dSFrançois Tigeot 
15109edbd4a0SFrançois Tigeot 	intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
151119df918dSFrançois Tigeot 	if (!intel_dig_port)
151219df918dSFrançois Tigeot 		return;
151319df918dSFrançois Tigeot 
15149edbd4a0SFrançois Tigeot 	intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
151519df918dSFrançois Tigeot 	if (!intel_connector) {
1516158486a6SFrançois Tigeot 		kfree(intel_dig_port);
151719df918dSFrançois Tigeot 		return;
151819df918dSFrançois Tigeot 	}
151919df918dSFrançois Tigeot 
152019df918dSFrançois Tigeot 	intel_encoder = &intel_dig_port->base;
152119df918dSFrançois Tigeot 
152219df918dSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
152319df918dSFrançois Tigeot 			 DRM_MODE_ENCODER_TMDS);
152419df918dSFrançois Tigeot 
15258e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_hdmi_compute_config;
152619df918dSFrançois Tigeot 	intel_encoder->disable = intel_disable_hdmi;
152719df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
15285d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_hdmi_get_config;
1529*ba55f2f5SFrançois Tigeot 	if (IS_CHERRYVIEW(dev)) {
1530*ba55f2f5SFrançois Tigeot 		intel_encoder->pre_enable = chv_hdmi_pre_enable;
1531*ba55f2f5SFrançois Tigeot 		intel_encoder->enable = vlv_enable_hdmi;
1532*ba55f2f5SFrançois Tigeot 		intel_encoder->post_disable = chv_hdmi_post_disable;
1533*ba55f2f5SFrançois Tigeot 	} else if (IS_VALLEYVIEW(dev)) {
15349edbd4a0SFrançois Tigeot 		intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable;
15359edbd4a0SFrançois Tigeot 		intel_encoder->pre_enable = vlv_hdmi_pre_enable;
15369edbd4a0SFrançois Tigeot 		intel_encoder->enable = vlv_enable_hdmi;
15379edbd4a0SFrançois Tigeot 		intel_encoder->post_disable = vlv_hdmi_post_disable;
15389edbd4a0SFrançois Tigeot 	} else {
1539*ba55f2f5SFrançois Tigeot 		intel_encoder->pre_enable = intel_hdmi_pre_enable;
15409edbd4a0SFrançois Tigeot 		intel_encoder->enable = intel_enable_hdmi;
15415d0b1887SFrançois Tigeot 	}
154219df918dSFrançois Tigeot 
154319df918dSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_HDMI;
1544*ba55f2f5SFrançois Tigeot 	if (IS_CHERRYVIEW(dev)) {
1545*ba55f2f5SFrançois Tigeot 		if (port == PORT_D)
1546*ba55f2f5SFrançois Tigeot 			intel_encoder->crtc_mask = 1 << 2;
1547*ba55f2f5SFrançois Tigeot 		else
1548*ba55f2f5SFrançois Tigeot 			intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1549*ba55f2f5SFrançois Tigeot 	} else {
155019df918dSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
1551*ba55f2f5SFrançois Tigeot 	}
1552*ba55f2f5SFrançois Tigeot 	intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG;
1553*ba55f2f5SFrançois Tigeot 	/*
1554*ba55f2f5SFrançois Tigeot 	 * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems
1555*ba55f2f5SFrançois Tigeot 	 * to work on real hardware. And since g4x can send infoframes to
1556*ba55f2f5SFrançois Tigeot 	 * only one port anyway, nothing is lost by allowing it.
1557*ba55f2f5SFrançois Tigeot 	 */
1558*ba55f2f5SFrançois Tigeot 	if (IS_G4X(dev))
1559*ba55f2f5SFrançois Tigeot 		intel_encoder->cloneable |= 1 << INTEL_OUTPUT_HDMI;
156019df918dSFrançois Tigeot 
156119df918dSFrançois Tigeot 	intel_dig_port->port = port;
15628e26cdf6SFrançois Tigeot 	intel_dig_port->hdmi.hdmi_reg = hdmi_reg;
156319df918dSFrançois Tigeot 	intel_dig_port->dp.output_reg = 0;
156419df918dSFrançois Tigeot 
156519df918dSFrançois Tigeot 	intel_hdmi_init_connector(intel_dig_port, intel_connector);
156619df918dSFrançois Tigeot }
1567