diff mbox

[1/3] drm/i915: add PWM and BLC assertion checks

Message ID 1396289637-1013-1-git-send-email-jbarnes@virtuousgeek.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jesse Barnes March 31, 2014, 6:13 p.m. UTC
To make sure we properly follow the enable/disable sequences.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
 drivers/gpu/drm/i915/intel_dp.c    | 62 ++++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/i915/intel_drv.h   |  1 +
 drivers/gpu/drm/i915/intel_panel.c |  5 ++-
 3 files changed, 65 insertions(+), 3 deletions(-)

Comments

Jani Nikula April 1, 2014, 7:19 a.m. UTC | #1
On Mon, 31 Mar 2014, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
> To make sure we properly follow the enable/disable sequences.
>
> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> ---
>  drivers/gpu/drm/i915/intel_dp.c    | 62 ++++++++++++++++++++++++++++++++++++--
>  drivers/gpu/drm/i915/intel_drv.h   |  1 +
>  drivers/gpu/drm/i915/intel_panel.c |  5 ++-
>  3 files changed, 65 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index bf73771..b6f7087 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -301,6 +301,20 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp)
>  		return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
>  }
>  
> +static void assert_pwm(struct intel_connector *connector,
> +		       bool expected_state)
> +{
> +	bool state;
> +
> +	state = intel_panel_get_backlight(connector);

If the duty cycle is regarded as a binary on/off, I'd rather add an
additional "is enabled" call to intel_panel.c. Especially so because the
duty cycle value returned by intel_panel_get_backlight is meaningless
without the max value.

> +
> +	WARN(state != expected_state, "pwm state failure, expected %d, found "
> +	     "%d\n", expected_state, state);
> +}
> +
> +#define assert_pwm_enabled(c) assert_pwm((c), true)
> +#define assert_pwm_disabled(c) assert_pwm((c), false)
> +
>  static bool edp_have_panel_power(struct intel_dp *intel_dp)
>  {
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> @@ -884,6 +898,8 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
>  	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
>  	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
>  
> +	assert_pwm_disabled(intel_dp->attached_connector);
> +
>  	/*
>  	 * There are four kinds of DP registers:
>  	 *
> @@ -1167,6 +1183,23 @@ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
>  	}
>  }
>  
> +/*
> + * For this and the disable sequence below, Google for actual eDP LCD timing
> + * diagrams or check the eDP spec.  Below is for reference on asserts and
> + * does not contain Tx values for delays between steps.
> + *
> + * For panel on, the sequence should be:
> + * - LCD power supply on (PP regs or VDD AUX)
> + *   - eDP should display black at this point
> + * - HPD (if present) should go high
> + * - AUX channel becomes available
> + * - link training begins
> + * - LED backlight power on
> + * - LED PWM_EN goes high, duty cycle >min (PWM regs)
> + * - link training completes
> + * - LED_EN goes high (PP BLC_EN bit)
> + */
> +
>  void intel_edp_panel_on(struct intel_dp *intel_dp)
>  {
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> @@ -1212,6 +1245,19 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
>  	}
>  }
>  
> +/*
> + * For panel off the sequence should be:
> + * - LED_EN goes low (BLC_EN in our PP regs)
> + * - LED PWM_EN goes low (PWM duty cycle 0 and PWM enable = 0)
> + *   - eDP should display black video at this point
> + * - LED VCCS goes low (power for backlight)
> + * - DP link goes to idle or off
> + * - AUX goes down
> + * - HPD line (if present) drops to low
> + * - eDP black video stops
> + * - LCD power supply shuts down (PP regs and VDD AUX)
> + */
> +
>  void intel_edp_panel_off(struct intel_dp *intel_dp)
>  {
>  	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> @@ -1231,11 +1277,17 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
>  
>  	WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
>  
> +	/* By this time the PWM and BLC bits should be off already */
> +	assert_pwm_disabled(intel_dp->attached_connector);
> +
>  	pp = ironlake_get_pp_control(intel_dp);
> +
> +	WARN(pp & EDP_BLC_ENABLE,
> +	     "backlight controller still on at panel off time\n");
> +
>  	/* We need to switch off panel power _and_ force vdd, for otherwise some
>  	 * panels get very unhappy and cease to work. */
> -	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
> -		EDP_BLC_ENABLE);
> +	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD);
>  
>  	pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
>  
> @@ -1271,6 +1323,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp)
>  	 * allowing it to appear.
>  	 */
>  	wait_backlight_on(intel_dp);
> +
> +	assert_pwm_enabled(intel_dp->attached_connector);
> +
>  	pp = ironlake_get_pp_control(intel_dp);
>  	pp |= EDP_BLC_ENABLE;
>  
> @@ -1292,6 +1347,9 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp)
>  	if (!is_edp(intel_dp))
>  		return;
>  
> +	/* PWM must still be enabled here */
> +	assert_pwm_enabled(intel_dp->attached_connector);
> +
>  	intel_panel_disable_backlight(intel_dp->attached_connector);
>  
>  	DRM_DEBUG_KMS("\n");
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 9002e77..0e91c40 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -861,6 +861,7 @@ void intel_gmch_panel_fitting(struct intel_crtc *crtc,
>  			      int fitting_mode);
>  void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
>  			       u32 max);
> +u32 intel_panel_get_backlight(struct intel_connector *connector);
>  int intel_panel_setup_backlight(struct drm_connector *connector);
>  void intel_panel_enable_backlight(struct intel_connector *connector);
>  void intel_panel_disable_backlight(struct intel_connector *connector);
> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> index cb05840..21c5e6f 100644
> --- a/drivers/gpu/drm/i915/intel_panel.c
> +++ b/drivers/gpu/drm/i915/intel_panel.c
> @@ -384,6 +384,9 @@ static u32 _vlv_get_backlight(struct drm_device *dev, enum pipe pipe)
>  {
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  
> +	if (I915_READ(VLV_BLC_PWM_CTL2(pipe) & BLM_PWM_ENABLE))
> +		return 0;
> +

If our internal state is consistent, I don't think this should be
necessary. And if our internal state isn't consistent, we should fix
that and maybe add internal asserts within intel_panel.c.

>  	return I915_READ(VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK;
>  }
>  
> @@ -395,7 +398,7 @@ static u32 vlv_get_backlight(struct intel_connector *connector)
>  	return _vlv_get_backlight(dev, pipe);
>  }
>  
> -static u32 intel_panel_get_backlight(struct intel_connector *connector)
> +u32 intel_panel_get_backlight(struct intel_connector *connector)
>  {
>  	struct drm_device *dev = connector->base.dev;
>  	struct drm_i915_private *dev_priv = dev->dev_private;
> -- 
> 1.8.4.2
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Jani Nikula April 1, 2014, 9:27 a.m. UTC | #2
On Tue, 01 Apr 2014, Jani Nikula <jani.nikula@linux.intel.com> wrote:
> On Mon, 31 Mar 2014, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
>> To make sure we properly follow the enable/disable sequences.
>>
>> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
>> ---
>>  drivers/gpu/drm/i915/intel_dp.c    | 62 ++++++++++++++++++++++++++++++++++++--
>>  drivers/gpu/drm/i915/intel_drv.h   |  1 +
>>  drivers/gpu/drm/i915/intel_panel.c |  5 ++-
>>  3 files changed, 65 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
>> index bf73771..b6f7087 100644
>> --- a/drivers/gpu/drm/i915/intel_dp.c
>> +++ b/drivers/gpu/drm/i915/intel_dp.c
>> @@ -301,6 +301,20 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp)
>>  		return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
>>  }
>>  
>> +static void assert_pwm(struct intel_connector *connector,
>> +		       bool expected_state)
>> +{
>> +	bool state;
>> +
>> +	state = intel_panel_get_backlight(connector);
>
> If the duty cycle is regarded as a binary on/off, I'd rather add an
> additional "is enabled" call to intel_panel.c. Especially so because the
> duty cycle value returned by intel_panel_get_backlight is meaningless
> without the max value.
>
>> +
>> +	WARN(state != expected_state, "pwm state failure, expected %d, found "
>> +	     "%d\n", expected_state, state);
>> +}
>> +
>> +#define assert_pwm_enabled(c) assert_pwm((c), true)
>> +#define assert_pwm_disabled(c) assert_pwm((c), false)
>> +
>>  static bool edp_have_panel_power(struct intel_dp *intel_dp)
>>  {
>>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> @@ -884,6 +898,8 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
>>  	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
>>  	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
>>  
>> +	assert_pwm_disabled(intel_dp->attached_connector);
>> +
>>  	/*
>>  	 * There are four kinds of DP registers:
>>  	 *
>> @@ -1167,6 +1183,23 @@ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
>>  	}
>>  }
>>  
>> +/*
>> + * For this and the disable sequence below, Google for actual eDP LCD timing
>> + * diagrams or check the eDP spec.  Below is for reference on asserts and
>> + * does not contain Tx values for delays between steps.
>> + *
>> + * For panel on, the sequence should be:
>> + * - LCD power supply on (PP regs or VDD AUX)
>> + *   - eDP should display black at this point
>> + * - HPD (if present) should go high
>> + * - AUX channel becomes available
>> + * - link training begins
>> + * - LED backlight power on
>> + * - LED PWM_EN goes high, duty cycle >min (PWM regs)
>> + * - link training completes
>> + * - LED_EN goes high (PP BLC_EN bit)
>> + */
>> +
>>  void intel_edp_panel_on(struct intel_dp *intel_dp)
>>  {
>>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> @@ -1212,6 +1245,19 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
>>  	}
>>  }
>>  
>> +/*
>> + * For panel off the sequence should be:
>> + * - LED_EN goes low (BLC_EN in our PP regs)
>> + * - LED PWM_EN goes low (PWM duty cycle 0 and PWM enable = 0)
>> + *   - eDP should display black video at this point
>> + * - LED VCCS goes low (power for backlight)
>> + * - DP link goes to idle or off
>> + * - AUX goes down
>> + * - HPD line (if present) drops to low
>> + * - eDP black video stops
>> + * - LCD power supply shuts down (PP regs and VDD AUX)
>> + */
>> +
>>  void intel_edp_panel_off(struct intel_dp *intel_dp)
>>  {
>>  	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> @@ -1231,11 +1277,17 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
>>  
>>  	WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
>>  
>> +	/* By this time the PWM and BLC bits should be off already */
>> +	assert_pwm_disabled(intel_dp->attached_connector);
>> +
>>  	pp = ironlake_get_pp_control(intel_dp);
>> +
>> +	WARN(pp & EDP_BLC_ENABLE,
>> +	     "backlight controller still on at panel off time\n");
>> +
>>  	/* We need to switch off panel power _and_ force vdd, for otherwise some
>>  	 * panels get very unhappy and cease to work. */
>> -	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
>> -		EDP_BLC_ENABLE);
>> +	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD);
>>  
>>  	pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
>>  
>> @@ -1271,6 +1323,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp)
>>  	 * allowing it to appear.
>>  	 */
>>  	wait_backlight_on(intel_dp);
>> +
>> +	assert_pwm_enabled(intel_dp->attached_connector);
>> +
>>  	pp = ironlake_get_pp_control(intel_dp);
>>  	pp |= EDP_BLC_ENABLE;
>>  
>> @@ -1292,6 +1347,9 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp)
>>  	if (!is_edp(intel_dp))
>>  		return;
>>  
>> +	/* PWM must still be enabled here */
>> +	assert_pwm_enabled(intel_dp->attached_connector);
>> +
>>  	intel_panel_disable_backlight(intel_dp->attached_connector);
>>  
>>  	DRM_DEBUG_KMS("\n");
>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> index 9002e77..0e91c40 100644
>> --- a/drivers/gpu/drm/i915/intel_drv.h
>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>> @@ -861,6 +861,7 @@ void intel_gmch_panel_fitting(struct intel_crtc *crtc,
>>  			      int fitting_mode);
>>  void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
>>  			       u32 max);
>> +u32 intel_panel_get_backlight(struct intel_connector *connector);
>>  int intel_panel_setup_backlight(struct drm_connector *connector);
>>  void intel_panel_enable_backlight(struct intel_connector *connector);
>>  void intel_panel_disable_backlight(struct intel_connector *connector);
>> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
>> index cb05840..21c5e6f 100644
>> --- a/drivers/gpu/drm/i915/intel_panel.c
>> +++ b/drivers/gpu/drm/i915/intel_panel.c
>> @@ -384,6 +384,9 @@ static u32 _vlv_get_backlight(struct drm_device *dev, enum pipe pipe)
>>  {
>>  	struct drm_i915_private *dev_priv = dev->dev_private;
>>  
>> +	if (I915_READ(VLV_BLC_PWM_CTL2(pipe) & BLM_PWM_ENABLE))
>> +		return 0;
>> +
>
> If our internal state is consistent, I don't think this should be
> necessary. And if our internal state isn't consistent, we should fix
> that and maybe add internal asserts within intel_panel.c.

I wrote this with the bigger picture in mind, but your check above is
also doubly wrong!

BR,
Jani.


>
>>  	return I915_READ(VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK;
>>  }
>>  
>> @@ -395,7 +398,7 @@ static u32 vlv_get_backlight(struct intel_connector *connector)
>>  	return _vlv_get_backlight(dev, pipe);
>>  }
>>  
>> -static u32 intel_panel_get_backlight(struct intel_connector *connector)
>> +u32 intel_panel_get_backlight(struct intel_connector *connector)
>>  {
>>  	struct drm_device *dev = connector->base.dev;
>>  	struct drm_i915_private *dev_priv = dev->dev_private;
>> -- 
>> 1.8.4.2
>>
>> _______________________________________________
>> Intel-gfx mailing list
>> Intel-gfx@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
> -- 
> Jani Nikula, Intel Open Source Technology Center
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Jesse Barnes April 1, 2014, 4:13 p.m. UTC | #3
On Tue, 01 Apr 2014 10:19:29 +0300
Jani Nikula <jani.nikula@linux.intel.com> wrote:

> On Mon, 31 Mar 2014, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
> > To make sure we properly follow the enable/disable sequences.
> >
> > Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> > ---
> >  drivers/gpu/drm/i915/intel_dp.c    | 62 ++++++++++++++++++++++++++++++++++++--
> >  drivers/gpu/drm/i915/intel_drv.h   |  1 +
> >  drivers/gpu/drm/i915/intel_panel.c |  5 ++-
> >  3 files changed, 65 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> > index bf73771..b6f7087 100644
> > --- a/drivers/gpu/drm/i915/intel_dp.c
> > +++ b/drivers/gpu/drm/i915/intel_dp.c
> > @@ -301,6 +301,20 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp)
> >  		return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
> >  }
> >  
> > +static void assert_pwm(struct intel_connector *connector,
> > +		       bool expected_state)
> > +{
> > +	bool state;
> > +
> > +	state = intel_panel_get_backlight(connector);
> 
> If the duty cycle is regarded as a binary on/off, I'd rather add an
> additional "is enabled" call to intel_panel.c. Especially so because the
> duty cycle value returned by intel_panel_get_backlight is meaningless
> without the max value.

Hm I guess that would be cleaner; for my purposes I thought any
non-zero PWM duty cycle would be sufficient, but of course other checks
are needed as well, like whether the PWM enable bit is on, and checks
against the BLC_EN bit in the PP regs, but those are logically
separate.  is_enabled might better map back to the PWM_EN bit rather
than a non-zero duty cycle though.

> >  
> > +	if (I915_READ(VLV_BLC_PWM_CTL2(pipe) & BLM_PWM_ENABLE))
> > +		return 0;
> > +
> 
> If our internal state is consistent, I don't think this should be
> necessary. And if our internal state isn't consistent, we should fix
> that and maybe add internal asserts within intel_panel.c.

Yeah this could be covered with other asserts as long as we have them
in all the right places.
Jesse Barnes April 1, 2014, 4:14 p.m. UTC | #4
On Tue, 01 Apr 2014 12:27:43 +0300
Jani Nikula <jani.nikula@linux.intel.com> wrote:

> On Tue, 01 Apr 2014, Jani Nikula <jani.nikula@linux.intel.com> wrote:
> > On Mon, 31 Mar 2014, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
> >> To make sure we properly follow the enable/disable sequences.
> >>
> >> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> >> ---
> >>  drivers/gpu/drm/i915/intel_dp.c    | 62 ++++++++++++++++++++++++++++++++++++--
> >>  drivers/gpu/drm/i915/intel_drv.h   |  1 +
> >>  drivers/gpu/drm/i915/intel_panel.c |  5 ++-
> >>  3 files changed, 65 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> >> index bf73771..b6f7087 100644
> >> --- a/drivers/gpu/drm/i915/intel_dp.c
> >> +++ b/drivers/gpu/drm/i915/intel_dp.c
> >> @@ -301,6 +301,20 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp)
> >>  		return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
> >>  }
> >>  
> >> +static void assert_pwm(struct intel_connector *connector,
> >> +		       bool expected_state)
> >> +{
> >> +	bool state;
> >> +
> >> +	state = intel_panel_get_backlight(connector);
> >
> > If the duty cycle is regarded as a binary on/off, I'd rather add an
> > additional "is enabled" call to intel_panel.c. Especially so because the
> > duty cycle value returned by intel_panel_get_backlight is meaningless
> > without the max value.
> >
> >> +
> >> +	WARN(state != expected_state, "pwm state failure, expected %d, found "
> >> +	     "%d\n", expected_state, state);
> >> +}
> >> +
> >> +#define assert_pwm_enabled(c) assert_pwm((c), true)
> >> +#define assert_pwm_disabled(c) assert_pwm((c), false)
> >> +
> >>  static bool edp_have_panel_power(struct intel_dp *intel_dp)
> >>  {
> >>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> >> @@ -884,6 +898,8 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
> >>  	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
> >>  	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
> >>  
> >> +	assert_pwm_disabled(intel_dp->attached_connector);
> >> +
> >>  	/*
> >>  	 * There are four kinds of DP registers:
> >>  	 *
> >> @@ -1167,6 +1183,23 @@ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
> >>  	}
> >>  }
> >>  
> >> +/*
> >> + * For this and the disable sequence below, Google for actual eDP LCD timing
> >> + * diagrams or check the eDP spec.  Below is for reference on asserts and
> >> + * does not contain Tx values for delays between steps.
> >> + *
> >> + * For panel on, the sequence should be:
> >> + * - LCD power supply on (PP regs or VDD AUX)
> >> + *   - eDP should display black at this point
> >> + * - HPD (if present) should go high
> >> + * - AUX channel becomes available
> >> + * - link training begins
> >> + * - LED backlight power on
> >> + * - LED PWM_EN goes high, duty cycle >min (PWM regs)
> >> + * - link training completes
> >> + * - LED_EN goes high (PP BLC_EN bit)
> >> + */
> >> +
> >>  void intel_edp_panel_on(struct intel_dp *intel_dp)
> >>  {
> >>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> >> @@ -1212,6 +1245,19 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
> >>  	}
> >>  }
> >>  
> >> +/*
> >> + * For panel off the sequence should be:
> >> + * - LED_EN goes low (BLC_EN in our PP regs)
> >> + * - LED PWM_EN goes low (PWM duty cycle 0 and PWM enable = 0)
> >> + *   - eDP should display black video at this point
> >> + * - LED VCCS goes low (power for backlight)
> >> + * - DP link goes to idle or off
> >> + * - AUX goes down
> >> + * - HPD line (if present) drops to low
> >> + * - eDP black video stops
> >> + * - LCD power supply shuts down (PP regs and VDD AUX)
> >> + */
> >> +
> >>  void intel_edp_panel_off(struct intel_dp *intel_dp)
> >>  {
> >>  	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> >> @@ -1231,11 +1277,17 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
> >>  
> >>  	WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
> >>  
> >> +	/* By this time the PWM and BLC bits should be off already */
> >> +	assert_pwm_disabled(intel_dp->attached_connector);
> >> +
> >>  	pp = ironlake_get_pp_control(intel_dp);
> >> +
> >> +	WARN(pp & EDP_BLC_ENABLE,
> >> +	     "backlight controller still on at panel off time\n");
> >> +
> >>  	/* We need to switch off panel power _and_ force vdd, for otherwise some
> >>  	 * panels get very unhappy and cease to work. */
> >> -	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
> >> -		EDP_BLC_ENABLE);
> >> +	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD);
> >>  
> >>  	pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
> >>  
> >> @@ -1271,6 +1323,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp)
> >>  	 * allowing it to appear.
> >>  	 */
> >>  	wait_backlight_on(intel_dp);
> >> +
> >> +	assert_pwm_enabled(intel_dp->attached_connector);
> >> +
> >>  	pp = ironlake_get_pp_control(intel_dp);
> >>  	pp |= EDP_BLC_ENABLE;
> >>  
> >> @@ -1292,6 +1347,9 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp)
> >>  	if (!is_edp(intel_dp))
> >>  		return;
> >>  
> >> +	/* PWM must still be enabled here */
> >> +	assert_pwm_enabled(intel_dp->attached_connector);
> >> +
> >>  	intel_panel_disable_backlight(intel_dp->attached_connector);
> >>  
> >>  	DRM_DEBUG_KMS("\n");
> >> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> >> index 9002e77..0e91c40 100644
> >> --- a/drivers/gpu/drm/i915/intel_drv.h
> >> +++ b/drivers/gpu/drm/i915/intel_drv.h
> >> @@ -861,6 +861,7 @@ void intel_gmch_panel_fitting(struct intel_crtc *crtc,
> >>  			      int fitting_mode);
> >>  void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
> >>  			       u32 max);
> >> +u32 intel_panel_get_backlight(struct intel_connector *connector);
> >>  int intel_panel_setup_backlight(struct drm_connector *connector);
> >>  void intel_panel_enable_backlight(struct intel_connector *connector);
> >>  void intel_panel_disable_backlight(struct intel_connector *connector);
> >> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> >> index cb05840..21c5e6f 100644
> >> --- a/drivers/gpu/drm/i915/intel_panel.c
> >> +++ b/drivers/gpu/drm/i915/intel_panel.c
> >> @@ -384,6 +384,9 @@ static u32 _vlv_get_backlight(struct drm_device *dev, enum pipe pipe)
> >>  {
> >>  	struct drm_i915_private *dev_priv = dev->dev_private;
> >>  
> >> +	if (I915_READ(VLV_BLC_PWM_CTL2(pipe) & BLM_PWM_ENABLE))
> >> +		return 0;
> >> +
> >
> > If our internal state is consistent, I don't think this should be
> > necessary. And if our internal state isn't consistent, we should fix
> > that and maybe add internal asserts within intel_panel.c.
> 
> I wrote this with the bigger picture in mind, but your check above is
> also doubly wrong!

Hah it is!  I had it correct in one tree but must have copy & pasted
wrong into this one. :)
Jani Nikula June 25, 2014, 12:45 p.m. UTC | #5
On Tue, 01 Apr 2014, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
> On Tue, 01 Apr 2014 10:19:29 +0300
> Jani Nikula <jani.nikula@linux.intel.com> wrote:
>
>> On Mon, 31 Mar 2014, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
>> > To make sure we properly follow the enable/disable sequences.
>> >
>> > Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
>> > ---
>> >  drivers/gpu/drm/i915/intel_dp.c    | 62 ++++++++++++++++++++++++++++++++++++--
>> >  drivers/gpu/drm/i915/intel_drv.h   |  1 +
>> >  drivers/gpu/drm/i915/intel_panel.c |  5 ++-
>> >  3 files changed, 65 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
>> > index bf73771..b6f7087 100644
>> > --- a/drivers/gpu/drm/i915/intel_dp.c
>> > +++ b/drivers/gpu/drm/i915/intel_dp.c
>> > @@ -301,6 +301,20 @@ static u32 _pp_stat_reg(struct intel_dp *intel_dp)
>> >  		return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
>> >  }
>> >  
>> > +static void assert_pwm(struct intel_connector *connector,
>> > +		       bool expected_state)
>> > +{
>> > +	bool state;
>> > +
>> > +	state = intel_panel_get_backlight(connector);
>> 
>> If the duty cycle is regarded as a binary on/off, I'd rather add an
>> additional "is enabled" call to intel_panel.c. Especially so because the
>> duty cycle value returned by intel_panel_get_backlight is meaningless
>> without the max value.
>
> Hm I guess that would be cleaner; for my purposes I thought any
> non-zero PWM duty cycle would be sufficient, but of course other checks
> are needed as well, like whether the PWM enable bit is on, and checks
> against the BLC_EN bit in the PP regs, but those are logically
> separate.  is_enabled might better map back to the PWM_EN bit rather
> than a non-zero duty cycle though.

We could add intel_panel_backlight_enabled() call that returns
connector->panel.backlight.enabled.

BR,
Jani.






>
>> >  
>> > +	if (I915_READ(VLV_BLC_PWM_CTL2(pipe) & BLM_PWM_ENABLE))
>> > +		return 0;
>> > +
>> 
>> If our internal state is consistent, I don't think this should be
>> necessary. And if our internal state isn't consistent, we should fix
>> that and maybe add internal asserts within intel_panel.c.
>
> Yeah this could be covered with other asserts as long as we have them
> in all the right places.
>
> -- 
> Jesse Barnes, Intel Open Source Technology Center
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index bf73771..b6f7087 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -301,6 +301,20 @@  static u32 _pp_stat_reg(struct intel_dp *intel_dp)
 		return VLV_PIPE_PP_STATUS(vlv_power_sequencer_pipe(intel_dp));
 }
 
