Message ID | 1436388361-11130-9-git-send-email-ville.syrjala@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 07/09/2015 02:15 AM, ville.syrjala@linux.intel.com wrote: > From: Ville Syrjälä <ville.syrjala@linux.intel.com> > > Powergate the PHY lanes when they're not needed. For HDMI all four lanes > are needed always, but for DP we can enable only the needed lanes. To > power down the unused lanes we use some power down override bits in the > DISPLAY_PHY_CONTROL register. Without the overrides it appears that the > hardware always powers on all the lanes. When the port is disabled the > power down override is not needed and the lanes will shut off on their > own. That also means the override is critical to actually be able to > access the DPIO registers before the port is actually enabled. > > Additionally the common lanes will power down when not needed. CL1 > remains on as long as anything else is on, CL2 will shut down when > all the lanes in the same channel will shut down. There is one exception > for CL2 that will be dealt in a separate patch for clarity. > > With potentially some lanes powered down, the DP code now has to check > the number of active lanes before accessing PCS/TX registers. All > registers in powered down blocks will reads as 0xffffffff, and soe we > would drown in warnings from vlv_dpio_read() if we allowed the code > to access all those registers. > > Another important detail in the DP code is the "TX latency optimal" > setting. Normally the second TX lane acts as some kind of reset master, > with the other lanes as slaves. But when only a single lane is enabled, > that single lane obviously has to be the master. > > A bit of extra care is needed to reconstruct the initial state of the > DISPLAY_PHY_CONTROL register since it can't be read safely. So instead > read the actual lane status from the DPLL/PHY_STATUS registers and > use that to determine which lanes ought to be powergated initially. > > We also need to switch the PHY power modes to "deep PSR" to avoid > a hard system hang when powering down the single channel PHY. > > Also sprinkle a few debug prints around so that we can monitor the > DISPLAY_PHY_STATUS changes without having to read it and risk > corrupting it. > > v2: Add locking to chv_powergate_phy_lanes() > v3: Actually enable dynamic powerdown in the PHY and deal with the > fallout > > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> > --- > drivers/gpu/drm/i915/i915_reg.h | 8 ++ > drivers/gpu/drm/i915/intel_dp.c | 141 +++++++++++++++++++++----------- > drivers/gpu/drm/i915/intel_drv.h | 4 + > drivers/gpu/drm/i915/intel_hdmi.c | 4 + > drivers/gpu/drm/i915/intel_runtime_pm.c | 123 ++++++++++++++++++++++++++-- > 5 files changed, 221 insertions(+), 59 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index 17cb7e5..bcfcbb62 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -1125,9 +1125,15 @@ enum skl_disp_power_wells { > > #define _CHV_CMN_DW19_CH0 0x814c > #define _CHV_CMN_DW6_CH1 0x8098 > +#define DPIO_DYNPWRDOWNEN_CH1 (1 << 28) /* CL2 DW6 only */ > #define CHV_CMN_USEDCLKCHANNEL (1 << 13) > + > #define CHV_CMN_DW19(ch) _PIPE(ch, _CHV_CMN_DW19_CH0, _CHV_CMN_DW6_CH1) > > +#define CHV_CMN_DW28 0x8170 > +#define DPIO_CL1POWERDOWNEN (1 << 23) > +#define DPIO_DYNPWRDOWNEN_CH0 (1 << 22) > + > #define CHV_CMN_DW30 0x8178 > #define DPIO_LRC_BYPASS (1 << 3) > > @@ -2175,10 +2181,12 @@ enum skl_disp_power_wells { > #define DPIO_PHY_STATUS (VLV_DISPLAY_BASE + 0x6240) > #define DPLL_PORTD_READY_MASK (0xf) > #define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100) > +#define PHY_CH_POWER_DOWN_OVRD_EN(phy, ch) (1 << (2*(phy)+(ch)+27)) > #define PHY_LDO_DELAY_0NS 0x0 > #define PHY_LDO_DELAY_200NS 0x1 > #define PHY_LDO_DELAY_600NS 0x2 > #define PHY_LDO_SEQ_DELAY(delay, phy) ((delay) << (2*(phy)+23)) > +#define PHY_CH_POWER_DOWN_OVRD(mask, phy, ch) ((mask) << (8*(phy)+4*(ch)+11)) > #define PHY_CH_SU_PSR 0x1 > #define PHY_CH_DEEP_PSR 0x7 > #define PHY_CH_POWER_MODE(mode, phy, ch) ((mode) << (6*(phy)+3*(ch)+2)) > diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c > index 40b8430..6058129 100644 > --- a/drivers/gpu/drm/i915/intel_dp.c > +++ b/drivers/gpu/drm/i915/intel_dp.c > @@ -133,6 +133,11 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp); > static void vlv_steal_power_sequencer(struct drm_device *dev, > enum pipe pipe); > > +static unsigned int intel_dp_unused_lane_mask(int lane_count) > +{ > + return ~((1 << lane_count) - 1) & 0xf; > +} > + > static int > intel_dp_max_link_bw(struct intel_dp *intel_dp) > { > @@ -2395,17 +2400,21 @@ static void chv_post_disable_dp(struct intel_encoder *encoder) > val |= CHV_PCS_REQ_SOFTRESET_EN; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); > - val |= CHV_PCS_REQ_SOFTRESET_EN; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); > + val |= CHV_PCS_REQ_SOFTRESET_EN; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); > + } > > val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); > val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); > - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); > + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); > + } > > mutex_unlock(&dev_priv->sb_lock); > } > @@ -2525,7 +2534,6 @@ static void intel_enable_dp(struct intel_encoder *encoder) > struct drm_i915_private *dev_priv = dev->dev_private; > struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); > uint32_t dp_reg = I915_READ(intel_dp->output_reg); > - unsigned int lane_mask = 0x0; > > if (WARN_ON(dp_reg & DP_PORT_EN)) > return; > @@ -2543,9 +2551,15 @@ static void intel_enable_dp(struct intel_encoder *encoder) > > pps_unlock(intel_dp); > > - if (IS_VALLEYVIEW(dev)) > + if (IS_VALLEYVIEW(dev)) { > + unsigned int lane_mask = 0x0; > + > + if (IS_CHERRYVIEW(dev)) > + lane_mask = intel_dp_unused_lane_mask(crtc->config->lane_count); > + > vlv_wait_port_ready(dev_priv, dp_to_dig_port(intel_dp), > lane_mask); > + } > > intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); > intel_dp_start_link_train(intel_dp); > @@ -2772,31 +2786,40 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) > val &= ~DPIO_LANEDESKEW_STRAP_OVRD; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); > - val &= ~DPIO_LANEDESKEW_STRAP_OVRD; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); > + val &= ~DPIO_LANEDESKEW_STRAP_OVRD; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); > + } > > /* Deassert soft data lane reset*/ > val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); > val |= CHV_PCS_REQ_SOFTRESET_EN; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); > - val |= CHV_PCS_REQ_SOFTRESET_EN; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); > + val |= CHV_PCS_REQ_SOFTRESET_EN; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); > + } > > val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); > val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); > - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); > + val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); > + } > > /* Program Tx lane latency optimal setting*/ > - for (i = 0; i < 4; i++) { > + for (i = 0; i < intel_crtc->config->lane_count; i++) { > /* Set the upar bit */ > - data = (i == 1) ? 0x0 : 0x1; > + if (intel_crtc->config->lane_count == 1) > + data = 0x0; > + else > + data = (i == 1) ? 0x0 : 0x1; > vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i), > data << DPIO_UPAR_SHIFT); > } > @@ -2817,9 +2840,11 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) > val |= DPIO_TX2_STAGGER_MASK(0x1f); > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); > - val |= DPIO_TX2_STAGGER_MASK(0x1f); > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); > + val |= DPIO_TX2_STAGGER_MASK(0x1f); > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); > + } > > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW12(ch), > DPIO_LANESTAGGER_STRAP(stagger) | > @@ -2828,12 +2853,14 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) > DPIO_TX1_STAGGER_MULT(6) | > DPIO_TX2_STAGGER_MULT(0)); > > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), > - DPIO_LANESTAGGER_STRAP(stagger) | > - DPIO_LANESTAGGER_STRAP_OVRD | > - DPIO_TX1_STAGGER_MASK(0x1f) | > - DPIO_TX1_STAGGER_MULT(7) | > - DPIO_TX2_STAGGER_MULT(5)); > + if (intel_crtc->config->lane_count > 2) { > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), > + DPIO_LANESTAGGER_STRAP(stagger) | > + DPIO_LANESTAGGER_STRAP_OVRD | > + DPIO_TX1_STAGGER_MASK(0x1f) | > + DPIO_TX1_STAGGER_MULT(7) | > + DPIO_TX2_STAGGER_MULT(5)); > + } > > mutex_unlock(&dev_priv->sb_lock); > > @@ -2849,10 +2876,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) > to_intel_crtc(encoder->base.crtc); > enum dpio_channel ch = vlv_dport_to_channel(dport); > enum pipe pipe = intel_crtc->pipe; > + unsigned int lane_mask = > + intel_dp_unused_lane_mask(intel_crtc->config->lane_count); > u32 val; > > intel_dp_prepare(encoder); > > + chv_phy_powergate_lanes(encoder, true, lane_mask); > + > mutex_lock(&dev_priv->sb_lock); > > /* program left/right clock distribution */ > @@ -2883,13 +2914,15 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) > val |= CHV_PCS_USEDCLKCHANNEL; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); > - val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; > - if (pipe != PIPE_B) > - val &= ~CHV_PCS_USEDCLKCHANNEL; > - else > - val |= CHV_PCS_USEDCLKCHANNEL; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); > + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; > + if (pipe != PIPE_B) > + val &= ~CHV_PCS_USEDCLKCHANNEL; > + else > + val |= CHV_PCS_USEDCLKCHANNEL; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); > + } > > /* > * This a a bit weird since generally CL > @@ -2926,6 +2959,8 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder) > } > > mutex_unlock(&dev_priv->sb_lock); > + > + chv_phy_powergate_lanes(encoder, false, 0x0); > } > > /* > @@ -3261,24 +3296,28 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) > val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); > - val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); > - val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); > - val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); > + val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); > + val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); > + val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); > + } > > val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW9(ch)); > val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); > val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW9(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); > - val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); > - val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); > + val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); > + val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); > + } > > /* Program swing deemph */ > - for (i = 0; i < 4; i++) { > + for (i = 0; i < intel_crtc->config->lane_count; i++) { > val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i)); > val &= ~DPIO_SWING_DEEMPH9P5_MASK; > val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT; > @@ -3286,7 +3325,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) > } > > /* Program swing margin */ > - for (i = 0; i < 4; i++) { > + for (i = 0; i < intel_crtc->config->lane_count; i++) { > val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); > > val &= ~DPIO_SWING_MARGIN000_MASK; > @@ -3309,7 +3348,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) > * For now, for this unique transition scale selection, set bit > * 27 for ch0 and ch1. > */ > - for (i = 0; i < 4; i++) { > + for (i = 0; i < intel_crtc->config->lane_count; i++) { > val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); > if (chv_need_uniq_trans_scale(train_set)) > val |= DPIO_TX_UNIQ_TRANS_SCALE_EN; > @@ -3323,9 +3362,11 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) > val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; > vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); > > - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); > - val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; > - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); > + if (intel_crtc->config->lane_count > 2) { > + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); > + val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; > + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); > + } > > /* LRC Bypass */ > val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index 23d5e46..f8a16dc 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -1370,6 +1370,10 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv); > > void intel_display_set_init_power(struct drm_i915_private *dev, bool enable); > > +void chv_phy_powergate_lanes(struct intel_encoder *encoder, > + bool override, unsigned int mask); > + > + > /* intel_pm.c */ > void intel_init_clock_gating(struct drm_device *dev); > void intel_suspend_hw(struct drm_device *dev); > diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c > index 86b1a2c..b3f6c9f 100644 > --- a/drivers/gpu/drm/i915/intel_hdmi.c > +++ b/drivers/gpu/drm/i915/intel_hdmi.c > @@ -1625,6 +1625,8 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) > > intel_hdmi_prepare(encoder); > > + chv_phy_powergate_lanes(encoder, true, 0x0); > + > mutex_lock(&dev_priv->sb_lock); > > /* program left/right clock distribution */ > @@ -1698,6 +1700,8 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder) > } > > mutex_unlock(&dev_priv->sb_lock); > + > + chv_phy_powergate_lanes(encoder, false, 0x0); > } > > static void vlv_hdmi_post_disable(struct intel_encoder *encoder) > diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c > index dab1da9..506a8cc 100644 > --- a/drivers/gpu/drm/i915/intel_runtime_pm.c > +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c > @@ -946,14 +946,19 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, > struct i915_power_well *power_well) > { > enum dpio_phy phy; > + enum pipe pipe; > + uint32_t tmp; > > WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && > power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); > > - if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) > + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { > + pipe = PIPE_A; > phy = DPIO_PHY0; > - else > + } else { > + pipe = PIPE_C; > phy = DPIO_PHY1; > + } > > /* since ref/cri clock was enabled */ > udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ > @@ -963,8 +968,26 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, > if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1)) > DRM_ERROR("Display PHY %d is not power up\n", phy); > > + mutex_lock(&dev_priv->sb_lock); > + > + /* Enable dynamic power down */ > + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); > + tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN; > + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); > + > + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { > + tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); > + tmp |= DPIO_DYNPWRDOWNEN_CH1; > + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); > + } > + > + mutex_unlock(&dev_priv->sb_lock); > + > dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy); > I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > + > + DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", > + phy, dev_priv->chv_phy_control); > } > > static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, > @@ -988,6 +1011,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, > I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > > vlv_set_power_well(dev_priv, power_well, false); > + > + DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", > + phy, dev_priv->chv_phy_control); > +} > + > +void chv_phy_powergate_lanes(struct intel_encoder *encoder, > + bool override, unsigned int mask) > +{ > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + struct i915_power_domains *power_domains = &dev_priv->power_domains; > + enum dpio_phy phy = vlv_dport_to_phy(enc_to_dig_port(&encoder->base)); > + enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); > + > + mutex_lock(&power_domains->lock); > + > + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); > + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); > + > + if (override) > + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); > + else > + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); > + > + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > + > + DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", > + phy, ch, mask, dev_priv->chv_phy_control); > + > + mutex_unlock(&power_domains->lock); > } > > static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, > @@ -1620,19 +1672,72 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv) > * DISPLAY_PHY_CONTROL can get corrupted if read. As a > * workaround never ever read DISPLAY_PHY_CONTROL, and > * instead maintain a shadow copy ourselves. Use the actual > - * power well state to reconstruct the expected initial > - * value. > + * power well state and lane status to reconstruct the > + * expected initial value. > */ > dev_priv->chv_phy_control = > PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY0) | > PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY1) | > - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH0) | > - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH1) | > - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY1, DPIO_CH0); > - if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) > + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH0) | > + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH1) | > + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY1, DPIO_CH0); > + > + /* > + * If all lanes are disabled we leave the override disabled > + * with all power down bits cleared to match the state we > + * would use after disabling the port. Otherwise enable the > + * override and set the lane powerdown bits accding to the > + * current lane status. > + */ > + if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { > + uint32_t status = I915_READ(DPLL(PIPE_A)); > + unsigned int mask; > + > + mask = status & DPLL_PORTB_READY_MASK; > + if (mask == 0xf) > + mask = 0x0; > + else > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0); > + > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH0); > + > + mask = (status & DPLL_PORTC_READY_MASK) >> 4; > + if (mask == 0xf) > + mask = 0x0; > + else > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1); > + > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH1); > + > dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY0); > - if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) > + } > + > + if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { > + uint32_t status = I915_READ(DPIO_PHY_STATUS); > + unsigned int mask; > + > + mask = status & DPLL_PORTD_READY_MASK; > + > + if (mask == 0xf) > + mask = 0x0; > + else > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0); > + > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY1, DPIO_CH0); > + > dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY1); > + } > + > + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > + > + DRM_DEBUG_KMS("Initial PHY_CONTROL=0x%08x\n", > + dev_priv->chv_phy_control); > } > > static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) Looks fine i think we have covered all the scenarios. Reviewed-by: Deepak S <deepak.s@linux.intel.com>
On Wed, Jul 08, 2015 at 11:45:54PM +0300, ville.syrjala@linux.intel.com wrote: > diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c > index dab1da9..506a8cc 100644 > --- a/drivers/gpu/drm/i915/intel_runtime_pm.c > +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c > @@ -946,14 +946,19 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, > struct i915_power_well *power_well) > { > enum dpio_phy phy; > + enum pipe pipe; > + uint32_t tmp; > > WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && > power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); > > - if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) > + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { > + pipe = PIPE_A; > phy = DPIO_PHY0; > - else > + } else { > + pipe = PIPE_C; > phy = DPIO_PHY1; > + } > > /* since ref/cri clock was enabled */ > udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ I have some minor context conflicts in this hunk and the next one and it looks like I'm missing a patch. Since I'm not sure what's going on here please either rebase or tell me which series I'm missing. Merged the first 7 patches from this series meanwhile, thanks. -Daniel > @@ -963,8 +968,26 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, > if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1)) > DRM_ERROR("Display PHY %d is not power up\n", phy); > > + mutex_lock(&dev_priv->sb_lock); > + > + /* Enable dynamic power down */ > + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); > + tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN; > + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); > + > + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { > + tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); > + tmp |= DPIO_DYNPWRDOWNEN_CH1; > + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); > + } > + > + mutex_unlock(&dev_priv->sb_lock); > + > dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy); > I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > + > + DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", > + phy, dev_priv->chv_phy_control); > } > > static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, > @@ -988,6 +1011,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, > I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > > vlv_set_power_well(dev_priv, power_well, false); > + > + DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", > + phy, dev_priv->chv_phy_control); > +} > + > +void chv_phy_powergate_lanes(struct intel_encoder *encoder, > + bool override, unsigned int mask) > +{ > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + struct i915_power_domains *power_domains = &dev_priv->power_domains; > + enum dpio_phy phy = vlv_dport_to_phy(enc_to_dig_port(&encoder->base)); > + enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); > + > + mutex_lock(&power_domains->lock); > + > + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); > + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); > + > + if (override) > + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); > + else > + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); > + > + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > + > + DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", > + phy, ch, mask, dev_priv->chv_phy_control); > + > + mutex_unlock(&power_domains->lock); > } > > static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, > @@ -1620,19 +1672,72 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv) > * DISPLAY_PHY_CONTROL can get corrupted if read. As a > * workaround never ever read DISPLAY_PHY_CONTROL, and > * instead maintain a shadow copy ourselves. Use the actual > - * power well state to reconstruct the expected initial > - * value. > + * power well state and lane status to reconstruct the > + * expected initial value. > */ > dev_priv->chv_phy_control = > PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY0) | > PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY1) | > - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH0) | > - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH1) | > - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY1, DPIO_CH0); > - if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) > + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH0) | > + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH1) | > + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY1, DPIO_CH0); > + > + /* > + * If all lanes are disabled we leave the override disabled > + * with all power down bits cleared to match the state we > + * would use after disabling the port. Otherwise enable the > + * override and set the lane powerdown bits accding to the > + * current lane status. > + */ > + if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { > + uint32_t status = I915_READ(DPLL(PIPE_A)); > + unsigned int mask; > + > + mask = status & DPLL_PORTB_READY_MASK; > + if (mask == 0xf) > + mask = 0x0; > + else > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0); > + > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH0); > + > + mask = (status & DPLL_PORTC_READY_MASK) >> 4; > + if (mask == 0xf) > + mask = 0x0; > + else > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1); > + > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH1); > + > dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY0); > - if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) > + } > + > + if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { > + uint32_t status = I915_READ(DPIO_PHY_STATUS); > + unsigned int mask; > + > + mask = status & DPLL_PORTD_READY_MASK; > + > + if (mask == 0xf) > + mask = 0x0; > + else > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0); > + > + dev_priv->chv_phy_control |= > + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY1, DPIO_CH0); > + > dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY1); > + } > + > + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); > + > + DRM_DEBUG_KMS("Initial PHY_CONTROL=0x%08x\n", > + dev_priv->chv_phy_control); > } > > static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) > -- > 2.3.6 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 17cb7e5..bcfcbb62 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1125,9 +1125,15 @@ enum skl_disp_power_wells { #define _CHV_CMN_DW19_CH0 0x814c #define _CHV_CMN_DW6_CH1 0x8098 +#define DPIO_DYNPWRDOWNEN_CH1 (1 << 28) /* CL2 DW6 only */ #define CHV_CMN_USEDCLKCHANNEL (1 << 13) + #define CHV_CMN_DW19(ch) _PIPE(ch, _CHV_CMN_DW19_CH0, _CHV_CMN_DW6_CH1) +#define CHV_CMN_DW28 0x8170 +#define DPIO_CL1POWERDOWNEN (1 << 23) +#define DPIO_DYNPWRDOWNEN_CH0 (1 << 22) + #define CHV_CMN_DW30 0x8178 #define DPIO_LRC_BYPASS (1 << 3) @@ -2175,10 +2181,12 @@ enum skl_disp_power_wells { #define DPIO_PHY_STATUS (VLV_DISPLAY_BASE + 0x6240) #define DPLL_PORTD_READY_MASK (0xf) #define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100) +#define PHY_CH_POWER_DOWN_OVRD_EN(phy, ch) (1 << (2*(phy)+(ch)+27)) #define PHY_LDO_DELAY_0NS 0x0 #define PHY_LDO_DELAY_200NS 0x1 #define PHY_LDO_DELAY_600NS 0x2 #define PHY_LDO_SEQ_DELAY(delay, phy) ((delay) << (2*(phy)+23)) +#define PHY_CH_POWER_DOWN_OVRD(mask, phy, ch) ((mask) << (8*(phy)+4*(ch)+11)) #define PHY_CH_SU_PSR 0x1 #define PHY_CH_DEEP_PSR 0x7 #define PHY_CH_POWER_MODE(mode, phy, ch) ((mode) << (6*(phy)+3*(ch)+2)) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 40b8430..6058129 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -133,6 +133,11 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp); static void vlv_steal_power_sequencer(struct drm_device *dev, enum pipe pipe); +static unsigned int intel_dp_unused_lane_mask(int lane_count) +{ + return ~((1 << lane_count) - 1) & 0xf; +} + static int intel_dp_max_link_bw(struct intel_dp *intel_dp) { @@ -2395,17 +2400,21 @@ static void chv_post_disable_dp(struct intel_encoder *encoder) val |= CHV_PCS_REQ_SOFTRESET_EN; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + } val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + } mutex_unlock(&dev_priv->sb_lock); } @@ -2525,7 +2534,6 @@ static void intel_enable_dp(struct intel_encoder *encoder) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); uint32_t dp_reg = I915_READ(intel_dp->output_reg); - unsigned int lane_mask = 0x0; if (WARN_ON(dp_reg & DP_PORT_EN)) return; @@ -2543,9 +2551,15 @@ static void intel_enable_dp(struct intel_encoder *encoder) pps_unlock(intel_dp); - if (IS_VALLEYVIEW(dev)) + if (IS_VALLEYVIEW(dev)) { + unsigned int lane_mask = 0x0; + + if (IS_CHERRYVIEW(dev)) + lane_mask = intel_dp_unused_lane_mask(crtc->config->lane_count); + vlv_wait_port_ready(dev_priv, dp_to_dig_port(intel_dp), lane_mask); + } intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); @@ -2772,31 +2786,40 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) val &= ~DPIO_LANEDESKEW_STRAP_OVRD; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); - val &= ~DPIO_LANEDESKEW_STRAP_OVRD; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val &= ~DPIO_LANEDESKEW_STRAP_OVRD; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + } /* Deassert soft data lane reset*/ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); val |= CHV_PCS_REQ_SOFTRESET_EN; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + } val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + } /* Program Tx lane latency optimal setting*/ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { /* Set the upar bit */ - data = (i == 1) ? 0x0 : 0x1; + if (intel_crtc->config->lane_count == 1) + data = 0x0; + else + data = (i == 1) ? 0x0 : 0x1; vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i), data << DPIO_UPAR_SHIFT); } @@ -2817,9 +2840,11 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) val |= DPIO_TX2_STAGGER_MASK(0x1f); vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); - val |= DPIO_TX2_STAGGER_MASK(0x1f); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val |= DPIO_TX2_STAGGER_MASK(0x1f); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + } vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW12(ch), DPIO_LANESTAGGER_STRAP(stagger) | @@ -2828,12 +2853,14 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) DPIO_TX1_STAGGER_MULT(6) | DPIO_TX2_STAGGER_MULT(0)); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), - DPIO_LANESTAGGER_STRAP(stagger) | - DPIO_LANESTAGGER_STRAP_OVRD | - DPIO_TX1_STAGGER_MASK(0x1f) | - DPIO_TX1_STAGGER_MULT(7) | - DPIO_TX2_STAGGER_MULT(5)); + if (intel_crtc->config->lane_count > 2) { + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), + DPIO_LANESTAGGER_STRAP(stagger) | + DPIO_LANESTAGGER_STRAP_OVRD | + DPIO_TX1_STAGGER_MASK(0x1f) | + DPIO_TX1_STAGGER_MULT(7) | + DPIO_TX2_STAGGER_MULT(5)); + } mutex_unlock(&dev_priv->sb_lock); @@ -2849,10 +2876,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) to_intel_crtc(encoder->base.crtc); enum dpio_channel ch = vlv_dport_to_channel(dport); enum pipe pipe = intel_crtc->pipe; + unsigned int lane_mask = + intel_dp_unused_lane_mask(intel_crtc->config->lane_count); u32 val; intel_dp_prepare(encoder); + chv_phy_powergate_lanes(encoder, true, lane_mask); + mutex_lock(&dev_priv->sb_lock); /* program left/right clock distribution */ @@ -2883,13 +2914,15 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) val |= CHV_PCS_USEDCLKCHANNEL; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); - val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; - if (pipe != PIPE_B) - val &= ~CHV_PCS_USEDCLKCHANNEL; - else - val |= CHV_PCS_USEDCLKCHANNEL; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; + if (pipe != PIPE_B) + val &= ~CHV_PCS_USEDCLKCHANNEL; + else + val |= CHV_PCS_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + } /* * This a a bit weird since generally CL @@ -2926,6 +2959,8 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder) } mutex_unlock(&dev_priv->sb_lock); + + chv_phy_powergate_lanes(encoder, false, 0x0); } /* @@ -3261,24 +3296,28 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); - val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); - val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); - val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); + val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); + val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); + val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + } val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW9(ch)); val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW9(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); - val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); - val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); + val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); + val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); + } /* Program swing deemph */ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i)); val &= ~DPIO_SWING_DEEMPH9P5_MASK; val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT; @@ -3286,7 +3325,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) } /* Program swing margin */ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); val &= ~DPIO_SWING_MARGIN000_MASK; @@ -3309,7 +3348,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) * For now, for this unique transition scale selection, set bit * 27 for ch0 and ch1. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); if (chv_need_uniq_trans_scale(train_set)) val |= DPIO_TX_UNIQ_TRANS_SCALE_EN; @@ -3323,9 +3362,11 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); - val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); + val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + } /* LRC Bypass */ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 23d5e46..f8a16dc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1370,6 +1370,10 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv); void intel_display_set_init_power(struct drm_i915_private *dev, bool enable); +void chv_phy_powergate_lanes(struct intel_encoder *encoder, + bool override, unsigned int mask); + + /* intel_pm.c */ void intel_init_clock_gating(struct drm_device *dev); void intel_suspend_hw(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 86b1a2c..b3f6c9f 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1625,6 +1625,8 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) intel_hdmi_prepare(encoder); + chv_phy_powergate_lanes(encoder, true, 0x0); + mutex_lock(&dev_priv->sb_lock); /* program left/right clock distribution */ @@ -1698,6 +1700,8 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder) } mutex_unlock(&dev_priv->sb_lock); + + chv_phy_powergate_lanes(encoder, false, 0x0); } static void vlv_hdmi_post_disable(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index dab1da9..506a8cc 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -946,14 +946,19 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { enum dpio_phy phy; + enum pipe pipe; + uint32_t tmp; WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); - if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + pipe = PIPE_A; phy = DPIO_PHY0; - else + } else { + pipe = PIPE_C; phy = DPIO_PHY1; + } /* since ref/cri clock was enabled */ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ @@ -963,8 +968,26 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1)) DRM_ERROR("Display PHY %d is not power up\n", phy); + mutex_lock(&dev_priv->sb_lock); + + /* Enable dynamic power down */ + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); + tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); + + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); + tmp |= DPIO_DYNPWRDOWNEN_CH1; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); + } + + mutex_unlock(&dev_priv->sb_lock); + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy); I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", + phy, dev_priv->chv_phy_control); } static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, @@ -988,6 +1011,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); vlv_set_power_well(dev_priv, power_well, false); + + DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", + phy, dev_priv->chv_phy_control); +} + +void chv_phy_powergate_lanes(struct intel_encoder *encoder, + bool override, unsigned int mask) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct i915_power_domains *power_domains = &dev_priv->power_domains; + enum dpio_phy phy = vlv_dport_to_phy(enc_to_dig_port(&encoder->base)); + enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); + + mutex_lock(&power_domains->lock); + + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); + + if (override) + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + else + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", + phy, ch, mask, dev_priv->chv_phy_control); + + mutex_unlock(&power_domains->lock); } static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, @@ -1620,19 +1672,72 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv) * DISPLAY_PHY_CONTROL can get corrupted if read. As a * workaround never ever read DISPLAY_PHY_CONTROL, and * instead maintain a shadow copy ourselves. Use the actual - * power well state to reconstruct the expected initial - * value. + * power well state and lane status to reconstruct the + * expected initial value. */ dev_priv->chv_phy_control = PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY0) | PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY1) | - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH0) | - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH1) | - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY1, DPIO_CH0); - if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH0) | + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH1) | + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY1, DPIO_CH0); + + /* + * If all lanes are disabled we leave the override disabled + * with all power down bits cleared to match the state we + * would use after disabling the port. Otherwise enable the + * override and set the lane powerdown bits accding to the + * current lane status. + */ + if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { + uint32_t status = I915_READ(DPLL(PIPE_A)); + unsigned int mask; + + mask = status & DPLL_PORTB_READY_MASK; + if (mask == 0xf) + mask = 0x0; + else + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0); + + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH0); + + mask = (status & DPLL_PORTC_READY_MASK) >> 4; + if (mask == 0xf) + mask = 0x0; + else + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1); + + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH1); + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY0); - if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) + } + + if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { + uint32_t status = I915_READ(DPIO_PHY_STATUS); + unsigned int mask; + + mask = status & DPLL_PORTD_READY_MASK; + + if (mask == 0xf) + mask = 0x0; + else + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0); + + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY1, DPIO_CH0); + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY1); + } + + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Initial PHY_CONTROL=0x%08x\n", + dev_priv->chv_phy_control); } static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv)