[09/15] drm/i915: Trick CL2 into life on CHV when using pipe B with port B
diff mbox

Message ID 1436388361-11130-10-git-send-email-ville.syrjala@linux.intel.com
State New
Headers show

Commit Message

Ville Syrjälä July 8, 2015, 8:45 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

Normmally the common lane in a PHY channel gets powered up when some
of the data lanes get powered up. But when we're driving port B with
pipe B we don't want to enabled any of the data lanes, and just want
the DPLL in the common lane to be active.

To make that happens we have to temporarily enable some data lanes
after which we can access the DPLL registers in the common lane. Once
the pipe is up and running we can drop the power override on the data
lanes allowing them to shut down. From this point forward the common
lane will in fact stay powered on until the data lanes in the other
channel get powered down.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_dp.c         | 23 +++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h        |  3 +++
 drivers/gpu/drm/i915/intel_hdmi.c       | 23 +++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_runtime_pm.c | 29 +++++++++++++++++++++++++++++
 4 files changed, 78 insertions(+)

Comments

deepak.s@linux.intel.com Aug. 19, 2015, 2:17 a.m. UTC | #1
On 07/09/2015 02:15 AM, ville.syrjala@linux.intel.com wrote:
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
>
> Normmally the common lane in a PHY channel gets powered up when some
> of the data lanes get powered up. But when we're driving port B with
> pipe B we don't want to enabled any of the data lanes, and just want
> the DPLL in the common lane to be active.
>
> To make that happens we have to temporarily enable some data lanes
> after which we can access the DPLL registers in the common lane. Once
> the pipe is up and running we can drop the power override on the data
> lanes allowing them to shut down. From this point forward the common
> lane will in fact stay powered on until the data lanes in the other
> channel get powered down.
Patch looks fine. It does what it says. One Q, why only for port B? Port 
C is also in same common lane right?
Reviewed-by: Deepak S <deepak.s@linux.intel.com>
> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> ---
>   drivers/gpu/drm/i915/intel_dp.c         | 23 +++++++++++++++++++++++
>   drivers/gpu/drm/i915/intel_drv.h        |  3 +++
>   drivers/gpu/drm/i915/intel_hdmi.c       | 23 +++++++++++++++++++++++
>   drivers/gpu/drm/i915/intel_runtime_pm.c | 29 +++++++++++++++++++++++++++++
>   4 files changed, 78 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index 6058129..8d088f3 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -2865,6 +2865,12 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
>   	mutex_unlock(&dev_priv->sb_lock);
>   
>   	intel_enable_dp(encoder);
> +
> +	/* Second common lane will stay alive on its own now */
> +	if (dport->release_cl2_override) {
> +		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
> +		dport->release_cl2_override = false;
> +	}
>   }
>   
>   static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
> @@ -2882,6 +2888,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
>   
>   	intel_dp_prepare(encoder);
>   
> +	/*
> +	 * Must trick the second common lane into life.
> +	 * Otherwise we can't even access the PLL.
> +	 */
> +	if (ch == DPIO_CH0 && pipe == PIPE_B)
> +		dport->release_cl2_override =
> +			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
> +
>   	chv_phy_powergate_lanes(encoder, true, lane_mask);
>   
>   	mutex_lock(&dev_priv->sb_lock);
> @@ -2960,6 +2974,15 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder)
>   
>   	mutex_unlock(&dev_priv->sb_lock);
>   
> +	/*
> +	 * Leave the power down bit cleared for at least one
> +	 * lane so that chv_powergate_phy_ch() will power
> +	 * on something when the channel is otherwise unused.
> +	 * When the port is off and the override is removed
> +	 * the lanes power down anyway, so otherwise it doesn't
> +	 * really matter what the state of power down bits is
> +	 * after this.
> +	 */
>   	chv_phy_powergate_lanes(encoder, false, 0x0);
>   }
>   
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index f8a16dc..6133a98 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -782,6 +782,7 @@ struct intel_digital_port {
>   	struct intel_dp dp;
>   	struct intel_hdmi hdmi;
>   	enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool);
> +	bool release_cl2_override;
>   };
>   
>   struct intel_dp_mst_encoder {
> @@ -1372,6 +1373,8 @@ 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);
> +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
> +			  enum dpio_channel ch, bool override);
>   
>   
>   /* intel_pm.c */
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index b3f6c9f..4b604ee 100644
> --- a/drivers/gpu/drm/i915/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> @@ -1625,6 +1625,14 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
>   
>   	intel_hdmi_prepare(encoder);
>   
> +	/*
> +	 * Must trick the second common lane into life.
> +	 * Otherwise we can't even access the PLL.
> +	 */
> +	if (ch == DPIO_CH0 && pipe == PIPE_B)
> +		dport->release_cl2_override =
> +			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
> +
>   	chv_phy_powergate_lanes(encoder, true, 0x0);
>   
>   	mutex_lock(&dev_priv->sb_lock);
> @@ -1701,6 +1709,15 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder)
>   
>   	mutex_unlock(&dev_priv->sb_lock);
>   
> +	/*
> +	 * Leave the power down bit cleared for at least one
> +	 * lane so that chv_powergate_phy_ch() will power
> +	 * on something when the channel is otherwise unused.
> +	 * When the port is off and the override is removed
> +	 * the lanes power down anyway, so otherwise it doesn't
> +	 * really matter what the state of power down bits is
> +	 * after this.
> +	 */
>   	chv_phy_powergate_lanes(encoder, false, 0x0);
>   }
>   
> @@ -1922,6 +1939,12 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
>   	g4x_enable_hdmi(encoder);
>   
>   	vlv_wait_port_ready(dev_priv, dport, 0x0);
> +
> +	/* Second common lane will stay alive on its own now */
> +	if (dport->release_cl2_override) {
> +		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
> +		dport->release_cl2_override = false;
> +	}
>   }
>   
>   static void intel_hdmi_destroy(struct drm_connector *connector)
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index 506a8cc..551cf08 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -1016,6 +1016,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
>   		      phy, dev_priv->chv_phy_control);
>   }
>   
> +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
> +			  enum dpio_channel ch, bool override)
> +{
> +	struct i915_power_domains *power_domains = &dev_priv->power_domains;
> +	bool was_override;
> +
> +	mutex_lock(&power_domains->lock);
> +
> +	was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch);
> +
> +	if (override == was_override)
> +		goto out;
> +
> +	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 (DPIO_PHY_CONTROL=0x%08x)\n",
> +		      phy, ch, dev_priv->chv_phy_control);
> +
> +out:
> +	mutex_unlock(&power_domains->lock);
> +
> +	return was_override;
> +}
> +
>   void chv_phy_powergate_lanes(struct intel_encoder *encoder,
>   			     bool override, unsigned int mask)
>   {
Ville Syrjälä Aug. 19, 2015, 11:29 a.m. UTC | #2
On Wed, Aug 19, 2015 at 07:47:41AM +0530, Deepak wrote:
> 
> 
> On 07/09/2015 02:15 AM, ville.syrjala@linux.intel.com wrote:
> > From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > Normmally the common lane in a PHY channel gets powered up when some
> > of the data lanes get powered up. But when we're driving port B with
> > pipe B we don't want to enabled any of the data lanes, and just want
> > the DPLL in the common lane to be active.
> >
> > To make that happens we have to temporarily enable some data lanes
> > after which we can access the DPLL registers in the common lane. Once
> > the pipe is up and running we can drop the power override on the data
> > lanes allowing them to shut down. From this point forward the common
> > lane will in fact stay powered on until the data lanes in the other
> > channel get powered down.
> Patch looks fine. It does what it says. One Q, why only for port B? Port 
> C is also in same common lane right?

Port B is in the first PHY channel which also houses CL1. CL1 always
powers up whenever any lanes in either PHY channel are powered up.
CL2 only powers up if lanes in the second channel (ie. the one with
port C) powers up.

So in this scenario (pipe B->port B) we want the DPLL from CL2, but
ideally we only want to power up the lanes for port B. Powering up
port B lanes will only power up CL1, but as we need CL2 instead we
need to, temporarily, power up some lanes in port C as well.

Crossing the streams the other way (pipe A->port C) is not a problem
since CL1 powers up whenever anything else powers up. So powering up
some port C lanes is enough on its own to make the CL1 DPLL
operational, even though CL1 and the lanes live in separate channels.

> Reviewed-by: Deepak S <deepak.s@linux.intel.com>
> > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > ---
> >   drivers/gpu/drm/i915/intel_dp.c         | 23 +++++++++++++++++++++++
> >   drivers/gpu/drm/i915/intel_drv.h        |  3 +++
> >   drivers/gpu/drm/i915/intel_hdmi.c       | 23 +++++++++++++++++++++++
> >   drivers/gpu/drm/i915/intel_runtime_pm.c | 29 +++++++++++++++++++++++++++++
> >   4 files changed, 78 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> > index 6058129..8d088f3 100644
> > --- a/drivers/gpu/drm/i915/intel_dp.c
> > +++ b/drivers/gpu/drm/i915/intel_dp.c
> > @@ -2865,6 +2865,12 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
> >   	mutex_unlock(&dev_priv->sb_lock);
> >   
> >   	intel_enable_dp(encoder);
> > +
> > +	/* Second common lane will stay alive on its own now */
> > +	if (dport->release_cl2_override) {
> > +		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
> > +		dport->release_cl2_override = false;
> > +	}
> >   }
> >   
> >   static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
> > @@ -2882,6 +2888,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
> >   
> >   	intel_dp_prepare(encoder);
> >   
> > +	/*
> > +	 * Must trick the second common lane into life.
> > +	 * Otherwise we can't even access the PLL.
> > +	 */
> > +	if (ch == DPIO_CH0 && pipe == PIPE_B)
> > +		dport->release_cl2_override =
> > +			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
> > +
> >   	chv_phy_powergate_lanes(encoder, true, lane_mask);
> >   
> >   	mutex_lock(&dev_priv->sb_lock);
> > @@ -2960,6 +2974,15 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder)
> >   
> >   	mutex_unlock(&dev_priv->sb_lock);
> >   
> > +	/*
> > +	 * Leave the power down bit cleared for at least one
> > +	 * lane so that chv_powergate_phy_ch() will power
> > +	 * on something when the channel is otherwise unused.
> > +	 * When the port is off and the override is removed
> > +	 * the lanes power down anyway, so otherwise it doesn't
> > +	 * really matter what the state of power down bits is
> > +	 * after this.
> > +	 */
> >   	chv_phy_powergate_lanes(encoder, false, 0x0);
> >   }
> >   
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > index f8a16dc..6133a98 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -782,6 +782,7 @@ struct intel_digital_port {
> >   	struct intel_dp dp;
> >   	struct intel_hdmi hdmi;
> >   	enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool);
> > +	bool release_cl2_override;
> >   };
> >   
> >   struct intel_dp_mst_encoder {
> > @@ -1372,6 +1373,8 @@ 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);
> > +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
> > +			  enum dpio_channel ch, bool override);
> >   
> >   
> >   /* intel_pm.c */
> > diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> > index b3f6c9f..4b604ee 100644
> > --- a/drivers/gpu/drm/i915/intel_hdmi.c
> > +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> > @@ -1625,6 +1625,14 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
> >   
> >   	intel_hdmi_prepare(encoder);
> >   
> > +	/*
> > +	 * Must trick the second common lane into life.
> > +	 * Otherwise we can't even access the PLL.
> > +	 */
> > +	if (ch == DPIO_CH0 && pipe == PIPE_B)
> > +		dport->release_cl2_override =
> > +			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
> > +
> >   	chv_phy_powergate_lanes(encoder, true, 0x0);
> >   
> >   	mutex_lock(&dev_priv->sb_lock);
> > @@ -1701,6 +1709,15 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder)
> >   
> >   	mutex_unlock(&dev_priv->sb_lock);
> >   
> > +	/*
> > +	 * Leave the power down bit cleared for at least one
> > +	 * lane so that chv_powergate_phy_ch() will power
> > +	 * on something when the channel is otherwise unused.
> > +	 * When the port is off and the override is removed
> > +	 * the lanes power down anyway, so otherwise it doesn't
> > +	 * really matter what the state of power down bits is
> > +	 * after this.
> > +	 */
> >   	chv_phy_powergate_lanes(encoder, false, 0x0);
> >   }
> >   
> > @@ -1922,6 +1939,12 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
> >   	g4x_enable_hdmi(encoder);
> >   
> >   	vlv_wait_port_ready(dev_priv, dport, 0x0);
> > +
> > +	/* Second common lane will stay alive on its own now */
> > +	if (dport->release_cl2_override) {
> > +		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
> > +		dport->release_cl2_override = false;
> > +	}
> >   }
> >   
> >   static void intel_hdmi_destroy(struct drm_connector *connector)
> > diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> > index 506a8cc..551cf08 100644
> > --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> > +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> > @@ -1016,6 +1016,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
> >   		      phy, dev_priv->chv_phy_control);
> >   }
> >   
> > +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
> > +			  enum dpio_channel ch, bool override)
> > +{
> > +	struct i915_power_domains *power_domains = &dev_priv->power_domains;
> > +	bool was_override;
> > +
> > +	mutex_lock(&power_domains->lock);
> > +
> > +	was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch);
> > +
> > +	if (override == was_override)
> > +		goto out;
> > +
> > +	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 (DPIO_PHY_CONTROL=0x%08x)\n",
> > +		      phy, ch, dev_priv->chv_phy_control);
> > +
> > +out:
> > +	mutex_unlock(&power_domains->lock);
> > +
> > +	return was_override;
> > +}
> > +
> >   void chv_phy_powergate_lanes(struct intel_encoder *encoder,
> >   			     bool override, unsigned int mask)
> >   {
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Daniel Vetter Aug. 26, 2015, 12:36 p.m. UTC | #3
On Wed, Aug 19, 2015 at 02:29:37PM +0300, Ville Syrjälä wrote:
> On Wed, Aug 19, 2015 at 07:47:41AM +0530, Deepak wrote:
> > 
> > 
> > On 07/09/2015 02:15 AM, ville.syrjala@linux.intel.com wrote:
> > > From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > >
> > > Normmally the common lane in a PHY channel gets powered up when some
> > > of the data lanes get powered up. But when we're driving port B with
> > > pipe B we don't want to enabled any of the data lanes, and just want
> > > the DPLL in the common lane to be active.
> > >
> > > To make that happens we have to temporarily enable some data lanes
> > > after which we can access the DPLL registers in the common lane. Once
> > > the pipe is up and running we can drop the power override on the data
> > > lanes allowing them to shut down. From this point forward the common
> > > lane will in fact stay powered on until the data lanes in the other
> > > channel get powered down.
> > Patch looks fine. It does what it says. One Q, why only for port B? Port 
> > C is also in same common lane right?
> 
> Port B is in the first PHY channel which also houses CL1. CL1 always
> powers up whenever any lanes in either PHY channel are powered up.
> CL2 only powers up if lanes in the second channel (ie. the one with
> port C) powers up.
> 
> So in this scenario (pipe B->port B) we want the DPLL from CL2, but
> ideally we only want to power up the lanes for port B. Powering up
> port B lanes will only power up CL1, but as we need CL2 instead we
> need to, temporarily, power up some lanes in port C as well.
> 
> Crossing the streams the other way (pipe A->port C) is not a problem
> since CL1 powers up whenever anything else powers up. So powering up
> some port C lanes is enough on its own to make the CL1 DPLL
> operational, even though CL1 and the lanes live in separate channels.

I added this to the commit message when merging, seemed useful for the
future.
-Daniel

> 
> > Reviewed-by: Deepak S <deepak.s@linux.intel.com>
> > > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > ---
> > >   drivers/gpu/drm/i915/intel_dp.c         | 23 +++++++++++++++++++++++
> > >   drivers/gpu/drm/i915/intel_drv.h        |  3 +++
> > >   drivers/gpu/drm/i915/intel_hdmi.c       | 23 +++++++++++++++++++++++
> > >   drivers/gpu/drm/i915/intel_runtime_pm.c | 29 +++++++++++++++++++++++++++++
> > >   4 files changed, 78 insertions(+)
> > >
> > > diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> > > index 6058129..8d088f3 100644
> > > --- a/drivers/gpu/drm/i915/intel_dp.c
> > > +++ b/drivers/gpu/drm/i915/intel_dp.c
> > > @@ -2865,6 +2865,12 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
> > >   	mutex_unlock(&dev_priv->sb_lock);
> > >   
> > >   	intel_enable_dp(encoder);
> > > +
> > > +	/* Second common lane will stay alive on its own now */
> > > +	if (dport->release_cl2_override) {
> > > +		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
> > > +		dport->release_cl2_override = false;
> > > +	}
> > >   }
> > >   
> > >   static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
> > > @@ -2882,6 +2888,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
> > >   
> > >   	intel_dp_prepare(encoder);
> > >   
> > > +	/*
> > > +	 * Must trick the second common lane into life.
> > > +	 * Otherwise we can't even access the PLL.
> > > +	 */
> > > +	if (ch == DPIO_CH0 && pipe == PIPE_B)
> > > +		dport->release_cl2_override =
> > > +			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
> > > +
> > >   	chv_phy_powergate_lanes(encoder, true, lane_mask);
> > >   
> > >   	mutex_lock(&dev_priv->sb_lock);
> > > @@ -2960,6 +2974,15 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder)
> > >   
> > >   	mutex_unlock(&dev_priv->sb_lock);
> > >   
> > > +	/*
> > > +	 * Leave the power down bit cleared for at least one
> > > +	 * lane so that chv_powergate_phy_ch() will power
> > > +	 * on something when the channel is otherwise unused.
> > > +	 * When the port is off and the override is removed
> > > +	 * the lanes power down anyway, so otherwise it doesn't
> > > +	 * really matter what the state of power down bits is
> > > +	 * after this.
> > > +	 */
> > >   	chv_phy_powergate_lanes(encoder, false, 0x0);
> > >   }
> > >   
> > > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > > index f8a16dc..6133a98 100644
> > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > @@ -782,6 +782,7 @@ struct intel_digital_port {
> > >   	struct intel_dp dp;
> > >   	struct intel_hdmi hdmi;
> > >   	enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool);
> > > +	bool release_cl2_override;
> > >   };
> > >   
> > >   struct intel_dp_mst_encoder {
> > > @@ -1372,6 +1373,8 @@ 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);
> > > +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
> > > +			  enum dpio_channel ch, bool override);
> > >   
> > >   
> > >   /* intel_pm.c */
> > > diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> > > index b3f6c9f..4b604ee 100644
> > > --- a/drivers/gpu/drm/i915/intel_hdmi.c
> > > +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> > > @@ -1625,6 +1625,14 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
> > >   
> > >   	intel_hdmi_prepare(encoder);
> > >   
> > > +	/*
> > > +	 * Must trick the second common lane into life.
> > > +	 * Otherwise we can't even access the PLL.
> > > +	 */
> > > +	if (ch == DPIO_CH0 && pipe == PIPE_B)
> > > +		dport->release_cl2_override =
> > > +			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
> > > +
> > >   	chv_phy_powergate_lanes(encoder, true, 0x0);
> > >   
> > >   	mutex_lock(&dev_priv->sb_lock);
> > > @@ -1701,6 +1709,15 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder)
> > >   
> > >   	mutex_unlock(&dev_priv->sb_lock);
> > >   
> > > +	/*
> > > +	 * Leave the power down bit cleared for at least one
> > > +	 * lane so that chv_powergate_phy_ch() will power
> > > +	 * on something when the channel is otherwise unused.
> > > +	 * When the port is off and the override is removed
> > > +	 * the lanes power down anyway, so otherwise it doesn't
> > > +	 * really matter what the state of power down bits is
> > > +	 * after this.
> > > +	 */
> > >   	chv_phy_powergate_lanes(encoder, false, 0x0);
> > >   }
> > >   
> > > @@ -1922,6 +1939,12 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
> > >   	g4x_enable_hdmi(encoder);
> > >   
> > >   	vlv_wait_port_ready(dev_priv, dport, 0x0);
> > > +
> > > +	/* Second common lane will stay alive on its own now */
> > > +	if (dport->release_cl2_override) {
> > > +		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
> > > +		dport->release_cl2_override = false;
> > > +	}
> > >   }
> > >   
> > >   static void intel_hdmi_destroy(struct drm_connector *connector)
> > > diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > index 506a8cc..551cf08 100644
> > > --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > @@ -1016,6 +1016,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
> > >   		      phy, dev_priv->chv_phy_control);
> > >   }
> > >   
> > > +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
> > > +			  enum dpio_channel ch, bool override)
> > > +{
> > > +	struct i915_power_domains *power_domains = &dev_priv->power_domains;
> > > +	bool was_override;
> > > +
> > > +	mutex_lock(&power_domains->lock);
> > > +
> > > +	was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch);
> > > +
> > > +	if (override == was_override)
> > > +		goto out;
> > > +
> > > +	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 (DPIO_PHY_CONTROL=0x%08x)\n",
> > > +		      phy, ch, dev_priv->chv_phy_control);
> > > +
> > > +out:
> > > +	mutex_unlock(&power_domains->lock);
> > > +
> > > +	return was_override;
> > > +}
> > > +
> > >   void chv_phy_powergate_lanes(struct intel_encoder *encoder,
> > >   			     bool override, unsigned int mask)
> > >   {
> > 
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
> 
> -- 
> Ville Syrjälä
> Intel OTC
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Patch
diff mbox

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 6058129..8d088f3 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2865,6 +2865,12 @@  static void chv_pre_enable_dp(struct intel_encoder *encoder)
 	mutex_unlock(&dev_priv->sb_lock);
 
 	intel_enable_dp(encoder);
+
+	/* Second common lane will stay alive on its own now */
+	if (dport->release_cl2_override) {
+		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
+		dport->release_cl2_override = false;
+	}
 }
 
 static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
