diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_ddi.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_ddi.c | 1720 | 
1 files changed, 1720 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c new file mode 100644 index 00000000000..b17b9c7c769 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -0,0 +1,1720 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + *    Eugeni Dodonov <eugeni.dodonov@intel.com> + * + */ + +#include "i915_drv.h" +#include "intel_drv.h" + +/* HDMI/DVI modes ignore everything but the last 2 items. So we share + * them for both DP and FDI transports, allowing those ports to + * automatically adapt to HDMI connections as well + */ +static const u32 hsw_ddi_translations_dp[] = { +	0x00FFFFFF, 0x0006000E,		/* DP parameters */ +	0x00D75FFF, 0x0005000A, +	0x00C30FFF, 0x00040006, +	0x80AAAFFF, 0x000B0000, +	0x00FFFFFF, 0x0005000A, +	0x00D75FFF, 0x000C0004, +	0x80C30FFF, 0x000B0000, +	0x00FFFFFF, 0x00040006, +	0x80D75FFF, 0x000B0000, +}; + +static const u32 hsw_ddi_translations_fdi[] = { +	0x00FFFFFF, 0x0007000E,		/* FDI parameters */ +	0x00D75FFF, 0x000F000A, +	0x00C30FFF, 0x00060006, +	0x00AAAFFF, 0x001E0000, +	0x00FFFFFF, 0x000F000A, +	0x00D75FFF, 0x00160004, +	0x00C30FFF, 0x001E0000, +	0x00FFFFFF, 0x00060006, +	0x00D75FFF, 0x001E0000, +}; + +static const u32 hsw_ddi_translations_hdmi[] = { +				/* Idx	NT mV diff	T mV diff	db  */ +	0x00FFFFFF, 0x0006000E, /* 0:	400		400		0   */ +	0x00E79FFF, 0x000E000C, /* 1:	400		500		2   */ +	0x00D75FFF, 0x0005000A, /* 2:	400		600		3.5 */ +	0x00FFFFFF, 0x0005000A, /* 3:	600		600		0   */ +	0x00E79FFF, 0x001D0007, /* 4:	600		750		2   */ +	0x00D75FFF, 0x000C0004, /* 5:	600		900		3.5 */ +	0x00FFFFFF, 0x00040006, /* 6:	800		800		0   */ +	0x80E79FFF, 0x00030002, /* 7:	800		1000		2   */ +	0x00FFFFFF, 0x00140005, /* 8:	850		850		0   */ +	0x00FFFFFF, 0x000C0004, /* 9:	900		900		0   */ +	0x00FFFFFF, 0x001C0003, /* 10:	950		950		0   */ +	0x80FFFFFF, 0x00030002, /* 11:	1000		1000		0   */ +}; + +static const u32 bdw_ddi_translations_edp[] = { +	0x00FFFFFF, 0x00000012,		/* eDP parameters */ +	0x00EBAFFF, 0x00020011, +	0x00C71FFF, 0x0006000F, +	0x00FFFFFF, 0x00020011, +	0x00DB6FFF, 0x0005000F, +	0x00BEEFFF, 0x000A000C, +	0x00FFFFFF, 0x0005000F, +	0x00DB6FFF, 0x000A000C, +	0x00FFFFFF, 0x000A000C, +	0x00FFFFFF, 0x00140006		/* HDMI parameters 800mV 0dB*/ +}; + +static const u32 bdw_ddi_translations_dp[] = { +	0x00FFFFFF, 0x0007000E,		/* DP parameters */ +	0x00D75FFF, 0x000E000A, +	0x00BEFFFF, 0x00140006, +	0x00FFFFFF, 0x000E000A, +	0x00D75FFF, 0x00180004, +	0x80CB2FFF, 0x001B0002, +	0x00F7DFFF, 0x00180004, +	0x80D75FFF, 0x001B0002, +	0x80FFFFFF, 0x001B0002, +	0x00FFFFFF, 0x00140006		/* HDMI parameters 800mV 0dB*/ +}; + +static const u32 bdw_ddi_translations_fdi[] = { +	0x00FFFFFF, 0x0001000E,		/* FDI parameters */ +	0x00D75FFF, 0x0004000A, +	0x00C30FFF, 0x00070006, +	0x00AAAFFF, 0x000C0000, +	0x00FFFFFF, 0x0004000A, +	0x00D75FFF, 0x00090004, +	0x00C30FFF, 0x000C0000, +	0x00FFFFFF, 0x00070006, +	0x00D75FFF, 0x000C0000, +	0x00FFFFFF, 0x00140006		/* HDMI parameters 800mV 0dB*/ +}; + +enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) +{ +	struct drm_encoder *encoder = &intel_encoder->base; +	int type = intel_encoder->type; + +	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || +	    type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) { +		struct intel_digital_port *intel_dig_port = +			enc_to_dig_port(encoder); +		return intel_dig_port->port; + +	} else if (type == INTEL_OUTPUT_ANALOG) { +		return PORT_E; + +	} else { +		DRM_ERROR("Invalid DDI encoder type %d\n", type); +		BUG(); +	} +} + +/* + * Starting with Haswell, DDI port buffers must be programmed with correct + * values in advance. The buffer values are different for FDI and DP modes, + * but the HDMI/DVI fields are shared among those. So we program the DDI + * in either FDI or DP modes only, as HDMI connections will work with both + * of those + */ +static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	u32 reg; +	int i; +	int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift; +	const u32 *ddi_translations_fdi; +	const u32 *ddi_translations_dp; +	const u32 *ddi_translations_edp; +	const u32 *ddi_translations; + +	if (IS_BROADWELL(dev)) { +		ddi_translations_fdi = bdw_ddi_translations_fdi; +		ddi_translations_dp = bdw_ddi_translations_dp; +		ddi_translations_edp = bdw_ddi_translations_edp; +	} else if (IS_HASWELL(dev)) { +		ddi_translations_fdi = hsw_ddi_translations_fdi; +		ddi_translations_dp = hsw_ddi_translations_dp; +		ddi_translations_edp = hsw_ddi_translations_dp; +	} else { +		WARN(1, "ddi translation table missing\n"); +		ddi_translations_edp = bdw_ddi_translations_dp; +		ddi_translations_fdi = bdw_ddi_translations_fdi; +		ddi_translations_dp = bdw_ddi_translations_dp; +	} + +	switch (port) { +	case PORT_A: +		ddi_translations = ddi_translations_edp; +		break; +	case PORT_B: +	case PORT_C: +		ddi_translations = ddi_translations_dp; +		break; +	case PORT_D: +		if (intel_dp_is_edp(dev, PORT_D)) +			ddi_translations = ddi_translations_edp; +		else +			ddi_translations = ddi_translations_dp; +		break; +	case PORT_E: +		ddi_translations = ddi_translations_fdi; +		break; +	default: +		BUG(); +	} + +	for (i = 0, reg = DDI_BUF_TRANS(port); +	     i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { +		I915_WRITE(reg, ddi_translations[i]); +		reg += 4; +	} +	/* Entry 9 is for HDMI: */ +	for (i = 0; i < 2; i++) { +		I915_WRITE(reg, hsw_ddi_translations_hdmi[hdmi_level * 2 + i]); +		reg += 4; +	} +} + +/* Program DDI buffers translations for DP. By default, program ports A-D in DP + * mode and port E for FDI. + */ +void intel_prepare_ddi(struct drm_device *dev) +{ +	int port; + +	if (!HAS_DDI(dev)) +		return; + +	for (port = PORT_A; port <= PORT_E; port++) +		intel_prepare_ddi_buffers(dev, port); +} + +static const long hsw_ddi_buf_ctl_values[] = { +	DDI_BUF_EMP_400MV_0DB_HSW, +	DDI_BUF_EMP_400MV_3_5DB_HSW, +	DDI_BUF_EMP_400MV_6DB_HSW, +	DDI_BUF_EMP_400MV_9_5DB_HSW, +	DDI_BUF_EMP_600MV_0DB_HSW, +	DDI_BUF_EMP_600MV_3_5DB_HSW, +	DDI_BUF_EMP_600MV_6DB_HSW, +	DDI_BUF_EMP_800MV_0DB_HSW, +	DDI_BUF_EMP_800MV_3_5DB_HSW +}; + +static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, +				    enum port port) +{ +	uint32_t reg = DDI_BUF_CTL(port); +	int i; + +	for (i = 0; i < 8; i++) { +		udelay(1); +		if (I915_READ(reg) & DDI_BUF_IS_IDLE) +			return; +	} +	DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port)); +} + +/* Starting with Haswell, different DDI ports can work in FDI mode for + * connection to the PCH-located connectors. For this, it is necessary to train + * both the DDI port and PCH receiver for the desired DDI buffer settings. + * + * The recommended port to work in FDI mode is DDI E, which we use here. Also, + * please note that when FDI mode is active on DDI E, it shares 2 lines with + * DDI A (which is used for eDP) + */ + +void hsw_fdi_link_train(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	u32 temp, i, rx_ctl_val; + +	/* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the +	 * mode set "sequence for CRT port" document: +	 * - TP1 to TP2 time with the default value +	 * - FDI delay to 90h +	 * +	 * WaFDIAutoLinkSetTimingOverrride:hsw +	 */ +	I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) | +				  FDI_RX_PWRDN_LANE0_VAL(2) | +				  FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); + +	/* Enable the PCH Receiver FDI PLL */ +	rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | +		     FDI_RX_PLL_ENABLE | +		     FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); +	I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); +	POSTING_READ(_FDI_RXA_CTL); +	udelay(220); + +	/* Switch from Rawclk to PCDclk */ +	rx_ctl_val |= FDI_PCDCLK; +	I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); + +	/* Configure Port Clock Select */ +	I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel); + +	/* Start the training iterating through available voltages and emphasis, +	 * testing each value twice. */ +	for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) { +		/* Configure DP_TP_CTL with auto-training */ +		I915_WRITE(DP_TP_CTL(PORT_E), +					DP_TP_CTL_FDI_AUTOTRAIN | +					DP_TP_CTL_ENHANCED_FRAME_ENABLE | +					DP_TP_CTL_LINK_TRAIN_PAT1 | +					DP_TP_CTL_ENABLE); + +		/* Configure and enable DDI_BUF_CTL for DDI E with next voltage. +		 * DDI E does not support port reversal, the functionality is +		 * achieved on the PCH side in FDI_RX_CTL, so no need to set the +		 * port reversal bit */ +		I915_WRITE(DDI_BUF_CTL(PORT_E), +			   DDI_BUF_CTL_ENABLE | +			   ((intel_crtc->config.fdi_lanes - 1) << 1) | +			   hsw_ddi_buf_ctl_values[i / 2]); +		POSTING_READ(DDI_BUF_CTL(PORT_E)); + +		udelay(600); + +		/* Program PCH FDI Receiver TU */ +		I915_WRITE(_FDI_RXA_TUSIZE1, TU_SIZE(64)); + +		/* Enable PCH FDI Receiver with auto-training */ +		rx_ctl_val |= FDI_RX_ENABLE | FDI_LINK_TRAIN_AUTO; +		I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); +		POSTING_READ(_FDI_RXA_CTL); + +		/* Wait for FDI receiver lane calibration */ +		udelay(30); + +		/* Unset FDI_RX_MISC pwrdn lanes */ +		temp = I915_READ(_FDI_RXA_MISC); +		temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); +		I915_WRITE(_FDI_RXA_MISC, temp); +		POSTING_READ(_FDI_RXA_MISC); + +		/* Wait for FDI auto training time */ +		udelay(5); + +		temp = I915_READ(DP_TP_STATUS(PORT_E)); +		if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) { +			DRM_DEBUG_KMS("FDI link training done on step %d\n", i); + +			/* Enable normal pixel sending for FDI */ +			I915_WRITE(DP_TP_CTL(PORT_E), +				   DP_TP_CTL_FDI_AUTOTRAIN | +				   DP_TP_CTL_LINK_TRAIN_NORMAL | +				   DP_TP_CTL_ENHANCED_FRAME_ENABLE | +				   DP_TP_CTL_ENABLE); + +			return; +		} + +		temp = I915_READ(DDI_BUF_CTL(PORT_E)); +		temp &= ~DDI_BUF_CTL_ENABLE; +		I915_WRITE(DDI_BUF_CTL(PORT_E), temp); +		POSTING_READ(DDI_BUF_CTL(PORT_E)); + +		/* Disable DP_TP_CTL and FDI_RX_CTL and retry */ +		temp = I915_READ(DP_TP_CTL(PORT_E)); +		temp &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); +		temp |= DP_TP_CTL_LINK_TRAIN_PAT1; +		I915_WRITE(DP_TP_CTL(PORT_E), temp); +		POSTING_READ(DP_TP_CTL(PORT_E)); + +		intel_wait_ddi_buf_idle(dev_priv, PORT_E); + +		rx_ctl_val &= ~FDI_RX_ENABLE; +		I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); +		POSTING_READ(_FDI_RXA_CTL); + +		/* Reset FDI_RX_MISC pwrdn lanes */ +		temp = I915_READ(_FDI_RXA_MISC); +		temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); +		temp |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); +		I915_WRITE(_FDI_RXA_MISC, temp); +		POSTING_READ(_FDI_RXA_MISC); +	} + +	DRM_ERROR("FDI link training failed!\n"); +} + +static struct intel_encoder * +intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_encoder *intel_encoder, *ret = NULL; +	int num_encoders = 0; + +	for_each_encoder_on_crtc(dev, crtc, intel_encoder) { +		ret = intel_encoder; +		num_encoders++; +	} + +	if (num_encoders != 1) +		WARN(1, "%d encoders on crtc for pipe %c\n", num_encoders, +		     pipe_name(intel_crtc->pipe)); + +	BUG_ON(ret == NULL); +	return ret; +} + +void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) +{ +	struct drm_i915_private *dev_priv = crtc->dev->dev_private; +	struct intel_ddi_plls *plls = &dev_priv->ddi_plls; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	uint32_t val; + +	switch (intel_crtc->ddi_pll_sel) { +	case PORT_CLK_SEL_SPLL: +		plls->spll_refcount--; +		if (plls->spll_refcount == 0) { +			DRM_DEBUG_KMS("Disabling SPLL\n"); +			val = I915_READ(SPLL_CTL); +			WARN_ON(!(val & SPLL_PLL_ENABLE)); +			I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); +			POSTING_READ(SPLL_CTL); +		} +		break; +	case PORT_CLK_SEL_WRPLL1: +		plls->wrpll1_refcount--; +		if (plls->wrpll1_refcount == 0) { +			DRM_DEBUG_KMS("Disabling WRPLL 1\n"); +			val = I915_READ(WRPLL_CTL1); +			WARN_ON(!(val & WRPLL_PLL_ENABLE)); +			I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE); +			POSTING_READ(WRPLL_CTL1); +		} +		break; +	case PORT_CLK_SEL_WRPLL2: +		plls->wrpll2_refcount--; +		if (plls->wrpll2_refcount == 0) { +			DRM_DEBUG_KMS("Disabling WRPLL 2\n"); +			val = I915_READ(WRPLL_CTL2); +			WARN_ON(!(val & WRPLL_PLL_ENABLE)); +			I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE); +			POSTING_READ(WRPLL_CTL2); +		} +		break; +	} + +	WARN(plls->spll_refcount < 0, "Invalid SPLL refcount\n"); +	WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n"); +	WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n"); + +	intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; +} + +#define LC_FREQ 2700 +#define LC_FREQ_2K (LC_FREQ * 2000) + +#define P_MIN 2 +#define P_MAX 64 +#define P_INC 2 + +/* Constraints for PLL good behavior */ +#define REF_MIN 48 +#define REF_MAX 400 +#define VCO_MIN 2400 +#define VCO_MAX 4800 + +#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a)) + +struct wrpll_rnp { +	unsigned p, n2, r2; +}; + +static unsigned wrpll_get_budget_for_freq(int clock) +{ +	unsigned budget; + +	switch (clock) { +	case 25175000: +	case 25200000: +	case 27000000: +	case 27027000: +	case 37762500: +	case 37800000: +	case 40500000: +	case 40541000: +	case 54000000: +	case 54054000: +	case 59341000: +	case 59400000: +	case 72000000: +	case 74176000: +	case 74250000: +	case 81000000: +	case 81081000: +	case 89012000: +	case 89100000: +	case 108000000: +	case 108108000: +	case 111264000: +	case 111375000: +	case 148352000: +	case 148500000: +	case 162000000: +	case 162162000: +	case 222525000: +	case 222750000: +	case 296703000: +	case 297000000: +		budget = 0; +		break; +	case 233500000: +	case 245250000: +	case 247750000: +	case 253250000: +	case 298000000: +		budget = 1500; +		break; +	case 169128000: +	case 169500000: +	case 179500000: +	case 202000000: +		budget = 2000; +		break; +	case 256250000: +	case 262500000: +	case 270000000: +	case 272500000: +	case 273750000: +	case 280750000: +	case 281250000: +	case 286000000: +	case 291750000: +		budget = 4000; +		break; +	case 267250000: +	case 268500000: +		budget = 5000; +		break; +	default: +		budget = 1000; +		break; +	} + +	return budget; +} + +static void wrpll_update_rnp(uint64_t freq2k, unsigned budget, +			     unsigned r2, unsigned n2, unsigned p, +			     struct wrpll_rnp *best) +{ +	uint64_t a, b, c, d, diff, diff_best; + +	/* No best (r,n,p) yet */ +	if (best->p == 0) { +		best->p = p; +		best->n2 = n2; +		best->r2 = r2; +		return; +	} + +	/* +	 * Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to +	 * freq2k. +	 * +	 * delta = 1e6 * +	 *	   abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) / +	 *	   freq2k; +	 * +	 * and we would like delta <= budget. +	 * +	 * If the discrepancy is above the PPM-based budget, always prefer to +	 * improve upon the previous solution.  However, if you're within the +	 * budget, try to maximize Ref * VCO, that is N / (P * R^2). +	 */ +	a = freq2k * budget * p * r2; +	b = freq2k * budget * best->p * best->r2; +	diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2)); +	diff_best = ABS_DIFF((freq2k * best->p * best->r2), +			     (LC_FREQ_2K * best->n2)); +	c = 1000000 * diff; +	d = 1000000 * diff_best; + +	if (a < c && b < d) { +		/* If both are above the budget, pick the closer */ +		if (best->p * best->r2 * diff < p * r2 * diff_best) { +			best->p = p; +			best->n2 = n2; +			best->r2 = r2; +		} +	} else if (a >= c && b < d) { +		/* If A is below the threshold but B is above it?  Update. */ +		best->p = p; +		best->n2 = n2; +		best->r2 = r2; +	} else if (a >= c && b >= d) { +		/* Both are below the limit, so pick the higher n2/(r2*r2) */ +		if (n2 * best->r2 * best->r2 > best->n2 * r2 * r2) { +			best->p = p; +			best->n2 = n2; +			best->r2 = r2; +		} +	} +	/* Otherwise a < c && b >= d, do nothing */ +} + +static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv, +				     int reg) +{ +	int refclk = LC_FREQ; +	int n, p, r; +	u32 wrpll; + +	wrpll = I915_READ(reg); +	switch (wrpll & SPLL_PLL_REF_MASK) { +	case SPLL_PLL_SSC: +	case SPLL_PLL_NON_SSC: +		/* +		 * We could calculate spread here, but our checking +		 * code only cares about 5% accuracy, and spread is a max of +		 * 0.5% downspread. +		 */ +		refclk = 135; +		break; +	case SPLL_PLL_LCPLL: +		refclk = LC_FREQ; +		break; +	default: +		WARN(1, "bad wrpll refclk\n"); +		return 0; +	} + +	r = wrpll & WRPLL_DIVIDER_REF_MASK; +	p = (wrpll & WRPLL_DIVIDER_POST_MASK) >> WRPLL_DIVIDER_POST_SHIFT; +	n = (wrpll & WRPLL_DIVIDER_FB_MASK) >> WRPLL_DIVIDER_FB_SHIFT; + +	/* Convert to KHz, p & r have a fixed point portion */ +	return (refclk * n * 100) / (p * r); +} + +static void intel_ddi_clock_get(struct intel_encoder *encoder, +				struct intel_crtc_config *pipe_config) +{ +	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; +	enum port port = intel_ddi_get_encoder_port(encoder); +	int link_clock = 0; +	u32 val, pll; + +	val = I915_READ(PORT_CLK_SEL(port)); +	switch (val & PORT_CLK_SEL_MASK) { +	case PORT_CLK_SEL_LCPLL_810: +		link_clock = 81000; +		break; +	case PORT_CLK_SEL_LCPLL_1350: +		link_clock = 135000; +		break; +	case PORT_CLK_SEL_LCPLL_2700: +		link_clock = 270000; +		break; +	case PORT_CLK_SEL_WRPLL1: +		link_clock = intel_ddi_calc_wrpll_link(dev_priv, WRPLL_CTL1); +		break; +	case PORT_CLK_SEL_WRPLL2: +		link_clock = intel_ddi_calc_wrpll_link(dev_priv, WRPLL_CTL2); +		break; +	case PORT_CLK_SEL_SPLL: +		pll = I915_READ(SPLL_CTL) & SPLL_PLL_FREQ_MASK; +		if (pll == SPLL_PLL_FREQ_810MHz) +			link_clock = 81000; +		else if (pll == SPLL_PLL_FREQ_1350MHz) +			link_clock = 135000; +		else if (pll == SPLL_PLL_FREQ_2700MHz) +			link_clock = 270000; +		else { +			WARN(1, "bad spll freq\n"); +			return; +		} +		break; +	default: +		WARN(1, "bad port clock sel\n"); +		return; +	} + +	pipe_config->port_clock = link_clock * 2; + +	if (pipe_config->has_pch_encoder) +		pipe_config->adjusted_mode.crtc_clock = +			intel_dotclock_calculate(pipe_config->port_clock, +						 &pipe_config->fdi_m_n); +	else if (pipe_config->has_dp_encoder) +		pipe_config->adjusted_mode.crtc_clock = +			intel_dotclock_calculate(pipe_config->port_clock, +						 &pipe_config->dp_m_n); +	else +		pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock; +} + +static void +intel_ddi_calculate_wrpll(int clock /* in Hz */, +			  unsigned *r2_out, unsigned *n2_out, unsigned *p_out) +{ +	uint64_t freq2k; +	unsigned p, n2, r2; +	struct wrpll_rnp best = { 0, 0, 0 }; +	unsigned budget; + +	freq2k = clock / 100; + +	budget = wrpll_get_budget_for_freq(clock); + +	/* Special case handling for 540 pixel clock: bypass WR PLL entirely +	 * and directly pass the LC PLL to it. */ +	if (freq2k == 5400000) { +		*n2_out = 2; +		*p_out = 1; +		*r2_out = 2; +		return; +	} + +	/* +	 * Ref = LC_FREQ / R, where Ref is the actual reference input seen by +	 * the WR PLL. +	 * +	 * We want R so that REF_MIN <= Ref <= REF_MAX. +	 * Injecting R2 = 2 * R gives: +	 *   REF_MAX * r2 > LC_FREQ * 2 and +	 *   REF_MIN * r2 < LC_FREQ * 2 +	 * +	 * Which means the desired boundaries for r2 are: +	 *  LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN +	 * +	 */ +	for (r2 = LC_FREQ * 2 / REF_MAX + 1; +	     r2 <= LC_FREQ * 2 / REF_MIN; +	     r2++) { + +		/* +		 * VCO = N * Ref, that is: VCO = N * LC_FREQ / R +		 * +		 * Once again we want VCO_MIN <= VCO <= VCO_MAX. +		 * Injecting R2 = 2 * R and N2 = 2 * N, we get: +		 *   VCO_MAX * r2 > n2 * LC_FREQ and +		 *   VCO_MIN * r2 < n2 * LC_FREQ) +		 * +		 * Which means the desired boundaries for n2 are: +		 * VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ +		 */ +		for (n2 = VCO_MIN * r2 / LC_FREQ + 1; +		     n2 <= VCO_MAX * r2 / LC_FREQ; +		     n2++) { + +			for (p = P_MIN; p <= P_MAX; p += P_INC) +				wrpll_update_rnp(freq2k, budget, +						 r2, n2, p, &best); +		} +	} + +	*n2_out = best.n2; +	*p_out = best.p; +	*r2_out = best.r2; +} + +/* + * Tries to find a PLL for the CRTC. If it finds, it increases the refcount and + * stores it in intel_crtc->ddi_pll_sel, so other mode sets won't be able to + * steal the selected PLL. You need to call intel_ddi_pll_enable to actually + * enable the PLL. + */ +bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) +{ +	struct drm_crtc *crtc = &intel_crtc->base; +	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); +	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_i915_private *dev_priv = crtc->dev->dev_private; +	struct intel_ddi_plls *plls = &dev_priv->ddi_plls; +	int type = intel_encoder->type; +	enum pipe pipe = intel_crtc->pipe; +	int clock = intel_crtc->config.port_clock; + +	intel_ddi_put_crtc_pll(crtc); + +	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + +		switch (intel_dp->link_bw) { +		case DP_LINK_BW_1_62: +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; +			break; +		case DP_LINK_BW_2_7: +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; +			break; +		case DP_LINK_BW_5_4: +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; +			break; +		default: +			DRM_ERROR("Link bandwidth %d unsupported\n", +				  intel_dp->link_bw); +			return false; +		} + +	} else if (type == INTEL_OUTPUT_HDMI) { +		uint32_t reg, val; +		unsigned p, n2, r2; + +		intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); + +		val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | +		      WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | +		      WRPLL_DIVIDER_POST(p); + +		if (val == I915_READ(WRPLL_CTL1)) { +			DRM_DEBUG_KMS("Reusing WRPLL 1 on pipe %c\n", +				      pipe_name(pipe)); +			reg = WRPLL_CTL1; +		} else if (val == I915_READ(WRPLL_CTL2)) { +			DRM_DEBUG_KMS("Reusing WRPLL 2 on pipe %c\n", +				      pipe_name(pipe)); +			reg = WRPLL_CTL2; +		} else if (plls->wrpll1_refcount == 0) { +			DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n", +				      pipe_name(pipe)); +			reg = WRPLL_CTL1; +		} else if (plls->wrpll2_refcount == 0) { +			DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n", +				      pipe_name(pipe)); +			reg = WRPLL_CTL2; +		} else { +			DRM_ERROR("No WRPLLs available!\n"); +			return false; +		} + +		DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n", +			      clock, p, n2, r2); + +		if (reg == WRPLL_CTL1) { +			plls->wrpll1_refcount++; +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL1; +		} else { +			plls->wrpll2_refcount++; +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL2; +		} + +	} else if (type == INTEL_OUTPUT_ANALOG) { +		if (plls->spll_refcount == 0) { +			DRM_DEBUG_KMS("Using SPLL on pipe %c\n", +				      pipe_name(pipe)); +			plls->spll_refcount++; +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL; +		} else { +			DRM_ERROR("SPLL already in use\n"); +			return false; +		} + +	} else { +		WARN(1, "Invalid DDI encoder type %d\n", type); +		return false; +	} + +	return true; +} + +/* + * To be called after intel_ddi_pll_select(). That one selects the PLL to be + * used, this one actually enables the PLL. + */ +void intel_ddi_pll_enable(struct intel_crtc *crtc) +{ +	struct drm_device *dev = crtc->base.dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_ddi_plls *plls = &dev_priv->ddi_plls; +	int clock = crtc->config.port_clock; +	uint32_t reg, cur_val, new_val; +	int refcount; +	const char *pll_name; +	uint32_t enable_bit = (1 << 31); +	unsigned int p, n2, r2; + +	BUILD_BUG_ON(enable_bit != SPLL_PLL_ENABLE); +	BUILD_BUG_ON(enable_bit != WRPLL_PLL_ENABLE); + +	switch (crtc->ddi_pll_sel) { +	case PORT_CLK_SEL_LCPLL_2700: +	case PORT_CLK_SEL_LCPLL_1350: +	case PORT_CLK_SEL_LCPLL_810: +		/* +		 * LCPLL should always be enabled at this point of the mode set +		 * sequence, so nothing to do. +		 */ +		return; + +	case PORT_CLK_SEL_SPLL: +		pll_name = "SPLL"; +		reg = SPLL_CTL; +		refcount = plls->spll_refcount; +		new_val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | +			  SPLL_PLL_SSC; +		break; + +	case PORT_CLK_SEL_WRPLL1: +	case PORT_CLK_SEL_WRPLL2: +		if (crtc->ddi_pll_sel == PORT_CLK_SEL_WRPLL1) { +			pll_name = "WRPLL1"; +			reg = WRPLL_CTL1; +			refcount = plls->wrpll1_refcount; +		} else { +			pll_name = "WRPLL2"; +			reg = WRPLL_CTL2; +			refcount = plls->wrpll2_refcount; +		} + +		intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); + +		new_val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | +			  WRPLL_DIVIDER_REFERENCE(r2) | +			  WRPLL_DIVIDER_FEEDBACK(n2) | WRPLL_DIVIDER_POST(p); + +		break; + +	case PORT_CLK_SEL_NONE: +		WARN(1, "Bad selected pll: PORT_CLK_SEL_NONE\n"); +		return; +	default: +		WARN(1, "Bad selected pll: 0x%08x\n", crtc->ddi_pll_sel); +		return; +	} + +	cur_val = I915_READ(reg); + +	WARN(refcount < 1, "Bad %s refcount: %d\n", pll_name, refcount); +	if (refcount == 1) { +		WARN(cur_val & enable_bit, "%s already enabled\n", pll_name); +		I915_WRITE(reg, new_val); +		POSTING_READ(reg); +		udelay(20); +	} else { +		WARN((cur_val & enable_bit) == 0, "%s disabled\n", pll_name); +	} +} + +void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) +{ +	struct drm_i915_private *dev_priv = crtc->dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); +	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; +	int type = intel_encoder->type; +	uint32_t temp; + +	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { + +		temp = TRANS_MSA_SYNC_CLK; +		switch (intel_crtc->config.pipe_bpp) { +		case 18: +			temp |= TRANS_MSA_6_BPC; +			break; +		case 24: +			temp |= TRANS_MSA_8_BPC; +			break; +		case 30: +			temp |= TRANS_MSA_10_BPC; +			break; +		case 36: +			temp |= TRANS_MSA_12_BPC; +			break; +		default: +			BUG(); +		} +		I915_WRITE(TRANS_MSA_MISC(cpu_transcoder), temp); +	} +} + +void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) +{ +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); +	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_device *dev = crtc->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	enum pipe pipe = intel_crtc->pipe; +	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; +	enum port port = intel_ddi_get_encoder_port(intel_encoder); +	int type = intel_encoder->type; +	uint32_t temp; + +	/* Enable TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */ +	temp = TRANS_DDI_FUNC_ENABLE; +	temp |= TRANS_DDI_SELECT_PORT(port); + +	switch (intel_crtc->config.pipe_bpp) { +	case 18: +		temp |= TRANS_DDI_BPC_6; +		break; +	case 24: +		temp |= TRANS_DDI_BPC_8; +		break; +	case 30: +		temp |= TRANS_DDI_BPC_10; +		break; +	case 36: +		temp |= TRANS_DDI_BPC_12; +		break; +	default: +		BUG(); +	} + +	if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_PVSYNC) +		temp |= TRANS_DDI_PVSYNC; +	if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_PHSYNC) +		temp |= TRANS_DDI_PHSYNC; + +	if (cpu_transcoder == TRANSCODER_EDP) { +		switch (pipe) { +		case PIPE_A: +			/* On Haswell, can only use the always-on power well for +			 * eDP when not using the panel fitter, and when not +			 * using motion blur mitigation (which we don't +			 * support). */ +			if (IS_HASWELL(dev) && intel_crtc->config.pch_pfit.enabled) +				temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; +			else +				temp |= TRANS_DDI_EDP_INPUT_A_ON; +			break; +		case PIPE_B: +			temp |= TRANS_DDI_EDP_INPUT_B_ONOFF; +			break; +		case PIPE_C: +			temp |= TRANS_DDI_EDP_INPUT_C_ONOFF; +			break; +		default: +			BUG(); +			break; +		} +	} + +	if (type == INTEL_OUTPUT_HDMI) { +		if (intel_crtc->config.has_hdmi_sink) +			temp |= TRANS_DDI_MODE_SELECT_HDMI; +		else +			temp |= TRANS_DDI_MODE_SELECT_DVI; + +	} else if (type == INTEL_OUTPUT_ANALOG) { +		temp |= TRANS_DDI_MODE_SELECT_FDI; +		temp |= (intel_crtc->config.fdi_lanes - 1) << 1; + +	} else if (type == INTEL_OUTPUT_DISPLAYPORT || +		   type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + +		temp |= TRANS_DDI_MODE_SELECT_DP_SST; + +		temp |= DDI_PORT_WIDTH(intel_dp->lane_count); +	} else { +		WARN(1, "Invalid encoder type %d for pipe %c\n", +		     intel_encoder->type, pipe_name(pipe)); +	} + +	I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp); +} + +void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, +				       enum transcoder cpu_transcoder) +{ +	uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); +	uint32_t val = I915_READ(reg); + +	val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK); +	val |= TRANS_DDI_PORT_NONE; +	I915_WRITE(reg, val); +} + +bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) +{ +	struct drm_device *dev = intel_connector->base.dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_encoder *intel_encoder = intel_connector->encoder; +	int type = intel_connector->base.connector_type; +	enum port port = intel_ddi_get_encoder_port(intel_encoder); +	enum pipe pipe = 0; +	enum transcoder cpu_transcoder; +	enum intel_display_power_domain power_domain; +	uint32_t tmp; + +	power_domain = intel_display_port_power_domain(intel_encoder); +	if (!intel_display_power_enabled(dev_priv, power_domain)) +		return false; + +	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) +		return false; + +	if (port == PORT_A) +		cpu_transcoder = TRANSCODER_EDP; +	else +		cpu_transcoder = (enum transcoder) pipe; + +	tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); + +	switch (tmp & TRANS_DDI_MODE_SELECT_MASK) { +	case TRANS_DDI_MODE_SELECT_HDMI: +	case TRANS_DDI_MODE_SELECT_DVI: +		return (type == DRM_MODE_CONNECTOR_HDMIA); + +	case TRANS_DDI_MODE_SELECT_DP_SST: +		if (type == DRM_MODE_CONNECTOR_eDP) +			return true; +	case TRANS_DDI_MODE_SELECT_DP_MST: +		return (type == DRM_MODE_CONNECTOR_DisplayPort); + +	case TRANS_DDI_MODE_SELECT_FDI: +		return (type == DRM_MODE_CONNECTOR_VGA); + +	default: +		return false; +	} +} + +bool intel_ddi_get_hw_state(struct intel_encoder *encoder, +			    enum pipe *pipe) +{ +	struct drm_device *dev = encoder->base.dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	enum port port = intel_ddi_get_encoder_port(encoder); +	enum intel_display_power_domain power_domain; +	u32 tmp; +	int i; + +	power_domain = intel_display_port_power_domain(encoder); +	if (!intel_display_power_enabled(dev_priv, power_domain)) +		return false; + +	tmp = I915_READ(DDI_BUF_CTL(port)); + +	if (!(tmp & DDI_BUF_CTL_ENABLE)) +		return false; + +	if (port == PORT_A) { +		tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); + +		switch (tmp & TRANS_DDI_EDP_INPUT_MASK) { +		case TRANS_DDI_EDP_INPUT_A_ON: +		case TRANS_DDI_EDP_INPUT_A_ONOFF: +			*pipe = PIPE_A; +			break; +		case TRANS_DDI_EDP_INPUT_B_ONOFF: +			*pipe = PIPE_B; +			break; +		case TRANS_DDI_EDP_INPUT_C_ONOFF: +			*pipe = PIPE_C; +			break; +		} + +		return true; +	} else { +		for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) { +			tmp = I915_READ(TRANS_DDI_FUNC_CTL(i)); + +			if ((tmp & TRANS_DDI_PORT_MASK) +			    == TRANS_DDI_SELECT_PORT(port)) { +				*pipe = i; +				return true; +			} +		} +	} + +	DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port)); + +	return false; +} + +static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv, +				       enum pipe pipe) +{ +	uint32_t temp, ret; +	enum port port = I915_MAX_PORTS; +	enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, +								      pipe); +	int i; + +	if (cpu_transcoder == TRANSCODER_EDP) { +		port = PORT_A; +	} else { +		temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); +		temp &= TRANS_DDI_PORT_MASK; + +		for (i = PORT_B; i <= PORT_E; i++) +			if (temp == TRANS_DDI_SELECT_PORT(i)) +				port = i; +	} + +	if (port == I915_MAX_PORTS) { +		WARN(1, "Pipe %c enabled on an unknown port\n", +		     pipe_name(pipe)); +		ret = PORT_CLK_SEL_NONE; +	} else { +		ret = I915_READ(PORT_CLK_SEL(port)); +		DRM_DEBUG_KMS("Pipe %c connected to port %c using clock " +			      "0x%08x\n", pipe_name(pipe), port_name(port), +			      ret); +	} + +	return ret; +} + +void intel_ddi_setup_hw_pll_state(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	enum pipe pipe; +	struct intel_crtc *intel_crtc; + +	dev_priv->ddi_plls.spll_refcount = 0; +	dev_priv->ddi_plls.wrpll1_refcount = 0; +	dev_priv->ddi_plls.wrpll2_refcount = 0; + +	for_each_pipe(pipe) { +		intel_crtc = +			to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + +		if (!intel_crtc->active) { +			intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; +			continue; +		} + +		intel_crtc->ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv, +								 pipe); + +		switch (intel_crtc->ddi_pll_sel) { +		case PORT_CLK_SEL_SPLL: +			dev_priv->ddi_plls.spll_refcount++; +			break; +		case PORT_CLK_SEL_WRPLL1: +			dev_priv->ddi_plls.wrpll1_refcount++; +			break; +		case PORT_CLK_SEL_WRPLL2: +			dev_priv->ddi_plls.wrpll2_refcount++; +			break; +		} +	} +} + +void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc) +{ +	struct drm_crtc *crtc = &intel_crtc->base; +	struct drm_i915_private *dev_priv = crtc->dev->dev_private; +	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); +	enum port port = intel_ddi_get_encoder_port(intel_encoder); +	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; + +	if (cpu_transcoder != TRANSCODER_EDP) +		I915_WRITE(TRANS_CLK_SEL(cpu_transcoder), +			   TRANS_CLK_SEL_PORT(port)); +} + +void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc) +{ +	struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; +	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; + +	if (cpu_transcoder != TRANSCODER_EDP) +		I915_WRITE(TRANS_CLK_SEL(cpu_transcoder), +			   TRANS_CLK_SEL_DISABLED); +} + +static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) +{ +	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_i915_private *dev_priv = encoder->dev->dev_private; +	struct intel_crtc *crtc = to_intel_crtc(encoder->crtc); +	enum port port = intel_ddi_get_encoder_port(intel_encoder); +	int type = intel_encoder->type; + +	if (crtc->config.has_audio) { +		DRM_DEBUG_DRIVER("Audio on pipe %c on DDI\n", +				 pipe_name(crtc->pipe)); + +		/* write eld */ +		DRM_DEBUG_DRIVER("DDI audio: write eld information\n"); +		intel_write_eld(encoder, &crtc->config.adjusted_mode); +	} + +	if (type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); +		intel_edp_panel_on(intel_dp); +	} + +	WARN_ON(crtc->ddi_pll_sel == PORT_CLK_SEL_NONE); +	I915_WRITE(PORT_CLK_SEL(port), crtc->ddi_pll_sel); + +	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); +		struct intel_digital_port *intel_dig_port = +			enc_to_dig_port(encoder); + +		intel_dp->DP = intel_dig_port->saved_port_bits | +			       DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; +		intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); + +		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); +		intel_dp_start_link_train(intel_dp); +		intel_dp_complete_link_train(intel_dp); +		if (port != PORT_A) +			intel_dp_stop_link_train(intel_dp); +	} else if (type == INTEL_OUTPUT_HDMI) { +		struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + +		intel_hdmi->set_infoframes(encoder, +					   crtc->config.has_hdmi_sink, +					   &crtc->config.adjusted_mode); +	} +} + +static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) +{ +	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_i915_private *dev_priv = encoder->dev->dev_private; +	enum port port = intel_ddi_get_encoder_port(intel_encoder); +	int type = intel_encoder->type; +	uint32_t val; +	bool wait = false; + +	val = I915_READ(DDI_BUF_CTL(port)); +	if (val & DDI_BUF_CTL_ENABLE) { +		val &= ~DDI_BUF_CTL_ENABLE; +		I915_WRITE(DDI_BUF_CTL(port), val); +		wait = true; +	} + +	val = I915_READ(DP_TP_CTL(port)); +	val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); +	val |= DP_TP_CTL_LINK_TRAIN_PAT1; +	I915_WRITE(DP_TP_CTL(port), val); + +	if (wait) +		intel_wait_ddi_buf_idle(dev_priv, port); + +	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); +		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); +		intel_edp_panel_vdd_on(intel_dp); +		intel_edp_panel_off(intel_dp); +	} + +	I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE); +} + +static void intel_enable_ddi(struct intel_encoder *intel_encoder) +{ +	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_crtc *crtc = encoder->crtc; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	enum port port = intel_ddi_get_encoder_port(intel_encoder); +	int type = intel_encoder->type; +	uint32_t tmp; + +	if (type == INTEL_OUTPUT_HDMI) { +		struct intel_digital_port *intel_dig_port = +			enc_to_dig_port(encoder); + +		/* In HDMI/DVI mode, the port width, and swing/emphasis values +		 * are ignored so nothing special needs to be done besides +		 * enabling the port. +		 */ +		I915_WRITE(DDI_BUF_CTL(port), +			   intel_dig_port->saved_port_bits | +			   DDI_BUF_CTL_ENABLE); +	} else if (type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + +		if (port == PORT_A) +			intel_dp_stop_link_train(intel_dp); + +		intel_edp_backlight_on(intel_dp); +		intel_edp_psr_enable(intel_dp); +	} + +	if (intel_crtc->config.has_audio) { +		intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); +		tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); +		tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); +		I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); +	} +} + +static void intel_disable_ddi(struct intel_encoder *intel_encoder) +{ +	struct drm_encoder *encoder = &intel_encoder->base; +	struct drm_crtc *crtc = encoder->crtc; +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	int pipe = intel_crtc->pipe; +	int type = intel_encoder->type; +	struct drm_device *dev = encoder->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	uint32_t tmp; + +	/* We can't touch HSW_AUD_PIN_ELD_CP_VLD uncionditionally because this +	 * register is part of the power well on Haswell. */ +	if (intel_crtc->config.has_audio) { +		tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); +		tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << +			 (pipe * 4)); +		I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); +		intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); +	} + +	if (type == INTEL_OUTPUT_EDP) { +		struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + +		intel_edp_psr_disable(intel_dp); +		intel_edp_backlight_off(intel_dp); +	} +} + +int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) +{ +	struct drm_device *dev = dev_priv->dev; +	uint32_t lcpll = I915_READ(LCPLL_CTL); +	uint32_t freq = lcpll & LCPLL_CLK_FREQ_MASK; + +	if (lcpll & LCPLL_CD_SOURCE_FCLK) { +		return 800000; +	} else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT) { +		return 450000; +	} else if (freq == LCPLL_CLK_FREQ_450) { +		return 450000; +	} else if (IS_HASWELL(dev)) { +		if (IS_ULT(dev)) +			return 337500; +		else +			return 540000; +	} else { +		if (freq == LCPLL_CLK_FREQ_54O_BDW) +			return 540000; +		else if (freq == LCPLL_CLK_FREQ_337_5_BDW) +			return 337500; +		else +			return 675000; +	} +} + +void intel_ddi_pll_init(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	uint32_t val = I915_READ(LCPLL_CTL); + +	/* The LCPLL register should be turned on by the BIOS. For now let's +	 * just check its state and print errors in case something is wrong. +	 * Don't even try to turn it on. +	 */ + +	DRM_DEBUG_KMS("CDCLK running at %dKHz\n", +		      intel_ddi_get_cdclk_freq(dev_priv)); + +	if (val & LCPLL_CD_SOURCE_FCLK) +		DRM_ERROR("CDCLK source is not LCPLL\n"); + +	if (val & LCPLL_PLL_DISABLE) +		DRM_ERROR("LCPLL is disabled\n"); +} + +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder) +{ +	struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); +	struct intel_dp *intel_dp = &intel_dig_port->dp; +	struct drm_i915_private *dev_priv = encoder->dev->dev_private; +	enum port port = intel_dig_port->port; +	uint32_t val; +	bool wait = false; + +	if (I915_READ(DP_TP_CTL(port)) & DP_TP_CTL_ENABLE) { +		val = I915_READ(DDI_BUF_CTL(port)); +		if (val & DDI_BUF_CTL_ENABLE) { +			val &= ~DDI_BUF_CTL_ENABLE; +			I915_WRITE(DDI_BUF_CTL(port), val); +			wait = true; +		} + +		val = I915_READ(DP_TP_CTL(port)); +		val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); +		val |= DP_TP_CTL_LINK_TRAIN_PAT1; +		I915_WRITE(DP_TP_CTL(port), val); +		POSTING_READ(DP_TP_CTL(port)); + +		if (wait) +			intel_wait_ddi_buf_idle(dev_priv, port); +	} + +	val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST | +	      DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE; +	if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) +		val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; +	I915_WRITE(DP_TP_CTL(port), val); +	POSTING_READ(DP_TP_CTL(port)); + +	intel_dp->DP |= DDI_BUF_CTL_ENABLE; +	I915_WRITE(DDI_BUF_CTL(port), intel_dp->DP); +	POSTING_READ(DDI_BUF_CTL(port)); + +	udelay(600); +} + +void intel_ddi_fdi_disable(struct drm_crtc *crtc) +{ +	struct drm_i915_private *dev_priv = crtc->dev->dev_private; +	struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); +	uint32_t val; + +	intel_ddi_post_disable(intel_encoder); + +	val = I915_READ(_FDI_RXA_CTL); +	val &= ~FDI_RX_ENABLE; +	I915_WRITE(_FDI_RXA_CTL, val); + +	val = I915_READ(_FDI_RXA_MISC); +	val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); +	val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); +	I915_WRITE(_FDI_RXA_MISC, val); + +	val = I915_READ(_FDI_RXA_CTL); +	val &= ~FDI_PCDCLK; +	I915_WRITE(_FDI_RXA_CTL, val); + +	val = I915_READ(_FDI_RXA_CTL); +	val &= ~FDI_RX_PLL_ENABLE; +	I915_WRITE(_FDI_RXA_CTL, val); +} + +static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder) +{ +	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); +	int type = intel_encoder->type; + +	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) +		intel_dp_check_link_status(intel_dp); +} + +void intel_ddi_get_config(struct intel_encoder *encoder, +			  struct intel_crtc_config *pipe_config) +{ +	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; +	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); +	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; +	u32 temp, flags = 0; + +	temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); +	if (temp & TRANS_DDI_PHSYNC) +		flags |= DRM_MODE_FLAG_PHSYNC; +	else +		flags |= DRM_MODE_FLAG_NHSYNC; +	if (temp & TRANS_DDI_PVSYNC) +		flags |= DRM_MODE_FLAG_PVSYNC; +	else +		flags |= DRM_MODE_FLAG_NVSYNC; + +	pipe_config->adjusted_mode.flags |= flags; + +	switch (temp & TRANS_DDI_BPC_MASK) { +	case TRANS_DDI_BPC_6: +		pipe_config->pipe_bpp = 18; +		break; +	case TRANS_DDI_BPC_8: +		pipe_config->pipe_bpp = 24; +		break; +	case TRANS_DDI_BPC_10: +		pipe_config->pipe_bpp = 30; +		break; +	case TRANS_DDI_BPC_12: +		pipe_config->pipe_bpp = 36; +		break; +	default: +		break; +	} + +	switch (temp & TRANS_DDI_MODE_SELECT_MASK) { +	case TRANS_DDI_MODE_SELECT_HDMI: +		pipe_config->has_hdmi_sink = true; +	case TRANS_DDI_MODE_SELECT_DVI: +	case TRANS_DDI_MODE_SELECT_FDI: +		break; +	case TRANS_DDI_MODE_SELECT_DP_SST: +	case TRANS_DDI_MODE_SELECT_DP_MST: +		pipe_config->has_dp_encoder = true; +		intel_dp_get_m_n(intel_crtc, pipe_config); +		break; +	default: +		break; +	} + +	if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_AUDIO)) { +		temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); +		if (temp & (AUDIO_OUTPUT_ENABLE_A << (intel_crtc->pipe * 4))) +			pipe_config->has_audio = true; +	} + +	if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp_bpp && +	    pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) { +		/* +		 * This is a big fat ugly hack. +		 * +		 * Some machines in UEFI boot mode provide us a VBT that has 18 +		 * bpp and 1.62 GHz link bandwidth for eDP, which for reasons +		 * unknown we fail to light up. Yet the same BIOS boots up with +		 * 24 bpp and 2.7 GHz link. Use the same bpp as the BIOS uses as +		 * max, not what it tells us to use. +		 * +		 * Note: This will still be broken if the eDP panel is not lit +		 * up by the BIOS, and thus we can't get the mode at module +		 * load. +		 */ +		DRM_DEBUG_KMS("pipe has %d bpp for eDP panel, overriding BIOS-provided max %d bpp\n", +			      pipe_config->pipe_bpp, dev_priv->vbt.edp_bpp); +		dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp; +	} + +	intel_ddi_clock_get(encoder, pipe_config); +} + +static void intel_ddi_destroy(struct drm_encoder *encoder) +{ +	/* HDMI has nothing special to destroy, so we can go with this. */ +	intel_dp_encoder_destroy(encoder); +} + +static bool intel_ddi_compute_config(struct intel_encoder *encoder, +				     struct intel_crtc_config *pipe_config) +{ +	int type = encoder->type; +	int port = intel_ddi_get_encoder_port(encoder); + +	WARN(type == INTEL_OUTPUT_UNKNOWN, "compute_config() on unknown output!\n"); + +	if (port == PORT_A) +		pipe_config->cpu_transcoder = TRANSCODER_EDP; + +	if (type == INTEL_OUTPUT_HDMI) +		return intel_hdmi_compute_config(encoder, pipe_config); +	else +		return intel_dp_compute_config(encoder, pipe_config); +} + +static const struct drm_encoder_funcs intel_ddi_funcs = { +	.destroy = intel_ddi_destroy, +}; + +static struct intel_connector * +intel_ddi_init_dp_connector(struct intel_digital_port *intel_dig_port) +{ +	struct intel_connector *connector; +	enum port port = intel_dig_port->port; + +	connector = kzalloc(sizeof(*connector), GFP_KERNEL); +	if (!connector) +		return NULL; + +	intel_dig_port->dp.output_reg = DDI_BUF_CTL(port); +	if (!intel_dp_init_connector(intel_dig_port, connector)) { +		kfree(connector); +		return NULL; +	} + +	return connector; +} + +static struct intel_connector * +intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port) +{ +	struct intel_connector *connector; +	enum port port = intel_dig_port->port; + +	connector = kzalloc(sizeof(*connector), GFP_KERNEL); +	if (!connector) +		return NULL; + +	intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port); +	intel_hdmi_init_connector(intel_dig_port, connector); + +	return connector; +} + +void intel_ddi_init(struct drm_device *dev, enum port port) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_digital_port *intel_dig_port; +	struct intel_encoder *intel_encoder; +	struct drm_encoder *encoder; +	struct intel_connector *hdmi_connector = NULL; +	struct intel_connector *dp_connector = NULL; +	bool init_hdmi, init_dp; + +	init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi || +		     dev_priv->vbt.ddi_port_info[port].supports_hdmi); +	init_dp = dev_priv->vbt.ddi_port_info[port].supports_dp; +	if (!init_dp && !init_hdmi) { +		DRM_DEBUG_KMS("VBT says port %c is not DVI/HDMI/DP compatible\n", +			      port_name(port)); +		init_hdmi = true; +		init_dp = true; +	} + +	intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL); +	if (!intel_dig_port) +		return; + +	intel_encoder = &intel_dig_port->base; +	encoder = &intel_encoder->base; + +	drm_encoder_init(dev, encoder, &intel_ddi_funcs, +			 DRM_MODE_ENCODER_TMDS); + +	intel_encoder->compute_config = intel_ddi_compute_config; +	intel_encoder->enable = intel_enable_ddi; +	intel_encoder->pre_enable = intel_ddi_pre_enable; +	intel_encoder->disable = intel_disable_ddi; +	intel_encoder->post_disable = intel_ddi_post_disable; +	intel_encoder->get_hw_state = intel_ddi_get_hw_state; +	intel_encoder->get_config = intel_ddi_get_config; + +	intel_dig_port->port = port; +	intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) & +					  (DDI_BUF_PORT_REVERSAL | +					   DDI_A_4_LANES); + +	intel_encoder->type = INTEL_OUTPUT_UNKNOWN; +	intel_encoder->crtc_mask =  (1 << 0) | (1 << 1) | (1 << 2); +	intel_encoder->cloneable = 0; +	intel_encoder->hot_plug = intel_ddi_hot_plug; + +	if (init_dp) +		dp_connector = intel_ddi_init_dp_connector(intel_dig_port); + +	/* In theory we don't need the encoder->type check, but leave it just in +	 * case we have some really bad VBTs... */ +	if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi) +		hdmi_connector = intel_ddi_init_hdmi_connector(intel_dig_port); + +	if (!dp_connector && !hdmi_connector) { +		drm_encoder_cleanup(encoder); +		kfree(intel_dig_port); +	} +}  | 