+static void assert_pwm(struct intel_connector *connector,
+		       bool expected_state)
+{
+	bool state;
+
+	state = intel_panel_get_backlight(connector);
+
+	WARN(state != expected_state, "pwm state failure, expected %d, found "
+	     "%d\n", expected_state, state);
+}
+
+#define assert_pwm_enabled(c) assert_pwm((c), true)
+#define assert_pwm_disabled(c) assert_pwm((c), false)
+
 static bool edp_have_panel_power(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -884,6 +898,8 @@  static void intel_dp_mode_set(struct intel_encoder *encoder)
 	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
 	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
 
+	assert_pwm_disabled(intel_dp->attached_connector);
+
 	/*
 	 * There are four kinds of DP registers:
 	 *
@@ -1167,6 +1183,23 @@  static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
 	}
 }
 
+/*
+ * For this and the disable sequence below, Google for actual eDP LCD timing
+ * diagrams or check the eDP spec.  Below is for reference on asserts and
+ * does not contain Tx values for delays between steps.
+ *
+ * For panel on, the sequence should be:
+ * - LCD power supply on (PP regs or VDD AUX)
+ *   - eDP should display black at this point
+ * - HPD (if present) should go high
+ * - AUX channel becomes available
+ * - link training begins
+ * - LED backlight power on
+ * - LED PWM_EN goes high, duty cycle >min (PWM regs)
+ * - link training completes
+ * - LED_EN goes high (PP BLC_EN bit)
+ */
+
 void intel_edp_panel_on(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1212,6 +1245,19 @@  void intel_edp_panel_on(struct intel_dp *intel_dp)
 	}
 }
 