@@ -2882,6 +2888,14 @@  static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
 
 	intel_dp_prepare(encoder);
 
+	/*
+	 * Must trick the second common lane into life.
+	 * Otherwise we can't even access the PLL.
+	 */
+	if (ch == DPIO_CH0 && pipe == PIPE_B)
+		dport->release_cl2_override =
+			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
+
 	chv_phy_powergate_lanes(encoder, true, lane_mask);
 
 	mutex_lock(&dev_priv->sb_lock);
@@ -2960,6 +2974,15 @@  static void chv_dp_post_pll_disable(struct intel_encoder *encoder)
 
 	mutex_unlock(&dev_priv->sb_lock);
 
+	/*
+	 * Leave the power down bit cleared for at least one
+	 * lane so that chv_powergate_phy_ch() will power
+	 * on something when the channel is otherwise unused.
+	 * When the port is off and the override is removed
+	 * the lanes power down anyway, so otherwise it doesn't
+	 * really matter what the state of power down bits is
+	 * after this.
+	 */
 	chv_phy_powergate_lanes(encoder, false, 0x0);
 }
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index f8a16dc..6133a98 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -782,6 +782,7 @@  struct intel_digital_port {
 	struct intel_dp dp;
 	struct intel_hdmi hdmi;
 	enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool);
