From: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> The panel power sequencer locks into the port once used. We need to keep track wich power sequencers are locked to which ports. Signed-off-by: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> --- drivers/gpu/drm/i915/intel_dp.c | 172 ++++++++++++++++++++++++++++++++------- drivers/gpu/drm/i915/intel_drv.h | 6 ++ 2 files changed, 150 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 07b0320..240bc98 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -292,28 +292,84 @@ static enum pipe vlv_power_sequencer_pipe(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; + struct drm_device *dev = intel_dig_port->base.base.dev; + struct intel_encoder *encoder; + unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); + struct edp_power_seq power_seq; + + if (intel_dp->pipe != INVALID_PIPE) + return intel_dp->pipe; + + /* + * We don't have power sequencer currently. + * Pick one that's not used by other ports. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + struct intel_dp *tmp; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + tmp = enc_to_intel_dp(&encoder->base); + + if (tmp->pipe != INVALID_PIPE) + pipes &= ~(1 << tmp->pipe); + } + + /* + * Didn't find one. This should not happen since there + * are two power sequencers and up two eDP ports. + */ + if (WARN_ON(pipes == 0)) + return PIPE_A; + + intel_dp->pipe = ffs(pipes) - 1; + + DRM_DEBUG_KMS("picked pipe %c power sequencer for port %c\n", + pipe_name(intel_dp->pipe), port_name(intel_dig_port->port)); + + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); + + return intel_dp->pipe; +} + +static void +vlv_initial_power_sequencer_setup(struct intel_dp *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_i915_private *dev_priv = dev->dev_private; + struct edp_power_seq power_seq; enum port port = intel_dig_port->port; enum pipe pipe; - /* modeset should have pipe */ - if (crtc) - return to_intel_crtc(crtc)->pipe; - - /* init time, try to find a pipe with this port selected */ + /* try to find a pipe with this port selected */ for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) & PANEL_PORT_SELECT_MASK; - if (port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B) - return pipe; - if (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C) - return pipe; + if ((port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B) || + (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C)) { + intel_dp->pipe = pipe; + break; + } } - /* shrug */ - return PIPE_A; + /* just let vlv_power_sequencer_pipe() pick one when needed */ + if (intel_dp->pipe == INVALID_PIPE) { + DRM_DEBUG_KMS("no initial power sequencer for port %c\n", + port_name(port)); + return; + } + + DRM_DEBUG_KMS("initial power sequencer for port %c: pipe %c\n", + port_name(port), pipe_name(intel_dp->pipe)); + + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); } static u32 _pp_ctrl_reg(struct intel_dp *intel_dp) @@ -2088,6 +2144,70 @@ static void g4x_pre_enable_dp(struct intel_encoder *encoder) } } +static void vlv_steal_power_sequencer(struct drm_device *dev, + enum pipe pipe) +{ + struct intel_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + struct intel_dp *intel_dp; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + intel_dp = enc_to_intel_dp(&encoder->base); + + if (intel_dp->pipe != pipe) + continue; + + DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n", + pipe_name(pipe), + port_name(dp_to_dig_port(intel_dp)->port)); + + /* make sure vdd is off before we steal it */ + edp_panel_vdd_off(intel_dp, false); + + intel_dp->pipe = INVALID_PIPE; + } +} + +static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct edp_power_seq power_seq; + + if (intel_dp->pipe == crtc->pipe) + return; + + /* + * If another power sequencer was being used on this + * port previosuly make sure to turn off vdd there while + * we still have control of it. + */ + if (intel_dp->pipe != INVALID_PIPE) + edp_panel_vdd_off(intel_dp, false); + + /* + * We may be stealing the power + * sequencer from another port. + */ + vlv_steal_power_sequencer(dev, crtc->pipe); + + /* now it's all ours */ + intel_dp->pipe = crtc->pipe; + + DRM_DEBUG_KMS("initializing pipe %c power sequencer for port %c\n", + pipe_name(intel_dp->pipe), port_name(intel_dig_port->port)); + + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); +} + static void vlv_pre_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2097,7 +2217,6 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); enum dpio_channel port = vlv_dport_to_channel(dport); int pipe = intel_crtc->pipe; - struct edp_power_seq power_seq; u32 val; mutex_lock(&dev_priv->dpio_lock); @@ -2115,12 +2234,8 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); - if (is_edp(intel_dp)) { - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); - } + if (is_edp(intel_dp)) + vlv_init_panel_power_sequencer(intel_dp); intel_enable_dp(encoder); @@ -2163,7 +2278,6 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) struct intel_digital_port *dport = dp_to_dig_port(intel_dp); struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct edp_power_seq power_seq; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); enum dpio_channel ch = vlv_dport_to_channel(dport); @@ -2217,12 +2331,8 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); - if (is_edp(intel_dp)) { - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); - } + if (is_edp(intel_dp)) + vlv_init_panel_power_sequencer(intel_dp); intel_enable_dp(encoder); @@ -4350,6 +4460,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct edp_power_seq power_seq = { 0 }; int type; + intel_dp->pipe = INVALID_PIPE; + /* intel_dp vfuncs */ if (IS_VALLEYVIEW(dev)) intel_dp->get_aux_clock_divider = vlv_get_aux_clock_divider; @@ -4420,8 +4532,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, } if (is_edp(intel_dp)) { - intel_dp_init_panel_power_timestamps(intel_dp); - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + if (IS_VALLEYVIEW(dev)) { + vlv_initial_power_sequencer_setup(intel_dp); + } else { + intel_dp_init_panel_power_timestamps(intel_dp); + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + } } intel_dp_aux_init(intel_dp, intel_connector); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 0ef04ea..984627c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -542,6 +542,12 @@ struct intel_dp { unsigned long last_power_on; unsigned long last_backlight_off; bool use_tps3; + /* + * Pipe whose power sequencer is currently locked into + * this port. Ie. the last pipe that was feeding this + * port. Only relevant on VLV/CHV. + */ + enum pipe pipe; struct intel_connector *attached_connector; uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index); -- 1.8.5.5 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx