[Intel-gfx] [PATCH v2] drm/i915/bxt: Fix DSI HW state readout

Imre Deak imre.deak at intel.com
Thu Mar 24 10:02:52 UTC 2016


On to, 2016-03-24 at 11:15 +0200, Jani Nikula wrote:
> On Wed, 23 Mar 2016, Imre Deak <imre.deak at intel.com> wrote:
> > Currently the machine hangs during booting while accessing the
> > BXT_MIPI_PORT_CTRL register during pipe HW state readout. After
> > some
> > experimentation I found that the hang is caused by the DSI PLL
> > being
> > disabled, or it being enabled but with an incorrect divider
> > configuration. Enabling the PLL got rid of the boot problem, so fix
> > this by checking the PLL enabled state/configuration before
> > attempting
> > to read out the HW state.
> > 
> > The DSI_PLL_ENABLE register is in the always-on power well, while
> > the
> > BXT_DSI_PLL_CTL is in power well 0. This isn't exactly matched by
> > the
> > transcoder power domain, but what we really need is just a runtime
> > PM
> > reference, which is provided by any power domain.
> > 
> > Ville also found this dependency specified in BSpec, so I added a
> > reference to that too.
> > 
> > v2:
> > - Make sure we hold a power reference while accessing the PLL
> > registers.
> > 
> > CC: Shashank Sharma <shashank.sharma at intel.com>
> > CC: Uma Shankar <uma.shankar at intel.com>
> > CC: Jani Nikula <jani.nikula at intel.com>
> > Fixes: c6c794a2fc5e ("drm/i915/bxt: Initialize MIPI DSI for BXT")
> 
> I figured this might blow something up now, but then again we needed
> to
> do it to find out all the ways it would blow up. Apologies and
> thanks. ;)
> 
> > Signed-off-by: Imre Deak <imre.deak at intel.com>
> > ---
> >  drivers/gpu/drm/i915/i915_reg.h      |  2 ++
> >  drivers/gpu/drm/i915/intel_display.c | 13 +++++++++++++
> >  drivers/gpu/drm/i915/intel_dsi.c     |  9 +++++++++
> >  drivers/gpu/drm/i915/intel_dsi.h     |  1 +
> >  drivers/gpu/drm/i915/intel_dsi_pll.c | 37
> > ++++++++++++++++++++++++++++++++++++
> >  5 files changed, 62 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/i915/i915_reg.h
> > b/drivers/gpu/drm/i915/i915_reg.h
> > index f3ba43c..c839ce9 100644
> > --- a/drivers/gpu/drm/i915/i915_reg.h
> > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > @@ -7811,9 +7811,11 @@ enum skl_disp_power_wells {
> >  #define  BXT_DSIC_16X_BY2		(1 << 10)
> >  #define  BXT_DSIC_16X_BY3		(2 << 10)
> >  #define  BXT_DSIC_16X_BY4		(3 << 10)
> > +#define  BXT_DSIC_16X_MASK		(3 << 10)
> >  #define  BXT_DSIA_16X_BY2		(1 << 8)
> >  #define  BXT_DSIA_16X_BY3		(2 << 8)
> >  #define  BXT_DSIA_16X_BY4		(3 << 8)
> > +#define  BXT_DSIA_16X_MASK		(3 << 8)
> >  #define  BXT_DSI_FREQ_SEL_SHIFT		8
> >  #define  BXT_DSI_FREQ_SEL_MASK		(0xF <<
> > BXT_DSI_FREQ_SEL_SHIFT)
> >  
> > diff --git a/drivers/gpu/drm/i915/intel_display.c
> > b/drivers/gpu/drm/i915/intel_display.c
> > index 009b03b..6fd94c4 100644
> > --- a/drivers/gpu/drm/i915/intel_display.c
> > +++ b/drivers/gpu/drm/i915/intel_display.c
> > @@ -36,6 +36,7 @@
> >  #include "intel_drv.h"
> >  #include <drm/i915_drm.h>
> >  #include "i915_drv.h"
> > +#include "intel_dsi.h"
> >  #include "i915_trace.h"
> >  #include <drm/drm_atomic.h>
> >  #include <drm/drm_atomic_helper.h>
> > @@ -9852,10 +9853,12 @@ static bool
> > bxt_get_dsi_transcoder_state(struct intel_crtc *crtc,
> >  	enum intel_display_power_domain power_domain;
> >  	enum port port;
> >  	enum transcoder cpu_transcoder;
> > +	bool pll_enabled;
> >  	u32 tmp;
> >  
> >  	pipe_config->has_dsi_encoder = false;
> >  
> > +	pll_enabled = false;
> 
> It seems to me you could just bail out early here if the dsi pll is
> disabled.
>
> I'm guessing you put the check in the loop because of the power
> domain reference.

Yes.

> Two thoughts on that:
> 
> a) BXT_DSI_PLL_ENABLE is in always on power.

