xref: /dflybsd-src/sys/dev/drm/i915/intel_hdmi.c (revision 9edbd4a07c3138f5c4f076f77de5d722fcc606cc)
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>
31*9edbd4a0SFranç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 
69*9edbd4a0SFrançois Tigeot static u32 g4x_infoframe_index(enum hdmi_infoframe_type type)
70e3adcf8fSFrançois Tigeot {
71*9edbd4a0SFrançois Tigeot 	switch (type) {
72*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
7319df918dSFrançois Tigeot 		return VIDEO_DIP_SELECT_AVI;
74*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
7519df918dSFrançois Tigeot 		return VIDEO_DIP_SELECT_SPD;
76*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
77*9edbd4a0SFrançois Tigeot 		return VIDEO_DIP_SELECT_VENDOR;
78e3adcf8fSFrançois Tigeot 	default:
79*9edbd4a0SFranç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 
84*9edbd4a0SFrançois Tigeot static u32 g4x_infoframe_enable(enum hdmi_infoframe_type type)
85e3adcf8fSFrançois Tigeot {
86*9edbd4a0SFrançois Tigeot 	switch (type) {
87*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
8819df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_AVI;
89*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
9019df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_SPD;
91*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
92*9edbd4a0SFrançois Tigeot 		return VIDEO_DIP_ENABLE_VENDOR;
93e3adcf8fSFrançois Tigeot 	default:
94*9edbd4a0SFranç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 
99*9edbd4a0SFrançois Tigeot static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type)
10019df918dSFrançois Tigeot {
101*9edbd4a0SFrançois Tigeot 	switch (type) {
102*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
10319df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_AVI_HSW;
104*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
10519df918dSFrançois Tigeot 		return VIDEO_DIP_ENABLE_SPD_HSW;
106*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
107*9edbd4a0SFrançois Tigeot 		return VIDEO_DIP_ENABLE_VS_HSW;
10819df918dSFrançois Tigeot 	default:
109*9edbd4a0SFranç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 
114*9edbd4a0SFrançois Tigeot static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type,
1158e26cdf6SFrançois Tigeot 				  enum transcoder cpu_transcoder)
11619df918dSFrançois Tigeot {
117*9edbd4a0SFrançois Tigeot 	switch (type) {
118*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_AVI:
1198e26cdf6SFrançois Tigeot 		return HSW_TVIDEO_DIP_AVI_DATA(cpu_transcoder);
120*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_SPD:
1218e26cdf6SFrançois Tigeot 		return HSW_TVIDEO_DIP_SPD_DATA(cpu_transcoder);
122*9edbd4a0SFrançois Tigeot 	case HDMI_INFOFRAME_TYPE_VENDOR:
123*9edbd4a0SFrançois Tigeot 		return HSW_TVIDEO_DIP_VS_DATA(cpu_transcoder);
12419df918dSFrançois Tigeot 	default:
125*9edbd4a0SFrançois Tigeot 		DRM_DEBUG_DRIVER("unknown info frame type %d\n", type);
12619df918dSFrançois Tigeot 		return 0;
12719df918dSFrançois Tigeot 	}
12819df918dSFrançois Tigeot }
12919df918dSFrançois Tigeot 
13019df918dSFrançois Tigeot static void g4x_write_infoframe(struct drm_encoder *encoder,
131*9edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
132*9edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
133e3adcf8fSFrançois Tigeot {
134*9edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
135e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
136e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
13719df918dSFrançois Tigeot 	u32 val = I915_READ(VIDEO_DIP_CTL);
138*9edbd4a0SFrançois Tigeot 	int i;
139e3adcf8fSFrançois Tigeot 
14019df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
141e3adcf8fSFrançois Tigeot 
14219df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
143*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
144e3adcf8fSFrançois Tigeot 
145*9edbd4a0SFrançois Tigeot 	val &= ~g4x_infoframe_enable(type);
146e3adcf8fSFrançois Tigeot 
14719df918dSFrançois Tigeot 	I915_WRITE(VIDEO_DIP_CTL, val);
148e3adcf8fSFrançois Tigeot 
14919df918dSFrançois Tigeot 	mmiowb();
150e3adcf8fSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
151e3adcf8fSFrançois Tigeot 		I915_WRITE(VIDEO_DIP_DATA, *data);
152e3adcf8fSFrançois Tigeot 		data++;
153e3adcf8fSFrançois Tigeot 	}
15419df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
15519df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
15619df918dSFrançois Tigeot 		I915_WRITE(VIDEO_DIP_DATA, 0);
15719df918dSFrançois Tigeot 	mmiowb();
158e3adcf8fSFrançois Tigeot 
159*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
16019df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
16119df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
162e3adcf8fSFrançois Tigeot 
16319df918dSFrançois Tigeot 	I915_WRITE(VIDEO_DIP_CTL, val);
16419df918dSFrançois Tigeot 	POSTING_READ(VIDEO_DIP_CTL);
165e3adcf8fSFrançois Tigeot }
166e3adcf8fSFrançois Tigeot 
16719df918dSFrançois Tigeot static void ibx_write_infoframe(struct drm_encoder *encoder,
168*9edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
169*9edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
170e3adcf8fSFrançois Tigeot {
171*9edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
172e3adcf8fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
173e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
17419df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
175*9edbd4a0SFrançois Tigeot 	int i, reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
17619df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
177e3adcf8fSFrançois Tigeot 
17819df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
179e3adcf8fSFrançois Tigeot 
180e3adcf8fSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
181*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
182e3adcf8fSFrançois Tigeot 
183*9edbd4a0SFrançois Tigeot 	val &= ~g4x_infoframe_enable(type);
184e3adcf8fSFrançois Tigeot 
18519df918dSFrançois Tigeot 	I915_WRITE(reg, val);
18619df918dSFrançois Tigeot 
18719df918dSFrançois Tigeot 	mmiowb();
188e3adcf8fSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
189e3adcf8fSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
190e3adcf8fSFrançois Tigeot 		data++;
191e3adcf8fSFrançois Tigeot 	}
19219df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
19319df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
19419df918dSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
19519df918dSFrançois Tigeot 	mmiowb();
196e3adcf8fSFrançois Tigeot 
197*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
19819df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
19919df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
200e3adcf8fSFrançois Tigeot 
20119df918dSFrançois Tigeot 	I915_WRITE(reg, val);
20219df918dSFrançois Tigeot 	POSTING_READ(reg);
20319df918dSFrançois Tigeot }
20419df918dSFrançois Tigeot 
20519df918dSFrançois Tigeot static void cpt_write_infoframe(struct drm_encoder *encoder,
206*9edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
207*9edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
20819df918dSFrançois Tigeot {
209*9edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
21019df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
21119df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
21219df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
213*9edbd4a0SFrançois Tigeot 	int i, reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
21419df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
21519df918dSFrançois Tigeot 
21619df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
21719df918dSFrançois Tigeot 
21819df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
219*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
22019df918dSFrançois Tigeot 
22119df918dSFrançois Tigeot 	/* The DIP control register spec says that we need to update the AVI
22219df918dSFrançois Tigeot 	 * infoframe without clearing its enable bit */
223*9edbd4a0SFrançois Tigeot 	if (type != HDMI_INFOFRAME_TYPE_AVI)
224*9edbd4a0SFrançois Tigeot 		val &= ~g4x_infoframe_enable(type);
22519df918dSFrançois Tigeot 
22619df918dSFrançois Tigeot 	I915_WRITE(reg, val);
22719df918dSFrançois Tigeot 
22819df918dSFrançois Tigeot 	mmiowb();
22919df918dSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
23019df918dSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
23119df918dSFrançois Tigeot 		data++;
23219df918dSFrançois Tigeot 	}
23319df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
23419df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
23519df918dSFrançois Tigeot 		I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
23619df918dSFrançois Tigeot 	mmiowb();
23719df918dSFrançois Tigeot 
238*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
23919df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
24019df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
24119df918dSFrançois Tigeot 
24219df918dSFrançois Tigeot 	I915_WRITE(reg, val);
24319df918dSFrançois Tigeot 	POSTING_READ(reg);
24419df918dSFrançois Tigeot }
24519df918dSFrançois Tigeot 
24619df918dSFrançois Tigeot static void vlv_write_infoframe(struct drm_encoder *encoder,
247*9edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
248*9edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
24919df918dSFrançois Tigeot {
250*9edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
25119df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
25219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
25319df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
254*9edbd4a0SFrançois Tigeot 	int i, reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
25519df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
25619df918dSFrançois Tigeot 
25719df918dSFrançois Tigeot 	WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
25819df918dSFrançois Tigeot 
25919df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
260*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_index(type);
26119df918dSFrançois Tigeot 
262*9edbd4a0SFrançois Tigeot 	val &= ~g4x_infoframe_enable(type);
26319df918dSFrançois Tigeot 
26419df918dSFrançois Tigeot 	I915_WRITE(reg, val);
26519df918dSFrançois Tigeot 
26619df918dSFrançois Tigeot 	mmiowb();
26719df918dSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
26819df918dSFrançois Tigeot 		I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
26919df918dSFrançois Tigeot 		data++;
27019df918dSFrançois Tigeot 	}
27119df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
27219df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
27319df918dSFrançois Tigeot 		I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
27419df918dSFrançois Tigeot 	mmiowb();
27519df918dSFrançois Tigeot 
276*9edbd4a0SFrançois Tigeot 	val |= g4x_infoframe_enable(type);
27719df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_FREQ_MASK;
27819df918dSFrançois Tigeot 	val |= VIDEO_DIP_FREQ_VSYNC;
27919df918dSFrançois Tigeot 
28019df918dSFrançois Tigeot 	I915_WRITE(reg, val);
28119df918dSFrançois Tigeot 	POSTING_READ(reg);
28219df918dSFrançois Tigeot }
28319df918dSFrançois Tigeot 
28419df918dSFrançois Tigeot static void hsw_write_infoframe(struct drm_encoder *encoder,
285*9edbd4a0SFrançois Tigeot 				enum hdmi_infoframe_type type,
286*9edbd4a0SFrançois Tigeot 				const void *frame, ssize_t len)
28719df918dSFrançois Tigeot {
288*9edbd4a0SFrançois Tigeot 	const uint32_t *data = frame;
28919df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
29019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
29119df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
2928e26cdf6SFrançois Tigeot 	u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
293*9edbd4a0SFrançois Tigeot 	u32 data_reg;
294*9edbd4a0SFrançois Tigeot 	int i;
29519df918dSFrançois Tigeot 	u32 val = I915_READ(ctl_reg);
29619df918dSFrançois Tigeot 
297*9edbd4a0SFrançois Tigeot 	data_reg = hsw_infoframe_data_reg(type,
298*9edbd4a0SFrançois Tigeot 					  intel_crtc->config.cpu_transcoder);
29919df918dSFrançois Tigeot 	if (data_reg == 0)
30019df918dSFrançois Tigeot 		return;
30119df918dSFrançois Tigeot 
302*9edbd4a0SFrançois Tigeot 	val &= ~hsw_infoframe_enable(type);
30319df918dSFrançois Tigeot 	I915_WRITE(ctl_reg, val);
30419df918dSFrançois Tigeot 
30519df918dSFrançois Tigeot 	mmiowb();
30619df918dSFrançois Tigeot 	for (i = 0; i < len; i += 4) {
30719df918dSFrançois Tigeot 		I915_WRITE(data_reg + i, *data);
30819df918dSFrançois Tigeot 		data++;
30919df918dSFrançois Tigeot 	}
31019df918dSFrançois Tigeot 	/* Write every possible data byte to force correct ECC calculation. */
31119df918dSFrançois Tigeot 	for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
31219df918dSFrançois Tigeot 		I915_WRITE(data_reg + i, 0);
31319df918dSFrançois Tigeot 	mmiowb();
31419df918dSFrançois Tigeot 
315*9edbd4a0SFrançois Tigeot 	val |= hsw_infoframe_enable(type);
31619df918dSFrançois Tigeot 	I915_WRITE(ctl_reg, val);
31719df918dSFrançois Tigeot 	POSTING_READ(ctl_reg);
318e3adcf8fSFrançois Tigeot }
319e3adcf8fSFrançois Tigeot 
320*9edbd4a0SFrançois Tigeot /*
321*9edbd4a0SFrançois Tigeot  * The data we write to the DIP data buffer registers is 1 byte bigger than the
322*9edbd4a0SFrançois Tigeot  * HDMI infoframe size because of an ECC/reserved byte at position 3 (starting
323*9edbd4a0SFrançois Tigeot  * at 0). It's also a byte used by DisplayPort so the same DIP registers can be
324*9edbd4a0SFrançois Tigeot  * used for both technologies.
325*9edbd4a0SFrançois Tigeot  *
326*9edbd4a0SFrançois Tigeot  * DW0: Reserved/ECC/DP | HB2 | HB1 | HB0
327*9edbd4a0SFrançois Tigeot  * DW1:       DB3       | DB2 | DB1 | DB0
328*9edbd4a0SFrançois Tigeot  * DW2:       DB7       | DB6 | DB5 | DB4
329*9edbd4a0SFrançois Tigeot  * DW3: ...
330*9edbd4a0SFrançois Tigeot  *
331*9edbd4a0SFrançois Tigeot  * (HB is Header Byte, DB is Data Byte)
332*9edbd4a0SFrançois Tigeot  *
333*9edbd4a0SFrançois Tigeot  * The hdmi pack() functions don't know about that hardware specific hole so we
334*9edbd4a0SFrançois Tigeot  * trick them by giving an offset into the buffer and moving back the header
335*9edbd4a0SFrançois Tigeot  * bytes by one.
336*9edbd4a0SFrançois Tigeot  */
337*9edbd4a0SFrançois Tigeot static void intel_write_infoframe(struct drm_encoder *encoder,
338*9edbd4a0SFrançois Tigeot 				  union hdmi_infoframe *frame)
339e3adcf8fSFrançois Tigeot {
340e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
341*9edbd4a0SFrançois Tigeot 	uint8_t buffer[VIDEO_DIP_DATA_SIZE];
342*9edbd4a0SFrançois Tigeot 	ssize_t len;
343e3adcf8fSFrançois Tigeot 
344*9edbd4a0SFrançois Tigeot 	/* see comment above for the reason for this offset */
345*9edbd4a0SFrançois Tigeot 	len = hdmi_infoframe_pack(frame, buffer + 1, sizeof(buffer) - 1);
346*9edbd4a0SFrançois Tigeot 	if (len < 0)
347*9edbd4a0SFrançois Tigeot 		return;
348*9edbd4a0SFrançois Tigeot 
349*9edbd4a0SFrançois Tigeot 	/* Insert the 'hole' (see big comment above) at position 3 */
350*9edbd4a0SFrançois Tigeot 	buffer[0] = buffer[1];
351*9edbd4a0SFrançois Tigeot 	buffer[1] = buffer[2];
352*9edbd4a0SFrançois Tigeot 	buffer[2] = buffer[3];
353*9edbd4a0SFrançois Tigeot 	buffer[3] = 0;
354*9edbd4a0SFrançois Tigeot 	len++;
355*9edbd4a0SFrançois Tigeot 
356*9edbd4a0SFrançois Tigeot 	intel_hdmi->write_infoframe(encoder, frame->any.type, buffer, len);
357e3adcf8fSFrançois Tigeot }
358e3adcf8fSFrançois Tigeot 
35919df918dSFrançois Tigeot static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
36019df918dSFrançois Tigeot 					 struct drm_display_mode *adjusted_mode)
361e3adcf8fSFrançois Tigeot {
362a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
3638e26cdf6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
364*9edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
365*9edbd4a0SFrançois Tigeot 	int ret;
366e3adcf8fSFrançois Tigeot 
367*9edbd4a0SFrançois Tigeot 	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
368*9edbd4a0SFrançois Tigeot 						       adjusted_mode);
369*9edbd4a0SFrançois Tigeot 	if (ret < 0) {
370*9edbd4a0SFrançois Tigeot 		DRM_ERROR("couldn't fill AVI infoframe\n");
371*9edbd4a0SFrançois Tigeot 		return;
372*9edbd4a0SFrançois Tigeot 	}
37319df918dSFrançois Tigeot 
374a2fdbec6SFrançois Tigeot 	if (intel_hdmi->rgb_quant_range_selectable) {
3758e26cdf6SFrançois Tigeot 		if (intel_crtc->config.limited_color_range)
376*9edbd4a0SFrançois Tigeot 			frame.avi.quantization_range =
377*9edbd4a0SFrançois Tigeot 				HDMI_QUANTIZATION_RANGE_LIMITED;
378a2fdbec6SFrançois Tigeot 		else
379*9edbd4a0SFrançois Tigeot 			frame.avi.quantization_range =
380*9edbd4a0SFrançois Tigeot 				HDMI_QUANTIZATION_RANGE_FULL;
381a2fdbec6SFrançois Tigeot 	}
382a2fdbec6SFrançois Tigeot 
383*9edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
384e3adcf8fSFrançois Tigeot }
385e3adcf8fSFrançois Tigeot 
386e3adcf8fSFrançois Tigeot static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
387e3adcf8fSFrançois Tigeot {
388*9edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
389*9edbd4a0SFrançois Tigeot 	int ret;
390e3adcf8fSFrançois Tigeot 
391*9edbd4a0SFrançois Tigeot 	ret = hdmi_spd_infoframe_init(&frame.spd, "Intel", "Integrated gfx");
392*9edbd4a0SFrançois Tigeot 	if (ret < 0) {
393*9edbd4a0SFrançois Tigeot 		DRM_ERROR("couldn't fill SPD infoframe\n");
394*9edbd4a0SFrançois Tigeot 		return;
395*9edbd4a0SFrançois Tigeot 	}
396e3adcf8fSFrançois Tigeot 
397*9edbd4a0SFrançois Tigeot 	frame.spd.sdi = HDMI_SPD_SDI_PC;
398*9edbd4a0SFrançois Tigeot 
399*9edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
400*9edbd4a0SFrançois Tigeot }
401*9edbd4a0SFrançois Tigeot 
402*9edbd4a0SFrançois Tigeot static void
403*9edbd4a0SFrançois Tigeot intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder,
404*9edbd4a0SFrançois Tigeot 			      struct drm_display_mode *adjusted_mode)
405*9edbd4a0SFrançois Tigeot {
406*9edbd4a0SFrançois Tigeot 	union hdmi_infoframe frame;
407*9edbd4a0SFrançois Tigeot 	int ret;
408*9edbd4a0SFrançois Tigeot 
409*9edbd4a0SFrançois Tigeot 	ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
410*9edbd4a0SFrançois Tigeot 							  adjusted_mode);
411*9edbd4a0SFrançois Tigeot 	if (ret < 0)
412*9edbd4a0SFrançois Tigeot 		return;
413*9edbd4a0SFrançois Tigeot 
414*9edbd4a0SFrançois Tigeot 	intel_write_infoframe(encoder, &frame);
415e3adcf8fSFrançois Tigeot }
416e3adcf8fSFrançois Tigeot 
41719df918dSFrançois Tigeot static void g4x_set_infoframes(struct drm_encoder *encoder,
41819df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
41919df918dSFrançois Tigeot {
42019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
421a2fdbec6SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
422a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
42319df918dSFrançois Tigeot 	u32 reg = VIDEO_DIP_CTL;
42419df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
42519df918dSFrançois Tigeot 	u32 port;
42619df918dSFrançois Tigeot 
42719df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
42819df918dSFrançois Tigeot 
42919df918dSFrançois Tigeot 	/* If the registers were not initialized yet, they might be zeroes,
43019df918dSFrançois Tigeot 	 * which means we're selecting the AVI DIP and we're setting its
43119df918dSFrançois Tigeot 	 * frequency to once. This seems to really confuse the HW and make
43219df918dSFrançois Tigeot 	 * things stop working (the register spec says the AVI always needs to
43319df918dSFrançois Tigeot 	 * be sent every VSync). So here we avoid writing to the register more
43419df918dSFrançois Tigeot 	 * than we need and also explicitly select the AVI DIP and explicitly
43519df918dSFrançois Tigeot 	 * set its frequency to every VSync. Avoiding to write it twice seems to
43619df918dSFrançois Tigeot 	 * be enough to solve the problem, but being defensive shouldn't hurt us
43719df918dSFrançois Tigeot 	 * either. */
43819df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
43919df918dSFrançois Tigeot 
44019df918dSFrançois Tigeot 	if (!intel_hdmi->has_hdmi_sink) {
44119df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
44219df918dSFrançois Tigeot 			return;
44319df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
44419df918dSFrançois Tigeot 		I915_WRITE(reg, val);
44519df918dSFrançois Tigeot 		POSTING_READ(reg);
44619df918dSFrançois Tigeot 		return;
44719df918dSFrançois Tigeot 	}
44819df918dSFrançois Tigeot 
449a2fdbec6SFrançois Tigeot 	switch (intel_dig_port->port) {
450a2fdbec6SFrançois Tigeot 	case PORT_B:
45119df918dSFrançois Tigeot 		port = VIDEO_DIP_PORT_B;
45219df918dSFrançois Tigeot 		break;
453a2fdbec6SFrançois Tigeot 	case PORT_C:
45419df918dSFrançois Tigeot 		port = VIDEO_DIP_PORT_C;
45519df918dSFrançois Tigeot 		break;
45619df918dSFrançois Tigeot 	default:
45719df918dSFrançois Tigeot 		BUG();
45819df918dSFrançois Tigeot 		return;
45919df918dSFrançois Tigeot 	}
46019df918dSFrançois Tigeot 
46119df918dSFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
46219df918dSFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
46319df918dSFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
46419df918dSFrançois Tigeot 			I915_WRITE(reg, val);
46519df918dSFrançois Tigeot 			POSTING_READ(reg);
46619df918dSFrançois Tigeot 		}
46719df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
46819df918dSFrançois Tigeot 		val |= port;
46919df918dSFrançois Tigeot 	}
47019df918dSFrançois Tigeot 
47119df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
47219df918dSFrançois Tigeot 	val &= ~VIDEO_DIP_ENABLE_VENDOR;
47319df918dSFrançois Tigeot 
47419df918dSFrançois Tigeot 	I915_WRITE(reg, val);
47519df918dSFrançois Tigeot 	POSTING_READ(reg);
47619df918dSFrançois Tigeot 
47719df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
47819df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
479*9edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
48019df918dSFrançois Tigeot }
48119df918dSFrançois Tigeot 
48219df918dSFrançois Tigeot static void ibx_set_infoframes(struct drm_encoder *encoder,
48319df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
48419df918dSFrançois Tigeot {
48519df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
48619df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
487a2fdbec6SFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
488a2fdbec6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
48919df918dSFrançois Tigeot 	u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
49019df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
49119df918dSFrançois Tigeot 	u32 port;
49219df918dSFrançois Tigeot 
49319df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
49419df918dSFrançois Tigeot 
49519df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
49619df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
49719df918dSFrançois Tigeot 
49819df918dSFrançois Tigeot 	if (!intel_hdmi->has_hdmi_sink) {
49919df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
50019df918dSFrançois Tigeot 			return;
50119df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
50219df918dSFrançois Tigeot 		I915_WRITE(reg, val);
50319df918dSFrançois Tigeot 		POSTING_READ(reg);
50419df918dSFrançois Tigeot 		return;
50519df918dSFrançois Tigeot 	}
50619df918dSFrançois Tigeot 
507a2fdbec6SFrançois Tigeot 	switch (intel_dig_port->port) {
508a2fdbec6SFrançois Tigeot 	case PORT_B:
50919df918dSFrançois Tigeot 		port = VIDEO_DIP_PORT_B;
51019df918dSFrançois Tigeot 		break;
511a2fdbec6SFrançois Tigeot 	case PORT_C:
51219df918dSFrançois Tigeot 		port = VIDEO_DIP_PORT_C;
51319df918dSFrançois Tigeot 		break;
514a2fdbec6SFrançois Tigeot 	case PORT_D:
51519df918dSFrançois Tigeot 		port = VIDEO_DIP_PORT_D;
51619df918dSFrançois Tigeot 		break;
51719df918dSFrançois Tigeot 	default:
51819df918dSFrançois Tigeot 		BUG();
51919df918dSFrançois Tigeot 		return;
52019df918dSFrançois Tigeot 	}
52119df918dSFrançois Tigeot 
52219df918dSFrançois Tigeot 	if (port != (val & VIDEO_DIP_PORT_MASK)) {
52319df918dSFrançois Tigeot 		if (val & VIDEO_DIP_ENABLE) {
52419df918dSFrançois Tigeot 			val &= ~VIDEO_DIP_ENABLE;
52519df918dSFrançois Tigeot 			I915_WRITE(reg, val);
52619df918dSFrançois Tigeot 			POSTING_READ(reg);
52719df918dSFrançois Tigeot 		}
52819df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_PORT_MASK;
52919df918dSFrançois Tigeot 		val |= port;
53019df918dSFrançois Tigeot 	}
53119df918dSFrançois Tigeot 
53219df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
53319df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
53419df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
53519df918dSFrançois Tigeot 
53619df918dSFrançois Tigeot 	I915_WRITE(reg, val);
53719df918dSFrançois Tigeot 	POSTING_READ(reg);
53819df918dSFrançois Tigeot 
53919df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
54019df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
541*9edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
54219df918dSFrançois Tigeot }
54319df918dSFrançois Tigeot 
54419df918dSFrançois Tigeot static void cpt_set_infoframes(struct drm_encoder *encoder,
54519df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
54619df918dSFrançois Tigeot {
54719df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
54819df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
54919df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
55019df918dSFrançois Tigeot 	u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
55119df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
55219df918dSFrançois Tigeot 
55319df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
55419df918dSFrançois Tigeot 
55519df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
55619df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
55719df918dSFrançois Tigeot 
55819df918dSFrançois Tigeot 	if (!intel_hdmi->has_hdmi_sink) {
55919df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
56019df918dSFrançois Tigeot 			return;
56119df918dSFrançois Tigeot 		val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
56219df918dSFrançois Tigeot 		I915_WRITE(reg, val);
56319df918dSFrançois Tigeot 		POSTING_READ(reg);
56419df918dSFrançois Tigeot 		return;
56519df918dSFrançois Tigeot 	}
56619df918dSFrançois Tigeot 
56719df918dSFrançois Tigeot 	/* Set both together, unset both together: see the spec. */
56819df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI;
56919df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
57019df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
57119df918dSFrançois Tigeot 
57219df918dSFrançois Tigeot 	I915_WRITE(reg, val);
57319df918dSFrançois Tigeot 	POSTING_READ(reg);
57419df918dSFrançois Tigeot 
57519df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
57619df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
577*9edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
57819df918dSFrançois Tigeot }
57919df918dSFrançois Tigeot 
58019df918dSFrançois Tigeot static void vlv_set_infoframes(struct drm_encoder *encoder,
58119df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
58219df918dSFrançois Tigeot {
58319df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
58419df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
58519df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
58619df918dSFrançois Tigeot 	u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
58719df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
58819df918dSFrançois Tigeot 
58919df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
59019df918dSFrançois Tigeot 
59119df918dSFrançois Tigeot 	/* See the big comment in g4x_set_infoframes() */
59219df918dSFrançois Tigeot 	val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
59319df918dSFrançois Tigeot 
59419df918dSFrançois Tigeot 	if (!intel_hdmi->has_hdmi_sink) {
59519df918dSFrançois Tigeot 		if (!(val & VIDEO_DIP_ENABLE))
59619df918dSFrançois Tigeot 			return;
59719df918dSFrançois Tigeot 		val &= ~VIDEO_DIP_ENABLE;
59819df918dSFrançois Tigeot 		I915_WRITE(reg, val);
59919df918dSFrançois Tigeot 		POSTING_READ(reg);
60019df918dSFrançois Tigeot 		return;
60119df918dSFrançois Tigeot 	}
60219df918dSFrançois Tigeot 
60319df918dSFrançois Tigeot 	val |= VIDEO_DIP_ENABLE;
60419df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
60519df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_GCP);
60619df918dSFrançois Tigeot 
60719df918dSFrançois Tigeot 	I915_WRITE(reg, val);
60819df918dSFrançois Tigeot 	POSTING_READ(reg);
60919df918dSFrançois Tigeot 
61019df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
61119df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
612*9edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
61319df918dSFrançois Tigeot }
61419df918dSFrançois Tigeot 
61519df918dSFrançois Tigeot static void hsw_set_infoframes(struct drm_encoder *encoder,
61619df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
61719df918dSFrançois Tigeot {
61819df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
61919df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
62019df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
6218e26cdf6SFrançois Tigeot 	u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config.cpu_transcoder);
62219df918dSFrançois Tigeot 	u32 val = I915_READ(reg);
62319df918dSFrançois Tigeot 
62419df918dSFrançois Tigeot 	assert_hdmi_port_disabled(intel_hdmi);
62519df918dSFrançois Tigeot 
62619df918dSFrançois Tigeot 	if (!intel_hdmi->has_hdmi_sink) {
62719df918dSFrançois Tigeot 		I915_WRITE(reg, 0);
62819df918dSFrançois Tigeot 		POSTING_READ(reg);
62919df918dSFrançois Tigeot 		return;
63019df918dSFrançois Tigeot 	}
63119df918dSFrançois Tigeot 
63219df918dSFrançois Tigeot 	val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW |
63319df918dSFrançois Tigeot 		 VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW);
63419df918dSFrançois Tigeot 
63519df918dSFrançois Tigeot 	I915_WRITE(reg, val);
63619df918dSFrançois Tigeot 	POSTING_READ(reg);
63719df918dSFrançois Tigeot 
63819df918dSFrançois Tigeot 	intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
63919df918dSFrançois Tigeot 	intel_hdmi_set_spd_infoframe(encoder);
640*9edbd4a0SFrançois Tigeot 	intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
64119df918dSFrançois Tigeot }
64219df918dSFrançois Tigeot 
643*9edbd4a0SFrançois Tigeot static void intel_hdmi_mode_set(struct intel_encoder *encoder)
644e3adcf8fSFrançois Tigeot {
645*9edbd4a0SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
646e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
647*9edbd4a0SFrançois Tigeot 	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
648*9edbd4a0SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
649*9edbd4a0SFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
6508e26cdf6SFrançois Tigeot 	u32 hdmi_val;
651e3adcf8fSFrançois Tigeot 
6528e26cdf6SFrançois Tigeot 	hdmi_val = SDVO_ENCODING_HDMI;
6535d0b1887SFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev))
6548e26cdf6SFrançois Tigeot 		hdmi_val |= intel_hdmi->color_range;
655e3adcf8fSFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
6568e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH;
657e3adcf8fSFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
6588e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH;
659e3adcf8fSFrançois Tigeot 
660*9edbd4a0SFrançois Tigeot 	if (crtc->config.pipe_bpp > 24)
6618e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_COLOR_FORMAT_12bpc;
662e3adcf8fSFrançois Tigeot 	else
6638e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_COLOR_FORMAT_8bpc;
664e3adcf8fSFrançois Tigeot 
665e3adcf8fSFrançois Tigeot 	/* Required on CPT */
666e3adcf8fSFrançois Tigeot 	if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
6678e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_MODE_SELECT_HDMI;
668e3adcf8fSFrançois Tigeot 
669e3adcf8fSFrançois Tigeot 	if (intel_hdmi->has_audio) {
67019df918dSFrançois Tigeot 		DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
671*9edbd4a0SFrançois Tigeot 				 pipe_name(crtc->pipe));
6728e26cdf6SFrançois Tigeot 		hdmi_val |= SDVO_AUDIO_ENABLE;
6738e26cdf6SFrançois Tigeot 		hdmi_val |= HDMI_MODE_SELECT_HDMI;
674*9edbd4a0SFrançois Tigeot 		intel_write_eld(&encoder->base, adjusted_mode);
675e3adcf8fSFrançois Tigeot 	}
676e3adcf8fSFrançois Tigeot 
677e3adcf8fSFrançois Tigeot 	if (HAS_PCH_CPT(dev))
678*9edbd4a0SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe);
6798e26cdf6SFrançois Tigeot 	else
680*9edbd4a0SFrançois Tigeot 		hdmi_val |= SDVO_PIPE_SEL(crtc->pipe);
681e3adcf8fSFrançois Tigeot 
6828e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val);
6838e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
684e3adcf8fSFrançois Tigeot 
685*9edbd4a0SFrançois Tigeot 	intel_hdmi->set_infoframes(&encoder->base, adjusted_mode);
686e3adcf8fSFrançois Tigeot }
687e3adcf8fSFrançois Tigeot 
68819df918dSFrançois Tigeot static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
68919df918dSFrançois Tigeot 				    enum i915_pipe *pipe)
690e3adcf8fSFrançois Tigeot {
69119df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
692e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
69319df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
69419df918dSFrançois Tigeot 	u32 tmp;
69519df918dSFrançois Tigeot 
6968e26cdf6SFrançois Tigeot 	tmp = I915_READ(intel_hdmi->hdmi_reg);
69719df918dSFrançois Tigeot 
69819df918dSFrançois Tigeot 	if (!(tmp & SDVO_ENABLE))
69919df918dSFrançois Tigeot 		return false;
70019df918dSFrançois Tigeot 
70119df918dSFrançois Tigeot 	if (HAS_PCH_CPT(dev))
70219df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE_CPT(tmp);
70319df918dSFrançois Tigeot 	else
70419df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE(tmp);
70519df918dSFrançois Tigeot 
70619df918dSFrançois Tigeot 	return true;
70719df918dSFrançois Tigeot }
70819df918dSFrançois Tigeot 
7095d0b1887SFrançois Tigeot static void intel_hdmi_get_config(struct intel_encoder *encoder,
7105d0b1887SFrançois Tigeot 				  struct intel_crtc_config *pipe_config)
7115d0b1887SFrançois Tigeot {
7125d0b1887SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
7135d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
7145d0b1887SFrançois Tigeot 	u32 tmp, flags = 0;
715*9edbd4a0SFrançois Tigeot 	int dotclock;
7165d0b1887SFrançois Tigeot 
7175d0b1887SFrançois Tigeot 	tmp = I915_READ(intel_hdmi->hdmi_reg);
7185d0b1887SFrançois Tigeot 
7195d0b1887SFrançois Tigeot 	if (tmp & SDVO_HSYNC_ACTIVE_HIGH)
7205d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PHSYNC;
7215d0b1887SFrançois Tigeot 	else
7225d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NHSYNC;
7235d0b1887SFrançois Tigeot 
7245d0b1887SFrançois Tigeot 	if (tmp & SDVO_VSYNC_ACTIVE_HIGH)
7255d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PVSYNC;
7265d0b1887SFrançois Tigeot 	else
7275d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NVSYNC;
7285d0b1887SFrançois Tigeot 
7295d0b1887SFrançois Tigeot 	pipe_config->adjusted_mode.flags |= flags;
730*9edbd4a0SFrançois Tigeot 
731*9edbd4a0SFrançois Tigeot 	if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
732*9edbd4a0SFrançois Tigeot 		dotclock = pipe_config->port_clock * 2 / 3;
733*9edbd4a0SFrançois Tigeot 	else
734*9edbd4a0SFrançois Tigeot 		dotclock = pipe_config->port_clock;
735*9edbd4a0SFrançois Tigeot 
736*9edbd4a0SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv->dev))
737*9edbd4a0SFrançois Tigeot 		ironlake_check_encoder_dotclock(pipe_config, dotclock);
738*9edbd4a0SFrançois Tigeot 
739*9edbd4a0SFrançois Tigeot 	pipe_config->adjusted_mode.crtc_clock = dotclock;
7405d0b1887SFrançois Tigeot }
7415d0b1887SFrançois Tigeot 
74219df918dSFrançois Tigeot static void intel_enable_hdmi(struct intel_encoder *encoder)
74319df918dSFrançois Tigeot {
74419df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
74519df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
7468e26cdf6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
74719df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
748e3adcf8fSFrançois Tigeot 	u32 temp;
749e3adcf8fSFrançois Tigeot 	u32 enable_bits = SDVO_ENABLE;
750e3adcf8fSFrançois Tigeot 
751e3adcf8fSFrançois Tigeot 	if (intel_hdmi->has_audio)
752e3adcf8fSFrançois Tigeot 		enable_bits |= SDVO_AUDIO_ENABLE;
753e3adcf8fSFrançois Tigeot 
7548e26cdf6SFrançois Tigeot 	temp = I915_READ(intel_hdmi->hdmi_reg);
755e3adcf8fSFrançois Tigeot 
75619df918dSFrançois Tigeot 	/* HW workaround for IBX, we need to move the port to transcoder A
7578e26cdf6SFrançois Tigeot 	 * before disabling it, so restore the transcoder select bit here. */
7588e26cdf6SFrançois Tigeot 	if (HAS_PCH_IBX(dev))
7598e26cdf6SFrançois Tigeot 		enable_bits |= SDVO_PIPE_SEL(intel_crtc->pipe);
76019df918dSFrançois Tigeot 
761e3adcf8fSFrançois Tigeot 	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
762e3adcf8fSFrançois Tigeot 	 * we do this anyway which shows more stable in testing.
763e3adcf8fSFrançois Tigeot 	 */
764e3adcf8fSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
7658e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE);
7668e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
767e3adcf8fSFrançois Tigeot 	}
768e3adcf8fSFrançois Tigeot 
769e3adcf8fSFrançois Tigeot 	temp |= enable_bits;
77019df918dSFrançois Tigeot 
7718e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, temp);
7728e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
77319df918dSFrançois Tigeot 
77419df918dSFrançois Tigeot 	/* HW workaround, need to write this twice for issue that may result
77519df918dSFrançois Tigeot 	 * in first write getting masked.
77619df918dSFrançois Tigeot 	 */
77719df918dSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
7788e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp);
7798e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
780e3adcf8fSFrançois Tigeot 	}
7815d0b1887SFrançois Tigeot }
782*9edbd4a0SFrançois Tigeot 
783*9edbd4a0SFrançois Tigeot static void vlv_enable_hdmi(struct intel_encoder *encoder)
784*9edbd4a0SFrançois Tigeot {
78519df918dSFrançois Tigeot }
78619df918dSFrançois Tigeot 
78719df918dSFrançois Tigeot static void intel_disable_hdmi(struct intel_encoder *encoder)
78819df918dSFrançois Tigeot {
78919df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
79019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
79119df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
79219df918dSFrançois Tigeot 	u32 temp;
79319df918dSFrançois Tigeot 	u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
79419df918dSFrançois Tigeot 
7958e26cdf6SFrançois Tigeot 	temp = I915_READ(intel_hdmi->hdmi_reg);
79619df918dSFrançois Tigeot 
79719df918dSFrançois Tigeot 	/* HW workaround for IBX, we need to move the port to transcoder A
79819df918dSFrançois Tigeot 	 * before disabling it. */
79919df918dSFrançois Tigeot 	if (HAS_PCH_IBX(dev)) {
80019df918dSFrançois Tigeot 		struct drm_crtc *crtc = encoder->base.crtc;
80119df918dSFrançois Tigeot 		int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
80219df918dSFrançois Tigeot 
80319df918dSFrançois Tigeot 		if (temp & SDVO_PIPE_B_SELECT) {
80419df918dSFrançois Tigeot 			temp &= ~SDVO_PIPE_B_SELECT;
8058e26cdf6SFrançois Tigeot 			I915_WRITE(intel_hdmi->hdmi_reg, temp);
8068e26cdf6SFrançois Tigeot 			POSTING_READ(intel_hdmi->hdmi_reg);
80719df918dSFrançois Tigeot 
80819df918dSFrançois Tigeot 			/* Again we need to write this twice. */
8098e26cdf6SFrançois Tigeot 			I915_WRITE(intel_hdmi->hdmi_reg, temp);
8108e26cdf6SFrançois Tigeot 			POSTING_READ(intel_hdmi->hdmi_reg);
81119df918dSFrançois Tigeot 
81219df918dSFrançois Tigeot 			/* Transcoder selection bits only update
81319df918dSFrançois Tigeot 			 * effectively on vblank. */
81419df918dSFrançois Tigeot 			if (crtc)
81519df918dSFrançois Tigeot 				intel_wait_for_vblank(dev, pipe);
81619df918dSFrançois Tigeot 			else
81719df918dSFrançois Tigeot 				msleep(50);
81819df918dSFrançois Tigeot 		}
81919df918dSFrançois Tigeot 	}
82019df918dSFrançois Tigeot 
82119df918dSFrançois Tigeot 	/* HW workaround, need to toggle enable bit off and on for 12bpc, but
82219df918dSFrançois Tigeot 	 * we do this anyway which shows more stable in testing.
82319df918dSFrançois Tigeot 	 */
82419df918dSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
8258e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE);
8268e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
82719df918dSFrançois Tigeot 	}
82819df918dSFrançois Tigeot 
82919df918dSFrançois Tigeot 	temp &= ~enable_bits;
830e3adcf8fSFrançois Tigeot 
8318e26cdf6SFrançois Tigeot 	I915_WRITE(intel_hdmi->hdmi_reg, temp);
8328e26cdf6SFrançois Tigeot 	POSTING_READ(intel_hdmi->hdmi_reg);
833e3adcf8fSFrançois Tigeot 
834e3adcf8fSFrançois Tigeot 	/* HW workaround, need to write this twice for issue that may result
835e3adcf8fSFrançois Tigeot 	 * in first write getting masked.
836e3adcf8fSFrançois Tigeot 	 */
837e3adcf8fSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev)) {
8388e26cdf6SFrançois Tigeot 		I915_WRITE(intel_hdmi->hdmi_reg, temp);
8398e26cdf6SFrançois Tigeot 		POSTING_READ(intel_hdmi->hdmi_reg);
840e3adcf8fSFrançois Tigeot 	}
841e3adcf8fSFrançois Tigeot }
842e3adcf8fSFrançois Tigeot 
8435d0b1887SFrançois Tigeot static int hdmi_portclock_limit(struct intel_hdmi *hdmi)
8445d0b1887SFrançois Tigeot {
8455d0b1887SFrançois Tigeot 	struct drm_device *dev = intel_hdmi_to_dev(hdmi);
8465d0b1887SFrançois Tigeot 
847*9edbd4a0SFrançois Tigeot 	if (!hdmi->has_hdmi_sink || IS_G4X(dev))
8485d0b1887SFrançois Tigeot 		return 165000;
849*9edbd4a0SFrançois Tigeot 	else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8)
8505d0b1887SFrançois Tigeot 		return 300000;
8515d0b1887SFrançois Tigeot 	else
8525d0b1887SFrançois Tigeot 		return 225000;
8535d0b1887SFrançois Tigeot }
8545d0b1887SFrançois Tigeot 
855*9edbd4a0SFrançois Tigeot static enum drm_mode_status
856*9edbd4a0SFrançois Tigeot intel_hdmi_mode_valid(struct drm_connector *connector,
857e3adcf8fSFrançois Tigeot 		      struct drm_display_mode *mode)
858e3adcf8fSFrançois Tigeot {
8595d0b1887SFrançois Tigeot 	if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector)))
860e3adcf8fSFrançois Tigeot 		return MODE_CLOCK_HIGH;
861e3adcf8fSFrançois Tigeot 	if (mode->clock < 20000)
862e3adcf8fSFrançois Tigeot 		return MODE_CLOCK_LOW;
863e3adcf8fSFrançois Tigeot 
864e3adcf8fSFrançois Tigeot 	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
865e3adcf8fSFrançois Tigeot 		return MODE_NO_DBLESCAN;
866e3adcf8fSFrançois Tigeot 
867e3adcf8fSFrançois Tigeot 	return MODE_OK;
868e3adcf8fSFrançois Tigeot }
869e3adcf8fSFrançois Tigeot 
8708e26cdf6SFrançois Tigeot bool intel_hdmi_compute_config(struct intel_encoder *encoder,
8718e26cdf6SFrançois Tigeot 			       struct intel_crtc_config *pipe_config)
872e3adcf8fSFrançois Tigeot {
8738e26cdf6SFrançois Tigeot 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
8748e26cdf6SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
8758e26cdf6SFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
876*9edbd4a0SFrançois Tigeot 	int clock_12bpc = pipe_config->adjusted_mode.crtc_clock * 3 / 2;
8775d0b1887SFrançois Tigeot 	int portclock_limit = hdmi_portclock_limit(intel_hdmi);
8785d0b1887SFrançois Tigeot 	int desired_bpp;
879a2fdbec6SFrançois Tigeot 
880a2fdbec6SFrançois Tigeot 	if (intel_hdmi->color_range_auto) {
881a2fdbec6SFrançois Tigeot 		/* See CEA-861-E - 5.1 Default Encoding Parameters */
882a2fdbec6SFrançois Tigeot 		if (intel_hdmi->has_hdmi_sink &&
883a2fdbec6SFrançois Tigeot 		    drm_match_cea_mode(adjusted_mode) > 1)
8848e26cdf6SFrançois Tigeot 			intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
885a2fdbec6SFrançois Tigeot 		else
886a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range = 0;
887a2fdbec6SFrançois Tigeot 	}
888a2fdbec6SFrançois Tigeot 
889a2fdbec6SFrançois Tigeot 	if (intel_hdmi->color_range)
8908e26cdf6SFrançois Tigeot 		pipe_config->limited_color_range = true;
8918e26cdf6SFrançois Tigeot 
8928e26cdf6SFrançois Tigeot 	if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev))
8938e26cdf6SFrançois Tigeot 		pipe_config->has_pch_encoder = true;
8948e26cdf6SFrançois Tigeot 
8958e26cdf6SFrançois Tigeot 	/*
8968e26cdf6SFrançois Tigeot 	 * HDMI is either 12 or 8, so if the display lets 10bpc sneak
8978e26cdf6SFrançois Tigeot 	 * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi
8985d0b1887SFrançois Tigeot 	 * outputs. We also need to check that the higher clock still fits
8995d0b1887SFrançois Tigeot 	 * within limits.
9008e26cdf6SFrançois Tigeot 	 */
901*9edbd4a0SFrançois Tigeot 	if (pipe_config->pipe_bpp > 8*3 && intel_hdmi->has_hdmi_sink &&
902*9edbd4a0SFrançois Tigeot 	    clock_12bpc <= portclock_limit && HAS_PCH_SPLIT(dev)) {
9035d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
9045d0b1887SFrançois Tigeot 		desired_bpp = 12*3;
9055d0b1887SFrançois Tigeot 
9065d0b1887SFrançois Tigeot 		/* Need to adjust the port link by 1.5x for 12bpc. */
9075d0b1887SFrançois Tigeot 		pipe_config->port_clock = clock_12bpc;
9088e26cdf6SFrançois Tigeot 	} else {
9095d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n");
9105d0b1887SFrançois Tigeot 		desired_bpp = 8*3;
9115d0b1887SFrançois Tigeot 	}
9125d0b1887SFrançois Tigeot 
9135d0b1887SFrançois Tigeot 	if (!pipe_config->bw_constrained) {
9145d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp);
9155d0b1887SFrançois Tigeot 		pipe_config->pipe_bpp = desired_bpp;
9165d0b1887SFrançois Tigeot 	}
9175d0b1887SFrançois Tigeot 
918*9edbd4a0SFrançois Tigeot 	if (adjusted_mode->crtc_clock > portclock_limit) {
9195d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n");
9205d0b1887SFrançois Tigeot 		return false;
9218e26cdf6SFrançois Tigeot 	}
922a2fdbec6SFrançois Tigeot 
923e3adcf8fSFrançois Tigeot 	return true;
924e3adcf8fSFrançois Tigeot }
925e3adcf8fSFrançois Tigeot 
926e3adcf8fSFrançois Tigeot static enum drm_connector_status
927e3adcf8fSFrançois Tigeot intel_hdmi_detect(struct drm_connector *connector, bool force)
928e3adcf8fSFrançois Tigeot {
929a2fdbec6SFrançois Tigeot 	struct drm_device *dev = connector->dev;
930e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
93119df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
93219df918dSFrançois Tigeot 		hdmi_to_dig_port(intel_hdmi);
93319df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = &intel_dig_port->base;
934a2fdbec6SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
935e3adcf8fSFrançois Tigeot 	struct edid *edid;
936e3adcf8fSFrançois Tigeot 	enum drm_connector_status status = connector_status_disconnected;
937e3adcf8fSFrançois Tigeot 
938*9edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
939*9edbd4a0SFrançois Tigeot 		      connector->base.id, drm_get_connector_name(connector));
940*9edbd4a0SFrançois Tigeot 
941e3adcf8fSFrançois Tigeot 	intel_hdmi->has_hdmi_sink = false;
942e3adcf8fSFrançois Tigeot 	intel_hdmi->has_audio = false;
943a2fdbec6SFrançois Tigeot 	intel_hdmi->rgb_quant_range_selectable = false;
94419df918dSFrançois Tigeot 	edid = drm_get_edid(connector,
94519df918dSFrançois Tigeot 			    intel_gmbus_get_adapter(dev_priv,
94619df918dSFrançois Tigeot 						    intel_hdmi->ddc_bus));
947e3adcf8fSFrançois Tigeot 
948e3adcf8fSFrançois Tigeot 	if (edid) {
949e3adcf8fSFrançois Tigeot 		if (edid->input & DRM_EDID_INPUT_DIGITAL) {
950e3adcf8fSFrançois Tigeot 			status = connector_status_connected;
951e3adcf8fSFrançois Tigeot 			if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
952e3adcf8fSFrançois Tigeot 				intel_hdmi->has_hdmi_sink =
953e3adcf8fSFrançois Tigeot 						drm_detect_hdmi_monitor(edid);
954e3adcf8fSFrançois Tigeot 			intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
955a2fdbec6SFrançois Tigeot 			intel_hdmi->rgb_quant_range_selectable =
956a2fdbec6SFrançois Tigeot 				drm_rgb_quant_range_selectable(edid);
957e3adcf8fSFrançois Tigeot 		}
9588e26cdf6SFrançois Tigeot 		kfree(edid);
959e3adcf8fSFrançois Tigeot 	}
960e3adcf8fSFrançois Tigeot 
961e3adcf8fSFrançois Tigeot 	if (status == connector_status_connected) {
962e3adcf8fSFrançois Tigeot 		if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
963e3adcf8fSFrançois Tigeot 			intel_hdmi->has_audio =
964e3adcf8fSFrançois Tigeot 				(intel_hdmi->force_audio == HDMI_AUDIO_ON);
96519df918dSFrançois Tigeot 		intel_encoder->type = INTEL_OUTPUT_HDMI;
966e3adcf8fSFrançois Tigeot 	}
967e3adcf8fSFrançois Tigeot 
968e3adcf8fSFrançois Tigeot 	return status;
969e3adcf8fSFrançois Tigeot }
970e3adcf8fSFrançois Tigeot 
971e3adcf8fSFrançois Tigeot static int intel_hdmi_get_modes(struct drm_connector *connector)
972e3adcf8fSFrançois Tigeot {
973e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
974e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
975e3adcf8fSFrançois Tigeot 
976e3adcf8fSFrançois Tigeot 	/* We should parse the EDID data and find out if it's an HDMI sink so
977e3adcf8fSFrançois Tigeot 	 * we can send audio to it.
978e3adcf8fSFrançois Tigeot 	 */
979e3adcf8fSFrançois Tigeot 
980e3adcf8fSFrançois Tigeot 	return intel_ddc_get_modes(connector,
98119df918dSFrançois Tigeot 				   intel_gmbus_get_adapter(dev_priv,
98219df918dSFrançois Tigeot 							   intel_hdmi->ddc_bus));
983e3adcf8fSFrançois Tigeot }
984e3adcf8fSFrançois Tigeot 
985e3adcf8fSFrançois Tigeot static bool
986e3adcf8fSFrançois Tigeot intel_hdmi_detect_audio(struct drm_connector *connector)
987e3adcf8fSFrançois Tigeot {
988e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
989e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
990e3adcf8fSFrançois Tigeot 	struct edid *edid;
991e3adcf8fSFrançois Tigeot 	bool has_audio = false;
992e3adcf8fSFrançois Tigeot 
99319df918dSFrançois Tigeot 	edid = drm_get_edid(connector,
99419df918dSFrançois Tigeot 			    intel_gmbus_get_adapter(dev_priv,
99519df918dSFrançois Tigeot 						    intel_hdmi->ddc_bus));
996e3adcf8fSFrançois Tigeot 	if (edid) {
997e3adcf8fSFrançois Tigeot 		if (edid->input & DRM_EDID_INPUT_DIGITAL)
998e3adcf8fSFrançois Tigeot 			has_audio = drm_detect_monitor_audio(edid);
9998e26cdf6SFrançois Tigeot 		kfree(edid);
1000e3adcf8fSFrançois Tigeot 	}
1001e3adcf8fSFrançois Tigeot 
1002e3adcf8fSFrançois Tigeot 	return has_audio;
1003e3adcf8fSFrançois Tigeot }
1004e3adcf8fSFrançois Tigeot 
1005e3adcf8fSFrançois Tigeot static int
1006e3adcf8fSFrançois Tigeot intel_hdmi_set_property(struct drm_connector *connector,
1007e3adcf8fSFrançois Tigeot 			struct drm_property *property,
1008e3adcf8fSFrançois Tigeot 			uint64_t val)
1009e3adcf8fSFrançois Tigeot {
1010e3adcf8fSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
101119df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port =
101219df918dSFrançois Tigeot 		hdmi_to_dig_port(intel_hdmi);
1013e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
1014e3adcf8fSFrançois Tigeot 	int ret;
1015e3adcf8fSFrançois Tigeot 
1016b5162e19SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base, property, val);
1017e3adcf8fSFrançois Tigeot 	if (ret)
1018e3adcf8fSFrançois Tigeot 		return ret;
1019e3adcf8fSFrançois Tigeot 
1020e3adcf8fSFrançois Tigeot 	if (property == dev_priv->force_audio_property) {
1021e3adcf8fSFrançois Tigeot 		enum hdmi_force_audio i = val;
1022e3adcf8fSFrançois Tigeot 		bool has_audio;
1023e3adcf8fSFrançois Tigeot 
1024e3adcf8fSFrançois Tigeot 		if (i == intel_hdmi->force_audio)
1025e3adcf8fSFrançois Tigeot 			return 0;
1026e3adcf8fSFrançois Tigeot 
1027e3adcf8fSFrançois Tigeot 		intel_hdmi->force_audio = i;
1028e3adcf8fSFrançois Tigeot 
1029e3adcf8fSFrançois Tigeot 		if (i == HDMI_AUDIO_AUTO)
1030e3adcf8fSFrançois Tigeot 			has_audio = intel_hdmi_detect_audio(connector);
1031e3adcf8fSFrançois Tigeot 		else
1032e3adcf8fSFrançois Tigeot 			has_audio = (i == HDMI_AUDIO_ON);
1033e3adcf8fSFrançois Tigeot 
1034e3adcf8fSFrançois Tigeot 		if (i == HDMI_AUDIO_OFF_DVI)
1035e3adcf8fSFrançois Tigeot 			intel_hdmi->has_hdmi_sink = 0;
1036e3adcf8fSFrançois Tigeot 
1037e3adcf8fSFrançois Tigeot 		intel_hdmi->has_audio = has_audio;
1038e3adcf8fSFrançois Tigeot 		goto done;
1039e3adcf8fSFrançois Tigeot 	}
1040e3adcf8fSFrançois Tigeot 
1041e3adcf8fSFrançois Tigeot 	if (property == dev_priv->broadcast_rgb_property) {
10428e26cdf6SFrançois Tigeot 		bool old_auto = intel_hdmi->color_range_auto;
10438e26cdf6SFrançois Tigeot 		uint32_t old_range = intel_hdmi->color_range;
10448e26cdf6SFrançois Tigeot 
1045a2fdbec6SFrançois Tigeot 		switch (val) {
1046a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_AUTO:
1047a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = true;
1048a2fdbec6SFrançois Tigeot 			break;
1049a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_FULL:
1050a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = false;
1051a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range = 0;
1052a2fdbec6SFrançois Tigeot 			break;
1053a2fdbec6SFrançois Tigeot 		case INTEL_BROADCAST_RGB_LIMITED:
1054a2fdbec6SFrançois Tigeot 			intel_hdmi->color_range_auto = false;
10558e26cdf6SFrançois Tigeot 			intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
1056a2fdbec6SFrançois Tigeot 			break;
1057a2fdbec6SFrançois Tigeot 		default:
1058a2fdbec6SFrançois Tigeot 			return -EINVAL;
1059a2fdbec6SFrançois Tigeot 		}
10608e26cdf6SFrançois Tigeot 
10618e26cdf6SFrançois Tigeot 		if (old_auto == intel_hdmi->color_range_auto &&
10628e26cdf6SFrançois Tigeot 		    old_range == intel_hdmi->color_range)
10638e26cdf6SFrançois Tigeot 			return 0;
10648e26cdf6SFrançois Tigeot 
1065e3adcf8fSFrançois Tigeot 		goto done;
1066e3adcf8fSFrançois Tigeot 	}
1067e3adcf8fSFrançois Tigeot 
1068e3adcf8fSFrançois Tigeot 	return -EINVAL;
1069e3adcf8fSFrançois Tigeot 
1070e3adcf8fSFrançois Tigeot done:
1071a2fdbec6SFrançois Tigeot 	if (intel_dig_port->base.base.crtc)
1072a2fdbec6SFrançois Tigeot 		intel_crtc_restore_mode(intel_dig_port->base.base.crtc);
1073e3adcf8fSFrançois Tigeot 
1074e3adcf8fSFrançois Tigeot 	return 0;
1075e3adcf8fSFrançois Tigeot }
1076e3adcf8fSFrançois Tigeot 
1077*9edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
10785d0b1887SFrançois Tigeot {
10795d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
10805d0b1887SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
10815d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
10825d0b1887SFrançois Tigeot 	struct intel_crtc *intel_crtc =
10835d0b1887SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1084*9edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
10855d0b1887SFrançois Tigeot 	int pipe = intel_crtc->pipe;
10865d0b1887SFrançois Tigeot 	u32 val;
10875d0b1887SFrançois Tigeot 
10885d0b1887SFrançois Tigeot 	if (!IS_VALLEYVIEW(dev))
10895d0b1887SFrançois Tigeot 		return;
10905d0b1887SFrançois Tigeot 
10915d0b1887SFrançois Tigeot 	/* Enable clock channels for this port */
1092*9edbd4a0SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1093*9edbd4a0SFrançois Tigeot 	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port));
10945d0b1887SFrançois Tigeot 	val = 0;
10955d0b1887SFrançois Tigeot 	if (pipe)
10965d0b1887SFrançois Tigeot 		val |= (1<<21);
10975d0b1887SFrançois Tigeot 	else
10985d0b1887SFrançois Tigeot 		val &= ~(1<<21);
10995d0b1887SFrançois Tigeot 	val |= 0x001000c4;
1100*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW8(port), val);
11015d0b1887SFrançois Tigeot 
11025d0b1887SFrançois Tigeot 	/* HDMI 1.0V-2dB */
1103*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), 0);
1104*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW4(port), 0x2b245f5f);
1105*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(port), 0x5578b83a);
1106*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(port), 0x0c782040);
1107*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX3_DW4(port), 0x2b247878);
1108*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW11(port), 0x00030000);
1109*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000);
1110*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN);
11115d0b1887SFrançois Tigeot 
11125d0b1887SFrançois Tigeot 	/* Program lane clock */
1113*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW14(port), 0x00760018);
1114*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888);
1115*9edbd4a0SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
1116*9edbd4a0SFrançois Tigeot 
1117*9edbd4a0SFrançois Tigeot 	intel_enable_hdmi(encoder);
1118*9edbd4a0SFrançois Tigeot 
1119*9edbd4a0SFrançois Tigeot 	vlv_wait_port_ready(dev_priv, dport);
11205d0b1887SFrançois Tigeot }
11215d0b1887SFrançois Tigeot 
1122*9edbd4a0SFrançois Tigeot static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
11235d0b1887SFrançois Tigeot {
11245d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
11255d0b1887SFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
11265d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1127*9edbd4a0SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1128*9edbd4a0SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1129*9edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
1130*9edbd4a0SFrançois Tigeot 	int pipe = intel_crtc->pipe;
11315d0b1887SFrançois Tigeot 
11325d0b1887SFrançois Tigeot 	if (!IS_VALLEYVIEW(dev))
11335d0b1887SFrançois Tigeot 		return;
11345d0b1887SFrançois Tigeot 
11355d0b1887SFrançois Tigeot 	/* Program Tx lane resets to default */
1136*9edbd4a0SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1137*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port),
11385d0b1887SFrançois Tigeot 			 DPIO_PCS_TX_LANE2_RESET |
11395d0b1887SFrançois Tigeot 			 DPIO_PCS_TX_LANE1_RESET);
1140*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port),
11415d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_CRI_RXEB_EIOS_EN |
11425d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN |
11435d0b1887SFrançois Tigeot 			 (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) |
11445d0b1887SFrançois Tigeot 			 DPIO_PCS_CLK_SOFT_RESET);
11455d0b1887SFrançois Tigeot 
11465d0b1887SFrançois Tigeot 	/* Fix up inter-pair skew failure */
1147*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW12(port), 0x00750f00);
1148*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW11(port), 0x00001500);
1149*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW14(port), 0x40400000);
11505d0b1887SFrançois Tigeot 
1151*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000);
1152*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN);
1153*9edbd4a0SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
11545d0b1887SFrançois Tigeot }
11555d0b1887SFrançois Tigeot 
1156*9edbd4a0SFrançois Tigeot static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
11575d0b1887SFrançois Tigeot {
11585d0b1887SFrançois Tigeot 	struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
11595d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
1160*9edbd4a0SFrançois Tigeot 	struct intel_crtc *intel_crtc =
1161*9edbd4a0SFrançois Tigeot 		to_intel_crtc(encoder->base.crtc);
1162*9edbd4a0SFrançois Tigeot 	enum dpio_channel port = vlv_dport_to_channel(dport);
1163*9edbd4a0SFrançois Tigeot 	int pipe = intel_crtc->pipe;
11645d0b1887SFrançois Tigeot 
11655d0b1887SFrançois Tigeot 	/* Reset lanes to avoid HDMI flicker (VLV w/a) */
11665d0b1887SFrançois Tigeot 	mutex_lock(&dev_priv->dpio_lock);
1167*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 0x00000000);
1168*9edbd4a0SFrançois Tigeot 	vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 0x00e00060);
11695d0b1887SFrançois Tigeot 	mutex_unlock(&dev_priv->dpio_lock);
11705d0b1887SFrançois Tigeot }
11715d0b1887SFrançois Tigeot 
1172e3adcf8fSFrançois Tigeot static void intel_hdmi_destroy(struct drm_connector *connector)
1173e3adcf8fSFrançois Tigeot {
1174e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
11758e26cdf6SFrançois Tigeot 	kfree(connector);
1176e3adcf8fSFrançois Tigeot }
1177e3adcf8fSFrançois Tigeot 
1178e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
117919df918dSFrançois Tigeot 	.dpms = intel_connector_dpms,
1180e3adcf8fSFrançois Tigeot 	.detect = intel_hdmi_detect,
1181e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
1182e3adcf8fSFrançois Tigeot 	.set_property = intel_hdmi_set_property,
1183e3adcf8fSFrançois Tigeot 	.destroy = intel_hdmi_destroy,
1184e3adcf8fSFrançois Tigeot };
1185e3adcf8fSFrançois Tigeot 
1186e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
1187e3adcf8fSFrançois Tigeot 	.get_modes = intel_hdmi_get_modes,
1188e3adcf8fSFrançois Tigeot 	.mode_valid = intel_hdmi_mode_valid,
1189e3adcf8fSFrançois Tigeot 	.best_encoder = intel_best_encoder,
1190e3adcf8fSFrançois Tigeot };
1191e3adcf8fSFrançois Tigeot 
1192e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
1193e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
1194e3adcf8fSFrançois Tigeot };
1195e3adcf8fSFrançois Tigeot 
1196e3adcf8fSFrançois Tigeot static void
1197e3adcf8fSFrançois Tigeot intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
1198e3adcf8fSFrançois Tigeot {
1199e3adcf8fSFrançois Tigeot 	intel_attach_force_audio_property(connector);
1200e3adcf8fSFrançois Tigeot 	intel_attach_broadcast_rgb_property(connector);
1201a2fdbec6SFrançois Tigeot 	intel_hdmi->color_range_auto = true;
1202e3adcf8fSFrançois Tigeot }
1203e3adcf8fSFrançois Tigeot 
120419df918dSFrançois Tigeot void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
120519df918dSFrançois Tigeot 			       struct intel_connector *intel_connector)
1206e3adcf8fSFrançois Tigeot {
120719df918dSFrançois Tigeot 	struct drm_connector *connector = &intel_connector->base;
120819df918dSFrançois Tigeot 	struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi;
120919df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = &intel_dig_port->base;
121019df918dSFrançois Tigeot 	struct drm_device *dev = intel_encoder->base.dev;
1211e3adcf8fSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
121219df918dSFrançois Tigeot 	enum port port = intel_dig_port->port;
1213e3adcf8fSFrançois Tigeot 
1214e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
1215e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_HDMIA);
1216e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
1217e3adcf8fSFrançois Tigeot 
1218e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = 1;
1219e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = 0;
1220*9edbd4a0SFrançois Tigeot 	connector->stereo_allowed = 1;
1221e3adcf8fSFrançois Tigeot 
122219df918dSFrançois Tigeot 	switch (port) {
122319df918dSFrançois Tigeot 	case PORT_B:
1224e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
12258e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_B;
122619df918dSFrançois Tigeot 		break;
122719df918dSFrançois Tigeot 	case PORT_C:
1228e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
12298e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_C;
123019df918dSFrançois Tigeot 		break;
123119df918dSFrançois Tigeot 	case PORT_D:
1232e3adcf8fSFrançois Tigeot 		intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
12338e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_D;
123419df918dSFrançois Tigeot 		break;
123519df918dSFrançois Tigeot 	case PORT_A:
12368e26cdf6SFrançois Tigeot 		intel_encoder->hpd_pin = HPD_PORT_A;
123719df918dSFrançois Tigeot 		/* Internal port only for eDP. */
123819df918dSFrançois Tigeot 	default:
123919df918dSFrançois Tigeot 		BUG();
1240e3adcf8fSFrançois Tigeot 	}
1241e3adcf8fSFrançois Tigeot 
12428e26cdf6SFrançois Tigeot 	if (IS_VALLEYVIEW(dev)) {
124319df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = vlv_write_infoframe;
124419df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = vlv_set_infoframes;
12458e26cdf6SFrançois Tigeot 	} else if (!HAS_PCH_SPLIT(dev)) {
12468e26cdf6SFrançois Tigeot 		intel_hdmi->write_infoframe = g4x_write_infoframe;
12478e26cdf6SFrançois Tigeot 		intel_hdmi->set_infoframes = g4x_set_infoframes;
12488e26cdf6SFrançois Tigeot 	} else if (HAS_DDI(dev)) {
124919df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = hsw_write_infoframe;
125019df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = hsw_set_infoframes;
125119df918dSFrançois Tigeot 	} else if (HAS_PCH_IBX(dev)) {
125219df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = ibx_write_infoframe;
125319df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = ibx_set_infoframes;
1254e3adcf8fSFrançois Tigeot 	} else {
125519df918dSFrançois Tigeot 		intel_hdmi->write_infoframe = cpt_write_infoframe;
125619df918dSFrançois Tigeot 		intel_hdmi->set_infoframes = cpt_set_infoframes;
1257e3adcf8fSFrançois Tigeot 	}
1258e3adcf8fSFrançois Tigeot 
1259a2fdbec6SFrançois Tigeot 	if (HAS_DDI(dev))
126019df918dSFrançois Tigeot 		intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
126119df918dSFrançois Tigeot 	else
126219df918dSFrançois Tigeot 		intel_connector->get_hw_state = intel_connector_get_hw_state;
1263e3adcf8fSFrançois Tigeot 
1264e3adcf8fSFrançois Tigeot 	intel_hdmi_add_properties(intel_hdmi, connector);
1265e3adcf8fSFrançois Tigeot 
1266e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1267e3adcf8fSFrançois Tigeot 	drm_sysfs_connector_add(connector);
1268e3adcf8fSFrançois Tigeot 
1269e3adcf8fSFrançois Tigeot 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
1270e3adcf8fSFrançois Tigeot 	 * 0xd.  Failure to do so will result in spurious interrupts being
1271e3adcf8fSFrançois Tigeot 	 * generated on the port when a cable is not attached.
1272e3adcf8fSFrançois Tigeot 	 */
1273e3adcf8fSFrançois Tigeot 	if (IS_G4X(dev) && !IS_GM45(dev)) {
1274e3adcf8fSFrançois Tigeot 		u32 temp = I915_READ(PEG_BAND_GAP_DATA);
1275e3adcf8fSFrançois Tigeot 		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
1276e3adcf8fSFrançois Tigeot 	}
1277e3adcf8fSFrançois Tigeot }
127819df918dSFrançois Tigeot 
12798e26cdf6SFrançois Tigeot void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
128019df918dSFrançois Tigeot {
128119df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port;
128219df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder;
128319df918dSFrançois Tigeot 	struct intel_connector *intel_connector;
128419df918dSFrançois Tigeot 
1285*9edbd4a0SFrançois Tigeot 	intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
128619df918dSFrançois Tigeot 	if (!intel_dig_port)
128719df918dSFrançois Tigeot 		return;
128819df918dSFrançois Tigeot 
1289*9edbd4a0SFrançois Tigeot 	intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
129019df918dSFrançois Tigeot 	if (!intel_connector) {
1291158486a6SFrançois Tigeot 		kfree(intel_dig_port);
129219df918dSFrançois Tigeot 		return;
129319df918dSFrançois Tigeot 	}
129419df918dSFrançois Tigeot 
129519df918dSFrançois Tigeot 	intel_encoder = &intel_dig_port->base;
129619df918dSFrançois Tigeot 
129719df918dSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
129819df918dSFrançois Tigeot 			 DRM_MODE_ENCODER_TMDS);
129919df918dSFrançois Tigeot 
13008e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_hdmi_compute_config;
1301*9edbd4a0SFrançois Tigeot 	intel_encoder->mode_set = intel_hdmi_mode_set;
130219df918dSFrançois Tigeot 	intel_encoder->disable = intel_disable_hdmi;
130319df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
13045d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_hdmi_get_config;
13055d0b1887SFrançois Tigeot 	if (IS_VALLEYVIEW(dev)) {
1306*9edbd4a0SFrançois Tigeot 		intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable;
1307*9edbd4a0SFrançois Tigeot 		intel_encoder->pre_enable = vlv_hdmi_pre_enable;
1308*9edbd4a0SFrançois Tigeot 		intel_encoder->enable = vlv_enable_hdmi;
1309*9edbd4a0SFrançois Tigeot 		intel_encoder->post_disable = vlv_hdmi_post_disable;
1310*9edbd4a0SFrançois Tigeot 	} else {
1311*9edbd4a0SFrançois Tigeot 		intel_encoder->enable = intel_enable_hdmi;
13125d0b1887SFrançois Tigeot 	}
131319df918dSFrançois Tigeot 
131419df918dSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_HDMI;
131519df918dSFrançois Tigeot 	intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
131619df918dSFrançois Tigeot 	intel_encoder->cloneable = false;
131719df918dSFrançois Tigeot 
131819df918dSFrançois Tigeot 	intel_dig_port->port = port;
13198e26cdf6SFrançois Tigeot 	intel_dig_port->hdmi.hdmi_reg = hdmi_reg;
132019df918dSFrançois Tigeot 	intel_dig_port->dp.output_reg = 0;
132119df918dSFrançois Tigeot 
132219df918dSFrançois Tigeot 	intel_hdmi_init_connector(intel_dig_port, intel_connector);
132319df918dSFrançois Tigeot }
1324