xref: /dflybsd-src/sys/dev/drm/i915/intel_hdmi.c (revision 24edb8848e2499ece59b84a04f554a7a897feeab)
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,
115ba55f2f5SFrançois Tigeot 				  enum transcoder cpu_transcoder,
116ba55f2f5SFranç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,
299ba55f2f5SFrançois Tigeot 					  intel_crtc->config.cpu_transcoder,
300ba55f2f5SFranç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 
369*24edb884SFrançois Tigeot 	/* Set user selected PAR to incoming mode's member */
370*24edb884SFrançois Tigeot 	adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio;
371*24edb884SFrançois Tigeot 
3729edbd4a0SFrançois Tigeot 	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
3739edbd4a0SFrançois Tigeot 						       adjusted_mode);
3749edbd4a0SFrançois Tigeot 	if (ret < 0) {
3759edbd4a0SFrançois Tigeot 		DRM_ERROR("couldn't fill AVI infoframe\n");
3769edbd4a0SFrançois Tigeot 		return;
3779edbd4a0SFrançois Tigeot 	}
37819df918dSFrançois Tigeot 
379a2fdbec6SFrançois Tigeot 	if (intel_hdmi->rgb_quant_range_selectable) {
3808e26cdf6SFrançois Tigeot 		if (intel_crtc->config.limited_color_range)
3819edbd4a0SFrançois Tigeot 			frame.avi.quantization_range =
3829edbd4a0SFrançois Tigeot 				HDMI_QUANTIZATION_RANGE_LIMITED;
383a2fdbec6SFrançois Tigeot 		else
3849edbd4a0SFrançois Tigeot 			frame.avi.quantization_range =
3859edbd4a0SFrançois Tigeot 				HDMI_QUANTIZATION_RANGE_FULL;
386a2fdbec6SFrançois Tigeot 	}
387a2fdbec6SFrançois Tigeot 
3889edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
389e3adcf8fSFrançois Tigeot }
390e3adcf8fSFrançois Tigeot 
391e3adcf8fSFrançois Tigeot static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
392e3adcf8fSFrançois Tigeot {
3939edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
3949edbd4a0SFrançois Tigeot 	int ret;
395e3adcf8fSFrançois Tigeot 
3969edbd4a0SFrançois Tigeot 	ret = hdmi_spd_infoframe_init(&frame.spd, "Intel", "Integrated gfx");
3979edbd4a0SFrançois Tigeot 	if (ret < 0) {
3989edbd4a0SFrançois Tigeot 		DRM_ERROR("couldn't fill SPD infoframe\n");
3999edbd4a0SFrançois Tigeot 		return;
4009edbd4a0SFrançois Tigeot 	}
401e3adcf8fSFrançois Tigeot 
4029edbd4a0SFrançois Tigeot 	frame.spd.sdi = HDMI_SPD_SDI_PC;
4039edbd4a0SFrançois Tigeot 
4049edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
4059edbd4a0SFrançois Tigeot }
4069edbd4a0SFrançois Tigeot 
4079edbd4a0SFrançois Tigeot static void
4089edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder,
4099edbd4a0SFrançois Tigeot 			      struct drm_display_mode *adjusted_mode)
4109edbd4a0SFrançois Tigeot {
4119edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
4129edbd4a0SFrançois Tigeot 	int ret;
4139edbd4a0SFrançois Tigeot 
4149edbd4a0SFrançois Tigeot 	ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
4159edbd4a0SFrançois Tigeot 							  adjusted_mode);
4169edbd4a0SFrançois Tigeot 	if (ret < 0)
4179edbd4a0SFrançois Tigeot 		return;
4189edbd4a0SFrançois Tigeot 
4199edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
420e3adcf8fSFrançois Tigeot }
421e3adcf8fSFrançois Tigeot 
42219df918dSFrançois Tigeot static void g4x_set_infoframes(struct drm_encoder *encoder,
423ba55f2f5SFrançois Tigeot 			       bool enable,
42419df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
42519df918dSFrançois Tigeot {
42619df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
427a2fdbec6SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
428a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
42919df918dSFrançois Tigeot 	u32 reg = VIDEO_DIP_CTL;
43019df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
431ba55f2f5SFrançois Tigeot 	u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
43219df918dSFrançois Tigeot 
43319df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
43419df918dSFrançois Tigeot 
43519df918dSFrançois Tigeot 	/* If the registers were not initialized yet, they might be zeroes,
43619df918dSFrançois Tigeot 	 * which means we're selecting the AVI DIP and we're setting its
43719df918dSFrançois Tigeot 	 * frequency to once. This seems to really confuse the HW and make
43819df918dSFrançois Tigeot 	 * things stop working (the register spec says the AVI always needs to
43919df918dSFrançois Tigeot 	 * be sent every VSync). So here we avoid writing to the register more
44019df918dSFrançois Tigeot 	 * than we need and also explicitly select the AVI DIP and explicitly
44119df918dSFrançois Tigeot 	 * set its frequency to every VSync. Avoiding to write it twice seems to
44219df918dSFrançois Tigeot 	 * be enough to solve the problem, but being defensive shouldn't hurt us
44319df918dSFrançois Tigeot 	 * either. */
44419df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
44519df918dSFrançois Tigeot 
446ba55f2f5SFrançois Tigeot 	if (!enable) {
44719df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
44819df918dSFrançois Tigeot 			return;
44919df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
45019df918dSFrançois Tigeot 		I915_WRITE(reg, val);
45119df918dSFrançois Tigeot 		POSTING_READ(reg);
45219df918dSFrançois Tigeot 		return;
45319df918dSFrançois Tigeot 	}
45419df918dSFrançois Tigeot 
45519df918dSFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
45619df918dSFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
45719df918dSFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
45819df918dSFrançois Tigeot 			I915_WRITE(reg, val);
45919df918dSFrançois Tigeot 			POSTING_READ(reg);
46019df918dSFrançois Tigeot 		}
46119df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
46219df918dSFrançois Tigeot 		val |= port;
46319df918dSFrançois Tigeot 	}
46419df918dSFrançois Tigeot 
46519df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
46619df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_ENABLE_VENDOR;
46719df918dSFrançois Tigeot 
46819df918dSFrançois Tigeot 	I915_WRITE(reg, val);
46919df918dSFrançois Tigeot 	POSTING_READ(reg);
47019df918dSFrançois Tigeot 
47119df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
47219df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
4739edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
47419df918dSFrançois Tigeot }
47519df918dSFrançois Tigeot 
47619df918dSFrançois Tigeot static void ibx_set_infoframes(struct drm_encoder *encoder,
477ba55f2f5SFrançois Tigeot 			       bool enable,
47819df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
47919df918dSFrançois Tigeot {
48019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
48119df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
482a2fdbec6SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
483a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
48419df918dSFrançois Tigeot 	u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
48519df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
486ba55f2f5SFrançois Tigeot 	u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
48719df918dSFrançois Tigeot 
48819df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
48919df918dSFrançois Tigeot 
49019df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
49119df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
49219df918dSFrançois Tigeot 
493ba55f2f5SFrançois Tigeot 	if (!enable) {
49419df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
49519df918dSFrançois Tigeot 			return;
49619df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
49719df918dSFrançois Tigeot 		I915_WRITE(reg, val);
49819df918dSFrançois Tigeot 		POSTING_READ(reg);
49919df918dSFrançois Tigeot 		return;
50019df918dSFrançois Tigeot 	}
50119df918dSFrançois Tigeot 
50219df918dSFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
50319df918dSFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
50419df918dSFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
50519df918dSFrançois Tigeot 			I915_WRITE(reg, val);
50619df918dSFrançois Tigeot 			POSTING_READ(reg);
50719df918dSFrançois Tigeot 		}
50819df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
50919df918dSFrançois Tigeot 		val |= port;
51019df918dSFrançois Tigeot 	}
51119df918dSFrançois Tigeot 
51219df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
51319df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
51419df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
51519df918dSFrançois Tigeot 
51619df918dSFrançois Tigeot 	I915_WRITE(reg, val);
51719df918dSFrançois Tigeot 	POSTING_READ(reg);
51819df918dSFrançois Tigeot 
51919df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
52019df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
5219edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
52219df918dSFrançois Tigeot }
52319df918dSFrançois Tigeot 
52419df918dSFrançois Tigeot static void cpt_set_infoframes(struct drm_encoder *encoder,
525ba55f2f5SFrançois Tigeot 			       bool enable,
52619df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
52719df918dSFrançois Tigeot {
52819df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
52919df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
53019df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
53119df918dSFrançois Tigeot 	u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
53219df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
53319df918dSFrançois Tigeot 
53419df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
53519df918dSFrançois Tigeot 
53619df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
53719df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
53819df918dSFrançois Tigeot 
539ba55f2f5SFrançois Tigeot 	if (!enable) {
54019df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
54119df918dSFrançois Tigeot 			return;
54219df918dSFrançois Tigeot 		val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
54319df918dSFrançois Tigeot 		I915_WRITE(reg, val);
54419df918dSFrançois Tigeot 		POSTING_READ(reg);
54519df918dSFrançois Tigeot 		return;
54619df918dSFrançois Tigeot 	}
54719df918dSFrançois Tigeot 
54819df918dSFrançois Tigeot 	/* Set both together, unset both together: see the spec. */
54919df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI;
55019df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
55119df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
55219df918dSFrançois Tigeot 
55319df918dSFrançois Tigeot 	I915_WRITE(reg, val);
55419df918dSFrançois Tigeot 	POSTING_READ(reg);
55519df918dSFrançois Tigeot 
55619df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
55719df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
5589edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
55919df918dSFrançois Tigeot }
56019df918dSFrançois Tigeot 
56119df918dSFrançois Tigeot static void vlv_set_infoframes(struct drm_encoder *encoder,
562ba55f2f5SFrançois Tigeot 			       bool enable,
56319df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
56419df918dSFrançois Tigeot {
56519df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
566ba55f2f5SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
56719df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
56819df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
56919df918dSFrançois Tigeot 	u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
57019df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
571ba55f2f5SFrançois Tigeot 	u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
57219df918dSFrançois Tigeot 
57319df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
57419df918dSFrançois Tigeot 
57519df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
57619df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
57719df918dSFrançois Tigeot 
578ba55f2f5SFrançois Tigeot 	if (!enable) {
57919df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
58019df918dSFrançois Tigeot 			return;
58119df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
58219df918dSFrançois Tigeot 		I915_WRITE(reg, val);
58319df918dSFrançois Tigeot 		POSTING_READ(reg);
58419df918dSFrançois Tigeot 		return;
58519df918dSFrançois Tigeot 	}
58619df918dSFrançois Tigeot 
587ba55f2f5SFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
588ba55f2f5SFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
589ba55f2f5SFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
590ba55f2f5SFrançois Tigeot 			I915_WRITE(reg, val);
591ba55f2f5SFrançois Tigeot 			POSTING_READ(reg);
592ba55f2f5SFrançois Tigeot 		}
593ba55f2f5SFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
594ba55f2f5SFrançois Tigeot 		val |= port;
595ba55f2f5SFrançois Tigeot 	}
596ba55f2f5SFrançois Tigeot 
59719df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
598ba55f2f5SFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_ENABLE_VENDOR |
599ba55f2f5SFrançois Tigeot 		 VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_GCP);
60019df918dSFrançois Tigeot 
60119df918dSFrançois Tigeot 	I915_WRITE(reg, val);
60219df918dSFrançois Tigeot 	POSTING_READ(reg);
60319df918dSFrançois Tigeot 
60419df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
60519df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
6069edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
60719df918dSFrançois Tigeot }
60819df918dSFrançois Tigeot 
60919df918dSFrançois Tigeot static void hsw_set_infoframes(struct drm_encoder *encoder,
610ba55f2f5SFrançois Tigeot 			       bool enable,
61119df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
61219df918dSFrançois Tigeot {
61319df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
61419df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
61519df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
6168e26cdf6SFrançois Tigeot 	u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
61719df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
61819df918dSFrançois Tigeot 
61919df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
62019df918dSFrançois Tigeot 
621ba55f2f5SFrançois Tigeot 	if (!enable) {
62219df918dSFrançois Tigeot 		I915_WRITE(reg, 0);
62319df918dSFrançois Tigeot 		POSTING_READ(reg);
62419df918dSFrançois Tigeot 		return;
62519df918dSFrançois Tigeot 	}
62619df918dSFrançois Tigeot 
62719df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW |
62819df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW);
62919df918dSFrançois Tigeot 
63019df918dSFrançois Tigeot 	I915_WRITE(reg, val);
63119df918dSFrançois Tigeot 	POSTING_READ(reg);
63219df918dSFrançois Tigeot 
63319df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
63419df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
6359edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
63619df918dSFrançois Tigeot }
63719df918dSFrançois Tigeot 
638ba55f2f5SFrançois Tigeot static void intel_hdmi_prepare(struct intel_encoder *encoder)
639e3adcf8fSFrançois Tigeot {
6409edbd4a0SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
641e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
6429edbd4a0SFrançois Tigeot 	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
6439edbd4a0SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
6449edbd4a0SFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
6458e26cdf6SFrançois Tigeot 	u32 hdmi_val;
646e3adcf8fSFrançois Tigeot 
6478e26cdf6SFrançois Tigeot 	hdmi_val = SDVO_ENCODING_HDMI;
6485d0b1887SFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev))
6498e26cdf6SFrançois Tigeot 		hdmi_val |= intel_hdmi->color_range;
650e3adcf8fSFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
6518e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH;
652e3adcf8fSFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
6538e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH;
654e3adcf8fSFrançois Tigeot 
6559edbd4a0SFrançois Tigeot 	if (crtc->config.pipe_bpp > 24)
6568e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_COLOR_FORMAT_12bpc;
657e3adcf8fSFrançois Tigeot 	else
6588e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_COLOR_FORMAT_8bpc;
659e3adcf8fSFrançois Tigeot 
660ba55f2f5SFrançois Tigeot 	if (crtc->config.has_hdmi_sink)
6618e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_MODE_SELECT_HDMI;
662e3adcf8fSFrançois Tigeot 
663ba55f2f5SFrançois Tigeot 	if (crtc->config.has_audio) {
664ba55f2f5SFrançois Tigeot 		WARN_ON(!crtc->config.has_hdmi_sink);
66519df918dSFrançois Tigeot 		DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
6669edbd4a0SFrançois Tigeot 				 pipe_name(crtc->pipe));
6678e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_AUDIO_ENABLE;
6689edbd4a0SFrançois Tigeot 		intel_write_eld(&encoder->base, adjusted_mode);
669e3adcf8fSFrançois Tigeot 	}
670e3adcf8fSFrançois Tigeot 
671e3adcf8fSFrançois Tigeot 	if (HAS_PCH_CPT(dev))
6729edbd4a0SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe);
673ba55f2f5SFrançois Tigeot 	else if (IS_CHERRYVIEW(dev))
674ba55f2f5SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL_CHV(crtc->pipe);
6758e26cdf6SFrançois Tigeot 	else
6769edbd4a0SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL(crtc->pipe);
677e3adcf8fSFrançois Tigeot 
6788e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val);
6798e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
680e3adcf8fSFrançois Tigeot }
681e3adcf8fSFrançois Tigeot 
68219df918dSFrançois Tigeot static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
68319df918dSFrançois Tigeot 				    enum i915_pipe *pipe)
684e3adcf8fSFrançois Tigeot {
68519df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
686e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
68719df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
688ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
68919df918dSFrançois Tigeot 	u32 tmp;
69019df918dSFrançois Tigeot 
691ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(encoder);
692ba55f2f5SFrançois Tigeot 	if (!intel_display_power_enabled(dev_priv, power_domain))
693ba55f2f5SFrançois Tigeot 		return false;
694ba55f2f5SFrançois Tigeot 
6958e26cdf6SFrançois Tigeot 	tmp = I915_READ(intel_hdmi->hdmi_reg);
69619df918dSFrançois Tigeot 
69719df918dSFrançois Tigeot 	if (!(tmp & SDVO_ENABLE))
69819df918dSFrançois Tigeot 		return false;
69919df918dSFrançois Tigeot 
70019df918dSFrançois Tigeot 	if (HAS_PCH_CPT(dev))
70119df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE_CPT(tmp);
702ba55f2f5SFrançois Tigeot 	else if (IS_CHERRYVIEW(dev))
703ba55f2f5SFrançois Tigeot 		*pipe = SDVO_PORT_TO_PIPE_CHV(tmp);
70419df918dSFrançois Tigeot 	else
70519df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE(tmp);
70619df918dSFrançois Tigeot 
70719df918dSFrançois Tigeot 	return true;
70819df918dSFrançois Tigeot }
70919df918dSFrançois Tigeot 
7105d0b1887SFrançois Tigeot static void intel_hdmi_get_config(struct intel_encoder *encoder,
7115d0b1887SFrançois Tigeot 				  struct intel_crtc_config *pipe_config)
7125d0b1887SFrançois Tigeot {
7135d0b1887SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
714*24edb884SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
715*24edb884SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
7165d0b1887SFrançois Tigeot 	u32 tmp, flags = 0;
7179edbd4a0SFrançois Tigeot 	int dotclock;
7185d0b1887SFrançois Tigeot 
7195d0b1887SFrançois Tigeot 	tmp = I915_READ(intel_hdmi->hdmi_reg);
7205d0b1887SFrançois Tigeot 
7215d0b1887SFrançois Tigeot 	if (tmp & SDVO_HSYNC_ACTIVE_HIGH)
7225d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PHSYNC;
7235d0b1887SFrançois Tigeot 	else
7245d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NHSYNC;
7255d0b1887SFrançois Tigeot 
7265d0b1887SFrançois Tigeot 	if (tmp & SDVO_VSYNC_ACTIVE_HIGH)
7275d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PVSYNC;
7285d0b1887SFrançois Tigeot 	else
7295d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NVSYNC;
7305d0b1887SFrançois Tigeot 
731ba55f2f5SFrançois Tigeot 	if (tmp & HDMI_MODE_SELECT_HDMI)
732ba55f2f5SFrançois Tigeot 		pipe_config->has_hdmi_sink = true;
733ba55f2f5SFrançois Tigeot 
734*24edb884SFrançois Tigeot 	if (tmp & SDVO_AUDIO_ENABLE)
735ba55f2f5SFrançois Tigeot 		pipe_config->has_audio = true;
736ba55f2f5SFrançois Tigeot 
737*24edb884SFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev) &&
738*24edb884SFrançois Tigeot 	    tmp & HDMI_COLOR_RANGE_16_235)
739*24edb884SFrançois Tigeot 		pipe_config->limited_color_range = true;
740*24edb884SFrançois Tigeot 
7415d0b1887SFrançois Tigeot 	pipe_config->adjusted_mode.flags |= flags;
7429edbd4a0SFrançois Tigeot 
7439edbd4a0SFrançois Tigeot 	if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
7449edbd4a0SFrançois Tigeot 		dotclock = pipe_config->port_clock * 2 / 3;
7459edbd4a0SFrançois Tigeot 	else
7469edbd4a0SFrançois Tigeot 		dotclock = pipe_config->port_clock;
7479edbd4a0SFrançois Tigeot 
7489edbd4a0SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv->dev))
7499edbd4a0SFrançois Tigeot 		ironlake_check_encoder_dotclock(pipe_config, dotclock);
7509edbd4a0SFrançois Tigeot 
7519edbd4a0SFrançois Tigeot 	pipe_config->adjusted_mode.crtc_clock = dotclock;
7525d0b1887SFrançois Tigeot }
7535d0b1887SFrançois Tigeot 
75419df918dSFrançois Tigeot static void intel_enable_hdmi(struct intel_encoder *encoder)
75519df918dSFrançois Tigeot {
75619df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
75719df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
7588e26cdf6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
75919df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
760e3adcf8fSFrançois Tigeot 	u32 temp;
761e3adcf8fSFrançois Tigeot 	u32 enable_bits = SDVO_ENABLE;
762e3adcf8fSFrançois Tigeot 
763ba55f2f5SFrançois Tigeot 	if (intel_crtc->config.has_audio)
764e3adcf8fSFrançois Tigeot 		enable_bits |= SDVO_AUDIO_ENABLE;
765e3adcf8fSFrançois Tigeot 
7668e26cdf6SFrançois Tigeot 	temp = I915_READ(intel_hdmi->hdmi_reg);
767e3adcf8fSFrançois Tigeot 
76819df918dSFrançois Tigeot 	/* HW workaround for IBX, we need to move the port to transcoder A
7698e26cdf6SFrançois Tigeot 	 * before disabling it, so restore the transcoder select bit here. */
7708e26cdf6SFrançois Tigeot 	if (HAS_PCH_IBX(dev))
7718e26cdf6SFrançois Tigeot 		enable_bits |= SDVO_PIPE_SEL(intel_crtc->pipe);
77219df918dSFrançois Tigeot 
773e3adcf8fSFrançois Tigeot 	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
774e3adcf8fSFrançois Tigeot 	 * we do this anyway which shows more stable in testing.
775e3adcf8fSFrançois Tigeot 	 */
776e3adcf8fSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
7778e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE);
7788e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
779e3adcf8fSFrançois Tigeot 	}
780e3adcf8fSFrançois Tigeot 
781e3adcf8fSFrançois Tigeot 	temp |= enable_bits;
78219df918dSFrançois Tigeot 
7838e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, temp);
7848e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
78519df918dSFrançois Tigeot 
78619df918dSFrançois Tigeot 	/* HW workaround, need to write this twice for issue that may result
78719df918dSFrançois Tigeot 	 * in first write getting masked.
78819df918dSFrançois Tigeot 	 */
78919df918dSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
7908e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp);
7918e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
792e3adcf8fSFrançois Tigeot 	}
7935d0b1887SFrançois Tigeot }
7949edbd4a0SFrançois Tigeot 
7959edbd4a0SFrançois Tigeot static void vlv_enable_hdmi(struct intel_encoder *encoder)
7969edbd4a0SFrançois Tigeot {
79719df918dSFrançois Tigeot }
79819df918dSFrançois Tigeot 
79919df918dSFrançois Tigeot static void intel_disable_hdmi(struct intel_encoder *encoder)
80019df918dSFrançois Tigeot {
80119df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
80219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
80319df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
80419df918dSFrançois Tigeot 	u32 temp;
80519df918dSFrançois Tigeot 	u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
80619df918dSFrançois Tigeot 
8078e26cdf6SFrançois Tigeot 	temp = I915_READ(intel_hdmi->hdmi_reg);
80819df918dSFrançois Tigeot 
80919df918dSFrançois Tigeot 	/* HW workaround for IBX, we need to move the port to transcoder A
81019df918dSFrançois Tigeot 	 * before disabling it. */
81119df918dSFrançois Tigeot 	if (HAS_PCH_IBX(dev)) {
81219df918dSFrançois Tigeot 		struct drm_crtc *crtc = encoder->base.crtc;
81319df918dSFrançois Tigeot 		int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
81419df918dSFrançois Tigeot 
81519df918dSFrançois Tigeot 		if (temp & SDVO_PIPE_B_SELECT) {
81619df918dSFrançois Tigeot 			temp &= ~SDVO_PIPE_B_SELECT;
8178e26cdf6SFrançois Tigeot 			I915_WRITE(intel_hdmi->hdmi_reg, temp);
8188e26cdf6SFrançois Tigeot 			POSTING_READ(intel_hdmi->hdmi_reg);
81919df918dSFrançois Tigeot 
82019df918dSFrançois Tigeot 			/* Again we need to write this twice. */
8218e26cdf6SFrançois Tigeot 			I915_WRITE(intel_hdmi->hdmi_reg, temp);
8228e26cdf6SFrançois Tigeot 			POSTING_READ(intel_hdmi->hdmi_reg);
82319df918dSFrançois Tigeot 
82419df918dSFrançois Tigeot 			/* Transcoder selection bits only update
82519df918dSFrançois Tigeot 			 * effectively on vblank. */
82619df918dSFrançois Tigeot 			if (crtc)
82719df918dSFrançois Tigeot 				intel_wait_for_vblank(dev, pipe);
82819df918dSFrançois Tigeot 			else
82919df918dSFrançois Tigeot 				msleep(50);
83019df918dSFrançois Tigeot 		}
83119df918dSFrançois Tigeot 	}
83219df918dSFrançois Tigeot 
83319df918dSFrançois Tigeot 	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
83419df918dSFrançois Tigeot 	 * we do this anyway which shows more stable in testing.
83519df918dSFrançois Tigeot 	 */
83619df918dSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
8378e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE);
8388e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
83919df918dSFrançois Tigeot 	}
84019df918dSFrançois Tigeot 
84119df918dSFrançois Tigeot 	temp &= ~enable_bits;
842e3adcf8fSFrançois Tigeot 
8438e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, temp);
8448e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
845e3adcf8fSFrançois Tigeot 
846e3adcf8fSFrançois Tigeot 	/* HW workaround, need to write this twice for issue that may result
847e3adcf8fSFrançois Tigeot 	 * in first write getting masked.
848e3adcf8fSFrançois Tigeot 	 */
849e3adcf8fSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
8508e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp);
8518e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
852e3adcf8fSFrançois Tigeot 	}
853e3adcf8fSFrançois Tigeot }
854e3adcf8fSFrançois Tigeot 
855ba55f2f5SFrançois Tigeot static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit)
8565d0b1887SFrançois Tigeot {
8575d0b1887SFrançois Tigeot 	struct drm_device *dev = intel_hdmi_to_dev(hdmi);
8585d0b1887SFrançois Tigeot 
859ba55f2f5SFrançois Tigeot 	if ((respect_dvi_limit && !hdmi->has_hdmi_sink) || IS_G4X(dev))
8605d0b1887SFrançois Tigeot 		return 165000;
8619edbd4a0SFrançois Tigeot 	else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8)
8625d0b1887SFrançois Tigeot 		return 300000;
8635d0b1887SFrançois Tigeot 	else
8645d0b1887SFrançois Tigeot 		return 225000;
8655d0b1887SFrançois Tigeot }
8665d0b1887SFrançois Tigeot 
8679edbd4a0SFrançois Tigeot static enum drm_mode_status
8689edbd4a0SFrançois Tigeot intel_hdmi_mode_valid(struct drm_connector *connector,
869e3adcf8fSFrançois Tigeot 		      struct drm_display_mode *mode)
870e3adcf8fSFrançois Tigeot {
871ba55f2f5SFrançois Tigeot 	if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector),
872ba55f2f5SFrançois Tigeot 					       true))
873e3adcf8fSFrançois Tigeot 		return MODE_CLOCK_HIGH;
874e3adcf8fSFrançois Tigeot 	if (mode->clock < 20000)
875e3adcf8fSFrançois Tigeot 		return MODE_CLOCK_LOW;
876e3adcf8fSFrançois Tigeot 
877e3adcf8fSFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
878e3adcf8fSFrançois Tigeot 		return MODE_NO_DBLESCAN;
879e3adcf8fSFrançois Tigeot 
880e3adcf8fSFrançois Tigeot 	return MODE_OK;
881e3adcf8fSFrançois Tigeot }
882e3adcf8fSFrançois Tigeot 
883ba55f2f5SFrançois Tigeot static bool hdmi_12bpc_possible(struct intel_crtc *crtc)
884ba55f2f5SFrançois Tigeot {
885ba55f2f5SFrançois Tigeot 	struct drm_device *dev = crtc->base.dev;
886ba55f2f5SFrançois Tigeot 	struct intel_encoder *encoder;
887ba55f2f5SFrançois Tigeot 	int count = 0, count_hdmi = 0;
888ba55f2f5SFrançois Tigeot 
889*24edb884SFrançois Tigeot 	if (HAS_GMCH_DISPLAY(dev))
890ba55f2f5SFrançois Tigeot 		return false;
891ba55f2f5SFrançois Tigeot 
892ba55f2f5SFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
893ba55f2f5SFrançois Tigeot 		if (encoder->new_crtc != crtc)
894ba55f2f5SFrançois Tigeot 			continue;
895ba55f2f5SFrançois Tigeot 
896ba55f2f5SFrançois Tigeot 		count_hdmi += encoder->type == INTEL_OUTPUT_HDMI;
897ba55f2f5SFrançois Tigeot 		count++;
898ba55f2f5SFrançois Tigeot 	}
899ba55f2f5SFrançois Tigeot 
900ba55f2f5SFrançois Tigeot 	/*
901ba55f2f5SFrançois Tigeot 	 * HDMI 12bpc affects the clocks, so it's only possible
902ba55f2f5SFrançois Tigeot 	 * when not cloning with other encoder types.
903ba55f2f5SFrançois Tigeot 	 */
904ba55f2f5SFrançois Tigeot 	return count_hdmi > 0 && count_hdmi == count;
905ba55f2f5SFrançois Tigeot }
906ba55f2f5SFrançois Tigeot 
9078e26cdf6SFrançois Tigeot bool intel_hdmi_compute_config(struct intel_encoder *encoder,
9088e26cdf6SFrançois Tigeot 			       struct intel_crtc_config *pipe_config)
909e3adcf8fSFrançois Tigeot {
9108e26cdf6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
9118e26cdf6SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
9128e26cdf6SFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
9139edbd4a0SFrançois Tigeot 	int clock_12bpc = pipe_config->adjusted_mode.crtc_clock * 3 / 2;
914ba55f2f5SFrançois Tigeot 	int portclock_limit = hdmi_portclock_limit(intel_hdmi, false);
9155d0b1887SFrançois Tigeot 	int desired_bpp;
916a2fdbec6SFrançois Tigeot 
917ba55f2f5SFrançois Tigeot 	pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink;
918ba55f2f5SFrançois Tigeot 
919a2fdbec6SFrançois Tigeot 	if (intel_hdmi->color_range_auto) {
920a2fdbec6SFrançois Tigeot 		/* See CEA-861-E - 5.1 Default Encoding Parameters */
921ba55f2f5SFrançois Tigeot 		if (pipe_config->has_hdmi_sink &&
922a2fdbec6SFrançois Tigeot 		    drm_match_cea_mode(adjusted_mode) > 1)
9238e26cdf6SFrançois Tigeot 			intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
924a2fdbec6SFrançois Tigeot 		else
925a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range = 0;
926a2fdbec6SFrançois Tigeot 	}
927a2fdbec6SFrançois Tigeot 
928a2fdbec6SFrançois Tigeot 	if (intel_hdmi->color_range)
9298e26cdf6SFrançois Tigeot 		pipe_config->limited_color_range = true;
9308e26cdf6SFrançois Tigeot 
9318e26cdf6SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev))
9328e26cdf6SFrançois Tigeot 		pipe_config->has_pch_encoder = true;
9338e26cdf6SFrançois Tigeot 
934ba55f2f5SFrançois Tigeot 	if (pipe_config->has_hdmi_sink && intel_hdmi->has_audio)
935ba55f2f5SFrançois Tigeot 		pipe_config->has_audio = true;
936ba55f2f5SFrançois Tigeot 
9378e26cdf6SFrançois Tigeot 	/*
9388e26cdf6SFrançois Tigeot 	 * HDMI is either 12 or 8, so if the display lets 10bpc sneak
9398e26cdf6SFrançois Tigeot 	 * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi
9405d0b1887SFrançois Tigeot 	 * outputs. We also need to check that the higher clock still fits
9415d0b1887SFrançois Tigeot 	 * within limits.
9428e26cdf6SFrançois Tigeot 	 */
943ba55f2f5SFrançois Tigeot 	if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
944ba55f2f5SFrançois Tigeot 	    clock_12bpc <= portclock_limit &&
945ba55f2f5SFrançois Tigeot 	    hdmi_12bpc_possible(encoder->new_crtc)) {
9465d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
9475d0b1887SFrançois Tigeot 		desired_bpp = 12*3;
9485d0b1887SFrançois Tigeot 
9495d0b1887SFrançois Tigeot 		/* Need to adjust the port link by 1.5x for 12bpc. */
9505d0b1887SFrançois Tigeot 		pipe_config->port_clock = clock_12bpc;
9518e26cdf6SFrançois Tigeot 	} else {
9525d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n");
9535d0b1887SFrançois Tigeot 		desired_bpp = 8*3;
9545d0b1887SFrançois Tigeot 	}
9555d0b1887SFrançois Tigeot 
9565d0b1887SFrançois Tigeot 	if (!pipe_config->bw_constrained) {
9575d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp);
9585d0b1887SFrançois Tigeot 		pipe_config->pipe_bpp = desired_bpp;
9595d0b1887SFrançois Tigeot 	}
9605d0b1887SFrançois Tigeot 
9619edbd4a0SFrançois Tigeot 	if (adjusted_mode->crtc_clock > portclock_limit) {
9625d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n");
9635d0b1887SFrançois Tigeot 		return false;
9648e26cdf6SFrançois Tigeot 	}
965a2fdbec6SFrançois Tigeot 
966e3adcf8fSFrançois Tigeot 	return true;
967e3adcf8fSFrançois Tigeot }
968e3adcf8fSFrançois Tigeot 
969e3adcf8fSFrançois Tigeot static enum drm_connector_status
970e3adcf8fSFrançois Tigeot intel_hdmi_detect(struct drm_connector *connector, bool force)
971e3adcf8fSFrançois Tigeot {
972a2fdbec6SFrançois Tigeot 	struct drm_device *dev = connector->dev;
973e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
97419df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
97519df918dSFrançois Tigeot 		hdmi_to_dig_port(intel_hdmi);
97619df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = &intel_dig_port->base;
977a2fdbec6SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
978e3adcf8fSFrançois Tigeot 	struct edid *edid;
979ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
980e3adcf8fSFrançois Tigeot 	enum drm_connector_status status = connector_status_disconnected;
981e3adcf8fSFrançois Tigeot 
9829edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
983b4efbf42Szrj 		      connector->base.id, connector->name);
984ba55f2f5SFrançois Tigeot 
985ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(intel_encoder);
986ba55f2f5SFrançois Tigeot 	intel_display_power_get(dev_priv, power_domain);
9879edbd4a0SFrançois Tigeot 
988e3adcf8fSFrançois Tigeot 	intel_hdmi->has_hdmi_sink = false;
989e3adcf8fSFrançois Tigeot 	intel_hdmi->has_audio = false;
990a2fdbec6SFrançois Tigeot 	intel_hdmi->rgb_quant_range_selectable = false;
99119df918dSFrançois Tigeot 	edid = drm_get_edid(connector,
99219df918dSFrançois Tigeot 			    intel_gmbus_get_adapter(dev_priv,
99319df918dSFrançois Tigeot 						    intel_hdmi->ddc_bus));
994e3adcf8fSFrançois Tigeot 
995e3adcf8fSFrançois Tigeot 	if (edid) {
996e3adcf8fSFrançois Tigeot 		if (edid->input & DRM_EDID_INPUT_DIGITAL) {
997e3adcf8fSFrançois Tigeot 			status = connector_status_connected;
998e3adcf8fSFrançois Tigeot 			if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
999e3adcf8fSFrançois Tigeot 				intel_hdmi->has_hdmi_sink =
1000e3adcf8fSFrançois Tigeot 						drm_detect_hdmi_monitor(edid);
1001e3adcf8fSFrançois Tigeot 			intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
1002a2fdbec6SFrançois Tigeot 			intel_hdmi->rgb_quant_range_selectable =
1003a2fdbec6SFrançois Tigeot 				drm_rgb_quant_range_selectable(edid);
1004e3adcf8fSFrançois Tigeot 		}
10058e26cdf6SFrançois Tigeot 		kfree(edid);
1006e3adcf8fSFrançois Tigeot 	}
1007e3adcf8fSFrançois Tigeot 
1008e3adcf8fSFrançois Tigeot 	if (status == connector_status_connected) {
1009e3adcf8fSFrançois Tigeot 		if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
1010e3adcf8fSFrançois Tigeot 			intel_hdmi->has_audio =
1011e3adcf8fSFrançois Tigeot 				(intel_hdmi->force_audio == HDMI_AUDIO_ON);
101219df918dSFrançois Tigeot 		intel_encoder->type = INTEL_OUTPUT_HDMI;
1013e3adcf8fSFrançois Tigeot 	}
1014e3adcf8fSFrançois Tigeot 
1015ba55f2f5SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
1016ba55f2f5SFrançois Tigeot 
1017e3adcf8fSFrançois Tigeot 	return status;
1018e3adcf8fSFrançois Tigeot }
1019e3adcf8fSFrançois Tigeot 
1020e3adcf8fSFrançois Tigeot static int intel_hdmi_get_modes(struct drm_connector *connector)
1021e3adcf8fSFrançois Tigeot {
1022ba55f2f5SFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
1023ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
1024e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1025ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
1026ba55f2f5SFrançois Tigeot 	int ret;
1027e3adcf8fSFrançois Tigeot 
1028e3adcf8fSFrançois Tigeot 	/* We should parse the EDID data and find out if it's an HDMI sink so
1029e3adcf8fSFrançois Tigeot 	 * we can send audio to it.
1030e3adcf8fSFrançois Tigeot 	 */
1031e3adcf8fSFrançois Tigeot 
1032ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(intel_encoder);
1033ba55f2f5SFrançois Tigeot 	intel_display_power_get(dev_priv, power_domain);
1034ba55f2f5SFrançois Tigeot 
1035ba55f2f5SFrançois Tigeot 	ret = intel_ddc_get_modes(connector,
103619df918dSFrançois Tigeot 				   intel_gmbus_get_adapter(dev_priv,
103719df918dSFrançois Tigeot 							   intel_hdmi->ddc_bus));
1038ba55f2f5SFrançois Tigeot 
1039ba55f2f5SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
1040ba55f2f5SFrançois Tigeot 
1041ba55f2f5SFrançois Tigeot 	return ret;
1042e3adcf8fSFrançois Tigeot }
1043e3adcf8fSFrançois Tigeot 
1044e3adcf8fSFrançois Tigeot static bool
1045e3adcf8fSFrançois Tigeot intel_hdmi_detect_audio(struct drm_connector *connector)
1046e3adcf8fSFrançois Tigeot {
1047ba55f2f5SFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
1048ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
1049e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1050ba55f2f5SFrançois Tigeot 	enum intel_display_power_domain power_domain;
1051e3adcf8fSFrançois Tigeot 	struct edid *edid;
1052e3adcf8fSFrançois Tigeot 	bool has_audio = false;
1053e3adcf8fSFrançois Tigeot 
1054ba55f2f5SFrançois Tigeot 	power_domain = intel_display_port_power_domain(intel_encoder);
1055ba55f2f5SFrançois Tigeot 	intel_display_power_get(dev_priv, power_domain);
1056ba55f2f5SFrançois Tigeot 
105719df918dSFrançois Tigeot 	edid = drm_get_edid(connector,
105819df918dSFrançois Tigeot 			    intel_gmbus_get_adapter(dev_priv,
105919df918dSFrançois Tigeot 						    intel_hdmi->ddc_bus));
1060e3adcf8fSFrançois Tigeot 	if (edid) {
1061e3adcf8fSFrançois Tigeot 		if (edid->input & DRM_EDID_INPUT_DIGITAL)
1062e3adcf8fSFrançois Tigeot 			has_audio = drm_detect_monitor_audio(edid);
10638e26cdf6SFrançois Tigeot 		kfree(edid);
1064e3adcf8fSFrançois Tigeot 	}
1065e3adcf8fSFrançois Tigeot 
1066ba55f2f5SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
1067ba55f2f5SFrançois Tigeot 
1068e3adcf8fSFrançois Tigeot 	return has_audio;
1069e3adcf8fSFrançois Tigeot }
1070e3adcf8fSFrançois Tigeot 
1071e3adcf8fSFrançois Tigeot static int
1072e3adcf8fSFrançois Tigeot intel_hdmi_set_property(struct drm_connector *connector,
1073e3adcf8fSFrançois Tigeot 			struct drm_property *property,
1074e3adcf8fSFrançois Tigeot 			uint64_t val)
1075e3adcf8fSFrançois Tigeot {
1076e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
107719df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
107819df918dSFrançois Tigeot 		hdmi_to_dig_port(intel_hdmi);
1079e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1080e3adcf8fSFrançois Tigeot 	int ret;
1081e3adcf8fSFrançois Tigeot 
1082b5162e19SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base, property, val);
1083e3adcf8fSFrançois Tigeot 	if (ret)
1084e3adcf8fSFrançois Tigeot 		return ret;
1085e3adcf8fSFrançois Tigeot 
1086e3adcf8fSFrançois Tigeot 	if (property == dev_priv->force_audio_property) {
1087e3adcf8fSFrançois Tigeot 		enum hdmi_force_audio i = val;
1088e3adcf8fSFrançois Tigeot 		bool has_audio;
1089e3adcf8fSFrançois Tigeot 
1090e3adcf8fSFrançois Tigeot 		if (i == intel_hdmi->force_audio)
1091e3adcf8fSFrançois Tigeot 			return 0;
1092e3adcf8fSFrançois Tigeot 
1093e3adcf8fSFrançois Tigeot 		intel_hdmi->force_audio = i;
1094e3adcf8fSFrançois Tigeot 
1095e3adcf8fSFrançois Tigeot 		if (i == HDMI_AUDIO_AUTO)
1096e3adcf8fSFrançois Tigeot 			has_audio = intel_hdmi_detect_audio(connector);
1097e3adcf8fSFrançois Tigeot 		else
1098e3adcf8fSFrançois Tigeot 			has_audio = (i == HDMI_AUDIO_ON);
1099e3adcf8fSFrançois Tigeot 
1100e3adcf8fSFrançois Tigeot 		if (i == HDMI_AUDIO_OFF_DVI)
1101e3adcf8fSFrançois Tigeot 			intel_hdmi->has_hdmi_sink = 0;
1102e3adcf8fSFrançois Tigeot 
1103e3adcf8fSFrançois Tigeot 		intel_hdmi->has_audio = has_audio;
1104e3adcf8fSFrançois Tigeot 		goto done;
1105e3adcf8fSFrançois Tigeot 	}
1106e3adcf8fSFrançois Tigeot 
1107e3adcf8fSFrançois Tigeot 	if (property == dev_priv->broadcast_rgb_property) {
11088e26cdf6SFrançois Tigeot 		bool old_auto = intel_hdmi->color_range_auto;
11098e26cdf6SFrançois Tigeot 		uint32_t old_range = intel_hdmi->color_range;
11108e26cdf6SFrançois Tigeot 
1111a2fdbec6SFrançois Tigeot 		switch (val) {
1112a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_AUTO:
1113a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = true;
1114a2fdbec6SFrançois Tigeot 			break;
1115a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_FULL:
1116a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = false;
1117a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range = 0;
1118a2fdbec6SFrançois Tigeot 			break;
1119a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_LIMITED:
1120a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = false;
11218e26cdf6SFrançois Tigeot 			intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
1122a2fdbec6SFrançois Tigeot 			break;
1123a2fdbec6SFrançois Tigeot 		default:
1124a2fdbec6SFrançois Tigeot 			return -EINVAL;
1125a2fdbec6SFrançois Tigeot 		}
11268e26cdf6SFrançois Tigeot 
11278e26cdf6SFrançois Tigeot 		if (old_auto == intel_hdmi->color_range_auto &&
11288e26cdf6SFrançois Tigeot 		    old_range == intel_hdmi->color_range)
11298e26cdf6SFrançois Tigeot 			return 0;
11308e26cdf6SFrançois Tigeot 
1131e3adcf8fSFrançois Tigeot 		goto done;
1132e3adcf8fSFrançois Tigeot 	}
1133e3adcf8fSFrançois Tigeot 
1134*24edb884SFrançois Tigeot 	if (property == connector->dev->mode_config.aspect_ratio_property) {
1135*24edb884SFrançois Tigeot 		switch (val) {
1136*24edb884SFrançois Tigeot 		case DRM_MODE_PICTURE_ASPECT_NONE:
1137*24edb884SFrançois Tigeot 			intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
1138*24edb884SFrançois Tigeot 			break;
1139*24edb884SFrançois Tigeot 		case DRM_MODE_PICTURE_ASPECT_4_3:
1140*24edb884SFrançois Tigeot 			intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
1141*24edb884SFrançois Tigeot 			break;
1142*24edb884SFrançois Tigeot 		case DRM_MODE_PICTURE_ASPECT_16_9:
1143*24edb884SFrançois Tigeot 			intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
1144*24edb884SFrançois Tigeot 			break;
1145*24edb884SFrançois Tigeot 		default:
1146*24edb884SFrançois Tigeot 			return -EINVAL;
1147*24edb884SFrançois Tigeot 		}
1148*24edb884SFrançois Tigeot 		goto done;
1149*24edb884SFrançois Tigeot 	}
1150*24edb884SFrançois Tigeot 
1151e3adcf8fSFrançois Tigeot 	return -EINVAL;
1152e3adcf8fSFrançois Tigeot 
1153e3adcf8fSFrançois Tigeot done:
1154a2fdbec6SFrançois Tigeot 	if (intel_dig_port->base.base.crtc)
1155a2fdbec6SFrançois Tigeot 		intel_crtc_restore_mode(intel_dig_port->base.base.crtc);
1156e3adcf8fSFrançois Tigeot 
1157e3adcf8fSFrançois Tigeot 	return 0;
1158e3adcf8fSFrançois Tigeot }
1159e3adcf8fSFrançois Tigeot 
1160ba55f2f5SFrançois Tigeot static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
1161ba55f2f5SFrançois Tigeot {
1162ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
1163ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
1164ba55f2f5SFrançois Tigeot 	struct drm_display_mode *adjusted_mode =
1165ba55f2f5SFrançois Tigeot 		&intel_crtc->config.adjusted_mode;
1166ba55f2f5SFrançois Tigeot 
1167ba55f2f5SFrançois Tigeot 	intel_hdmi_prepare(encoder);
1168ba55f2f5SFrançois Tigeot 
1169ba55f2f5SFrançois Tigeot 	intel_hdmi->set_infoframes(&encoder->base,
1170ba55f2f5SFrançois Tigeot 				   intel_crtc->config.has_hdmi_sink,
1171ba55f2f5SFrançois Tigeot 				   adjusted_mode);
1172ba55f2f5SFrançois Tigeot }
1173ba55f2f5SFrançois Tigeot 
11749edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
11755d0b1887SFrançois Tigeot {
11765d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1177ba55f2f5SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &dport->hdmi;
11785d0b1887SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
11795d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
11805d0b1887SFrançois Tigeot 	struct intel_crtc *intel_crtc =
11815d0b1887SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1182ba55f2f5SFrançois Tigeot 	struct drm_display_mode *adjusted_mode =
1183ba55f2f5SFrançois Tigeot 		&intel_crtc->config.adjusted_mode;
11849edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
11855d0b1887SFrançois Tigeot 	int pipe = intel_crtc->pipe;
11865d0b1887SFrançois Tigeot 	u32 val;
11875d0b1887SFrançois Tigeot 
11885d0b1887SFrançois Tigeot 	/* Enable clock channels for this port */
11899edbd4a0SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
11909edbd4a0SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port));
11915d0b1887SFrançois Tigeot 	val = 0;
11925d0b1887SFrançois Tigeot 	if (pipe)
11935d0b1887SFrançois Tigeot 		val |= (1<<21);
11945d0b1887SFrançois Tigeot 	else
11955d0b1887SFrançois Tigeot 		val &= ~(1<<21);
11965d0b1887SFrançois Tigeot 	val |= 0x001000c4;
11979edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW8(port), val);
11985d0b1887SFrançois Tigeot 
11995d0b1887SFrançois Tigeot 	/* HDMI 1.0V-2dB */
12009edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), 0);
12019edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW4(port), 0x2b245f5f);
12029edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(port), 0x5578b83a);
12039edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(port), 0x0c782040);
12049edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX3_DW4(port), 0x2b247878);
12059edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW11(port), 0x00030000);
12069edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000);
12079edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN);
12085d0b1887SFrançois Tigeot 
12095d0b1887SFrançois Tigeot 	/* Program lane clock */
12109edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW14(port), 0x00760018);
12119edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888);
12129edbd4a0SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
12139edbd4a0SFrançois Tigeot 
1214ba55f2f5SFrançois Tigeot 	intel_hdmi->set_infoframes(&encoder->base,
1215ba55f2f5SFrançois Tigeot 				   intel_crtc->config.has_hdmi_sink,
1216ba55f2f5SFrançois Tigeot 				   adjusted_mode);
1217ba55f2f5SFrançois Tigeot 
12189edbd4a0SFrançois Tigeot 	intel_enable_hdmi(encoder);
12199edbd4a0SFrançois Tigeot 
12209edbd4a0SFrançois Tigeot 	vlv_wait_port_ready(dev_priv, dport);
12215d0b1887SFrançois Tigeot }
12225d0b1887SFrançois Tigeot 
12239edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
12245d0b1887SFrançois Tigeot {
12255d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
12265d0b1887SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
12275d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
12289edbd4a0SFrançois Tigeot 	struct intel_crtc *intel_crtc =
12299edbd4a0SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
12309edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
12319edbd4a0SFrançois Tigeot 	int pipe = intel_crtc->pipe;
12325d0b1887SFrançois Tigeot 
1233ba55f2f5SFrançois Tigeot 	intel_hdmi_prepare(encoder);
12345d0b1887SFrançois Tigeot 
12355d0b1887SFrançois Tigeot 	/* Program Tx lane resets to default */
12369edbd4a0SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
12379edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port),
12385d0b1887SFrançois Tigeot 			 DPIO_PCS_TX_LANE2_RESET |
12395d0b1887SFrançois Tigeot 			 DPIO_PCS_TX_LANE1_RESET);
12409edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port),
12415d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_CRI_RXEB_EIOS_EN |
12425d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN |
12435d0b1887SFrançois Tigeot 			 (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) |
12445d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_SOFT_RESET);
12455d0b1887SFrançois Tigeot 
12465d0b1887SFrançois Tigeot 	/* Fix up inter-pair skew failure */
12479edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW12(port), 0x00750f00);
12489edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW11(port), 0x00001500);
12499edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW14(port), 0x40400000);
12505d0b1887SFrançois Tigeot 
12519edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000);
12529edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN);
12539edbd4a0SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
12545d0b1887SFrançois Tigeot }
12555d0b1887SFrançois Tigeot 
1256*24edb884SFrançois Tigeot static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
1257*24edb884SFrançois Tigeot {
1258*24edb884SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1259*24edb884SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
1260*24edb884SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1261*24edb884SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1262*24edb884SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1263*24edb884SFrançois Tigeot 	enum dpio_channel ch = vlv_dport_to_channel(dport);
1264*24edb884SFrançois Tigeot 	enum i915_pipe pipe = intel_crtc->pipe;
1265*24edb884SFrançois Tigeot 	u32 val;
1266*24edb884SFrançois Tigeot 
1267*24edb884SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1268*24edb884SFrançois Tigeot 
1269*24edb884SFrançois Tigeot 	/* program left/right clock distribution */
1270*24edb884SFrançois Tigeot 	if (pipe != PIPE_B) {
1271*24edb884SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0);
1272*24edb884SFrançois Tigeot 		val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK);
1273*24edb884SFrançois Tigeot 		if (ch == DPIO_CH0)
1274*24edb884SFrançois Tigeot 			val |= CHV_BUFLEFTENA1_FORCE;
1275*24edb884SFrançois Tigeot 		if (ch == DPIO_CH1)
1276*24edb884SFrançois Tigeot 			val |= CHV_BUFRIGHTENA1_FORCE;
1277*24edb884SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val);
1278*24edb884SFrançois Tigeot 	} else {
1279*24edb884SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1);
1280*24edb884SFrançois Tigeot 		val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK);
1281*24edb884SFrançois Tigeot 		if (ch == DPIO_CH0)
1282*24edb884SFrançois Tigeot 			val |= CHV_BUFLEFTENA2_FORCE;
1283*24edb884SFrançois Tigeot 		if (ch == DPIO_CH1)
1284*24edb884SFrançois Tigeot 			val |= CHV_BUFRIGHTENA2_FORCE;
1285*24edb884SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val);
1286*24edb884SFrançois Tigeot 	}
1287*24edb884SFrançois Tigeot 
1288*24edb884SFrançois Tigeot 	/* program clock channel usage */
1289*24edb884SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch));
1290*24edb884SFrançois Tigeot 	val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
1291*24edb884SFrançois Tigeot 	if (pipe != PIPE_B)
1292*24edb884SFrançois Tigeot 		val &= ~CHV_PCS_USEDCLKCHANNEL;
1293*24edb884SFrançois Tigeot 	else
1294*24edb884SFrançois Tigeot 		val |= CHV_PCS_USEDCLKCHANNEL;
1295*24edb884SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val);
1296*24edb884SFrançois Tigeot 
1297*24edb884SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch));
1298*24edb884SFrançois Tigeot 	val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
1299*24edb884SFrançois Tigeot 	if (pipe != PIPE_B)
1300*24edb884SFrançois Tigeot 		val &= ~CHV_PCS_USEDCLKCHANNEL;
1301*24edb884SFrançois Tigeot 	else
1302*24edb884SFrançois Tigeot 		val |= CHV_PCS_USEDCLKCHANNEL;
1303*24edb884SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val);
1304*24edb884SFrançois Tigeot 
1305*24edb884SFrançois Tigeot 	/*
1306*24edb884SFrançois Tigeot 	 * This a a bit weird since generally CL
1307*24edb884SFrançois Tigeot 	 * matches the pipe, but here we need to
1308*24edb884SFrançois Tigeot 	 * pick the CL based on the port.
1309*24edb884SFrançois Tigeot 	 */
1310*24edb884SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch));
1311*24edb884SFrançois Tigeot 	if (pipe != PIPE_B)
1312*24edb884SFrançois Tigeot 		val &= ~CHV_CMN_USEDCLKCHANNEL;
1313*24edb884SFrançois Tigeot 	else
1314*24edb884SFrançois Tigeot 		val |= CHV_CMN_USEDCLKCHANNEL;
1315*24edb884SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val);
1316*24edb884SFrançois Tigeot 
1317*24edb884SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
1318*24edb884SFrançois Tigeot }
1319*24edb884SFrançois Tigeot 
13209edbd4a0SFrançois Tigeot static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
13215d0b1887SFrançois Tigeot {
13225d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
13235d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
13249edbd4a0SFrançois Tigeot 	struct intel_crtc *intel_crtc =
13259edbd4a0SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
13269edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
13279edbd4a0SFrançois Tigeot 	int pipe = intel_crtc->pipe;
13285d0b1887SFrançois Tigeot 
13295d0b1887SFrançois Tigeot 	/* Reset lanes to avoid HDMI flicker (VLV w/a) */
13305d0b1887SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
13319edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 0x00000000);
13329edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 0x00e00060);
13335d0b1887SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
13345d0b1887SFrançois Tigeot }
13355d0b1887SFrançois Tigeot 
1336ba55f2f5SFrançois Tigeot static void chv_hdmi_post_disable(struct intel_encoder *encoder)
1337ba55f2f5SFrançois Tigeot {
1338ba55f2f5SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1339ba55f2f5SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
1340ba55f2f5SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1341ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1342ba55f2f5SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1343ba55f2f5SFrançois Tigeot 	enum dpio_channel ch = vlv_dport_to_channel(dport);
1344ba55f2f5SFrançois Tigeot 	enum i915_pipe pipe = intel_crtc->pipe;
1345ba55f2f5SFrançois Tigeot 	u32 val;
1346ba55f2f5SFrançois Tigeot 
1347ba55f2f5SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1348ba55f2f5SFrançois Tigeot 
1349ba55f2f5SFrançois Tigeot 	/* Propagate soft reset to data lane reset */
1350ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
1351ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1352ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
1353ba55f2f5SFrançois Tigeot 
1354ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
1355ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1356ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
1357ba55f2f5SFrançois Tigeot 
1358ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
1359ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1360ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
1361ba55f2f5SFrançois Tigeot 
1362ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
1363ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1364ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
1365ba55f2f5SFrançois Tigeot 
1366ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
1367ba55f2f5SFrançois Tigeot }
1368ba55f2f5SFrançois Tigeot 
1369ba55f2f5SFrançois Tigeot static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
1370ba55f2f5SFrançois Tigeot {
1371ba55f2f5SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
1372ba55f2f5SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
1373ba55f2f5SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1374ba55f2f5SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1375ba55f2f5SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1376ba55f2f5SFrançois Tigeot 	enum dpio_channel ch = vlv_dport_to_channel(dport);
1377ba55f2f5SFrançois Tigeot 	int pipe = intel_crtc->pipe;
1378ba55f2f5SFrançois Tigeot 	int data, i;
1379ba55f2f5SFrançois Tigeot 	u32 val;
1380ba55f2f5SFrançois Tigeot 
1381ba55f2f5SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1382ba55f2f5SFrançois Tigeot 
1383ba55f2f5SFrançois Tigeot 	/* Deassert soft data lane reset*/
1384ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
1385ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1386ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
1387ba55f2f5SFrançois Tigeot 
1388ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
1389ba55f2f5SFrançois Tigeot 	val |= CHV_PCS_REQ_SOFTRESET_EN;
1390ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
1391ba55f2f5SFrançois Tigeot 
1392ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
1393ba55f2f5SFrançois Tigeot 	val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1394ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
1395ba55f2f5SFrançois Tigeot 
1396ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
1397ba55f2f5SFrançois Tigeot 	val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
1398ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
1399ba55f2f5SFrançois Tigeot 
1400ba55f2f5SFrançois Tigeot 	/* Program Tx latency optimal setting */
1401ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1402ba55f2f5SFrançois Tigeot 		/* Set the latency optimal bit */
1403ba55f2f5SFrançois Tigeot 		data = (i == 1) ? 0x0 : 0x6;
1404ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i),
1405ba55f2f5SFrançois Tigeot 				data << DPIO_FRC_LATENCY_SHFIT);
1406ba55f2f5SFrançois Tigeot 
1407ba55f2f5SFrançois Tigeot 		/* Set the upar bit */
1408ba55f2f5SFrançois Tigeot 		data = (i == 1) ? 0x0 : 0x1;
1409ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
1410ba55f2f5SFrançois Tigeot 				data << DPIO_UPAR_SHIFT);
1411ba55f2f5SFrançois Tigeot 	}
1412ba55f2f5SFrançois Tigeot 
1413ba55f2f5SFrançois Tigeot 	/* Data lane stagger programming */
1414ba55f2f5SFrançois Tigeot 	/* FIXME: Fix up value only after power analysis */
1415ba55f2f5SFrançois Tigeot 
1416ba55f2f5SFrançois Tigeot 	/* Clear calc init */
1417ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
1418ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
1419ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
1420ba55f2f5SFrançois Tigeot 
1421ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
1422ba55f2f5SFrançois Tigeot 	val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
1423ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
1424ba55f2f5SFrançois Tigeot 
1425ba55f2f5SFrançois Tigeot 	/* FIXME: Program the support xxx V-dB */
1426ba55f2f5SFrançois Tigeot 	/* Use 800mV-0dB */
1427ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1428ba55f2f5SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
1429ba55f2f5SFrançois Tigeot 		val &= ~DPIO_SWING_DEEMPH9P5_MASK;
1430ba55f2f5SFrançois Tigeot 		val |= 128 << DPIO_SWING_DEEMPH9P5_SHIFT;
1431ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val);
1432ba55f2f5SFrançois Tigeot 	}
1433ba55f2f5SFrançois Tigeot 
1434ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1435ba55f2f5SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
1436ba55f2f5SFrançois Tigeot 		val &= ~DPIO_SWING_MARGIN_MASK;
1437ba55f2f5SFrançois Tigeot 		val |= 102 << DPIO_SWING_MARGIN_SHIFT;
1438ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
1439ba55f2f5SFrançois Tigeot 	}
1440ba55f2f5SFrançois Tigeot 
1441ba55f2f5SFrançois Tigeot 	/* Disable unique transition scale */
1442ba55f2f5SFrançois Tigeot 	for (i = 0; i < 4; i++) {
1443ba55f2f5SFrançois Tigeot 		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
1444ba55f2f5SFrançois Tigeot 		val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
1445ba55f2f5SFrançois Tigeot 		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
1446ba55f2f5SFrançois Tigeot 	}
1447ba55f2f5SFrançois Tigeot 
1448ba55f2f5SFrançois Tigeot 	/* Additional steps for 1200mV-0dB */
1449ba55f2f5SFrançois Tigeot #if 0
1450ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_TX_DW3(ch));
1451ba55f2f5SFrançois Tigeot 	if (ch)
1452ba55f2f5SFrançois Tigeot 		val |= DPIO_TX_UNIQ_TRANS_SCALE_CH1;
1453ba55f2f5SFrançois Tigeot 	else
1454ba55f2f5SFrançois Tigeot 		val |= DPIO_TX_UNIQ_TRANS_SCALE_CH0;
1455ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(ch), val);
1456ba55f2f5SFrançois Tigeot 
1457ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(ch),
1458ba55f2f5SFrançois Tigeot 			vlv_dpio_read(dev_priv, pipe, VLV_TX_DW2(ch)) |
1459ba55f2f5SFrançois Tigeot 				(0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT));
1460ba55f2f5SFrançois Tigeot #endif
1461ba55f2f5SFrançois Tigeot 	/* Start swing calculation */
1462ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
1463ba55f2f5SFrançois Tigeot 	val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
1464ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
1465ba55f2f5SFrançois Tigeot 
1466ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
1467ba55f2f5SFrançois Tigeot 	val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
1468ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
1469ba55f2f5SFrançois Tigeot 
1470ba55f2f5SFrançois Tigeot 	/* LRC Bypass */
1471ba55f2f5SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
1472ba55f2f5SFrançois Tigeot 	val |= DPIO_LRC_BYPASS;
1473ba55f2f5SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val);
1474ba55f2f5SFrançois Tigeot 
1475ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
1476ba55f2f5SFrançois Tigeot 
1477ba55f2f5SFrançois Tigeot 	intel_enable_hdmi(encoder);
1478ba55f2f5SFrançois Tigeot 
1479ba55f2f5SFrançois Tigeot 	vlv_wait_port_ready(dev_priv, dport);
1480ba55f2f5SFrançois Tigeot }
1481ba55f2f5SFrançois Tigeot 
1482e3adcf8fSFrançois Tigeot static void intel_hdmi_destroy(struct drm_connector *connector)
1483e3adcf8fSFrançois Tigeot {
1484e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
14858e26cdf6SFrançois Tigeot 	kfree(connector);
1486e3adcf8fSFrançois Tigeot }
1487e3adcf8fSFrançois Tigeot 
1488e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
148919df918dSFrançois Tigeot 	.dpms = intel_connector_dpms,
1490e3adcf8fSFrançois Tigeot 	.detect = intel_hdmi_detect,
1491e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
1492e3adcf8fSFrançois Tigeot 	.set_property = intel_hdmi_set_property,
1493e3adcf8fSFrançois Tigeot 	.destroy = intel_hdmi_destroy,
1494e3adcf8fSFrançois Tigeot };
1495e3adcf8fSFrançois Tigeot 
1496e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
1497e3adcf8fSFrançois Tigeot 	.get_modes = intel_hdmi_get_modes,
1498e3adcf8fSFrançois Tigeot 	.mode_valid = intel_hdmi_mode_valid,
1499e3adcf8fSFrançois Tigeot 	.best_encoder = intel_best_encoder,
1500e3adcf8fSFrançois Tigeot };
1501e3adcf8fSFrançois Tigeot 
1502e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
1503e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
1504e3adcf8fSFrançois Tigeot };
1505e3adcf8fSFrançois Tigeot 
1506e3adcf8fSFrançois Tigeot static void
1507*24edb884SFrançois Tigeot intel_attach_aspect_ratio_property(struct drm_connector *connector)
1508*24edb884SFrançois Tigeot {
1509*24edb884SFrançois Tigeot 	if (!drm_mode_create_aspect_ratio_property(connector->dev))
1510*24edb884SFrançois Tigeot 		drm_object_attach_property(&connector->base,
1511*24edb884SFrançois Tigeot 			connector->dev->mode_config.aspect_ratio_property,
1512*24edb884SFrançois Tigeot 			DRM_MODE_PICTURE_ASPECT_NONE);
1513*24edb884SFrançois Tigeot }
1514*24edb884SFrançois Tigeot 
1515*24edb884SFrançois Tigeot static void
1516e3adcf8fSFrançois Tigeot intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
1517e3adcf8fSFrançois Tigeot {
1518e3adcf8fSFrançois Tigeot 	intel_attach_force_audio_property(connector);
1519e3adcf8fSFrançois Tigeot 	intel_attach_broadcast_rgb_property(connector);
1520a2fdbec6SFrançois Tigeot 	intel_hdmi->color_range_auto = true;
1521*24edb884SFrançois Tigeot 	intel_attach_aspect_ratio_property(connector);
1522*24edb884SFrançois Tigeot 	intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
1523e3adcf8fSFrançois Tigeot }
1524e3adcf8fSFrançois Tigeot 
152519df918dSFrançois Tigeot void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
152619df918dSFrançois Tigeot 			       struct intel_connector *intel_connector)
1527e3adcf8fSFrançois Tigeot {
152819df918dSFrançois Tigeot 	struct drm_connector *connector = &intel_connector->base;
152919df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
153019df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = &intel_dig_port->base;
153119df918dSFrançois Tigeot 	struct drm_device *dev = intel_encoder->base.dev;
1532e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
153319df918dSFrançois Tigeot 	enum port port = intel_dig_port->port;
1534e3adcf8fSFrançois Tigeot 
1535e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
1536e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_HDMIA);
1537e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
1538e3adcf8fSFrançois Tigeot 
1539e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = 1;
1540e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = 0;
15419edbd4a0SFrançois Tigeot 	connector->stereo_allowed = 1;
1542e3adcf8fSFrançois Tigeot 
154319df918dSFrançois Tigeot 	switch (port) {
154419df918dSFrançois Tigeot 	case PORT_B:
1545e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
15468e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_B;
154719df918dSFrançois Tigeot 		break;
154819df918dSFrançois Tigeot 	case PORT_C:
1549e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
15508e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_C;
155119df918dSFrançois Tigeot 		break;
155219df918dSFrançois Tigeot 	case PORT_D:
1553ba55f2f5SFrançois Tigeot 		if (IS_CHERRYVIEW(dev))
1554ba55f2f5SFrançois Tigeot 			intel_hdmi->ddc_bus = GMBUS_PORT_DPD_CHV;
1555ba55f2f5SFrançois Tigeot 		else
1556e3adcf8fSFrançois Tigeot 			intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
15578e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_D;
155819df918dSFrançois Tigeot 		break;
155919df918dSFrançois Tigeot 	case PORT_A:
15608e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_A;
156119df918dSFrançois Tigeot 		/* Internal port only for eDP. */
156219df918dSFrançois Tigeot 	default:
156319df918dSFrançois Tigeot 		BUG();
1564e3adcf8fSFrançois Tigeot 	}
1565e3adcf8fSFrançois Tigeot 
15668e26cdf6SFrançois Tigeot 	if (IS_VALLEYVIEW(dev)) {
156719df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = vlv_write_infoframe;
156819df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = vlv_set_infoframes;
1569*24edb884SFrançois Tigeot 	} else if (IS_G4X(dev)) {
15708e26cdf6SFrançois Tigeot 		intel_hdmi->write_infoframe = g4x_write_infoframe;
15718e26cdf6SFrançois Tigeot 		intel_hdmi->set_infoframes = g4x_set_infoframes;
15728e26cdf6SFrançois Tigeot 	} else if (HAS_DDI(dev)) {
157319df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = hsw_write_infoframe;
157419df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = hsw_set_infoframes;
157519df918dSFrançois Tigeot 	} else if (HAS_PCH_IBX(dev)) {
157619df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = ibx_write_infoframe;
157719df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = ibx_set_infoframes;
1578e3adcf8fSFrançois Tigeot 	} else {
157919df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = cpt_write_infoframe;
158019df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = cpt_set_infoframes;
1581e3adcf8fSFrançois Tigeot 	}
1582e3adcf8fSFrançois Tigeot 
1583a2fdbec6SFrançois Tigeot 	if (HAS_DDI(dev))
158419df918dSFrançois Tigeot 		intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
158519df918dSFrançois Tigeot 	else
158619df918dSFrançois Tigeot 		intel_connector->get_hw_state = intel_connector_get_hw_state;
1587ba55f2f5SFrançois Tigeot 	intel_connector->unregister = intel_connector_unregister;
1588e3adcf8fSFrançois Tigeot 
1589e3adcf8fSFrançois Tigeot 	intel_hdmi_add_properties(intel_hdmi, connector);
1590e3adcf8fSFrançois Tigeot 
1591e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1592c6f73aabSFrançois Tigeot 	drm_connector_register(connector);
1593e3adcf8fSFrançois Tigeot 
1594e3adcf8fSFrançois Tigeot 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
1595e3adcf8fSFrançois Tigeot 	 * 0xd.  Failure to do so will result in spurious interrupts being
1596e3adcf8fSFrançois Tigeot 	 * generated on the port when a cable is not attached.
1597e3adcf8fSFrançois Tigeot 	 */
1598e3adcf8fSFrançois Tigeot 	if (IS_G4X(dev) && !IS_GM45(dev)) {
1599e3adcf8fSFrançois Tigeot 		u32 temp = I915_READ(PEG_BAND_GAP_DATA);
1600e3adcf8fSFrançois Tigeot 		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
1601e3adcf8fSFrançois Tigeot 	}
1602e3adcf8fSFrançois Tigeot }
160319df918dSFrançois Tigeot 
16048e26cdf6SFrançois Tigeot void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
160519df918dSFrançois Tigeot {
160619df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port;
160719df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder;
160819df918dSFrançois Tigeot 	struct intel_connector *intel_connector;
160919df918dSFrançois Tigeot 
16109edbd4a0SFrançois Tigeot 	intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
161119df918dSFrançois Tigeot 	if (!intel_dig_port)
161219df918dSFrançois Tigeot 		return;
161319df918dSFrançois Tigeot 
16149edbd4a0SFrançois Tigeot 	intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
161519df918dSFrançois Tigeot 	if (!intel_connector) {
1616158486a6SFrançois Tigeot 		kfree(intel_dig_port);
161719df918dSFrançois Tigeot 		return;
161819df918dSFrançois Tigeot 	}
161919df918dSFrançois Tigeot 
162019df918dSFrançois Tigeot 	intel_encoder = &intel_dig_port->base;
162119df918dSFrançois Tigeot 
162219df918dSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
162319df918dSFrançois Tigeot 			 DRM_MODE_ENCODER_TMDS);
162419df918dSFrançois Tigeot 
16258e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_hdmi_compute_config;
162619df918dSFrançois Tigeot 	intel_encoder->disable = intel_disable_hdmi;
162719df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
16285d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_hdmi_get_config;
1629ba55f2f5SFrançois Tigeot 	if (IS_CHERRYVIEW(dev)) {
1630*24edb884SFrançois Tigeot 		intel_encoder->pre_pll_enable = chv_hdmi_pre_pll_enable;
1631ba55f2f5SFrançois Tigeot 		intel_encoder->pre_enable = chv_hdmi_pre_enable;
1632ba55f2f5SFrançois Tigeot 		intel_encoder->enable = vlv_enable_hdmi;
1633ba55f2f5SFrançois Tigeot 		intel_encoder->post_disable = chv_hdmi_post_disable;
1634ba55f2f5SFrançois Tigeot 	} else if (IS_VALLEYVIEW(dev)) {
16359edbd4a0SFrançois Tigeot 		intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable;
16369edbd4a0SFrançois Tigeot 		intel_encoder->pre_enable = vlv_hdmi_pre_enable;
16379edbd4a0SFrançois Tigeot 		intel_encoder->enable = vlv_enable_hdmi;
16389edbd4a0SFrançois Tigeot 		intel_encoder->post_disable = vlv_hdmi_post_disable;
16399edbd4a0SFrançois Tigeot 	} else {
1640ba55f2f5SFrançois Tigeot 		intel_encoder->pre_enable = intel_hdmi_pre_enable;
16419edbd4a0SFrançois Tigeot 		intel_encoder->enable = intel_enable_hdmi;
16425d0b1887SFrançois Tigeot 	}
164319df918dSFrançois Tigeot 
164419df918dSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_HDMI;
1645ba55f2f5SFrançois Tigeot 	if (IS_CHERRYVIEW(dev)) {
1646ba55f2f5SFrançois Tigeot 		if (port == PORT_D)
1647ba55f2f5SFrançois Tigeot 			intel_encoder->crtc_mask = 1 << 2;
1648ba55f2f5SFrançois Tigeot 		else
1649ba55f2f5SFrançois Tigeot 			intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1650ba55f2f5SFrançois Tigeot 	} else {
165119df918dSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
1652ba55f2f5SFrançois Tigeot 	}
1653ba55f2f5SFrançois Tigeot 	intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG;
1654ba55f2f5SFrançois Tigeot 	/*
1655ba55f2f5SFrançois Tigeot 	 * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems
1656ba55f2f5SFrançois Tigeot 	 * to work on real hardware. And since g4x can send infoframes to
1657ba55f2f5SFrançois Tigeot 	 * only one port anyway, nothing is lost by allowing it.
1658ba55f2f5SFrançois Tigeot 	 */
1659ba55f2f5SFrançois Tigeot 	if (IS_G4X(dev))
1660ba55f2f5SFrançois Tigeot 		intel_encoder->cloneable |= 1 << INTEL_OUTPUT_HDMI;
166119df918dSFrançois Tigeot 
166219df918dSFrançois Tigeot 	intel_dig_port->port = port;
16638e26cdf6SFrançois Tigeot 	intel_dig_port->hdmi.hdmi_reg = hdmi_reg;
166419df918dSFrançois Tigeot 	intel_dig_port->dp.output_reg = 0;
166519df918dSFrançois Tigeot 
166619df918dSFrançois Tigeot 	intel_hdmi_init_connector(intel_dig_port, intel_connector);
166719df918dSFrançois Tigeot }
1668