[Intel-gfx] [PATCH 08/15] drm/i915: Implement PHY lane power gating for CHV

Deepak deepak.s at linux.intel.com
Tue Aug 18 18:48:38 PDT 2015



On 07/09/2015 02:15 AM, ville.syrjala at linux.intel.com wrote:
> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
>
> Powergate the PHY lanes when they're not needed. For HDMI all four lanes
> are needed always, but for DP we can enable only the needed lanes. To
> power down the unused lanes we use some power down override bits in the
> DISPLAY_PHY_CONTROL register. Without the overrides it appears that the
> hardware always powers on all the lanes. When the port is disabled the
> power down override is not needed and the lanes will shut off on their
> own. That also means the override is critical to actually be able to
> access the DPIO registers before the port is actually enabled.
>
> Additionally the common lanes will power down when not needed. CL1
> remains on as long as anything else is on, CL2 will shut down when
> all the lanes in the same channel will shut down. There is one exception
> for CL2 that will be dealt in a separate patch for clarity.
>
> With potentially some lanes powered down, the DP code now has to check
> the number of active lanes before accessing PCS/TX registers. All
> registers in powered down blocks will reads as 0xffffffff, and soe we
> would drown in warnings from vlv_dpio_read() if we allowed the code
> to access all those registers.
>
> Another important detail in the DP code is the "TX latency optimal"
> setting. Normally the second TX lane acts as some kind of reset master,
> with the other lanes as slaves. But when only a single lane is enabled,
> that single lane obviously has to be the master.
>
> A bit of extra care is needed to reconstruct the initial state of the
> DISPLAY_PHY_CONTROL register since it can't be read safely. So instead
> read the actual lane status from the DPLL/PHY_STATUS registers and
> use that to determine which lanes ought to be powergated initially.
>
> We also need to switch the PHY power modes to "deep PSR" to avoid
> a hard system hang when powering down the single channel PHY.
>
> Also sprinkle a few debug prints around so that we can monitor the
> DISPLAY_PHY_STATUS changes without having to read it and risk
> corrupting it.
>
> v2: Add locking to chv_powergate_phy_lanes()
> v3: Actually enable dynamic powerdown in the PHY and deal with the
>      fallout
>
> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
>   drivers/gpu/drm/i915/i915_reg.h         |   8 ++
>   drivers/gpu/drm/i915/intel_dp.c         | 141 +++++++++++++++++++++-----------
>   drivers/gpu/drm/i915/intel_drv.h        |   4 +
>   drivers/gpu/drm/i915/intel_hdmi.c       |   4 +
>   drivers/gpu/drm/i915/intel_runtime_pm.c | 123 ++++++++++++++++++++++++++--
>   5 files changed, 221 insertions(+), 59 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 17cb7e5..bcfcbb62 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -1125,9 +1125,15 @@ enum skl_disp_power_wells {
>   
>   #define _CHV_CMN_DW19_CH0		0x814c
>   #define _CHV_CMN_DW6_CH1		0x8098
> +#define   DPIO_DYNPWRDOWNEN_CH1		(1 << 28) /* CL2 DW6 only */
>   #define   CHV_CMN_USEDCLKCHANNEL	(1 << 13)
> +
>   #define CHV_CMN_DW19(ch) _PIPE(ch, _CHV_CMN_DW19_CH0, _CHV_CMN_DW6_CH1)
>   
> +#define CHV_CMN_DW28			0x8170
> +#define   DPIO_CL1POWERDOWNEN		(1 << 23)
> +#define   DPIO_DYNPWRDOWNEN_CH0		(1 << 22)
> +
>   #define CHV_CMN_DW30			0x8178
>   #define   DPIO_LRC_BYPASS		(1 << 3)
>   
> @@ -2175,10 +2181,12 @@ enum skl_disp_power_wells {
>   #define DPIO_PHY_STATUS			(VLV_DISPLAY_BASE + 0x6240)
>   #define   DPLL_PORTD_READY_MASK		(0xf)
>   #define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100)
> +#define   PHY_CH_POWER_DOWN_OVRD_EN(phy, ch)	(1 << (2*(phy)+(ch)+27))
>   #define   PHY_LDO_DELAY_0NS			0x0
>   #define   PHY_LDO_DELAY_200NS			0x1
>   #define   PHY_LDO_DELAY_600NS			0x2
>   #define   PHY_LDO_SEQ_DELAY(delay, phy)		((delay) << (2*(phy)+23))
> +#define   PHY_CH_POWER_DOWN_OVRD(mask, phy, ch)	((mask) << (8*(phy)+4*(ch)+11))
>   #define   PHY_CH_SU_PSR				0x1
>   #define   PHY_CH_DEEP_PSR			0x7
>   #define   PHY_CH_POWER_MODE(mode, phy, ch)	((mode) << (6*(phy)+3*(ch)+2))
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index 40b8430..6058129 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -133,6 +133,11 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp);
>   static void vlv_steal_power_sequencer(struct drm_device *dev,
>   				      enum pipe pipe);
>   
> +static unsigned int intel_dp_unused_lane_mask(int lane_count)
> +{
> +	return ~((1 << lane_count) - 1) & 0xf;
> +}
> +
>   static int
>   intel_dp_max_link_bw(struct intel_dp  *intel_dp)
>   {
> @@ -2395,17 +2400,21 @@ static void chv_post_disable_dp(struct intel_encoder *encoder)
>   	val |= CHV_PCS_REQ_SOFTRESET_EN;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
> -	val |= CHV_PCS_REQ_SOFTRESET_EN;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
> +		val |= CHV_PCS_REQ_SOFTRESET_EN;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
> +	}
>   
>   	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
>   	val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
> -	val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
> +		val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
> +	}
>   
>   	mutex_unlock(&dev_priv->sb_lock);
>   }
> @@ -2525,7 +2534,6 @@ static void intel_enable_dp(struct intel_encoder *encoder)
>   	struct drm_i915_private *dev_priv = dev->dev_private;
>   	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
>   	uint32_t dp_reg = I915_READ(intel_dp->output_reg);
> -	unsigned int lane_mask = 0x0;
>   
>   	if (WARN_ON(dp_reg & DP_PORT_EN))
>   		return;
> @@ -2543,9 +2551,15 @@ static void intel_enable_dp(struct intel_encoder *encoder)
>   
>   	pps_unlock(intel_dp);
>   
> -	if (IS_VALLEYVIEW(dev))
> +	if (IS_VALLEYVIEW(dev)) {
> +		unsigned int lane_mask = 0x0;
> +
> +		if (IS_CHERRYVIEW(dev))
> +			lane_mask = intel_dp_unused_lane_mask(crtc->config->lane_count);
> +
>   		vlv_wait_port_ready(dev_priv, dp_to_dig_port(intel_dp),
>   				    lane_mask);
> +	}
>   
>   	intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
>   	intel_dp_start_link_train(intel_dp);
> @@ -2772,31 +2786,40 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
>   	val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch));
> -	val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch));
> +		val &= ~DPIO_LANEDESKEW_STRAP_OVRD;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val);
> +	}
>   
>   	/* Deassert soft data lane reset*/
>   	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
>   	val |= CHV_PCS_REQ_SOFTRESET_EN;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
> -	val |= CHV_PCS_REQ_SOFTRESET_EN;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
> +		val |= CHV_PCS_REQ_SOFTRESET_EN;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
> +	}
>   
>   	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
>   	val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
> -	val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
> +		val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
> +	}
>   
>   	/* Program Tx lane latency optimal setting*/
> -	for (i = 0; i < 4; i++) {
> +	for (i = 0; i < intel_crtc->config->lane_count; i++) {
>   		/* Set the upar bit */
> -		data = (i == 1) ? 0x0 : 0x1;
> +		if (intel_crtc->config->lane_count == 1)
> +			data = 0x0;
> +		else
> +			data = (i == 1) ? 0x0 : 0x1;
>   		vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
>   				data << DPIO_UPAR_SHIFT);
>   	}
> @@ -2817,9 +2840,11 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
>   	val |= DPIO_TX2_STAGGER_MASK(0x1f);
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch));
> -	val |= DPIO_TX2_STAGGER_MASK(0x1f);
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch));
> +		val |= DPIO_TX2_STAGGER_MASK(0x1f);
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val);
> +	}
>   
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW12(ch),
>   		       DPIO_LANESTAGGER_STRAP(stagger) |
> @@ -2828,12 +2853,14 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
>   		       DPIO_TX1_STAGGER_MULT(6) |
>   		       DPIO_TX2_STAGGER_MULT(0));
>   
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch),
> -		       DPIO_LANESTAGGER_STRAP(stagger) |
> -		       DPIO_LANESTAGGER_STRAP_OVRD |
> -		       DPIO_TX1_STAGGER_MASK(0x1f) |
> -		       DPIO_TX1_STAGGER_MULT(7) |
> -		       DPIO_TX2_STAGGER_MULT(5));
> +	if (intel_crtc->config->lane_count > 2) {
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch),
> +			       DPIO_LANESTAGGER_STRAP(stagger) |
> +			       DPIO_LANESTAGGER_STRAP_OVRD |
> +			       DPIO_TX1_STAGGER_MASK(0x1f) |
> +			       DPIO_TX1_STAGGER_MULT(7) |
> +			       DPIO_TX2_STAGGER_MULT(5));
> +	}
>   
>   	mutex_unlock(&dev_priv->sb_lock);
>   
> @@ -2849,10 +2876,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
>   		to_intel_crtc(encoder->base.crtc);
>   	enum dpio_channel ch = vlv_dport_to_channel(dport);
>   	enum pipe pipe = intel_crtc->pipe;
> +	unsigned int lane_mask =
> +		intel_dp_unused_lane_mask(intel_crtc->config->lane_count);
>   	u32 val;
>   
>   	intel_dp_prepare(encoder);
>   
> +	chv_phy_powergate_lanes(encoder, true, lane_mask);
> +
>   	mutex_lock(&dev_priv->sb_lock);
>   
>   	/* program left/right clock distribution */
> @@ -2883,13 +2914,15 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
>   		val |= CHV_PCS_USEDCLKCHANNEL;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch));
> -	val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
> -	if (pipe != PIPE_B)
> -		val &= ~CHV_PCS_USEDCLKCHANNEL;
> -	else
> -		val |= CHV_PCS_USEDCLKCHANNEL;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch));
> +		val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
> +		if (pipe != PIPE_B)
> +			val &= ~CHV_PCS_USEDCLKCHANNEL;
> +		else
> +			val |= CHV_PCS_USEDCLKCHANNEL;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val);
> +	}
>   
>   	/*
>   	 * This a a bit weird since generally CL
> @@ -2926,6 +2959,8 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder)
>   	}
>   
>   	mutex_unlock(&dev_priv->sb_lock);
> +
> +	chv_phy_powergate_lanes(encoder, false, 0x0);
>   }
>   
>   /*
> @@ -3261,24 +3296,28 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp)
>   	val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
> -	val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
> -	val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK);
> -	val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
> +		val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
> +		val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK);
> +		val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
> +	}
>   
>   	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW9(ch));
>   	val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
>   	val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW9(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch));
> -	val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
> -	val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch));
> +		val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK);
> +		val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val);
> +	}
>   
>   	/* Program swing deemph */
> -	for (i = 0; i < 4; i++) {
> +	for (i = 0; i < intel_crtc->config->lane_count; i++) {
>   		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
>   		val &= ~DPIO_SWING_DEEMPH9P5_MASK;
>   		val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT;
> @@ -3286,7 +3325,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp)
>   	}
>   
>   	/* Program swing margin */
> -	for (i = 0; i < 4; i++) {
> +	for (i = 0; i < intel_crtc->config->lane_count; i++) {
>   		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
>   
>   		val &= ~DPIO_SWING_MARGIN000_MASK;
> @@ -3309,7 +3348,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp)
>   	 * For now, for this unique transition scale selection, set bit
>   	 * 27 for ch0 and ch1.
>   	 */
> -	for (i = 0; i < 4; i++) {
> +	for (i = 0; i < intel_crtc->config->lane_count; i++) {
>   		val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
>   		if (chv_need_uniq_trans_scale(train_set))
>   			val |= DPIO_TX_UNIQ_TRANS_SCALE_EN;
> @@ -3323,9 +3362,11 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp)
>   	val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
>   	vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
>   
> -	val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
> -	val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
> -	vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
> +	if (intel_crtc->config->lane_count > 2) {
> +		val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
> +		val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
> +		vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
> +	}
>   
>   	/* LRC Bypass */
>   	val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 23d5e46..f8a16dc 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1370,6 +1370,10 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
>   
>   void intel_display_set_init_power(struct drm_i915_private *dev, bool enable);
>   
> +void chv_phy_powergate_lanes(struct intel_encoder *encoder,
> +			     bool override, unsigned int mask);
> +
> +
>   /* intel_pm.c */
>   void intel_init_clock_gating(struct drm_device *dev);
>   void intel_suspend_hw(struct drm_device *dev);
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 86b1a2c..b3f6c9f 100644
> --- a/drivers/gpu/drm/i915/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> @@ -1625,6 +1625,8 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
>   
>   	intel_hdmi_prepare(encoder);
>   
> +	chv_phy_powergate_lanes(encoder, true, 0x0);
> +
>   	mutex_lock(&dev_priv->sb_lock);
>   
>   	/* program left/right clock distribution */
> @@ -1698,6 +1700,8 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder)
>   	}
>   
>   	mutex_unlock(&dev_priv->sb_lock);
> +
> +	chv_phy_powergate_lanes(encoder, false, 0x0);
>   }
>   
>   static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index dab1da9..506a8cc 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -946,14 +946,19 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,
>   					   struct i915_power_well *power_well)
>   {
>   	enum dpio_phy phy;
> +	enum pipe pipe;
> +	uint32_t tmp;
>   
>   	WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC &&
>   		     power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D);
>   
> -	if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC)
> +	if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) {
> +		pipe = PIPE_A;
>   		phy = DPIO_PHY0;
> -	else
> +	} else {
> +		pipe = PIPE_C;
>   		phy = DPIO_PHY1;
> +	}
>   
>   	/* since ref/cri clock was enabled */
>   	udelay(1); /* >10ns for cmnreset, >0ns for sidereset */
> @@ -963,8 +968,26 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,
>   	if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1))
>   		DRM_ERROR("Display PHY %d is not power up\n", phy);
>   
> +	mutex_lock(&dev_priv->sb_lock);
> +
> +	/* Enable dynamic power down */
> +	tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28);
> +	tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN;
> +	vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp);
> +
> +	if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) {
> +		tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1);
> +		tmp |= DPIO_DYNPWRDOWNEN_CH1;
> +		vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp);
> +	}
> +
> +	mutex_unlock(&dev_priv->sb_lock);
> +
>   	dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy);
>   	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control);
> +
> +	DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n",
> +		      phy, dev_priv->chv_phy_control);
>   }
>   
>   static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
> @@ -988,6 +1011,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
>   	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control);
>   
>   	vlv_set_power_well(dev_priv, power_well, false);
> +
> +	DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n",
> +		      phy, dev_priv->chv_phy_control);
> +}
> +
> +void chv_phy_powergate_lanes(struct intel_encoder *encoder,
> +			     bool override, unsigned int mask)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
> +	struct i915_power_domains *power_domains = &dev_priv->power_domains;
> +	enum dpio_phy phy = vlv_dport_to_phy(enc_to_dig_port(&encoder->base));
> +	enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base));
> +
> +	mutex_lock(&power_domains->lock);
> +
> +	dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch);
> +	dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch);
> +
> +	if (override)
> +		dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch);
> +	else
> +		dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch);
> +
> +	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control);
> +
> +	DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n",
> +		      phy, ch, mask, dev_priv->chv_phy_control);
> +
> +	mutex_unlock(&power_domains->lock);
>   }
>   
>   static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv,
> @@ -1620,19 +1672,72 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv)
>   	 * DISPLAY_PHY_CONTROL can get corrupted if read. As a
>   	 * workaround never ever read DISPLAY_PHY_CONTROL, and
>   	 * instead maintain a shadow copy ourselves. Use the actual
> -	 * power well state to reconstruct the expected initial
> -	 * value.
> +	 * power well state and lane status to reconstruct the
> +	 * expected initial value.
>   	 */
>   	dev_priv->chv_phy_control =
>   		PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY0) |
>   		PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY1) |
> -		PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH0) |
> -		PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH1) |
> -		PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY1, DPIO_CH0);
> -	if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc))
> +		PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH0) |
> +		PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH1) |
> +		PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY1, DPIO_CH0);
> +
> +	/*
> +	 * If all lanes are disabled we leave the override disabled
> +	 * with all power down bits cleared to match the state we
> +	 * would use after disabling the port. Otherwise enable the
> +	 * override and set the lane powerdown bits accding to the
> +	 * current lane status.
> +	 */
> +	if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) {
> +		uint32_t status = I915_READ(DPLL(PIPE_A));
> +		unsigned int mask;
> +
> +		mask = status & DPLL_PORTB_READY_MASK;
> +		if (mask == 0xf)
> +			mask = 0x0;
> +		else
> +			dev_priv->chv_phy_control |=
> +				PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0);
> +
> +		dev_priv->chv_phy_control |=
> +			PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH0);
> +
> +		mask = (status & DPLL_PORTC_READY_MASK) >> 4;
> +		if (mask == 0xf)
> +			mask = 0x0;
> +		else
> +			dev_priv->chv_phy_control |=
> +				PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1);
> +
> +		dev_priv->chv_phy_control |=
> +			PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH1);
> +
>   		dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY0);
> -	if (cmn_d->ops->is_enabled(dev_priv, cmn_d))
> +	}
> +
> +	if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) {
> +		uint32_t status = I915_READ(DPIO_PHY_STATUS);
> +		unsigned int mask;
> +
> +		mask = status & DPLL_PORTD_READY_MASK;
> +
> +		if (mask == 0xf)
> +			mask = 0x0;
> +		else
> +			dev_priv->chv_phy_control |=
> +				PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0);
> +
> +		dev_priv->chv_phy_control |=
> +			PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY1, DPIO_CH0);
> +
>   		dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY1);
> +	}
> +
> +	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control);
> +
> +	DRM_DEBUG_KMS("Initial PHY_CONTROL=0x%08x\n",
> +		      dev_priv->chv_phy_control);
>   }
>   
>   static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv)
Looks fine i think we have covered all the scenarios.
Reviewed-by: Deepak S <deepak.s at linux.intel.com>


More information about the Intel-gfx mailing list