+	bool release_cl2_override;
 };
 
 struct intel_dp_mst_encoder {
@@ -1372,6 +1373,8 @@  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);
+bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
+			  enum dpio_channel ch, bool override);
 
 
 /* intel_pm.c */
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index b3f6c9f..4b604ee 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1625,6 +1625,14 @@  static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
 
 	intel_hdmi_prepare(encoder);
 
+	/*
+	 * Must trick the second common lane into life.
+	 * Otherwise we can't even access the PLL.
+	 */
+	if (ch == DPIO_CH0 && pipe == PIPE_B)
+		dport->release_cl2_override =
+			!chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true);
+
 	chv_phy_powergate_lanes(encoder, true, 0x0);
 
 	mutex_lock(&dev_priv->sb_lock);
@@ -1701,6 +1709,15 @@  static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder)
 
 	mutex_unlock(&dev_priv->sb_lock);
 
+	/*
+	 * Leave the power down bit cleared for at least one
+	 * lane so that chv_powergate_phy_ch() will power
+	 * on something when the channel is otherwise unused.
+	 * When the port is off and the override is removed
+	 * the lanes power down anyway, so otherwise it doesn't
+	 * really matter what the state of power down bits is
+	 * after this.
+	 */
 	chv_phy_powergate_lanes(encoder, false, 0x0);
 }
 
@@ -1922,6 +1939,12 @@  static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
 	g4x_enable_hdmi(encoder);
 
 	vlv_wait_port_ready(dev_priv, dport, 0x0);
+
+	/* Second common lane will stay alive on its own now */
+	if (dport->release_cl2_override) {
+		chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false);
+		dport->release_cl2_override = false;
+	}
 }
 
 static void intel_hdmi_destroy(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index 506a8cc..551cf08 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -1016,6 +1016,35 @@  static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
 		      phy, dev_priv->chv_phy_control);
 }
 
+bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
+			  enum dpio_channel ch, bool override)
+{
+	struct i915_power_domains *power_domains = &dev_priv->power_domains;
+	bool was_override;
+
+	mutex_lock(&power_domains->lock);
+
+	was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch);
+
+	if (override == was_override)
+		goto out;
+
+	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 (DPIO_PHY_CONTROL=0x%08x)\n",
+		      phy, ch, dev_priv->chv_phy_control);
+
+out:
+	mutex_unlock(&power_domains->lock);
+
+	return was_override;
+}
+
 void chv_phy_powergate_lanes(struct intel_encoder *encoder,
 			     bool override, unsigned int mask)
 {