Well, that means always-on, whenever the PCI device is on, that is we
still need to hold a runtime PM reference, which is provided by any
display power domain reference.

> If you dropped the paranoia
> in bxt_dsi_pll_is_enabled() about valid BXT_DSI_PLL_CTL values, you
> could simplify code here and in intel_dsi_get_hw_state().

I think it's better to check the dividers too. If the dividers are
incorrect that machine will hang even though the PLL is enabled. Ville
told me that he saw a similar misconfiguration by BIOS on CHV/VLV, so
it's not unimaginable that it could also happen on BXT. 

> b) BXT_DSI_PLL_CTL is in PG0. Here, you implicitly trust the
> transcoder
> power domain to match that. In intel_dsi_get_hw_state() you
> implicitly
> trust the DSI port power domain to match that. They both do, but this
> seems a somewhat coincidental choice.

I'm actually not sure if power well 0 is simply the same as the always-
on well, there is no separate always-on well shown in BSpec "Broxton
Display Connections". In any case we don't handle either of  these
wells manually (on-demand), it's only during runtime suspend when they
are powered off. So to keep them on we need a runtime PM reference and
that is guaranteed to be provided by any power domain ref. 

> >  	for_each_port_masked(port, BIT(PORT_A) | BIT(PORT_C)) {
> >  		if (port == PORT_A)
> >  			cpu_transcoder = TRANSCODER_DSI_A;
> > @@ -9867,6 +9870,16 @@ static bool
> > bxt_get_dsi_transcoder_state(struct intel_crtc *crtc,
> >  			continue;
> >  		*power_domain_mask |= BIT(power_domain);
> >  
> > +		/*
> > +		 * The PLL needs to be enabled with a valid
> > divider
> > +		 * configuration, otherwise accessing DSI
> > registers will hang
> > +		 * the machine. See BSpec North Display Engine
> > +		 * registers/MIPI[BXT].
> > +		 */
> > +		pll_enabled = pll_enabled ||
> > intel_dsi_pll_is_enabled(dev_priv);
> > +		if (!pll_enabled)
> > +			break;
> > +
> 
> I guess if we want to keep the paranoia and we are happy with how the
> power domain references map to the power well of BXT_DSI_PLL_CTL, the
> simplest you could do is throw away the local variable and just do
> this:
> 
> 		if (!intel_dsi_pll_is_enabled(dev_priv))
> 			continue;
> 
> This potentially leads to an extra loop here, but at least the code
> gets
> simpler and easier to grasp.

Yea, it will result in an extra HW access, but I can simplify things as
you suggested.

> >  		/* XXX: this works for video mode only */
> >  		tmp = I915_READ(BXT_MIPI_PORT_CTRL(port));
> >  		if (!(tmp & DPI_ENABLE))
> > diff --git a/drivers/gpu/drm/i915/intel_dsi.c
> > b/drivers/gpu/drm/i915/intel_dsi.c
> > index 96ea3f7..0de74e1 100644
> > --- a/drivers/gpu/drm/i915/intel_dsi.c
> > +++ b/drivers/gpu/drm/i915/intel_dsi.c
> > @@ -684,6 +684,14 @@ static bool intel_dsi_get_hw_state(struct
> > intel_encoder *encoder,
> >  	if (!intel_display_power_get_if_enabled(dev_priv,
> > power_domain))
> >  		return false;
> >  
> > +	/*
> > +	 * On Broxton the PLL needs to be enabled with a valid
> > divider
> > +	 * configuration, otherwise accessing DSI registers will
> > hang the
> > +	 * machine. See BSpec North Display Engine
> > registers/MIPI[BXT].
> > +	 */
> > +	if (IS_BROXTON(dev_priv) &&
> > !intel_dsi_pll_is_enabled(dev_priv))
> > +		goto out_put_power;
> > +
> >  	/* XXX: this only works for one DSI output */
> >  	for_each_dsi_port(port, intel_dsi->ports) {
> >  		i915_reg_t ctrl_reg = IS_BROXTON(dev) ?
> > @@ -726,6 +734,7 @@ static bool intel_dsi_get_hw_state(struct
> > intel_encoder *encoder,
> >  		break;
> >  	}
> >  
> > +out_put_power:
> >  	intel_display_power_put(dev_priv, power_domain);
> >  
> >  	return active;
> > diff --git a/drivers/gpu/drm/i915/intel_dsi.h
> > b/drivers/gpu/drm/i915/intel_dsi.h
> > index e582ef8..ec58ead 100644
> > --- a/drivers/gpu/drm/i915/intel_dsi.h
> > +++ b/drivers/gpu/drm/i915/intel_dsi.h
> > @@ -126,6 +126,7 @@ static inline struct intel_dsi
> > *enc_to_intel_dsi(struct drm_encoder *encoder)
> >  	return container_of(encoder, struct intel_dsi, base.base);
> >  }
> >  
> > +bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv);
> >  extern void intel_enable_dsi_pll(struct intel_encoder *encoder);
> >  extern void intel_disable_dsi_pll(struct intel_encoder *encoder);
> >  extern u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int
> > pipe_bpp);
> > diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c
> > b/drivers/gpu/drm/i915/intel_dsi_pll.c
> > index e3e343c..e5b0625 100644
> > --- a/drivers/gpu/drm/i915/intel_dsi_pll.c
> > +++ b/drivers/gpu/drm/i915/intel_dsi_pll.c
> > @@ -192,6 +192,33 @@ static void vlv_disable_dsi_pll(struct
> > intel_encoder *encoder)
> >  	mutex_unlock(&dev_priv->sb_lock);
> >  }
> >  
> > +static bool bxt_dsi_pll_is_enabled(struct drm_i915_private
> > *dev_priv)
> > +{
> > +	bool enabled;
> > +	u32 val;
> > +	u32 mask;
> > +
> > +	mask = BXT_DSI_PLL_DO_ENABLE | BXT_DSI_PLL_LOCKED;
> > +	val = I915_READ(BXT_DSI_PLL_ENABLE);
> > +	enabled = (val & mask) == mask;
> > +
> > +	if (!enabled)
> > +		return false;
> > +
> > +	/*
> > +	 * Both dividers must be programmed with valid values even
> > if only one
> > +	 * of the PLL is used. See BSpec/Broxton Clocks.
> > +	 */
> > +	val = I915_READ(BXT_DSI_PLL_CTL);
> > +	if (!(val & BXT_DSIA_16X_MASK) || !(val &
> > BXT_DSIC_16X_MASK)) {
> > +		DRM_DEBUG_DRIVER("PLL is enabled with invalid
> > divider settings "
> > +				 "(%08x)\n", val);
> > +		enabled = false;
> > +	}
> 
> Do we really need this level of paranoia?

Yes, based on my test I think so, see above.

> > +
> > +	return enabled;
> > +}
> > +
> >  static void bxt_disable_dsi_pll(struct intel_encoder *encoder)
> >  {
> >  	struct drm_i915_private *dev_priv = encoder->base.dev-
> > >dev_private;
> > @@ -486,6 +513,16 @@ static void bxt_enable_dsi_pll(struct
> > intel_encoder *encoder)
> >  	DRM_DEBUG_KMS("DSI PLL locked\n");
> >  }
> >  
> > +bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv)
> > +{
> > +	if (IS_BROXTON(dev_priv))
> > +		return bxt_dsi_pll_is_enabled(dev_priv);
> > +
> > +	MISSING_CASE(INTEL_DEVID(dev_priv));
> > +
> > +	return false;
> > +}
> > +
> >  void intel_enable_dsi_pll(struct intel_encoder *encoder)
> >  {
> >  	struct drm_device *dev = encoder->base.dev;
> 


More information about the Intel-gfx mailing list