forked from Minki/linux
drm/i915: Prevent PPS stealing from a normal DP port on VLV/CHV
VLV apparently gets upset if the PPS for a pipe currently driving an external DP port gets used for VDD stuff on another eDP port. The DP port falls over and fails to retrain when this happens, leaving the user staring at a black screen. Let's fix it by also tracking which pipe is driving which DP/eDP port. We'll track this under intel_dp so that we'll share the protection of the pps_mutex alongside the pps_pipe tracking, since the two things are intimately related. I had plans to reduce the protection of pps_mutex to cover only eDP ports, but with this we can't do that. Well, for for VLV/CHV at least. For other platforms it should still be possible, which would allow AUX communication to occur in parallel for multiple DP ports. v2: Drop stray crap from a comment (Imre) Grab pps_mutex when clearing active_pipe Fix a typo in the commit message v3: Make vlv_active_pipe() static Cc: stable@vger.kernel.org Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Link: http://patchwork.freedesktop.org/patch/msgid/1481738423-29738-1-git-send-email-ville.syrjala@linux.intel.com
This commit is contained in:
parent
bf51997c91
commit
9f2bdb006a
@ -492,14 +492,50 @@ vlv_power_sequencer_kick(struct intel_dp *intel_dp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum pipe vlv_find_free_pps(struct drm_i915_private *dev_priv)
|
||||||
|
{
|
||||||
|
struct intel_encoder *encoder;
|
||||||
|
unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't have power sequencer currently.
|
||||||
|
* Pick one that's not used by other ports.
|
||||||
|
*/
|
||||||
|
for_each_intel_encoder(&dev_priv->drm, encoder) {
|
||||||
|
struct intel_dp *intel_dp;
|
||||||
|
|
||||||
|
if (encoder->type != INTEL_OUTPUT_DP &&
|
||||||
|
encoder->type != INTEL_OUTPUT_EDP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
intel_dp = enc_to_intel_dp(&encoder->base);
|
||||||
|
|
||||||
|
if (encoder->type == INTEL_OUTPUT_EDP) {
|
||||||
|
WARN_ON(intel_dp->active_pipe != INVALID_PIPE &&
|
||||||
|
intel_dp->active_pipe != intel_dp->pps_pipe);
|
||||||
|
|
||||||
|
if (intel_dp->pps_pipe != INVALID_PIPE)
|
||||||
|
pipes &= ~(1 << intel_dp->pps_pipe);
|
||||||
|
} else {
|
||||||
|
WARN_ON(intel_dp->pps_pipe != INVALID_PIPE);
|
||||||
|
|
||||||
|
if (intel_dp->active_pipe != INVALID_PIPE)
|
||||||
|
pipes &= ~(1 << intel_dp->active_pipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipes == 0)
|
||||||
|
return INVALID_PIPE;
|
||||||
|
|
||||||
|
return ffs(pipes) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
static enum pipe
|
static enum pipe
|
||||||
vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
|
vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
|
||||||
{
|
{
|
||||||
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
||||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||||
struct intel_encoder *encoder;
|
|
||||||
unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B);
|
|
||||||
enum pipe pipe;
|
enum pipe pipe;
|
||||||
|
|
||||||
lockdep_assert_held(&dev_priv->pps_mutex);
|
lockdep_assert_held(&dev_priv->pps_mutex);
|
||||||
@ -507,33 +543,20 @@ vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
|
|||||||
/* We should never land here with regular DP ports */
|
/* We should never land here with regular DP ports */
|
||||||
WARN_ON(!is_edp(intel_dp));
|
WARN_ON(!is_edp(intel_dp));
|
||||||
|
|
||||||
|
WARN_ON(intel_dp->active_pipe != INVALID_PIPE &&
|
||||||
|
intel_dp->active_pipe != intel_dp->pps_pipe);
|
||||||
|
|
||||||
if (intel_dp->pps_pipe != INVALID_PIPE)
|
if (intel_dp->pps_pipe != INVALID_PIPE)
|
||||||
return intel_dp->pps_pipe;
|
return intel_dp->pps_pipe;
|
||||||
|
|
||||||
/*
|
pipe = vlv_find_free_pps(dev_priv);
|
||||||
* We don't have power sequencer currently.
|
|
||||||
* Pick one that's not used by other ports.
|
|
||||||
*/
|
|
||||||
for_each_intel_encoder(dev, encoder) {
|
|
||||||
struct intel_dp *tmp;
|
|
||||||
|
|
||||||
if (encoder->type != INTEL_OUTPUT_EDP)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
tmp = enc_to_intel_dp(&encoder->base);
|
|
||||||
|
|
||||||
if (tmp->pps_pipe != INVALID_PIPE)
|
|
||||||
pipes &= ~(1 << tmp->pps_pipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Didn't find one. This should not happen since there
|
* Didn't find one. This should not happen since there
|
||||||
* are two power sequencers and up to two eDP ports.
|
* are two power sequencers and up to two eDP ports.
|
||||||
*/
|
*/
|
||||||
if (WARN_ON(pipes == 0))
|
if (WARN_ON(pipe == INVALID_PIPE))
|
||||||
pipe = PIPE_A;
|
pipe = PIPE_A;
|
||||||
else
|
|
||||||
pipe = ffs(pipes) - 1;
|
|
||||||
|
|
||||||
vlv_steal_power_sequencer(dev, pipe);
|
vlv_steal_power_sequencer(dev, pipe);
|
||||||
intel_dp->pps_pipe = pipe;
|
intel_dp->pps_pipe = pipe;
|
||||||
@ -689,10 +712,17 @@ void intel_power_sequencer_reset(struct drm_i915_private *dev_priv)
|
|||||||
for_each_intel_encoder(dev, encoder) {
|
for_each_intel_encoder(dev, encoder) {
|
||||||
struct intel_dp *intel_dp;
|
struct intel_dp *intel_dp;
|
||||||
|
|
||||||
if (encoder->type != INTEL_OUTPUT_EDP)
|
if (encoder->type != INTEL_OUTPUT_DP &&
|
||||||
|
encoder->type != INTEL_OUTPUT_EDP)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
intel_dp = enc_to_intel_dp(&encoder->base);
|
intel_dp = enc_to_intel_dp(&encoder->base);
|
||||||
|
|
||||||
|
WARN_ON(intel_dp->active_pipe != INVALID_PIPE);
|
||||||
|
|
||||||
|
if (encoder->type != INTEL_OUTPUT_EDP)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (IS_GEN9_LP(dev_priv))
|
if (IS_GEN9_LP(dev_priv))
|
||||||
intel_dp->pps_reset = true;
|
intel_dp->pps_reset = true;
|
||||||
else
|
else
|
||||||
@ -2852,6 +2882,8 @@ static void vlv_detach_power_sequencer(struct intel_dp *intel_dp)
|
|||||||
enum pipe pipe = intel_dp->pps_pipe;
|
enum pipe pipe = intel_dp->pps_pipe;
|
||||||
i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe);
|
i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe);
|
||||||
|
|
||||||
|
WARN_ON(intel_dp->active_pipe != INVALID_PIPE);
|
||||||
|
|
||||||
edp_panel_vdd_off_sync(intel_dp);
|
edp_panel_vdd_off_sync(intel_dp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2886,22 +2918,23 @@ static void vlv_steal_power_sequencer(struct drm_device *dev,
|
|||||||
struct intel_dp *intel_dp;
|
struct intel_dp *intel_dp;
|
||||||
enum port port;
|
enum port port;
|
||||||
|
|
||||||
if (encoder->type != INTEL_OUTPUT_EDP)
|
if (encoder->type != INTEL_OUTPUT_DP &&
|
||||||
|
encoder->type != INTEL_OUTPUT_EDP)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
intel_dp = enc_to_intel_dp(&encoder->base);
|
intel_dp = enc_to_intel_dp(&encoder->base);
|
||||||
port = dp_to_dig_port(intel_dp)->port;
|
port = dp_to_dig_port(intel_dp)->port;
|
||||||
|
|
||||||
|
WARN(intel_dp->active_pipe == pipe,
|
||||||
|
"stealing pipe %c power sequencer from active (e)DP port %c\n",
|
||||||
|
pipe_name(pipe), port_name(port));
|
||||||
|
|
||||||
if (intel_dp->pps_pipe != pipe)
|
if (intel_dp->pps_pipe != pipe)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n",
|
DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n",
|
||||||
pipe_name(pipe), port_name(port));
|
pipe_name(pipe), port_name(port));
|
||||||
|
|
||||||
WARN(encoder->base.crtc,
|
|
||||||
"stealing pipe %c power sequencer from active eDP port %c\n",
|
|
||||||
pipe_name(pipe), port_name(port));
|
|
||||||
|
|
||||||
/* make sure vdd is off before we steal it */
|
/* make sure vdd is off before we steal it */
|
||||||
vlv_detach_power_sequencer(intel_dp);
|
vlv_detach_power_sequencer(intel_dp);
|
||||||
}
|
}
|
||||||
@ -2917,19 +2950,17 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp)
|
|||||||
|
|
||||||
lockdep_assert_held(&dev_priv->pps_mutex);
|
lockdep_assert_held(&dev_priv->pps_mutex);
|
||||||
|
|
||||||
if (!is_edp(intel_dp))
|
WARN_ON(intel_dp->active_pipe != INVALID_PIPE);
|
||||||
return;
|
|
||||||
|
|
||||||
if (intel_dp->pps_pipe == crtc->pipe)
|
if (intel_dp->pps_pipe != INVALID_PIPE &&
|
||||||
return;
|
intel_dp->pps_pipe != crtc->pipe) {
|
||||||
|
/*
|
||||||
/*
|
* If another power sequencer was being used on this
|
||||||
* If another power sequencer was being used on this
|
* port previously make sure to turn off vdd there while
|
||||||
* port previously make sure to turn off vdd there while
|
* we still have control of it.
|
||||||
* we still have control of it.
|
*/
|
||||||
*/
|
|
||||||
if (intel_dp->pps_pipe != INVALID_PIPE)
|
|
||||||
vlv_detach_power_sequencer(intel_dp);
|
vlv_detach_power_sequencer(intel_dp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We may be stealing the power
|
* We may be stealing the power
|
||||||
@ -2937,6 +2968,11 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp)
|
|||||||
*/
|
*/
|
||||||
vlv_steal_power_sequencer(dev, crtc->pipe);
|
vlv_steal_power_sequencer(dev, crtc->pipe);
|
||||||
|
|
||||||
|
intel_dp->active_pipe = crtc->pipe;
|
||||||
|
|
||||||
|
if (!is_edp(intel_dp))
|
||||||
|
return;
|
||||||
|
|
||||||
/* now it's all ours */
|
/* now it's all ours */
|
||||||
intel_dp->pps_pipe = crtc->pipe;
|
intel_dp->pps_pipe = crtc->pipe;
|
||||||
|
|
||||||
@ -3523,6 +3559,12 @@ intel_dp_link_down(struct intel_dp *intel_dp)
|
|||||||
msleep(intel_dp->panel_power_down_delay);
|
msleep(intel_dp->panel_power_down_delay);
|
||||||
|
|
||||||
intel_dp->DP = DP;
|
intel_dp->DP = DP;
|
||||||
|
|
||||||
|
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
|
||||||
|
pps_lock(intel_dp);
|
||||||
|
intel_dp->active_pipe = INVALID_PIPE;
|
||||||
|
pps_unlock(intel_dp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -4791,6 +4833,19 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
|
|||||||
edp_panel_vdd_schedule_off(intel_dp);
|
edp_panel_vdd_schedule_off(intel_dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum pipe vlv_active_pipe(struct intel_dp *intel_dp)
|
||||||
|
{
|
||||||
|
struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
|
||||||
|
|
||||||
|
if ((intel_dp->DP & DP_PORT_EN) == 0)
|
||||||
|
return INVALID_PIPE;
|
||||||
|
|
||||||
|
if (IS_CHERRYVIEW(dev_priv))
|
||||||
|
return DP_PORT_TO_PIPE_CHV(intel_dp->DP);
|
||||||
|
else
|
||||||
|
return PORT_TO_PIPE(intel_dp->DP);
|
||||||
|
}
|
||||||
|
|
||||||
void intel_dp_encoder_reset(struct drm_encoder *encoder)
|
void intel_dp_encoder_reset(struct drm_encoder *encoder)
|
||||||
{
|
{
|
||||||
struct drm_i915_private *dev_priv = to_i915(encoder->dev);
|
struct drm_i915_private *dev_priv = to_i915(encoder->dev);
|
||||||
@ -4803,14 +4858,16 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder)
|
|||||||
if (lspcon->active)
|
if (lspcon->active)
|
||||||
lspcon_resume(lspcon);
|
lspcon_resume(lspcon);
|
||||||
|
|
||||||
if (to_intel_encoder(encoder)->type != INTEL_OUTPUT_EDP)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pps_lock(intel_dp);
|
pps_lock(intel_dp);
|
||||||
|
|
||||||
/* Reinit the power sequencer, in case BIOS did something with it. */
|
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
|
||||||
intel_dp_pps_init(encoder->dev, intel_dp);
|
intel_dp->active_pipe = vlv_active_pipe(intel_dp);
|
||||||
intel_edp_panel_vdd_sanitize(intel_dp);
|
|
||||||
|
if (is_edp(intel_dp)) {
|
||||||
|
/* Reinit the power sequencer, in case BIOS did something with it. */
|
||||||
|
intel_dp_pps_init(encoder->dev, intel_dp);
|
||||||
|
intel_edp_panel_vdd_sanitize(intel_dp);
|
||||||
|
}
|
||||||
|
|
||||||
pps_unlock(intel_dp);
|
pps_unlock(intel_dp);
|
||||||
}
|
}
|
||||||
@ -5637,10 +5694,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
|
|||||||
* If the current pipe isn't valid, try the PPS pipe, and if that
|
* If the current pipe isn't valid, try the PPS pipe, and if that
|
||||||
* fails just assume pipe A.
|
* fails just assume pipe A.
|
||||||
*/
|
*/
|
||||||
if (IS_CHERRYVIEW(dev_priv))
|
pipe = vlv_active_pipe(intel_dp);
|
||||||
pipe = DP_PORT_TO_PIPE_CHV(intel_dp->DP);
|
|
||||||
else
|
|
||||||
pipe = PORT_TO_PIPE(intel_dp->DP);
|
|
||||||
|
|
||||||
if (pipe != PIPE_A && pipe != PIPE_B)
|
if (pipe != PIPE_A && pipe != PIPE_B)
|
||||||
pipe = intel_dp->pps_pipe;
|
pipe = intel_dp->pps_pipe;
|
||||||
@ -5689,6 +5743,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
intel_dp->pps_pipe = INVALID_PIPE;
|
intel_dp->pps_pipe = INVALID_PIPE;
|
||||||
|
intel_dp->active_pipe = INVALID_PIPE;
|
||||||
|
|
||||||
/* intel_dp vfuncs */
|
/* intel_dp vfuncs */
|
||||||
if (INTEL_GEN(dev_priv) >= 9)
|
if (INTEL_GEN(dev_priv) >= 9)
|
||||||
@ -5717,6 +5772,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
|||||||
else
|
else
|
||||||
type = DRM_MODE_CONNECTOR_DisplayPort;
|
type = DRM_MODE_CONNECTOR_DisplayPort;
|
||||||
|
|
||||||
|
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
|
||||||
|
intel_dp->active_pipe = vlv_active_pipe(intel_dp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but
|
* For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but
|
||||||
* for DP the encoder type can be set by the caller to
|
* for DP the encoder type can be set by the caller to
|
||||||
|
@ -942,6 +942,12 @@ struct intel_dp {
|
|||||||
* this port. Only relevant on VLV/CHV.
|
* this port. Only relevant on VLV/CHV.
|
||||||
*/
|
*/
|
||||||
enum pipe pps_pipe;
|
enum pipe pps_pipe;
|
||||||
|
/*
|
||||||
|
* Pipe currently driving the port. Used for preventing
|
||||||
|
* the use of the PPS for any pipe currentrly driving
|
||||||
|
* external DP as that will mess things up on VLV.
|
||||||
|
*/
|
||||||
|
enum pipe active_pipe;
|
||||||
/*
|
/*
|
||||||
* Set if the sequencer may be reset due to a power transition,
|
* Set if the sequencer may be reset due to a power transition,
|
||||||
* requiring a reinitialization. Only relevant on BXT.
|
* requiring a reinitialization. Only relevant on BXT.
|
||||||
|
Loading…
Reference in New Issue
Block a user