+/*
+ * For panel off the sequence should be:
+ * - LED_EN goes low (BLC_EN in our PP regs)
+ * - LED PWM_EN goes low (PWM duty cycle 0 and PWM enable = 0)
+ *   - eDP should display black video at this point
+ * - LED VCCS goes low (power for backlight)
+ * - DP link goes to idle or off
+ * - AUX goes down
+ * - HPD line (if present) drops to low
+ * - eDP black video stops
+ * - LCD power supply shuts down (PP regs and VDD AUX)
+ */
+
 void intel_edp_panel_off(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -1231,11 +1277,17 @@  void intel_edp_panel_off(struct intel_dp *intel_dp)
 
 	WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
 
+	/* By this time the PWM and BLC bits should be off already */
+	assert_pwm_disabled(intel_dp->attached_connector);
+
 	pp = ironlake_get_pp_control(intel_dp);
+
+	WARN(pp & EDP_BLC_ENABLE,
+	     "backlight controller still on at panel off time\n");
+
 	/* We need to switch off panel power _and_ force vdd, for otherwise some
 	 * panels get very unhappy and cease to work. */
-	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
-		EDP_BLC_ENABLE);
+	pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD);
 
 	pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
 
@@ -1271,6 +1323,9 @@  void intel_edp_backlight_on(struct intel_dp *intel_dp)
 	 * allowing it to appear.
 	 */
 	wait_backlight_on(intel_dp);
