xref: /dragonfly/sys/dev/drm/i915/intel_ddi.c (revision 5d0b1887)
119df918dSFrançois Tigeot /*
219df918dSFrançois Tigeot  * Copyright © 2012 Intel Corporation
319df918dSFrançois Tigeot  *
419df918dSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
519df918dSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
619df918dSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
719df918dSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
819df918dSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
919df918dSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
1019df918dSFrançois Tigeot  *
1119df918dSFrançois Tigeot  * The above copyright notice and this permission notice (including the next
1219df918dSFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
1319df918dSFrançois Tigeot  * Software.
1419df918dSFrançois Tigeot  *
1519df918dSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1619df918dSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1719df918dSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1819df918dSFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919df918dSFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2019df918dSFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2119df918dSFrançois Tigeot  * IN THE SOFTWARE.
2219df918dSFrançois Tigeot  *
2319df918dSFrançois Tigeot  * Authors:
2419df918dSFrançois Tigeot  *    Eugeni Dodonov <eugeni.dodonov@intel.com>
2519df918dSFrançois Tigeot  *
2619df918dSFrançois Tigeot  */
2719df918dSFrançois Tigeot 
2819df918dSFrançois Tigeot #include "i915_drv.h"
2919df918dSFrançois Tigeot #include "intel_drv.h"
3019df918dSFrançois Tigeot 
3119df918dSFrançois Tigeot /* HDMI/DVI modes ignore everything but the last 2 items. So we share
3219df918dSFrançois Tigeot  * them for both DP and FDI transports, allowing those ports to
3319df918dSFrançois Tigeot  * automatically adapt to HDMI connections as well
3419df918dSFrançois Tigeot  */
3519df918dSFrançois Tigeot static const u32 hsw_ddi_translations_dp[] = {
3619df918dSFrançois Tigeot 	0x00FFFFFF, 0x0006000E,		/* DP parameters */
3719df918dSFrançois Tigeot 	0x00D75FFF, 0x0005000A,
3819df918dSFrançois Tigeot 	0x00C30FFF, 0x00040006,
3919df918dSFrançois Tigeot 	0x80AAAFFF, 0x000B0000,
4019df918dSFrançois Tigeot 	0x00FFFFFF, 0x0005000A,
4119df918dSFrançois Tigeot 	0x00D75FFF, 0x000C0004,
4219df918dSFrançois Tigeot 	0x80C30FFF, 0x000B0000,
4319df918dSFrançois Tigeot 	0x00FFFFFF, 0x00040006,
4419df918dSFrançois Tigeot 	0x80D75FFF, 0x000B0000,
4519df918dSFrançois Tigeot 	0x00FFFFFF, 0x00040006		/* HDMI parameters */
4619df918dSFrançois Tigeot };
4719df918dSFrançois Tigeot 
4819df918dSFrançois Tigeot static const u32 hsw_ddi_translations_fdi[] = {
4919df918dSFrançois Tigeot 	0x00FFFFFF, 0x0007000E,		/* FDI parameters */
5019df918dSFrançois Tigeot 	0x00D75FFF, 0x000F000A,
5119df918dSFrançois Tigeot 	0x00C30FFF, 0x00060006,
5219df918dSFrançois Tigeot 	0x00AAAFFF, 0x001E0000,
5319df918dSFrançois Tigeot 	0x00FFFFFF, 0x000F000A,
5419df918dSFrançois Tigeot 	0x00D75FFF, 0x00160004,
5519df918dSFrançois Tigeot 	0x00C30FFF, 0x001E0000,
5619df918dSFrançois Tigeot 	0x00FFFFFF, 0x00060006,
5719df918dSFrançois Tigeot 	0x00D75FFF, 0x001E0000,
5819df918dSFrançois Tigeot 	0x00FFFFFF, 0x00040006		/* HDMI parameters */
5919df918dSFrançois Tigeot };
6019df918dSFrançois Tigeot 
6119df918dSFrançois Tigeot static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
6219df918dSFrançois Tigeot {
6319df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
6419df918dSFrançois Tigeot 	int type = intel_encoder->type;
6519df918dSFrançois Tigeot 
6619df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
6719df918dSFrançois Tigeot 	    type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
6819df918dSFrançois Tigeot 		struct intel_digital_port *intel_dig_port =
6919df918dSFrançois Tigeot 			enc_to_dig_port(encoder);
7019df918dSFrançois Tigeot 		return intel_dig_port->port;
7119df918dSFrançois Tigeot 
7219df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_ANALOG) {
7319df918dSFrançois Tigeot 		return PORT_E;
7419df918dSFrançois Tigeot 
7519df918dSFrançois Tigeot 	} else {
7619df918dSFrançois Tigeot 		DRM_ERROR("Invalid DDI encoder type %d\n", type);
7719df918dSFrançois Tigeot 		BUG();
7819df918dSFrançois Tigeot 	}
7919df918dSFrançois Tigeot }
8019df918dSFrançois Tigeot 
8119df918dSFrançois Tigeot /* On Haswell, DDI port buffers must be programmed with correct values
8219df918dSFrançois Tigeot  * in advance. The buffer values are different for FDI and DP modes,
8319df918dSFrançois Tigeot  * but the HDMI/DVI fields are shared among those. So we program the DDI
8419df918dSFrançois Tigeot  * in either FDI or DP modes only, as HDMI connections will work with both
8519df918dSFrançois Tigeot  * of those
8619df918dSFrançois Tigeot  */
87*5d0b1887SFrançois Tigeot static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
88*5d0b1887SFrançois Tigeot 				      bool use_fdi_mode)
8919df918dSFrançois Tigeot {
9019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
9119df918dSFrançois Tigeot 	u32 reg;
9219df918dSFrançois Tigeot 	int i;
9319df918dSFrançois Tigeot 	const u32 *ddi_translations = ((use_fdi_mode) ?
9419df918dSFrançois Tigeot 		hsw_ddi_translations_fdi :
9519df918dSFrançois Tigeot 		hsw_ddi_translations_dp);
9619df918dSFrançois Tigeot 
9719df918dSFrançois Tigeot 	DRM_DEBUG_DRIVER("Initializing DDI buffers for port %c in %s mode\n",
9819df918dSFrançois Tigeot 			port_name(port),
9919df918dSFrançois Tigeot 			use_fdi_mode ? "FDI" : "DP");
10019df918dSFrançois Tigeot 
10119df918dSFrançois Tigeot 	WARN((use_fdi_mode && (port != PORT_E)),
10219df918dSFrançois Tigeot 		"Programming port %c in FDI mode, this probably will not work.\n",
10319df918dSFrançois Tigeot 		port_name(port));
10419df918dSFrançois Tigeot 
10519df918dSFrançois Tigeot 	for (i=0, reg=DDI_BUF_TRANS(port); i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) {
10619df918dSFrançois Tigeot 		I915_WRITE(reg, ddi_translations[i]);
10719df918dSFrançois Tigeot 		reg += 4;
10819df918dSFrançois Tigeot 	}
10919df918dSFrançois Tigeot }
11019df918dSFrançois Tigeot 
11119df918dSFrançois Tigeot /* Program DDI buffers translations for DP. By default, program ports A-D in DP
11219df918dSFrançois Tigeot  * mode and port E for FDI.
11319df918dSFrançois Tigeot  */
11419df918dSFrançois Tigeot void intel_prepare_ddi(struct drm_device *dev)
11519df918dSFrançois Tigeot {
11619df918dSFrançois Tigeot 	int port;
11719df918dSFrançois Tigeot 
118a2fdbec6SFrançois Tigeot 	if (!HAS_DDI(dev))
119a2fdbec6SFrançois Tigeot 		return;
120a2fdbec6SFrançois Tigeot 
12119df918dSFrançois Tigeot 	for (port = PORT_A; port < PORT_E; port++)
12219df918dSFrançois Tigeot 		intel_prepare_ddi_buffers(dev, port, false);
12319df918dSFrançois Tigeot 
124a2fdbec6SFrançois Tigeot 	/* DDI E is the suggested one to work in FDI mode, so program is as such
125a2fdbec6SFrançois Tigeot 	 * by default. It will have to be re-programmed in case a digital DP
126a2fdbec6SFrançois Tigeot 	 * output will be detected on it
12719df918dSFrançois Tigeot 	 */
12819df918dSFrançois Tigeot 	intel_prepare_ddi_buffers(dev, PORT_E, true);
12919df918dSFrançois Tigeot }
13019df918dSFrançois Tigeot 
13119df918dSFrançois Tigeot static const long hsw_ddi_buf_ctl_values[] = {
13219df918dSFrançois Tigeot 	DDI_BUF_EMP_400MV_0DB_HSW,
13319df918dSFrançois Tigeot 	DDI_BUF_EMP_400MV_3_5DB_HSW,
13419df918dSFrançois Tigeot 	DDI_BUF_EMP_400MV_6DB_HSW,
13519df918dSFrançois Tigeot 	DDI_BUF_EMP_400MV_9_5DB_HSW,
13619df918dSFrançois Tigeot 	DDI_BUF_EMP_600MV_0DB_HSW,
13719df918dSFrançois Tigeot 	DDI_BUF_EMP_600MV_3_5DB_HSW,
13819df918dSFrançois Tigeot 	DDI_BUF_EMP_600MV_6DB_HSW,
13919df918dSFrançois Tigeot 	DDI_BUF_EMP_800MV_0DB_HSW,
14019df918dSFrançois Tigeot 	DDI_BUF_EMP_800MV_3_5DB_HSW
14119df918dSFrançois Tigeot };
14219df918dSFrançois Tigeot 
14319df918dSFrançois Tigeot static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv,
14419df918dSFrançois Tigeot 				    enum port port)
14519df918dSFrançois Tigeot {
14619df918dSFrançois Tigeot 	uint32_t reg = DDI_BUF_CTL(port);
14719df918dSFrançois Tigeot 	int i;
14819df918dSFrançois Tigeot 
14919df918dSFrançois Tigeot 	for (i = 0; i < 8; i++) {
15019df918dSFrançois Tigeot 		udelay(1);
15119df918dSFrançois Tigeot 		if (I915_READ(reg) & DDI_BUF_IS_IDLE)
15219df918dSFrançois Tigeot 			return;
15319df918dSFrançois Tigeot 	}
15419df918dSFrançois Tigeot 	DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port));
15519df918dSFrançois Tigeot }
15619df918dSFrançois Tigeot 
15719df918dSFrançois Tigeot /* Starting with Haswell, different DDI ports can work in FDI mode for
15819df918dSFrançois Tigeot  * connection to the PCH-located connectors. For this, it is necessary to train
15919df918dSFrançois Tigeot  * both the DDI port and PCH receiver for the desired DDI buffer settings.
16019df918dSFrançois Tigeot  *
16119df918dSFrançois Tigeot  * The recommended port to work in FDI mode is DDI E, which we use here. Also,
16219df918dSFrançois Tigeot  * please note that when FDI mode is active on DDI E, it shares 2 lines with
16319df918dSFrançois Tigeot  * DDI A (which is used for eDP)
16419df918dSFrançois Tigeot  */
16519df918dSFrançois Tigeot 
16619df918dSFrançois Tigeot void hsw_fdi_link_train(struct drm_crtc *crtc)
16719df918dSFrançois Tigeot {
16819df918dSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
16919df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
17019df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
17119df918dSFrançois Tigeot 	u32 temp, i, rx_ctl_val;
17219df918dSFrançois Tigeot 
17319df918dSFrançois Tigeot 	/* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the
17419df918dSFrançois Tigeot 	 * mode set "sequence for CRT port" document:
17519df918dSFrançois Tigeot 	 * - TP1 to TP2 time with the default value
17619df918dSFrançois Tigeot 	 * - FDI delay to 90h
177*5d0b1887SFrançois Tigeot 	 *
178*5d0b1887SFrançois Tigeot 	 * WaFDIAutoLinkSetTimingOverrride:hsw
17919df918dSFrançois Tigeot 	 */
18019df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) |
18119df918dSFrançois Tigeot 				  FDI_RX_PWRDN_LANE0_VAL(2) |
18219df918dSFrançois Tigeot 				  FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
18319df918dSFrançois Tigeot 
18419df918dSFrançois Tigeot 	/* Enable the PCH Receiver FDI PLL */
18519df918dSFrançois Tigeot 	rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE |
186*5d0b1887SFrançois Tigeot 		     FDI_RX_PLL_ENABLE |
187*5d0b1887SFrançois Tigeot 		     FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes);
18819df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
18919df918dSFrançois Tigeot 	POSTING_READ(_FDI_RXA_CTL);
19019df918dSFrançois Tigeot 	udelay(220);
19119df918dSFrançois Tigeot 
19219df918dSFrançois Tigeot 	/* Switch from Rawclk to PCDclk */
19319df918dSFrançois Tigeot 	rx_ctl_val |= FDI_PCDCLK;
19419df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
19519df918dSFrançois Tigeot 
19619df918dSFrançois Tigeot 	/* Configure Port Clock Select */
19719df918dSFrançois Tigeot 	I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel);
19819df918dSFrançois Tigeot 
19919df918dSFrançois Tigeot 	/* Start the training iterating through available voltages and emphasis,
20019df918dSFrançois Tigeot 	 * testing each value twice. */
20119df918dSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) {
20219df918dSFrançois Tigeot 		/* Configure DP_TP_CTL with auto-training */
20319df918dSFrançois Tigeot 		I915_WRITE(DP_TP_CTL(PORT_E),
20419df918dSFrançois Tigeot 					DP_TP_CTL_FDI_AUTOTRAIN |
20519df918dSFrançois Tigeot 					DP_TP_CTL_ENHANCED_FRAME_ENABLE |
20619df918dSFrançois Tigeot 					DP_TP_CTL_LINK_TRAIN_PAT1 |
20719df918dSFrançois Tigeot 					DP_TP_CTL_ENABLE);
20819df918dSFrançois Tigeot 
20919df918dSFrançois Tigeot 		/* Configure and enable DDI_BUF_CTL for DDI E with next voltage.
21019df918dSFrançois Tigeot 		 * DDI E does not support port reversal, the functionality is
21119df918dSFrançois Tigeot 		 * achieved on the PCH side in FDI_RX_CTL, so no need to set the
21219df918dSFrançois Tigeot 		 * port reversal bit */
21319df918dSFrançois Tigeot 		I915_WRITE(DDI_BUF_CTL(PORT_E),
21419df918dSFrançois Tigeot 			   DDI_BUF_CTL_ENABLE |
215*5d0b1887SFrançois Tigeot 			   ((intel_crtc->config.fdi_lanes - 1) << 1) |
21619df918dSFrançois Tigeot 			   hsw_ddi_buf_ctl_values[i / 2]);
21719df918dSFrançois Tigeot 		POSTING_READ(DDI_BUF_CTL(PORT_E));
21819df918dSFrançois Tigeot 
21919df918dSFrançois Tigeot 		udelay(600);
22019df918dSFrançois Tigeot 
22119df918dSFrançois Tigeot 		/* Program PCH FDI Receiver TU */
22219df918dSFrançois Tigeot 		I915_WRITE(_FDI_RXA_TUSIZE1, TU_SIZE(64));
22319df918dSFrançois Tigeot 
22419df918dSFrançois Tigeot 		/* Enable PCH FDI Receiver with auto-training */
22519df918dSFrançois Tigeot 		rx_ctl_val |= FDI_RX_ENABLE | FDI_LINK_TRAIN_AUTO;
22619df918dSFrançois Tigeot 		I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
22719df918dSFrançois Tigeot 		POSTING_READ(_FDI_RXA_CTL);
22819df918dSFrançois Tigeot 
22919df918dSFrançois Tigeot 		/* Wait for FDI receiver lane calibration */
23019df918dSFrançois Tigeot 		udelay(30);
23119df918dSFrançois Tigeot 
23219df918dSFrançois Tigeot 		/* Unset FDI_RX_MISC pwrdn lanes */
23319df918dSFrançois Tigeot 		temp = I915_READ(_FDI_RXA_MISC);
23419df918dSFrançois Tigeot 		temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
23519df918dSFrançois Tigeot 		I915_WRITE(_FDI_RXA_MISC, temp);
23619df918dSFrançois Tigeot 		POSTING_READ(_FDI_RXA_MISC);
23719df918dSFrançois Tigeot 
23819df918dSFrançois Tigeot 		/* Wait for FDI auto training time */
23919df918dSFrançois Tigeot 		udelay(5);
24019df918dSFrançois Tigeot 
24119df918dSFrançois Tigeot 		temp = I915_READ(DP_TP_STATUS(PORT_E));
24219df918dSFrançois Tigeot 		if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) {
24319df918dSFrançois Tigeot 			DRM_DEBUG_KMS("FDI link training done on step %d\n", i);
24419df918dSFrançois Tigeot 
24519df918dSFrançois Tigeot 			/* Enable normal pixel sending for FDI */
24619df918dSFrançois Tigeot 			I915_WRITE(DP_TP_CTL(PORT_E),
24719df918dSFrançois Tigeot 				   DP_TP_CTL_FDI_AUTOTRAIN |
24819df918dSFrançois Tigeot 				   DP_TP_CTL_LINK_TRAIN_NORMAL |
24919df918dSFrançois Tigeot 				   DP_TP_CTL_ENHANCED_FRAME_ENABLE |
25019df918dSFrançois Tigeot 				   DP_TP_CTL_ENABLE);
25119df918dSFrançois Tigeot 
25219df918dSFrançois Tigeot 			return;
25319df918dSFrançois Tigeot 		}
25419df918dSFrançois Tigeot 
25519df918dSFrançois Tigeot 		temp = I915_READ(DDI_BUF_CTL(PORT_E));
25619df918dSFrançois Tigeot 		temp &= ~DDI_BUF_CTL_ENABLE;
25719df918dSFrançois Tigeot 		I915_WRITE(DDI_BUF_CTL(PORT_E), temp);
25819df918dSFrançois Tigeot 		POSTING_READ(DDI_BUF_CTL(PORT_E));
25919df918dSFrançois Tigeot 
26019df918dSFrançois Tigeot 		/* Disable DP_TP_CTL and FDI_RX_CTL and retry */
26119df918dSFrançois Tigeot 		temp = I915_READ(DP_TP_CTL(PORT_E));
26219df918dSFrançois Tigeot 		temp &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK);
26319df918dSFrançois Tigeot 		temp |= DP_TP_CTL_LINK_TRAIN_PAT1;
26419df918dSFrançois Tigeot 		I915_WRITE(DP_TP_CTL(PORT_E), temp);
26519df918dSFrançois Tigeot 		POSTING_READ(DP_TP_CTL(PORT_E));
26619df918dSFrançois Tigeot 
26719df918dSFrançois Tigeot 		intel_wait_ddi_buf_idle(dev_priv, PORT_E);
26819df918dSFrançois Tigeot 
26919df918dSFrançois Tigeot 		rx_ctl_val &= ~FDI_RX_ENABLE;
27019df918dSFrançois Tigeot 		I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
27119df918dSFrançois Tigeot 		POSTING_READ(_FDI_RXA_CTL);
27219df918dSFrançois Tigeot 
27319df918dSFrançois Tigeot 		/* Reset FDI_RX_MISC pwrdn lanes */
27419df918dSFrançois Tigeot 		temp = I915_READ(_FDI_RXA_MISC);
27519df918dSFrançois Tigeot 		temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
27619df918dSFrançois Tigeot 		temp |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2);
27719df918dSFrançois Tigeot 		I915_WRITE(_FDI_RXA_MISC, temp);
27819df918dSFrançois Tigeot 		POSTING_READ(_FDI_RXA_MISC);
27919df918dSFrançois Tigeot 	}
28019df918dSFrançois Tigeot 
28119df918dSFrançois Tigeot 	DRM_ERROR("FDI link training failed!\n");
28219df918dSFrançois Tigeot }
28319df918dSFrançois Tigeot 
28419df918dSFrançois Tigeot static void intel_ddi_mode_set(struct drm_encoder *encoder,
28519df918dSFrançois Tigeot 			       struct drm_display_mode *mode,
28619df918dSFrançois Tigeot 			       struct drm_display_mode *adjusted_mode)
28719df918dSFrançois Tigeot {
28819df918dSFrançois Tigeot 	struct drm_crtc *crtc = encoder->crtc;
28919df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
29019df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
29119df918dSFrançois Tigeot 	int port = intel_ddi_get_encoder_port(intel_encoder);
29219df918dSFrançois Tigeot 	int pipe = intel_crtc->pipe;
29319df918dSFrançois Tigeot 	int type = intel_encoder->type;
29419df918dSFrançois Tigeot 
295*5d0b1887SFrançois Tigeot 	DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n",
29619df918dSFrançois Tigeot 		      port_name(port), pipe_name(pipe));
29719df918dSFrançois Tigeot 
298a2fdbec6SFrançois Tigeot 	intel_crtc->eld_vld = false;
29919df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
30019df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
30119df918dSFrançois Tigeot 		struct intel_digital_port *intel_dig_port =
30219df918dSFrançois Tigeot 			enc_to_dig_port(encoder);
30319df918dSFrançois Tigeot 
304*5d0b1887SFrançois Tigeot 		intel_dp->DP = intel_dig_port->saved_port_bits |
30519df918dSFrançois Tigeot 			       DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
306*5d0b1887SFrançois Tigeot 		intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
30719df918dSFrançois Tigeot 
30819df918dSFrançois Tigeot 		if (intel_dp->has_audio) {
30919df918dSFrançois Tigeot 			DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n",
31019df918dSFrançois Tigeot 					 pipe_name(intel_crtc->pipe));
31119df918dSFrançois Tigeot 
31219df918dSFrançois Tigeot 			/* write eld */
31319df918dSFrançois Tigeot 			DRM_DEBUG_DRIVER("DP audio: write eld information\n");
31419df918dSFrançois Tigeot 			intel_write_eld(encoder, adjusted_mode);
31519df918dSFrançois Tigeot 		}
31619df918dSFrançois Tigeot 
31719df918dSFrançois Tigeot 		intel_dp_init_link_config(intel_dp);
31819df918dSFrançois Tigeot 
31919df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_HDMI) {
32019df918dSFrançois Tigeot 		struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
32119df918dSFrançois Tigeot 
32219df918dSFrançois Tigeot 		if (intel_hdmi->has_audio) {
32319df918dSFrançois Tigeot 			/* Proper support for digital audio needs a new logic
32419df918dSFrançois Tigeot 			 * and a new set of registers, so we leave it for future
32519df918dSFrançois Tigeot 			 * patch bombing.
32619df918dSFrançois Tigeot 			 */
32719df918dSFrançois Tigeot 			DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
32819df918dSFrançois Tigeot 					 pipe_name(intel_crtc->pipe));
32919df918dSFrançois Tigeot 
33019df918dSFrançois Tigeot 			/* write eld */
33119df918dSFrançois Tigeot 			DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
33219df918dSFrançois Tigeot 			intel_write_eld(encoder, adjusted_mode);
33319df918dSFrançois Tigeot 		}
33419df918dSFrançois Tigeot 
33519df918dSFrançois Tigeot 		intel_hdmi->set_infoframes(encoder, adjusted_mode);
33619df918dSFrançois Tigeot 	}
33719df918dSFrançois Tigeot }
33819df918dSFrançois Tigeot 
33919df918dSFrançois Tigeot static struct intel_encoder *
34019df918dSFrançois Tigeot intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
34119df918dSFrançois Tigeot {
34219df918dSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
34319df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
34419df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder, *ret = NULL;
34519df918dSFrançois Tigeot 	int num_encoders = 0;
34619df918dSFrançois Tigeot 
34719df918dSFrançois Tigeot 	for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
34819df918dSFrançois Tigeot 		ret = intel_encoder;
34919df918dSFrançois Tigeot 		num_encoders++;
35019df918dSFrançois Tigeot 	}
35119df918dSFrançois Tigeot 
35219df918dSFrançois Tigeot 	if (num_encoders != 1)
353*5d0b1887SFrançois Tigeot 		WARN(1, "%d encoders on crtc for pipe %c\n", num_encoders,
354*5d0b1887SFrançois Tigeot 		     pipe_name(intel_crtc->pipe));
35519df918dSFrançois Tigeot 
35619df918dSFrançois Tigeot 	BUG_ON(ret == NULL);
35719df918dSFrançois Tigeot 	return ret;
35819df918dSFrançois Tigeot }
35919df918dSFrançois Tigeot 
36019df918dSFrançois Tigeot void intel_ddi_put_crtc_pll(struct drm_crtc *crtc)
36119df918dSFrançois Tigeot {
36219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
36319df918dSFrançois Tigeot 	struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
36419df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
36519df918dSFrançois Tigeot 	uint32_t val;
36619df918dSFrançois Tigeot 
36719df918dSFrançois Tigeot 	switch (intel_crtc->ddi_pll_sel) {
36819df918dSFrançois Tigeot 	case PORT_CLK_SEL_SPLL:
36919df918dSFrançois Tigeot 		plls->spll_refcount--;
37019df918dSFrançois Tigeot 		if (plls->spll_refcount == 0) {
37119df918dSFrançois Tigeot 			DRM_DEBUG_KMS("Disabling SPLL\n");
37219df918dSFrançois Tigeot 			val = I915_READ(SPLL_CTL);
37319df918dSFrançois Tigeot 			WARN_ON(!(val & SPLL_PLL_ENABLE));
37419df918dSFrançois Tigeot 			I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE);
37519df918dSFrançois Tigeot 			POSTING_READ(SPLL_CTL);
37619df918dSFrançois Tigeot 		}
37719df918dSFrançois Tigeot 		break;
37819df918dSFrançois Tigeot 	case PORT_CLK_SEL_WRPLL1:
37919df918dSFrançois Tigeot 		plls->wrpll1_refcount--;
38019df918dSFrançois Tigeot 		if (plls->wrpll1_refcount == 0) {
38119df918dSFrançois Tigeot 			DRM_DEBUG_KMS("Disabling WRPLL 1\n");
38219df918dSFrançois Tigeot 			val = I915_READ(WRPLL_CTL1);
38319df918dSFrançois Tigeot 			WARN_ON(!(val & WRPLL_PLL_ENABLE));
38419df918dSFrançois Tigeot 			I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE);
38519df918dSFrançois Tigeot 			POSTING_READ(WRPLL_CTL1);
38619df918dSFrançois Tigeot 		}
38719df918dSFrançois Tigeot 		break;
38819df918dSFrançois Tigeot 	case PORT_CLK_SEL_WRPLL2:
38919df918dSFrançois Tigeot 		plls->wrpll2_refcount--;
39019df918dSFrançois Tigeot 		if (plls->wrpll2_refcount == 0) {
39119df918dSFrançois Tigeot 			DRM_DEBUG_KMS("Disabling WRPLL 2\n");
39219df918dSFrançois Tigeot 			val = I915_READ(WRPLL_CTL2);
39319df918dSFrançois Tigeot 			WARN_ON(!(val & WRPLL_PLL_ENABLE));
39419df918dSFrançois Tigeot 			I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE);
39519df918dSFrançois Tigeot 			POSTING_READ(WRPLL_CTL2);
39619df918dSFrançois Tigeot 		}
39719df918dSFrançois Tigeot 		break;
39819df918dSFrançois Tigeot 	}
39919df918dSFrançois Tigeot 
40019df918dSFrançois Tigeot 	WARN(plls->spll_refcount < 0, "Invalid SPLL refcount\n");
40119df918dSFrançois Tigeot 	WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n");
40219df918dSFrançois Tigeot 	WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n");
40319df918dSFrançois Tigeot 
40419df918dSFrançois Tigeot 	intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE;
40519df918dSFrançois Tigeot }
40619df918dSFrançois Tigeot 
407*5d0b1887SFrançois Tigeot #define LC_FREQ 2700
408*5d0b1887SFrançois Tigeot #define LC_FREQ_2K (LC_FREQ * 2000)
409*5d0b1887SFrançois Tigeot 
410*5d0b1887SFrançois Tigeot #define P_MIN 2
411*5d0b1887SFrançois Tigeot #define P_MAX 64
412*5d0b1887SFrançois Tigeot #define P_INC 2
413*5d0b1887SFrançois Tigeot 
414*5d0b1887SFrançois Tigeot /* Constraints for PLL good behavior */
415*5d0b1887SFrançois Tigeot #define REF_MIN 48
416*5d0b1887SFrançois Tigeot #define REF_MAX 400
417*5d0b1887SFrançois Tigeot #define VCO_MIN 2400
418*5d0b1887SFrançois Tigeot #define VCO_MAX 4800
419*5d0b1887SFrançois Tigeot 
420*5d0b1887SFrançois Tigeot #define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a))
421*5d0b1887SFrançois Tigeot 
422*5d0b1887SFrançois Tigeot struct wrpll_rnp {
423*5d0b1887SFrançois Tigeot 	unsigned p, n2, r2;
424*5d0b1887SFrançois Tigeot };
425*5d0b1887SFrançois Tigeot 
426*5d0b1887SFrançois Tigeot static unsigned wrpll_get_budget_for_freq(int clock)
42719df918dSFrançois Tigeot {
428*5d0b1887SFrançois Tigeot 	unsigned budget;
42919df918dSFrançois Tigeot 
430*5d0b1887SFrançois Tigeot 	switch (clock) {
431*5d0b1887SFrançois Tigeot 	case 25175000:
432*5d0b1887SFrançois Tigeot 	case 25200000:
433*5d0b1887SFrançois Tigeot 	case 27000000:
434*5d0b1887SFrançois Tigeot 	case 27027000:
435*5d0b1887SFrançois Tigeot 	case 37762500:
436*5d0b1887SFrançois Tigeot 	case 37800000:
437*5d0b1887SFrançois Tigeot 	case 40500000:
438*5d0b1887SFrançois Tigeot 	case 40541000:
439*5d0b1887SFrançois Tigeot 	case 54000000:
440*5d0b1887SFrançois Tigeot 	case 54054000:
441*5d0b1887SFrançois Tigeot 	case 59341000:
442*5d0b1887SFrançois Tigeot 	case 59400000:
443*5d0b1887SFrançois Tigeot 	case 72000000:
444*5d0b1887SFrançois Tigeot 	case 74176000:
445*5d0b1887SFrançois Tigeot 	case 74250000:
446*5d0b1887SFrançois Tigeot 	case 81000000:
447*5d0b1887SFrançois Tigeot 	case 81081000:
448*5d0b1887SFrançois Tigeot 	case 89012000:
449*5d0b1887SFrançois Tigeot 	case 89100000:
450*5d0b1887SFrançois Tigeot 	case 108000000:
451*5d0b1887SFrançois Tigeot 	case 108108000:
452*5d0b1887SFrançois Tigeot 	case 111264000:
453*5d0b1887SFrançois Tigeot 	case 111375000:
454*5d0b1887SFrançois Tigeot 	case 148352000:
455*5d0b1887SFrançois Tigeot 	case 148500000:
456*5d0b1887SFrançois Tigeot 	case 162000000:
457*5d0b1887SFrançois Tigeot 	case 162162000:
458*5d0b1887SFrançois Tigeot 	case 222525000:
459*5d0b1887SFrançois Tigeot 	case 222750000:
460*5d0b1887SFrançois Tigeot 	case 296703000:
461*5d0b1887SFrançois Tigeot 	case 297000000:
462*5d0b1887SFrançois Tigeot 		budget = 0;
46319df918dSFrançois Tigeot 		break;
464*5d0b1887SFrançois Tigeot 	case 233500000:
465*5d0b1887SFrançois Tigeot 	case 245250000:
466*5d0b1887SFrançois Tigeot 	case 247750000:
467*5d0b1887SFrançois Tigeot 	case 253250000:
468*5d0b1887SFrançois Tigeot 	case 298000000:
469*5d0b1887SFrançois Tigeot 		budget = 1500;
470*5d0b1887SFrançois Tigeot 		break;
471*5d0b1887SFrançois Tigeot 	case 169128000:
472*5d0b1887SFrançois Tigeot 	case 169500000:
473*5d0b1887SFrançois Tigeot 	case 179500000:
474*5d0b1887SFrançois Tigeot 	case 202000000:
475*5d0b1887SFrançois Tigeot 		budget = 2000;
476*5d0b1887SFrançois Tigeot 		break;
477*5d0b1887SFrançois Tigeot 	case 256250000:
478*5d0b1887SFrançois Tigeot 	case 262500000:
479*5d0b1887SFrançois Tigeot 	case 270000000:
480*5d0b1887SFrançois Tigeot 	case 272500000:
481*5d0b1887SFrançois Tigeot 	case 273750000:
482*5d0b1887SFrançois Tigeot 	case 280750000:
483*5d0b1887SFrançois Tigeot 	case 281250000:
484*5d0b1887SFrançois Tigeot 	case 286000000:
485*5d0b1887SFrançois Tigeot 	case 291750000:
486*5d0b1887SFrançois Tigeot 		budget = 4000;
487*5d0b1887SFrançois Tigeot 		break;
488*5d0b1887SFrançois Tigeot 	case 267250000:
489*5d0b1887SFrançois Tigeot 	case 268500000:
490*5d0b1887SFrançois Tigeot 		budget = 5000;
491*5d0b1887SFrançois Tigeot 		break;
492*5d0b1887SFrançois Tigeot 	default:
493*5d0b1887SFrançois Tigeot 		budget = 1000;
494*5d0b1887SFrançois Tigeot 		break;
49519df918dSFrançois Tigeot 	}
49619df918dSFrançois Tigeot 
497*5d0b1887SFrançois Tigeot 	return budget;
498*5d0b1887SFrançois Tigeot }
499*5d0b1887SFrançois Tigeot 
500*5d0b1887SFrançois Tigeot static void wrpll_update_rnp(uint64_t freq2k, unsigned budget,
501*5d0b1887SFrançois Tigeot 			     unsigned r2, unsigned n2, unsigned p,
502*5d0b1887SFrançois Tigeot 			     struct wrpll_rnp *best)
503*5d0b1887SFrançois Tigeot {
504*5d0b1887SFrançois Tigeot 	uint64_t a, b, c, d, diff, diff_best;
505*5d0b1887SFrançois Tigeot 
506*5d0b1887SFrançois Tigeot 	/* No best (r,n,p) yet */
507*5d0b1887SFrançois Tigeot 	if (best->p == 0) {
508*5d0b1887SFrançois Tigeot 		best->p = p;
509*5d0b1887SFrançois Tigeot 		best->n2 = n2;
510*5d0b1887SFrançois Tigeot 		best->r2 = r2;
511*5d0b1887SFrançois Tigeot 		return;
512*5d0b1887SFrançois Tigeot 	}
513*5d0b1887SFrançois Tigeot 
514*5d0b1887SFrançois Tigeot 	/*
515*5d0b1887SFrançois Tigeot 	 * Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to
516*5d0b1887SFrançois Tigeot 	 * freq2k.
517*5d0b1887SFrançois Tigeot 	 *
518*5d0b1887SFrançois Tigeot 	 * delta = 1e6 *
519*5d0b1887SFrançois Tigeot 	 *	   abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) /
520*5d0b1887SFrançois Tigeot 	 *	   freq2k;
521*5d0b1887SFrançois Tigeot 	 *
522*5d0b1887SFrançois Tigeot 	 * and we would like delta <= budget.
523*5d0b1887SFrançois Tigeot 	 *
524*5d0b1887SFrançois Tigeot 	 * If the discrepancy is above the PPM-based budget, always prefer to
525*5d0b1887SFrançois Tigeot 	 * improve upon the previous solution.  However, if you're within the
526*5d0b1887SFrançois Tigeot 	 * budget, try to maximize Ref * VCO, that is N / (P * R^2).
527*5d0b1887SFrançois Tigeot 	 */
528*5d0b1887SFrançois Tigeot 	a = freq2k * budget * p * r2;
529*5d0b1887SFrançois Tigeot 	b = freq2k * budget * best->p * best->r2;
530*5d0b1887SFrançois Tigeot 	diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2));
531*5d0b1887SFrançois Tigeot 	diff_best = ABS_DIFF((freq2k * best->p * best->r2),
532*5d0b1887SFrançois Tigeot 			     (LC_FREQ_2K * best->n2));
533*5d0b1887SFrançois Tigeot 	c = 1000000 * diff;
534*5d0b1887SFrançois Tigeot 	d = 1000000 * diff_best;
535*5d0b1887SFrançois Tigeot 
536*5d0b1887SFrançois Tigeot 	if (a < c && b < d) {
537*5d0b1887SFrançois Tigeot 		/* If both are above the budget, pick the closer */
538*5d0b1887SFrançois Tigeot 		if (best->p * best->r2 * diff < p * r2 * diff_best) {
539*5d0b1887SFrançois Tigeot 			best->p = p;
540*5d0b1887SFrançois Tigeot 			best->n2 = n2;
541*5d0b1887SFrançois Tigeot 			best->r2 = r2;
542*5d0b1887SFrançois Tigeot 		}
543*5d0b1887SFrançois Tigeot 	} else if (a >= c && b < d) {
544*5d0b1887SFrançois Tigeot 		/* If A is below the threshold but B is above it?  Update. */
545*5d0b1887SFrançois Tigeot 		best->p = p;
546*5d0b1887SFrançois Tigeot 		best->n2 = n2;
547*5d0b1887SFrançois Tigeot 		best->r2 = r2;
548*5d0b1887SFrançois Tigeot 	} else if (a >= c && b >= d) {
549*5d0b1887SFrançois Tigeot 		/* Both are below the limit, so pick the higher n2/(r2*r2) */
550*5d0b1887SFrançois Tigeot 		if (n2 * best->r2 * best->r2 > best->n2 * r2 * r2) {
551*5d0b1887SFrançois Tigeot 			best->p = p;
552*5d0b1887SFrançois Tigeot 			best->n2 = n2;
553*5d0b1887SFrançois Tigeot 			best->r2 = r2;
554*5d0b1887SFrançois Tigeot 		}
555*5d0b1887SFrançois Tigeot 	}
556*5d0b1887SFrançois Tigeot 	/* Otherwise a < c && b >= d, do nothing */
557*5d0b1887SFrançois Tigeot }
558*5d0b1887SFrançois Tigeot 
559*5d0b1887SFrançois Tigeot static void
560*5d0b1887SFrançois Tigeot intel_ddi_calculate_wrpll(int clock /* in Hz */,
561*5d0b1887SFrançois Tigeot 			  unsigned *r2_out, unsigned *n2_out, unsigned *p_out)
562*5d0b1887SFrançois Tigeot {
563*5d0b1887SFrançois Tigeot 	uint64_t freq2k;
564*5d0b1887SFrançois Tigeot 	unsigned p, n2, r2;
565*5d0b1887SFrançois Tigeot 	struct wrpll_rnp best = { 0, 0, 0 };
566*5d0b1887SFrançois Tigeot 	unsigned budget;
567*5d0b1887SFrançois Tigeot 
568*5d0b1887SFrançois Tigeot 	freq2k = clock / 100;
569*5d0b1887SFrançois Tigeot 
570*5d0b1887SFrançois Tigeot 	budget = wrpll_get_budget_for_freq(clock);
571*5d0b1887SFrançois Tigeot 
572*5d0b1887SFrançois Tigeot 	/* Special case handling for 540 pixel clock: bypass WR PLL entirely
573*5d0b1887SFrançois Tigeot 	 * and directly pass the LC PLL to it. */
574*5d0b1887SFrançois Tigeot 	if (freq2k == 5400000) {
575*5d0b1887SFrançois Tigeot 		*n2_out = 2;
576*5d0b1887SFrançois Tigeot 		*p_out = 1;
577*5d0b1887SFrançois Tigeot 		*r2_out = 2;
578*5d0b1887SFrançois Tigeot 		return;
579*5d0b1887SFrançois Tigeot 	}
580*5d0b1887SFrançois Tigeot 
581*5d0b1887SFrançois Tigeot 	/*
582*5d0b1887SFrançois Tigeot 	 * Ref = LC_FREQ / R, where Ref is the actual reference input seen by
583*5d0b1887SFrançois Tigeot 	 * the WR PLL.
584*5d0b1887SFrançois Tigeot 	 *
585*5d0b1887SFrançois Tigeot 	 * We want R so that REF_MIN <= Ref <= REF_MAX.
586*5d0b1887SFrançois Tigeot 	 * Injecting R2 = 2 * R gives:
587*5d0b1887SFrançois Tigeot 	 *   REF_MAX * r2 > LC_FREQ * 2 and
588*5d0b1887SFrançois Tigeot 	 *   REF_MIN * r2 < LC_FREQ * 2
589*5d0b1887SFrançois Tigeot 	 *
590*5d0b1887SFrançois Tigeot 	 * Which means the desired boundaries for r2 are:
591*5d0b1887SFrançois Tigeot 	 *  LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
592*5d0b1887SFrançois Tigeot 	 *
593*5d0b1887SFrançois Tigeot 	 */
594*5d0b1887SFrançois Tigeot 	for (r2 = LC_FREQ * 2 / REF_MAX + 1;
595*5d0b1887SFrançois Tigeot 	     r2 <= LC_FREQ * 2 / REF_MIN;
596*5d0b1887SFrançois Tigeot 	     r2++) {
597*5d0b1887SFrançois Tigeot 
598*5d0b1887SFrançois Tigeot 		/*
599*5d0b1887SFrançois Tigeot 		 * VCO = N * Ref, that is: VCO = N * LC_FREQ / R
600*5d0b1887SFrançois Tigeot 		 *
601*5d0b1887SFrançois Tigeot 		 * Once again we want VCO_MIN <= VCO <= VCO_MAX.
602*5d0b1887SFrançois Tigeot 		 * Injecting R2 = 2 * R and N2 = 2 * N, we get:
603*5d0b1887SFrançois Tigeot 		 *   VCO_MAX * r2 > n2 * LC_FREQ and
604*5d0b1887SFrançois Tigeot 		 *   VCO_MIN * r2 < n2 * LC_FREQ)
605*5d0b1887SFrançois Tigeot 		 *
606*5d0b1887SFrançois Tigeot 		 * Which means the desired boundaries for n2 are:
607*5d0b1887SFrançois Tigeot 		 * VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
608*5d0b1887SFrançois Tigeot 		 */
609*5d0b1887SFrançois Tigeot 		for (n2 = VCO_MIN * r2 / LC_FREQ + 1;
610*5d0b1887SFrançois Tigeot 		     n2 <= VCO_MAX * r2 / LC_FREQ;
611*5d0b1887SFrançois Tigeot 		     n2++) {
612*5d0b1887SFrançois Tigeot 
613*5d0b1887SFrançois Tigeot 			for (p = P_MIN; p <= P_MAX; p += P_INC)
614*5d0b1887SFrançois Tigeot 				wrpll_update_rnp(freq2k, budget,
615*5d0b1887SFrançois Tigeot 						 r2, n2, p, &best);
616*5d0b1887SFrançois Tigeot 		}
617*5d0b1887SFrançois Tigeot 	}
618*5d0b1887SFrançois Tigeot 
619*5d0b1887SFrançois Tigeot 	*n2_out = best.n2;
620*5d0b1887SFrançois Tigeot 	*p_out = best.p;
621*5d0b1887SFrançois Tigeot 	*r2_out = best.r2;
622*5d0b1887SFrançois Tigeot 
623*5d0b1887SFrançois Tigeot 	DRM_DEBUG_KMS("WRPLL: %dHz refresh rate with p=%d, n2=%d r2=%d\n",
624*5d0b1887SFrançois Tigeot 		      clock, *p_out, *n2_out, *r2_out);
625*5d0b1887SFrançois Tigeot }
626*5d0b1887SFrançois Tigeot 
627*5d0b1887SFrançois Tigeot bool intel_ddi_pll_mode_set(struct drm_crtc *crtc)
62819df918dSFrançois Tigeot {
62919df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
63019df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
63119df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
63219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
63319df918dSFrançois Tigeot 	struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
63419df918dSFrançois Tigeot 	int type = intel_encoder->type;
63519df918dSFrançois Tigeot 	enum i915_pipe pipe = intel_crtc->pipe;
63619df918dSFrançois Tigeot 	uint32_t reg, val;
637*5d0b1887SFrançois Tigeot 	int clock = intel_crtc->config.port_clock;
63819df918dSFrançois Tigeot 
63919df918dSFrançois Tigeot 	/* TODO: reuse PLLs when possible (compare values) */
64019df918dSFrançois Tigeot 
64119df918dSFrançois Tigeot 	intel_ddi_put_crtc_pll(crtc);
64219df918dSFrançois Tigeot 
64319df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
64419df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
64519df918dSFrançois Tigeot 
64619df918dSFrançois Tigeot 		switch (intel_dp->link_bw) {
64719df918dSFrançois Tigeot 		case DP_LINK_BW_1_62:
64819df918dSFrançois Tigeot 			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810;
64919df918dSFrançois Tigeot 			break;
65019df918dSFrançois Tigeot 		case DP_LINK_BW_2_7:
65119df918dSFrançois Tigeot 			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350;
65219df918dSFrançois Tigeot 			break;
65319df918dSFrançois Tigeot 		case DP_LINK_BW_5_4:
65419df918dSFrançois Tigeot 			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700;
65519df918dSFrançois Tigeot 			break;
65619df918dSFrançois Tigeot 		default:
65719df918dSFrançois Tigeot 			DRM_ERROR("Link bandwidth %d unsupported\n",
65819df918dSFrançois Tigeot 				  intel_dp->link_bw);
65919df918dSFrançois Tigeot 			return false;
66019df918dSFrançois Tigeot 		}
66119df918dSFrançois Tigeot 
66219df918dSFrançois Tigeot 		/* We don't need to turn any PLL on because we'll use LCPLL. */
66319df918dSFrançois Tigeot 		return true;
66419df918dSFrançois Tigeot 
66519df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_HDMI) {
666*5d0b1887SFrançois Tigeot 		unsigned p, n2, r2;
66719df918dSFrançois Tigeot 
66819df918dSFrançois Tigeot 		if (plls->wrpll1_refcount == 0) {
66919df918dSFrançois Tigeot 			DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n",
67019df918dSFrançois Tigeot 				      pipe_name(pipe));
67119df918dSFrançois Tigeot 			plls->wrpll1_refcount++;
67219df918dSFrançois Tigeot 			reg = WRPLL_CTL1;
67319df918dSFrançois Tigeot 			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL1;
67419df918dSFrançois Tigeot 		} else if (plls->wrpll2_refcount == 0) {
67519df918dSFrançois Tigeot 			DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n",
67619df918dSFrançois Tigeot 				      pipe_name(pipe));
67719df918dSFrançois Tigeot 			plls->wrpll2_refcount++;
67819df918dSFrançois Tigeot 			reg = WRPLL_CTL2;
67919df918dSFrançois Tigeot 			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL2;
68019df918dSFrançois Tigeot 		} else {
68119df918dSFrançois Tigeot 			DRM_ERROR("No WRPLLs available!\n");
68219df918dSFrançois Tigeot 			return false;
68319df918dSFrançois Tigeot 		}
68419df918dSFrançois Tigeot 
68519df918dSFrançois Tigeot 		WARN(I915_READ(reg) & WRPLL_PLL_ENABLE,
68619df918dSFrançois Tigeot 		     "WRPLL already enabled\n");
68719df918dSFrançois Tigeot 
688*5d0b1887SFrançois Tigeot 		intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
68919df918dSFrançois Tigeot 
69019df918dSFrançois Tigeot 		val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 |
69119df918dSFrançois Tigeot 		      WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) |
69219df918dSFrançois Tigeot 		      WRPLL_DIVIDER_POST(p);
69319df918dSFrançois Tigeot 
69419df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_ANALOG) {
69519df918dSFrançois Tigeot 		if (plls->spll_refcount == 0) {
69619df918dSFrançois Tigeot 			DRM_DEBUG_KMS("Using SPLL on pipe %c\n",
69719df918dSFrançois Tigeot 				      pipe_name(pipe));
69819df918dSFrançois Tigeot 			plls->spll_refcount++;
69919df918dSFrançois Tigeot 			reg = SPLL_CTL;
70019df918dSFrançois Tigeot 			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL;
7018e26cdf6SFrançois Tigeot 		} else {
7028e26cdf6SFrançois Tigeot 			DRM_ERROR("SPLL already in use\n");
7038e26cdf6SFrançois Tigeot 			return false;
70419df918dSFrançois Tigeot 		}
70519df918dSFrançois Tigeot 
70619df918dSFrançois Tigeot 		WARN(I915_READ(reg) & SPLL_PLL_ENABLE,
70719df918dSFrançois Tigeot 		     "SPLL already enabled\n");
70819df918dSFrançois Tigeot 
70919df918dSFrançois Tigeot 		val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC;
71019df918dSFrançois Tigeot 
71119df918dSFrançois Tigeot 	} else {
71219df918dSFrançois Tigeot 		WARN(1, "Invalid DDI encoder type %d\n", type);
71319df918dSFrançois Tigeot 		return false;
71419df918dSFrançois Tigeot 	}
71519df918dSFrançois Tigeot 
71619df918dSFrançois Tigeot 	I915_WRITE(reg, val);
71719df918dSFrançois Tigeot 	udelay(20);
71819df918dSFrançois Tigeot 
71919df918dSFrançois Tigeot 	return true;
72019df918dSFrançois Tigeot }
72119df918dSFrançois Tigeot 
72219df918dSFrançois Tigeot void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
72319df918dSFrançois Tigeot {
72419df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
72519df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
72619df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
7278e26cdf6SFrançois Tigeot 	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
72819df918dSFrançois Tigeot 	int type = intel_encoder->type;
72919df918dSFrançois Tigeot 	uint32_t temp;
73019df918dSFrançois Tigeot 
73119df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
73219df918dSFrançois Tigeot 
73319df918dSFrançois Tigeot 		temp = TRANS_MSA_SYNC_CLK;
7348e26cdf6SFrançois Tigeot 		switch (intel_crtc->config.pipe_bpp) {
73519df918dSFrançois Tigeot 		case 18:
73619df918dSFrançois Tigeot 			temp |= TRANS_MSA_6_BPC;
73719df918dSFrançois Tigeot 			break;
73819df918dSFrançois Tigeot 		case 24:
73919df918dSFrançois Tigeot 			temp |= TRANS_MSA_8_BPC;
74019df918dSFrançois Tigeot 			break;
74119df918dSFrançois Tigeot 		case 30:
74219df918dSFrançois Tigeot 			temp |= TRANS_MSA_10_BPC;
74319df918dSFrançois Tigeot 			break;
74419df918dSFrançois Tigeot 		case 36:
74519df918dSFrançois Tigeot 			temp |= TRANS_MSA_12_BPC;
74619df918dSFrançois Tigeot 			break;
74719df918dSFrançois Tigeot 		default:
7488e26cdf6SFrançois Tigeot 			BUG();
74919df918dSFrançois Tigeot 		}
75019df918dSFrançois Tigeot 		I915_WRITE(TRANS_MSA_MISC(cpu_transcoder), temp);
75119df918dSFrançois Tigeot 	}
75219df918dSFrançois Tigeot }
75319df918dSFrançois Tigeot 
7548e26cdf6SFrançois Tigeot void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
75519df918dSFrançois Tigeot {
75619df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
75719df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
75819df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
75919df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
76019df918dSFrançois Tigeot 	enum i915_pipe pipe = intel_crtc->pipe;
7618e26cdf6SFrançois Tigeot 	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
76219df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(intel_encoder);
76319df918dSFrançois Tigeot 	int type = intel_encoder->type;
76419df918dSFrançois Tigeot 	uint32_t temp;
76519df918dSFrançois Tigeot 
76619df918dSFrançois Tigeot 	/* Enable TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */
76719df918dSFrançois Tigeot 	temp = TRANS_DDI_FUNC_ENABLE;
76819df918dSFrançois Tigeot 	temp |= TRANS_DDI_SELECT_PORT(port);
76919df918dSFrançois Tigeot 
7708e26cdf6SFrançois Tigeot 	switch (intel_crtc->config.pipe_bpp) {
77119df918dSFrançois Tigeot 	case 18:
77219df918dSFrançois Tigeot 		temp |= TRANS_DDI_BPC_6;
77319df918dSFrançois Tigeot 		break;
77419df918dSFrançois Tigeot 	case 24:
77519df918dSFrançois Tigeot 		temp |= TRANS_DDI_BPC_8;
77619df918dSFrançois Tigeot 		break;
77719df918dSFrançois Tigeot 	case 30:
77819df918dSFrançois Tigeot 		temp |= TRANS_DDI_BPC_10;
77919df918dSFrançois Tigeot 		break;
78019df918dSFrançois Tigeot 	case 36:
78119df918dSFrançois Tigeot 		temp |= TRANS_DDI_BPC_12;
78219df918dSFrançois Tigeot 		break;
78319df918dSFrançois Tigeot 	default:
7848e26cdf6SFrançois Tigeot 		BUG();
78519df918dSFrançois Tigeot 	}
78619df918dSFrançois Tigeot 
78719df918dSFrançois Tigeot 	if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
78819df918dSFrançois Tigeot 		temp |= TRANS_DDI_PVSYNC;
78919df918dSFrançois Tigeot 	if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
79019df918dSFrançois Tigeot 		temp |= TRANS_DDI_PHSYNC;
79119df918dSFrançois Tigeot 
79219df918dSFrançois Tigeot 	if (cpu_transcoder == TRANSCODER_EDP) {
79319df918dSFrançois Tigeot 		switch (pipe) {
79419df918dSFrançois Tigeot 		case PIPE_A:
795a2fdbec6SFrançois Tigeot 			/* Can only use the always-on power well for eDP when
796a2fdbec6SFrançois Tigeot 			 * not using the panel fitter, and when not using motion
797a2fdbec6SFrançois Tigeot 			  * blur mitigation (which we don't support). */
798*5d0b1887SFrançois Tigeot 			if (intel_crtc->config.pch_pfit.size)
79919df918dSFrançois Tigeot 				temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
800a2fdbec6SFrançois Tigeot 			else
801a2fdbec6SFrançois Tigeot 				temp |= TRANS_DDI_EDP_INPUT_A_ON;
80219df918dSFrançois Tigeot 			break;
80319df918dSFrançois Tigeot 		case PIPE_B:
80419df918dSFrançois Tigeot 			temp |= TRANS_DDI_EDP_INPUT_B_ONOFF;
80519df918dSFrançois Tigeot 			break;
80619df918dSFrançois Tigeot 		case PIPE_C:
80719df918dSFrançois Tigeot 			temp |= TRANS_DDI_EDP_INPUT_C_ONOFF;
80819df918dSFrançois Tigeot 			break;
80919df918dSFrançois Tigeot 		default:
81019df918dSFrançois Tigeot 			BUG();
81119df918dSFrançois Tigeot 			break;
81219df918dSFrançois Tigeot 		}
81319df918dSFrançois Tigeot 	}
81419df918dSFrançois Tigeot 
81519df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_HDMI) {
81619df918dSFrançois Tigeot 		struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
81719df918dSFrançois Tigeot 
81819df918dSFrançois Tigeot 		if (intel_hdmi->has_hdmi_sink)
81919df918dSFrançois Tigeot 			temp |= TRANS_DDI_MODE_SELECT_HDMI;
82019df918dSFrançois Tigeot 		else
82119df918dSFrançois Tigeot 			temp |= TRANS_DDI_MODE_SELECT_DVI;
82219df918dSFrançois Tigeot 
82319df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_ANALOG) {
82419df918dSFrançois Tigeot 		temp |= TRANS_DDI_MODE_SELECT_FDI;
825*5d0b1887SFrançois Tigeot 		temp |= (intel_crtc->config.fdi_lanes - 1) << 1;
82619df918dSFrançois Tigeot 
82719df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_DISPLAYPORT ||
82819df918dSFrançois Tigeot 		   type == INTEL_OUTPUT_EDP) {
82919df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
83019df918dSFrançois Tigeot 
83119df918dSFrançois Tigeot 		temp |= TRANS_DDI_MODE_SELECT_DP_SST;
83219df918dSFrançois Tigeot 
833*5d0b1887SFrançois Tigeot 		temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
83419df918dSFrançois Tigeot 	} else {
835*5d0b1887SFrançois Tigeot 		WARN(1, "Invalid encoder type %d for pipe %c\n",
836*5d0b1887SFrançois Tigeot 		     intel_encoder->type, pipe_name(pipe));
83719df918dSFrançois Tigeot 	}
83819df918dSFrançois Tigeot 
83919df918dSFrançois Tigeot 	I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
84019df918dSFrançois Tigeot }
84119df918dSFrançois Tigeot 
84219df918dSFrançois Tigeot void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
84319df918dSFrançois Tigeot 				       enum transcoder cpu_transcoder)
84419df918dSFrançois Tigeot {
84519df918dSFrançois Tigeot 	uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
84619df918dSFrançois Tigeot 	uint32_t val = I915_READ(reg);
84719df918dSFrançois Tigeot 
84819df918dSFrançois Tigeot 	val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
84919df918dSFrançois Tigeot 	val |= TRANS_DDI_PORT_NONE;
85019df918dSFrançois Tigeot 	I915_WRITE(reg, val);
85119df918dSFrançois Tigeot }
85219df918dSFrançois Tigeot 
85319df918dSFrançois Tigeot bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
85419df918dSFrançois Tigeot {
85519df918dSFrançois Tigeot 	struct drm_device *dev = intel_connector->base.dev;
85619df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
85719df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_connector->encoder;
85819df918dSFrançois Tigeot 	int type = intel_connector->base.connector_type;
85919df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(intel_encoder);
86019df918dSFrançois Tigeot 	enum i915_pipe pipe = 0;
86119df918dSFrançois Tigeot 	enum transcoder cpu_transcoder;
86219df918dSFrançois Tigeot 	uint32_t tmp;
86319df918dSFrançois Tigeot 
86419df918dSFrançois Tigeot 	if (!intel_encoder->get_hw_state(intel_encoder, &pipe))
86519df918dSFrançois Tigeot 		return false;
86619df918dSFrançois Tigeot 
86719df918dSFrançois Tigeot 	if (port == PORT_A)
86819df918dSFrançois Tigeot 		cpu_transcoder = TRANSCODER_EDP;
86919df918dSFrançois Tigeot 	else
870a2fdbec6SFrançois Tigeot 		cpu_transcoder = (enum transcoder) pipe;
87119df918dSFrançois Tigeot 
87219df918dSFrançois Tigeot 	tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
87319df918dSFrançois Tigeot 
87419df918dSFrançois Tigeot 	switch (tmp & TRANS_DDI_MODE_SELECT_MASK) {
87519df918dSFrançois Tigeot 	case TRANS_DDI_MODE_SELECT_HDMI:
87619df918dSFrançois Tigeot 	case TRANS_DDI_MODE_SELECT_DVI:
87719df918dSFrançois Tigeot 		return (type == DRM_MODE_CONNECTOR_HDMIA);
87819df918dSFrançois Tigeot 
87919df918dSFrançois Tigeot 	case TRANS_DDI_MODE_SELECT_DP_SST:
88019df918dSFrançois Tigeot 		if (type == DRM_MODE_CONNECTOR_eDP)
88119df918dSFrançois Tigeot 			return true;
88219df918dSFrançois Tigeot 	case TRANS_DDI_MODE_SELECT_DP_MST:
88319df918dSFrançois Tigeot 		return (type == DRM_MODE_CONNECTOR_DisplayPort);
88419df918dSFrançois Tigeot 
88519df918dSFrançois Tigeot 	case TRANS_DDI_MODE_SELECT_FDI:
88619df918dSFrançois Tigeot 		return (type == DRM_MODE_CONNECTOR_VGA);
88719df918dSFrançois Tigeot 
88819df918dSFrançois Tigeot 	default:
88919df918dSFrançois Tigeot 		return false;
89019df918dSFrançois Tigeot 	}
89119df918dSFrançois Tigeot }
89219df918dSFrançois Tigeot 
89319df918dSFrançois Tigeot bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
89419df918dSFrançois Tigeot 			    enum i915_pipe *pipe)
89519df918dSFrançois Tigeot {
89619df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
89719df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
89819df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(encoder);
89919df918dSFrançois Tigeot 	u32 tmp;
90019df918dSFrançois Tigeot 	int i;
90119df918dSFrançois Tigeot 
90219df918dSFrançois Tigeot 	tmp = I915_READ(DDI_BUF_CTL(port));
90319df918dSFrançois Tigeot 
90419df918dSFrançois Tigeot 	if (!(tmp & DDI_BUF_CTL_ENABLE))
90519df918dSFrançois Tigeot 		return false;
90619df918dSFrançois Tigeot 
90719df918dSFrançois Tigeot 	if (port == PORT_A) {
90819df918dSFrançois Tigeot 		tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP));
90919df918dSFrançois Tigeot 
91019df918dSFrançois Tigeot 		switch (tmp & TRANS_DDI_EDP_INPUT_MASK) {
91119df918dSFrançois Tigeot 		case TRANS_DDI_EDP_INPUT_A_ON:
91219df918dSFrançois Tigeot 		case TRANS_DDI_EDP_INPUT_A_ONOFF:
91319df918dSFrançois Tigeot 			*pipe = PIPE_A;
91419df918dSFrançois Tigeot 			break;
91519df918dSFrançois Tigeot 		case TRANS_DDI_EDP_INPUT_B_ONOFF:
91619df918dSFrançois Tigeot 			*pipe = PIPE_B;
91719df918dSFrançois Tigeot 			break;
91819df918dSFrançois Tigeot 		case TRANS_DDI_EDP_INPUT_C_ONOFF:
91919df918dSFrançois Tigeot 			*pipe = PIPE_C;
92019df918dSFrançois Tigeot 			break;
92119df918dSFrançois Tigeot 		}
92219df918dSFrançois Tigeot 
92319df918dSFrançois Tigeot 		return true;
92419df918dSFrançois Tigeot 	} else {
92519df918dSFrançois Tigeot 		for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) {
92619df918dSFrançois Tigeot 			tmp = I915_READ(TRANS_DDI_FUNC_CTL(i));
92719df918dSFrançois Tigeot 
92819df918dSFrançois Tigeot 			if ((tmp & TRANS_DDI_PORT_MASK)
92919df918dSFrançois Tigeot 			    == TRANS_DDI_SELECT_PORT(port)) {
93019df918dSFrançois Tigeot 				*pipe = i;
93119df918dSFrançois Tigeot 				return true;
93219df918dSFrançois Tigeot 			}
93319df918dSFrançois Tigeot 		}
93419df918dSFrançois Tigeot 	}
93519df918dSFrançois Tigeot 
936*5d0b1887SFrançois Tigeot 	DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port));
93719df918dSFrançois Tigeot 
9388e26cdf6SFrançois Tigeot 	return false;
93919df918dSFrançois Tigeot }
94019df918dSFrançois Tigeot 
94119df918dSFrançois Tigeot static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv,
94219df918dSFrançois Tigeot 				       enum i915_pipe pipe)
94319df918dSFrançois Tigeot {
94419df918dSFrançois Tigeot 	uint32_t temp, ret;
94519df918dSFrançois Tigeot 	enum port port = I915_MAX_PORTS;
94619df918dSFrançois Tigeot 	enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
94719df918dSFrançois Tigeot 								      pipe);
94819df918dSFrançois Tigeot 	int i;
94919df918dSFrançois Tigeot 
95019df918dSFrançois Tigeot 	if (cpu_transcoder == TRANSCODER_EDP) {
95119df918dSFrançois Tigeot 		port = PORT_A;
95219df918dSFrançois Tigeot 	} else {
95319df918dSFrançois Tigeot 		temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
95419df918dSFrançois Tigeot 		temp &= TRANS_DDI_PORT_MASK;
95519df918dSFrançois Tigeot 
95619df918dSFrançois Tigeot 		for (i = PORT_B; i <= PORT_E; i++)
95719df918dSFrançois Tigeot 			if (temp == TRANS_DDI_SELECT_PORT(i))
95819df918dSFrançois Tigeot 				port = i;
95919df918dSFrançois Tigeot 	}
96019df918dSFrançois Tigeot 
9618e26cdf6SFrançois Tigeot 	if (port == I915_MAX_PORTS) {
9628e26cdf6SFrançois Tigeot 		WARN(1, "Pipe %c enabled on an unknown port\n",
9638e26cdf6SFrançois Tigeot 		     pipe_name(pipe));
9648e26cdf6SFrançois Tigeot 		ret = PORT_CLK_SEL_NONE;
9658e26cdf6SFrançois Tigeot 	} else {
96619df918dSFrançois Tigeot 		ret = I915_READ(PORT_CLK_SEL(port));
9678e26cdf6SFrançois Tigeot 		DRM_DEBUG_KMS("Pipe %c connected to port %c using clock "
9688e26cdf6SFrançois Tigeot 			      "0x%08x\n", pipe_name(pipe), port_name(port),
9698e26cdf6SFrançois Tigeot 			      ret);
9708e26cdf6SFrançois Tigeot 	}
97119df918dSFrançois Tigeot 
97219df918dSFrançois Tigeot 	return ret;
97319df918dSFrançois Tigeot }
97419df918dSFrançois Tigeot 
97519df918dSFrançois Tigeot void intel_ddi_setup_hw_pll_state(struct drm_device *dev)
97619df918dSFrançois Tigeot {
97719df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
97819df918dSFrançois Tigeot 	enum i915_pipe pipe;
97919df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc;
98019df918dSFrançois Tigeot 
98119df918dSFrançois Tigeot 	for_each_pipe(pipe) {
98219df918dSFrançois Tigeot 		intel_crtc =
98319df918dSFrançois Tigeot 			to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
98419df918dSFrançois Tigeot 
98519df918dSFrançois Tigeot 		if (!intel_crtc->active)
98619df918dSFrançois Tigeot 			continue;
98719df918dSFrançois Tigeot 
98819df918dSFrançois Tigeot 		intel_crtc->ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv,
98919df918dSFrançois Tigeot 								 pipe);
99019df918dSFrançois Tigeot 
99119df918dSFrançois Tigeot 		switch (intel_crtc->ddi_pll_sel) {
99219df918dSFrançois Tigeot 		case PORT_CLK_SEL_SPLL:
99319df918dSFrançois Tigeot 			dev_priv->ddi_plls.spll_refcount++;
99419df918dSFrançois Tigeot 			break;
99519df918dSFrançois Tigeot 		case PORT_CLK_SEL_WRPLL1:
99619df918dSFrançois Tigeot 			dev_priv->ddi_plls.wrpll1_refcount++;
99719df918dSFrançois Tigeot 			break;
99819df918dSFrançois Tigeot 		case PORT_CLK_SEL_WRPLL2:
99919df918dSFrançois Tigeot 			dev_priv->ddi_plls.wrpll2_refcount++;
100019df918dSFrançois Tigeot 			break;
100119df918dSFrançois Tigeot 		}
100219df918dSFrançois Tigeot 	}
100319df918dSFrançois Tigeot }
100419df918dSFrançois Tigeot 
100519df918dSFrançois Tigeot void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc)
100619df918dSFrançois Tigeot {
100719df918dSFrançois Tigeot 	struct drm_crtc *crtc = &intel_crtc->base;
100819df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
100919df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
101019df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(intel_encoder);
10118e26cdf6SFrançois Tigeot 	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
101219df918dSFrançois Tigeot 
101319df918dSFrançois Tigeot 	if (cpu_transcoder != TRANSCODER_EDP)
101419df918dSFrançois Tigeot 		I915_WRITE(TRANS_CLK_SEL(cpu_transcoder),
101519df918dSFrançois Tigeot 			   TRANS_CLK_SEL_PORT(port));
101619df918dSFrançois Tigeot }
101719df918dSFrançois Tigeot 
101819df918dSFrançois Tigeot void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc)
101919df918dSFrançois Tigeot {
102019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private;
10218e26cdf6SFrançois Tigeot 	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
102219df918dSFrançois Tigeot 
102319df918dSFrançois Tigeot 	if (cpu_transcoder != TRANSCODER_EDP)
102419df918dSFrançois Tigeot 		I915_WRITE(TRANS_CLK_SEL(cpu_transcoder),
102519df918dSFrançois Tigeot 			   TRANS_CLK_SEL_DISABLED);
102619df918dSFrançois Tigeot }
102719df918dSFrançois Tigeot 
102819df918dSFrançois Tigeot static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
102919df918dSFrançois Tigeot {
103019df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
103119df918dSFrançois Tigeot 	struct drm_crtc *crtc = encoder->crtc;
103219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
103319df918dSFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
103419df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(intel_encoder);
103519df918dSFrançois Tigeot 	int type = intel_encoder->type;
103619df918dSFrançois Tigeot 
103719df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_EDP) {
103819df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
103919df918dSFrançois Tigeot 		ironlake_edp_panel_vdd_on(intel_dp);
104019df918dSFrançois Tigeot 		ironlake_edp_panel_on(intel_dp);
104119df918dSFrançois Tigeot 		ironlake_edp_panel_vdd_off(intel_dp, true);
104219df918dSFrançois Tigeot 	}
104319df918dSFrançois Tigeot 
104419df918dSFrançois Tigeot 	WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
104519df918dSFrançois Tigeot 	I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
104619df918dSFrançois Tigeot 
104719df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
104819df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
104919df918dSFrançois Tigeot 
105019df918dSFrançois Tigeot 		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
105119df918dSFrançois Tigeot 		intel_dp_start_link_train(intel_dp);
105219df918dSFrançois Tigeot 		intel_dp_complete_link_train(intel_dp);
10538e26cdf6SFrançois Tigeot 		if (port != PORT_A)
10548e26cdf6SFrançois Tigeot 			intel_dp_stop_link_train(intel_dp);
105519df918dSFrançois Tigeot 	}
105619df918dSFrançois Tigeot }
105719df918dSFrançois Tigeot 
105819df918dSFrançois Tigeot static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
105919df918dSFrançois Tigeot {
106019df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
106119df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
106219df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(intel_encoder);
106319df918dSFrançois Tigeot 	int type = intel_encoder->type;
106419df918dSFrançois Tigeot 	uint32_t val;
106519df918dSFrançois Tigeot 	bool wait = false;
106619df918dSFrançois Tigeot 
106719df918dSFrançois Tigeot 	val = I915_READ(DDI_BUF_CTL(port));
106819df918dSFrançois Tigeot 	if (val & DDI_BUF_CTL_ENABLE) {
106919df918dSFrançois Tigeot 		val &= ~DDI_BUF_CTL_ENABLE;
107019df918dSFrançois Tigeot 		I915_WRITE(DDI_BUF_CTL(port), val);
107119df918dSFrançois Tigeot 		wait = true;
107219df918dSFrançois Tigeot 	}
107319df918dSFrançois Tigeot 
107419df918dSFrançois Tigeot 	val = I915_READ(DP_TP_CTL(port));
107519df918dSFrançois Tigeot 	val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK);
107619df918dSFrançois Tigeot 	val |= DP_TP_CTL_LINK_TRAIN_PAT1;
107719df918dSFrançois Tigeot 	I915_WRITE(DP_TP_CTL(port), val);
107819df918dSFrançois Tigeot 
107919df918dSFrançois Tigeot 	if (wait)
108019df918dSFrançois Tigeot 		intel_wait_ddi_buf_idle(dev_priv, port);
108119df918dSFrançois Tigeot 
108219df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_EDP) {
108319df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
108419df918dSFrançois Tigeot 		ironlake_edp_panel_vdd_on(intel_dp);
108519df918dSFrançois Tigeot 		ironlake_edp_panel_off(intel_dp);
108619df918dSFrançois Tigeot 	}
108719df918dSFrançois Tigeot 
108819df918dSFrançois Tigeot 	I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE);
108919df918dSFrançois Tigeot }
109019df918dSFrançois Tigeot 
109119df918dSFrançois Tigeot static void intel_enable_ddi(struct intel_encoder *intel_encoder)
109219df918dSFrançois Tigeot {
109319df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
1094a2fdbec6SFrançois Tigeot 	struct drm_crtc *crtc = encoder->crtc;
1095a2fdbec6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1096a2fdbec6SFrançois Tigeot 	int pipe = intel_crtc->pipe;
109719df918dSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
109819df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
109919df918dSFrançois Tigeot 	enum port port = intel_ddi_get_encoder_port(intel_encoder);
110019df918dSFrançois Tigeot 	int type = intel_encoder->type;
1101a2fdbec6SFrançois Tigeot 	uint32_t tmp;
110219df918dSFrançois Tigeot 
110319df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_HDMI) {
110419df918dSFrançois Tigeot 		struct intel_digital_port *intel_dig_port =
110519df918dSFrançois Tigeot 			enc_to_dig_port(encoder);
110619df918dSFrançois Tigeot 
110719df918dSFrançois Tigeot 		/* In HDMI/DVI mode, the port width, and swing/emphasis values
110819df918dSFrançois Tigeot 		 * are ignored so nothing special needs to be done besides
110919df918dSFrançois Tigeot 		 * enabling the port.
111019df918dSFrançois Tigeot 		 */
111119df918dSFrançois Tigeot 		I915_WRITE(DDI_BUF_CTL(port),
1112*5d0b1887SFrançois Tigeot 			   intel_dig_port->saved_port_bits |
1113*5d0b1887SFrançois Tigeot 			   DDI_BUF_CTL_ENABLE);
111419df918dSFrançois Tigeot 	} else if (type == INTEL_OUTPUT_EDP) {
111519df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
111619df918dSFrançois Tigeot 
11178e26cdf6SFrançois Tigeot 		if (port == PORT_A)
11188e26cdf6SFrançois Tigeot 			intel_dp_stop_link_train(intel_dp);
11198e26cdf6SFrançois Tigeot 
112019df918dSFrançois Tigeot 		ironlake_edp_backlight_on(intel_dp);
112119df918dSFrançois Tigeot 	}
1122a2fdbec6SFrançois Tigeot 
1123*5d0b1887SFrançois Tigeot 	if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
1124a2fdbec6SFrançois Tigeot 		tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
1125a2fdbec6SFrançois Tigeot 		tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4));
1126a2fdbec6SFrançois Tigeot 		I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
1127a2fdbec6SFrançois Tigeot 	}
112819df918dSFrançois Tigeot }
112919df918dSFrançois Tigeot 
113019df918dSFrançois Tigeot static void intel_disable_ddi(struct intel_encoder *intel_encoder)
113119df918dSFrançois Tigeot {
113219df918dSFrançois Tigeot 	struct drm_encoder *encoder = &intel_encoder->base;
1133a2fdbec6SFrançois Tigeot 	struct drm_crtc *crtc = encoder->crtc;
1134a2fdbec6SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
1135a2fdbec6SFrançois Tigeot 	int pipe = intel_crtc->pipe;
113619df918dSFrançois Tigeot 	int type = intel_encoder->type;
1137a2fdbec6SFrançois Tigeot 	struct drm_device *dev = encoder->dev;
1138a2fdbec6SFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
1139a2fdbec6SFrançois Tigeot 	uint32_t tmp;
114019df918dSFrançois Tigeot 
1141*5d0b1887SFrançois Tigeot 	if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
11428e26cdf6SFrançois Tigeot 		tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
1143*5d0b1887SFrançois Tigeot 		tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) <<
1144*5d0b1887SFrançois Tigeot 			 (pipe * 4));
11458e26cdf6SFrançois Tigeot 		I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
1146*5d0b1887SFrançois Tigeot 	}
11478e26cdf6SFrançois Tigeot 
114819df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_EDP) {
114919df918dSFrançois Tigeot 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
115019df918dSFrançois Tigeot 
115119df918dSFrançois Tigeot 		ironlake_edp_backlight_off(intel_dp);
115219df918dSFrançois Tigeot 	}
115319df918dSFrançois Tigeot }
115419df918dSFrançois Tigeot 
115519df918dSFrançois Tigeot int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
115619df918dSFrançois Tigeot {
115719df918dSFrançois Tigeot 	if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT)
1158*5d0b1887SFrançois Tigeot 		return 450000;
115919df918dSFrançois Tigeot 	else if ((I915_READ(LCPLL_CTL) & LCPLL_CLK_FREQ_MASK) ==
116019df918dSFrançois Tigeot 		 LCPLL_CLK_FREQ_450)
1161*5d0b1887SFrançois Tigeot 		return 450000;
116219df918dSFrançois Tigeot 	else if (IS_ULT(dev_priv->dev))
1163*5d0b1887SFrançois Tigeot 		return 337500;
116419df918dSFrançois Tigeot 	else
1165*5d0b1887SFrançois Tigeot 		return 540000;
116619df918dSFrançois Tigeot }
116719df918dSFrançois Tigeot 
116819df918dSFrançois Tigeot void intel_ddi_pll_init(struct drm_device *dev)
116919df918dSFrançois Tigeot {
117019df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
117119df918dSFrançois Tigeot 	uint32_t val = I915_READ(LCPLL_CTL);
117219df918dSFrançois Tigeot 
117319df918dSFrançois Tigeot 	/* The LCPLL register should be turned on by the BIOS. For now let's
117419df918dSFrançois Tigeot 	 * just check its state and print errors in case something is wrong.
117519df918dSFrançois Tigeot 	 * Don't even try to turn it on.
117619df918dSFrançois Tigeot 	 */
117719df918dSFrançois Tigeot 
1178*5d0b1887SFrançois Tigeot 	DRM_DEBUG_KMS("CDCLK running at %dKHz\n",
117919df918dSFrançois Tigeot 		      intel_ddi_get_cdclk_freq(dev_priv));
118019df918dSFrançois Tigeot 
118119df918dSFrançois Tigeot 	if (val & LCPLL_CD_SOURCE_FCLK)
118219df918dSFrançois Tigeot 		DRM_ERROR("CDCLK source is not LCPLL\n");
118319df918dSFrançois Tigeot 
118419df918dSFrançois Tigeot 	if (val & LCPLL_PLL_DISABLE)
118519df918dSFrançois Tigeot 		DRM_ERROR("LCPLL is disabled\n");
118619df918dSFrançois Tigeot }
118719df918dSFrançois Tigeot 
118819df918dSFrançois Tigeot void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
118919df918dSFrançois Tigeot {
119019df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
119119df918dSFrançois Tigeot 	struct intel_dp *intel_dp = &intel_dig_port->dp;
119219df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->dev->dev_private;
119319df918dSFrançois Tigeot 	enum port port = intel_dig_port->port;
119419df918dSFrançois Tigeot 	uint32_t val;
1195a2fdbec6SFrançois Tigeot 	bool wait = false;
119619df918dSFrançois Tigeot 
119719df918dSFrançois Tigeot 	if (I915_READ(DP_TP_CTL(port)) & DP_TP_CTL_ENABLE) {
119819df918dSFrançois Tigeot 		val = I915_READ(DDI_BUF_CTL(port));
119919df918dSFrançois Tigeot 		if (val & DDI_BUF_CTL_ENABLE) {
120019df918dSFrançois Tigeot 			val &= ~DDI_BUF_CTL_ENABLE;
120119df918dSFrançois Tigeot 			I915_WRITE(DDI_BUF_CTL(port), val);
120219df918dSFrançois Tigeot 			wait = true;
120319df918dSFrançois Tigeot 		}
120419df918dSFrançois Tigeot 
120519df918dSFrançois Tigeot 		val = I915_READ(DP_TP_CTL(port));
120619df918dSFrançois Tigeot 		val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK);
120719df918dSFrançois Tigeot 		val |= DP_TP_CTL_LINK_TRAIN_PAT1;
120819df918dSFrançois Tigeot 		I915_WRITE(DP_TP_CTL(port), val);
120919df918dSFrançois Tigeot 		POSTING_READ(DP_TP_CTL(port));
121019df918dSFrançois Tigeot 
121119df918dSFrançois Tigeot 		if (wait)
121219df918dSFrançois Tigeot 			intel_wait_ddi_buf_idle(dev_priv, port);
121319df918dSFrançois Tigeot 	}
121419df918dSFrançois Tigeot 
121519df918dSFrançois Tigeot 	val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
121619df918dSFrançois Tigeot 	      DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
121719df918dSFrançois Tigeot 	if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
121819df918dSFrançois Tigeot 		val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
121919df918dSFrançois Tigeot 	I915_WRITE(DP_TP_CTL(port), val);
122019df918dSFrançois Tigeot 	POSTING_READ(DP_TP_CTL(port));
122119df918dSFrançois Tigeot 
122219df918dSFrançois Tigeot 	intel_dp->DP |= DDI_BUF_CTL_ENABLE;
122319df918dSFrançois Tigeot 	I915_WRITE(DDI_BUF_CTL(port), intel_dp->DP);
122419df918dSFrançois Tigeot 	POSTING_READ(DDI_BUF_CTL(port));
122519df918dSFrançois Tigeot 
122619df918dSFrançois Tigeot 	udelay(600);
122719df918dSFrançois Tigeot }
122819df918dSFrançois Tigeot 
122919df918dSFrançois Tigeot void intel_ddi_fdi_disable(struct drm_crtc *crtc)
123019df918dSFrançois Tigeot {
123119df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
123219df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
123319df918dSFrançois Tigeot 	uint32_t val;
123419df918dSFrançois Tigeot 
123519df918dSFrançois Tigeot 	intel_ddi_post_disable(intel_encoder);
123619df918dSFrançois Tigeot 
123719df918dSFrançois Tigeot 	val = I915_READ(_FDI_RXA_CTL);
123819df918dSFrançois Tigeot 	val &= ~FDI_RX_ENABLE;
123919df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_CTL, val);
124019df918dSFrançois Tigeot 
124119df918dSFrançois Tigeot 	val = I915_READ(_FDI_RXA_MISC);
124219df918dSFrançois Tigeot 	val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
124319df918dSFrançois Tigeot 	val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2);
124419df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_MISC, val);
124519df918dSFrançois Tigeot 
124619df918dSFrançois Tigeot 	val = I915_READ(_FDI_RXA_CTL);
124719df918dSFrançois Tigeot 	val &= ~FDI_PCDCLK;
124819df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_CTL, val);
124919df918dSFrançois Tigeot 
125019df918dSFrançois Tigeot 	val = I915_READ(_FDI_RXA_CTL);
125119df918dSFrançois Tigeot 	val &= ~FDI_RX_PLL_ENABLE;
125219df918dSFrançois Tigeot 	I915_WRITE(_FDI_RXA_CTL, val);
125319df918dSFrançois Tigeot }
125419df918dSFrançois Tigeot 
125519df918dSFrançois Tigeot static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
125619df918dSFrançois Tigeot {
125719df918dSFrançois Tigeot 	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
125819df918dSFrançois Tigeot 	int type = intel_encoder->type;
125919df918dSFrançois Tigeot 
126019df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP)
126119df918dSFrançois Tigeot 		intel_dp_check_link_status(intel_dp);
126219df918dSFrançois Tigeot }
126319df918dSFrançois Tigeot 
1264*5d0b1887SFrançois Tigeot static void intel_ddi_get_config(struct intel_encoder *encoder,
1265*5d0b1887SFrançois Tigeot 				 struct intel_crtc_config *pipe_config)
1266*5d0b1887SFrançois Tigeot {
1267*5d0b1887SFrançois Tigeot 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
1268*5d0b1887SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
1269*5d0b1887SFrançois Tigeot 	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
1270*5d0b1887SFrançois Tigeot 	u32 temp, flags = 0;
1271*5d0b1887SFrançois Tigeot 
1272*5d0b1887SFrançois Tigeot 	temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
1273*5d0b1887SFrançois Tigeot 	if (temp & TRANS_DDI_PHSYNC)
1274*5d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PHSYNC;
1275*5d0b1887SFrançois Tigeot 	else
1276*5d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NHSYNC;
1277*5d0b1887SFrançois Tigeot 	if (temp & TRANS_DDI_PVSYNC)
1278*5d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PVSYNC;
1279*5d0b1887SFrançois Tigeot 	else
1280*5d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NVSYNC;
1281*5d0b1887SFrançois Tigeot 
1282*5d0b1887SFrançois Tigeot 	pipe_config->adjusted_mode.flags |= flags;
1283*5d0b1887SFrançois Tigeot }
1284*5d0b1887SFrançois Tigeot 
128519df918dSFrançois Tigeot static void intel_ddi_destroy(struct drm_encoder *encoder)
128619df918dSFrançois Tigeot {
128719df918dSFrançois Tigeot 	/* HDMI has nothing special to destroy, so we can go with this. */
128819df918dSFrançois Tigeot 	intel_dp_encoder_destroy(encoder);
128919df918dSFrançois Tigeot }
129019df918dSFrançois Tigeot 
12918e26cdf6SFrançois Tigeot static bool intel_ddi_compute_config(struct intel_encoder *encoder,
12928e26cdf6SFrançois Tigeot 				     struct intel_crtc_config *pipe_config)
129319df918dSFrançois Tigeot {
12948e26cdf6SFrançois Tigeot 	int type = encoder->type;
1295*5d0b1887SFrançois Tigeot 	int port = intel_ddi_get_encoder_port(encoder);
129619df918dSFrançois Tigeot 
12978e26cdf6SFrançois Tigeot 	WARN(type == INTEL_OUTPUT_UNKNOWN, "compute_config() on unknown output!\n");
129819df918dSFrançois Tigeot 
1299*5d0b1887SFrançois Tigeot 	if (port == PORT_A)
1300*5d0b1887SFrançois Tigeot 		pipe_config->cpu_transcoder = TRANSCODER_EDP;
1301*5d0b1887SFrançois Tigeot 
130219df918dSFrançois Tigeot 	if (type == INTEL_OUTPUT_HDMI)
13038e26cdf6SFrançois Tigeot 		return intel_hdmi_compute_config(encoder, pipe_config);
130419df918dSFrançois Tigeot 	else
13058e26cdf6SFrançois Tigeot 		return intel_dp_compute_config(encoder, pipe_config);
130619df918dSFrançois Tigeot }
130719df918dSFrançois Tigeot 
130819df918dSFrançois Tigeot static const struct drm_encoder_funcs intel_ddi_funcs = {
130919df918dSFrançois Tigeot 	.destroy = intel_ddi_destroy,
131019df918dSFrançois Tigeot };
131119df918dSFrançois Tigeot 
131219df918dSFrançois Tigeot static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = {
131319df918dSFrançois Tigeot 	.mode_set = intel_ddi_mode_set,
131419df918dSFrançois Tigeot };
131519df918dSFrançois Tigeot 
131619df918dSFrançois Tigeot void intel_ddi_init(struct drm_device *dev, enum port port)
131719df918dSFrançois Tigeot {
131819df918dSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
131919df918dSFrançois Tigeot 	struct intel_digital_port *intel_dig_port;
132019df918dSFrançois Tigeot 	struct intel_encoder *intel_encoder;
132119df918dSFrançois Tigeot 	struct drm_encoder *encoder;
132219df918dSFrançois Tigeot 	struct intel_connector *hdmi_connector = NULL;
132319df918dSFrançois Tigeot 	struct intel_connector *dp_connector = NULL;
132419df918dSFrançois Tigeot 
1325159fc1d7SFrançois Tigeot 	intel_dig_port = kzalloc(sizeof(struct intel_digital_port), GFP_KERNEL);
132619df918dSFrançois Tigeot 	if (!intel_dig_port)
132719df918dSFrançois Tigeot 		return;
132819df918dSFrançois Tigeot 
1329159fc1d7SFrançois Tigeot 	dp_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
133019df918dSFrançois Tigeot 	if (!dp_connector) {
1331158486a6SFrançois Tigeot 		kfree(intel_dig_port);
133219df918dSFrançois Tigeot 		return;
133319df918dSFrançois Tigeot 	}
133419df918dSFrançois Tigeot 
133519df918dSFrançois Tigeot 	intel_encoder = &intel_dig_port->base;
133619df918dSFrançois Tigeot 	encoder = &intel_encoder->base;
133719df918dSFrançois Tigeot 
133819df918dSFrançois Tigeot 	drm_encoder_init(dev, encoder, &intel_ddi_funcs,
133919df918dSFrançois Tigeot 			 DRM_MODE_ENCODER_TMDS);
134019df918dSFrançois Tigeot 	drm_encoder_helper_add(encoder, &intel_ddi_helper_funcs);
134119df918dSFrançois Tigeot 
13428e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_ddi_compute_config;
134319df918dSFrançois Tigeot 	intel_encoder->enable = intel_enable_ddi;
134419df918dSFrançois Tigeot 	intel_encoder->pre_enable = intel_ddi_pre_enable;
134519df918dSFrançois Tigeot 	intel_encoder->disable = intel_disable_ddi;
134619df918dSFrançois Tigeot 	intel_encoder->post_disable = intel_ddi_post_disable;
134719df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_ddi_get_hw_state;
1348*5d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_ddi_get_config;
134919df918dSFrançois Tigeot 
135019df918dSFrançois Tigeot 	intel_dig_port->port = port;
1351*5d0b1887SFrançois Tigeot 	intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
1352*5d0b1887SFrançois Tigeot 					  (DDI_BUF_PORT_REVERSAL |
1353*5d0b1887SFrançois Tigeot 					   DDI_A_4_LANES);
135419df918dSFrançois Tigeot 	intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
135519df918dSFrançois Tigeot 
135619df918dSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
135719df918dSFrançois Tigeot 	intel_encoder->crtc_mask =  (1 << 0) | (1 << 1) | (1 << 2);
135819df918dSFrançois Tigeot 	intel_encoder->cloneable = false;
135919df918dSFrançois Tigeot 	intel_encoder->hot_plug = intel_ddi_hot_plug;
136019df918dSFrançois Tigeot 
1361*5d0b1887SFrançois Tigeot 	if (!intel_dp_init_connector(intel_dig_port, dp_connector)) {
1362*5d0b1887SFrançois Tigeot 		drm_encoder_cleanup(encoder);
1363*5d0b1887SFrançois Tigeot 		kfree(intel_dig_port);
1364*5d0b1887SFrançois Tigeot 		kfree(dp_connector);
1365*5d0b1887SFrançois Tigeot 		return;
1366*5d0b1887SFrançois Tigeot 	}
1367*5d0b1887SFrançois Tigeot 
1368*5d0b1887SFrançois Tigeot 	if (intel_encoder->type != INTEL_OUTPUT_EDP) {
1369*5d0b1887SFrançois Tigeot 		hdmi_connector = kzalloc(sizeof(struct intel_connector),
1370*5d0b1887SFrançois Tigeot 					 GFP_KERNEL);
1371*5d0b1887SFrançois Tigeot 		if (!hdmi_connector) {
1372*5d0b1887SFrançois Tigeot 			return;
1373*5d0b1887SFrançois Tigeot 		}
1374*5d0b1887SFrançois Tigeot 
1375*5d0b1887SFrançois Tigeot 		intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port);
137619df918dSFrançois Tigeot 		intel_hdmi_init_connector(intel_dig_port, hdmi_connector);
1377*5d0b1887SFrançois Tigeot 	}
137819df918dSFrançois Tigeot }
1379