[Intel-gfx] [PATCH] drm/i915/cnl: Implement voltage swing sequence.

Manasi Navare manasi.d.navare at intel.com
Tue Jun 6 00:00:48 UTC 2017


On Mon, Jun 05, 2017 at 01:53:07PM -0700, Rodrigo Vivi wrote:
> This is an important part of the DDI initalization as well as
> for changing the voltage during DisplayPort link training.
> 
> This new sequence for Cannonlake is more like Broxton style
> but still with different registers, different table and
> different steps.
> 
> v2: Do not write to DW4_GRP to avoid overwrite individual loadgen.
>     Fix PORT_CL_DW5 SUS Clock Config set.
> v3: As previous platforms use only eDP table if low voltage was
>     requested.
> v4: fix Werror:maybe uninitialized (Paulo)
> v5: Rebase on top of dw2_swing_sel changes
>     on previous patches.
> v6: Using flexible SCALING_MODE_SEL(x).
> 
> Cc: Manasi Navare <manasi.d.navare at intel.com>
> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
> Signed-off-by: Rodrigo Vivi <rodrigo.vivi at intel.com>
 Reviewed-by: Manasi Navare <manasi.d.navare at intel.com>


> ---
>  drivers/gpu/drm/i915/i915_reg.h  |   1 +
>  drivers/gpu/drm/i915/intel_ddi.c | 176 ++++++++++++++++++++++++++++++++++++++-
>  drivers/gpu/drm/i915/intel_dp.c  |   2 +-
>  3 files changed, 177 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 0d2063e..e61376f 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -1671,6 +1671,7 @@ enum skl_disp_power_wells {
>  
>  #define CNL_PORT_CL1CM_DW5		_MMIO(0x162014)
>  #define   CL_POWER_DOWN_ENABLE		(1 << 4)
> +#define   SUS_CLOCK_CONFIG		(3 << 0)
>  
>  #define _PORT_CL1CM_DW9_A		0x162024
>  #define _PORT_CL1CM_DW9_BC		0x6C024
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index 9f34038..56b0a2c 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -1720,6 +1720,173 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder)
>  		DP_TRAIN_VOLTAGE_SWING_MASK;
>  }
>  
> +static const struct cnl_ddi_buf_trans *
> +cnl_get_buf_trans_hdmi(struct drm_i915_private *dev_priv,
> +		       u32 voltage, int *n_entries)
> +{
> +	if (voltage == VOLTAGE_INFO_0_85V) {
> +		*n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_0_85V);
> +		return cnl_ddi_translations_hdmi_0_85V;
> +	} else if (voltage == VOLTAGE_INFO_0_95V) {
> +		*n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_0_95V);
> +		return cnl_ddi_translations_hdmi_0_95V;
> +	} else if (voltage == VOLTAGE_INFO_1_05V) {
> +		*n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_1_05V);
> +		return cnl_ddi_translations_hdmi_1_05V;
> +	}
> +	return NULL;
> +}
> +
> +static const struct cnl_ddi_buf_trans *
> +cnl_get_buf_trans_dp(struct drm_i915_private *dev_priv,
> +		     u32 voltage, int *n_entries)
> +{
> +	if (voltage == VOLTAGE_INFO_0_85V) {
> +		*n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_0_85V);
> +		return cnl_ddi_translations_dp_0_85V;
> +	} else if (voltage == VOLTAGE_INFO_0_95V) {
> +		*n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_0_95V);
> +		return cnl_ddi_translations_dp_0_95V;
> +	} else if (voltage == VOLTAGE_INFO_1_05V) {
> +		*n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_1_05V);
> +		return cnl_ddi_translations_dp_1_05V;
> +	}
> +	return NULL;
> +}
> +
> +static const struct cnl_ddi_buf_trans *
> +cnl_get_buf_trans_edp(struct drm_i915_private *dev_priv,
> +		      u32 voltage, int *n_entries)
> +{
> +	if (dev_priv->vbt.edp.low_vswing) {
> +		if (voltage == VOLTAGE_INFO_0_85V) {
> +			*n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_85V);
> +			return cnl_ddi_translations_dp_0_85V;
> +		} else if (voltage == VOLTAGE_INFO_0_95V) {
> +			*n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_95V);
> +			return cnl_ddi_translations_edp_0_95V;
> +		} else if (voltage == VOLTAGE_INFO_1_05V) {
> +			*n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_1_05V);
> +			return cnl_ddi_translations_edp_1_05V;
> +		}
> +		return NULL;
> +	} else {
> +		return cnl_get_buf_trans_dp(dev_priv, voltage, n_entries);
> +	}
> +}
> +
> +static void cnl_ddi_vswing_program(struct drm_i915_private *dev_priv,
> +				    u32 level, enum port port, int type)
> +{
> +	const struct cnl_ddi_buf_trans *ddi_translations = NULL;
> +	u32 n_entries, val, voltage;
> +	int ln;
> +
> +	/*
> +	 * Values for each port type are listed in
> +	 * voltage swing programming tables.
> +	 * Vccio voltage found in PORT_COMP_DW3.
> +	 */
> +	voltage = I915_READ(CNL_PORT_COMP_DW3) & VOLTAGE_INFO_MASK;
> +
> +	if (type == INTEL_OUTPUT_HDMI) {
> +		ddi_translations = cnl_get_buf_trans_hdmi(dev_priv,
> +							  voltage, &n_entries);
> +	} else if (type == INTEL_OUTPUT_DP) {
> +		ddi_translations = cnl_get_buf_trans_dp(dev_priv,
> +							voltage, &n_entries);
> +	} else if (type == INTEL_OUTPUT_EDP) {
> +		ddi_translations = cnl_get_buf_trans_edp(dev_priv,
> +							 voltage, &n_entries);
> +	}
> +
> +	if (ddi_translations == NULL) {
> +		MISSING_CASE(voltage);
> +		return;
> +	}
> +
> +	if (level >= n_entries) {
> +		DRM_DEBUG_KMS("DDI translation not found for level %d. Using %d instead.", level, n_entries - 1);
> +		level = n_entries - 1;
> +	}
> +
> +	/* Set PORT_TX_DW5 Scaling Mode Sel to 010b. */
> +	val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
> +	val |= SCALING_MODE_SEL(2);
> +	I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
> +
> +	/* Program PORT_TX_DW2 */
> +	val = I915_READ(CNL_PORT_TX_DW2_LN0(port));
> +	val |= SWING_SEL_UPPER(ddi_translations[level].dw2_swing_sel);
> +	val |= SWING_SEL_LOWER(ddi_translations[level].dw2_swing_sel);
> +	/* Rcomp scalar is fixed as 0x98 for every table entry */
> +	val |= RCOMP_SCALAR(0x98);
> +	I915_WRITE(CNL_PORT_TX_DW2_GRP(port), val);
> +
> +        /* Program PORT_TX_DW4 */
> +	/* We cannot write to GRP. It would overrite individual loadgen */
> +	for (ln = 0; ln < 4; ln++) {
> +		val = I915_READ(CNL_PORT_TX_DW4_LN(port, ln));
> +		val |= POST_CURSOR_1(ddi_translations[level].dw4_post_cursor_1);
> +		val |= POST_CURSOR_2(ddi_translations[level].dw4_post_cursor_2);
> +		val |= CURSOR_COEFF(ddi_translations[level].dw4_cursor_coeff);
> +		I915_WRITE(CNL_PORT_TX_DW4_LN(port, ln), val);
> +	}
> +
> +        /* Program PORT_TX_DW5 */
> +	/* All DW5 values are fixed for every table entry */
> +	val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
> +	val |= RTERM_SELECT(6);
> +	val |= TAP3_DISABLE;
> +	I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
> +
> +        /* Program PORT_TX_DW7 */
> +	val = I915_READ(CNL_PORT_TX_DW7_LN0(port));
> +	val |= N_SCALAR(ddi_translations[level].dw7_n_scalar);
> +	I915_WRITE(CNL_PORT_TX_DW7_GRP(port), val);
> +}
> +
> +static void cnl_ddi_vswing_sequence(struct drm_i915_private *dev_priv,
> +				    u32 level, enum port port, int type)
> +{
> +	u32 val;
> +
> +	/*
> +	 * 1. If port type is eDP or DP,
> +	 * set PORT_PCS_DW1 cmnkeeper_enable to 1b,
> +	 * else clear to 0b.
> +	 */
> +	val = I915_READ(CNL_PORT_PCS_DW1_LN0(port));
> +	if (type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP)
> +		val |= COMMON_KEEPER_EN;
> +	else
> +		val &= ~COMMON_KEEPER_EN;
> +	I915_WRITE(CNL_PORT_PCS_DW1_GRP(port), val);
> +
> +	/* 2. Program loadgen select */
> +	/*
> +	 * FIXME: Program PORT_TX_DW4_LN depending on Bit rate and used lanes
> +	 */
> +
> +	/* 3. Set PORT_CL_DW5 SUS Clock Config to 11b */
> +	val = I915_READ(CNL_PORT_CL1CM_DW5);
> +	val |= SUS_CLOCK_CONFIG;
> +	I915_WRITE(CNL_PORT_CL1CM_DW5, val);
> +
> +	/* 4. Clear training enable to change swing values */
> +	val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
> +	val &= ~TX_TRAINING_EN;
> +	I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
> +
> +	/* 5. Program swing and de-emphasis */
> +	cnl_ddi_vswing_program(dev_priv, level, port, type);
> +
> +	/* 6. Set training enable to trigger update */
> +	val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
> +	val |= TX_TRAINING_EN;
> +	I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
> +}
> +
>  static uint32_t translate_signal_level(int signal_levels)
>  {
>  	int i;
> @@ -1752,7 +1919,11 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp)
>  		skl_ddi_set_iboost(encoder, level);
>  	else if (IS_GEN9_LP(dev_priv))
>  		bxt_ddi_vswing_sequence(dev_priv, level, port, encoder->type);
> -
> +	else if (IS_CANNONLAKE(dev_priv)) {
> +		cnl_ddi_vswing_sequence(dev_priv, level, port, encoder->type);
> +		/* DDI_BUF_CTL bits 27:24 are reserved on CNL */
> +		return 0;
> +	}
>  	return DDI_BUF_TRANS_SELECT(level);
>  }
>  
> @@ -1850,6 +2021,9 @@ static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
>  	else if (IS_GEN9_LP(dev_priv))
>  		bxt_ddi_vswing_sequence(dev_priv, level, port,
>  					INTEL_OUTPUT_HDMI);
> +	else if (IS_CANNONLAKE(dev_priv))
> +		cnl_ddi_vswing_sequence(dev_priv, level, port,
> +					INTEL_OUTPUT_HDMI);
>  
>  	intel_hdmi->set_infoframes(drm_encoder,
>  				   has_hdmi_sink,
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index 1a27c72..ecf9dfc 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -3462,7 +3462,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp)
>  	if (HAS_DDI(dev_priv)) {
>  		signal_levels = ddi_signal_levels(intel_dp);
>  
> -		if (IS_GEN9_LP(dev_priv))
> +		if (IS_GEN9_LP(dev_priv) || IS_CANNONLAKE(dev_priv))
>  			signal_levels = 0;
>  		else
>  			mask = DDI_BUF_EMP_MASK;
> -- 
> 1.9.1
> 


More information about the Intel-gfx mailing list