+
+	assert_pwm_enabled(intel_dp->attached_connector);
+
 	pp = ironlake_get_pp_control(intel_dp);
 	pp |= EDP_BLC_ENABLE;
 
@@ -1292,6 +1347,9 @@  void intel_edp_backlight_off(struct intel_dp *intel_dp)
 	if (!is_edp(intel_dp))
 		return;
 
+	/* PWM must still be enabled here */
+	assert_pwm_enabled(intel_dp->attached_connector);
+
 	intel_panel_disable_backlight(intel_dp->attached_connector);
 
 	DRM_DEBUG_KMS("\n");
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9002e77..0e91c40 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -861,6 +861,7 @@  void intel_gmch_panel_fitting(struct intel_crtc *crtc,
 			      int fitting_mode);
 void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
 			       u32 max);
+u32 intel_panel_get_backlight(struct intel_connector *connector);
 int intel_panel_setup_backlight(struct drm_connector *connector);
 void intel_panel_enable_backlight(struct intel_connector *connector);
 void intel_panel_disable_backlight(struct intel_connector *connector);
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index cb05840..21c5e6f 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -384,6 +384,9 @@  static u32 _vlv_get_backlight(struct drm_device *dev, enum pipe pipe)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	if (I915_READ(VLV_BLC_PWM_CTL2(pipe) & BLM_PWM_ENABLE))
+		return 0;
+
 	return I915_READ(VLV_BLC_PWM_CTL(pipe)) & BACKLIGHT_DUTY_CYCLE_MASK;
 }
 
@@ -395,7 +398,7 @@  static u32 vlv_get_backlight(struct intel_connector *connector)
 	return _vlv_get_backlight(dev, pipe);
 }
 
-static u32 intel_panel_get_backlight(struct intel_connector *connector)
+u32 intel_panel_get_backlight(struct intel_connector *connector)
 {
 	struct drm_device *dev = connector->base.dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;