drm/i915: Sanity check PPS HW state

The wait for panel status helper will only function correctly if the
HW panel timings are programmed correctly. Returning prematurely from
this helper may lead to obscure bugs later, so sanity check the HW
timing registers.

v2:
- Check the T8, T9 fields too, we do program them (Ville)

CC: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1466096506-11937-1-git-send-email-imre.deak@intel.com
This commit is contained in:
Imre Deak 2016-06-16 20:01:46 +03:00
parent 546486184c
commit de9c1b6b08

View file

@ -1772,6 +1772,9 @@ static void intel_dp_prepare(struct intel_encoder *encoder)
#define IDLE_CYCLE_MASK (PP_ON | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK)
#define IDLE_CYCLE_VALUE (0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
static void intel_pps_verify_state(struct drm_i915_private *dev_priv,
struct intel_dp *intel_dp);
static void wait_panel_status(struct intel_dp *intel_dp,
u32 mask,
u32 value)
@ -1782,6 +1785,8 @@ static void wait_panel_status(struct intel_dp *intel_dp,
lockdep_assert_held(&dev_priv->pps_mutex);
intel_pps_verify_state(dev_priv, intel_dp);
pp_stat_reg = _pp_stat_reg(intel_dp);
pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
@ -4817,6 +4822,31 @@ intel_pps_readout_hw_state(struct drm_i915_private *dev_priv,
}
}
static void
intel_pps_dump_state(const char *state_name, const struct edp_power_seq *seq)
{
DRM_DEBUG_KMS("%s t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
state_name,
seq->t1_t3, seq->t8, seq->t9, seq->t10, seq->t11_t12);
}
static void
intel_pps_verify_state(struct drm_i915_private *dev_priv,
struct intel_dp *intel_dp)
{
struct edp_power_seq hw;
struct edp_power_seq *sw = &intel_dp->pps_delays;
intel_pps_readout_hw_state(dev_priv, intel_dp, &hw);
if (hw.t1_t3 != sw->t1_t3 || hw.t8 != sw->t8 || hw.t9 != sw->t9 ||
hw.t10 != sw->t10 || hw.t11_t12 != sw->t11_t12) {
DRM_ERROR("PPS state mismatch\n");
intel_pps_dump_state("sw", sw);
intel_pps_dump_state("hw", &hw);
}
}
static void
intel_dp_init_panel_power_sequencer(struct drm_device *dev,
struct intel_dp *intel_dp)
@ -4833,8 +4863,7 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
intel_pps_readout_hw_state(dev_priv, intel_dp, &cur);
DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12);
intel_pps_dump_state("cur", &cur);
vbt = dev_priv->vbt.edp.pps;
@ -4850,8 +4879,7 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
* too. */
spec.t11_t12 = (510 + 100) * 10;
DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12);
intel_pps_dump_state("vbt", &vbt);
/* Use the max of the register settings and vbt. If both are
* unset, fall back to the spec limits. */
@ -4879,6 +4907,16 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
/*
* We override the HW backlight delays to 1 because we do manual waits
* on them. For T8, even BSpec recommends doing it. For T9, if we
* don't do this, we'll end up waiting for the backlight off delay
* twice: once when we do the manual sleep, and once when we disable
* the panel and wait for the PP_STATUS bit to become zero.
*/
final->t8 = 1;
final->t9 = 1;
}
static void
@ -4896,17 +4934,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
intel_pps_get_registers(dev_priv, intel_dp, &regs);
/*
* And finally store the new values in the power sequencer. The
* backlight delays are set to 1 because we do manual waits on them. For
* T8, even BSpec recommends doing it. For T9, if we don't do this,
* we'll end up waiting for the backlight off delay twice: once when we
* do the manual sleep, and once when we disable the panel and wait for
* the PP_STATUS bit to become zero.
*/
pp_on = (seq->t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) |
(1 << PANEL_LIGHT_ON_DELAY_SHIFT);
pp_off = (1 << PANEL_LIGHT_OFF_DELAY_SHIFT) |
(seq->t8 << PANEL_LIGHT_ON_DELAY_SHIFT);
pp_off = (seq->t9 << PANEL_LIGHT_OFF_DELAY_SHIFT) |
(seq->t10 << PANEL_POWER_DOWN_DELAY_SHIFT);
/* Compute the divisor for the pp clock, simply match the Bspec
* formula. */