On Wed, Apr 16, 2025 at 7:13 PM Aleksandrs Vinarskis <alex.vinars...@gmail.com> wrote: > > DisplayPort requires per-segment link training when LTTPR are switched > to non-transparent mode, starting with LTTPR closest to the source. > Only when each segment is trained individually, source can link train > to sink. > > Implement per-segment link traning when LTTPR(s) are detected, to > support external docking stations. On higher level, changes are: > > * Pass phy being trained down to all required helpers > * Run CR, EQ link training per phy > * Set voltage swing, pre-emphasis levels per phy > > This ensures successful link training both when connected directly to > the monitor (single LTTPR onboard most X1E laptops) and via the docking > station (at least two LTTPRs). > > Tested-by: Stefan Schmidt <stefan.schm...@linaro.org> > Signed-off-by: Aleksandrs Vinarskis <alex.vinars...@gmail.com> > Reviewed-by: Abel Vesa <abel.v...@linaro.org>
Tested-by: Rob Clark <robdcl...@gmail.com> # yoga slim 7x > --- > drivers/gpu/drm/msm/dp/dp_ctrl.c | 126 ++++++++++++++++++++++--------- > 1 file changed, 89 insertions(+), 37 deletions(-) > > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c > b/drivers/gpu/drm/msm/dp/dp_ctrl.c > index 69a26bb5fabd..a50bfafbb4ea 100644 > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c > @@ -1034,10 +1034,12 @@ static int msm_dp_ctrl_set_vx_px(struct > msm_dp_ctrl_private *ctrl, > return 0; > } > > -static int msm_dp_ctrl_update_vx_px(struct msm_dp_ctrl_private *ctrl) > +static int msm_dp_ctrl_update_phy_vx_px(struct msm_dp_ctrl_private *ctrl, > + enum drm_dp_phy dp_phy) > { > struct msm_dp_link *link = ctrl->link; > - int ret = 0, lane, lane_cnt; > + int lane, lane_cnt, reg; > + int ret = 0; > u8 buf[4]; > u32 max_level_reached = 0; > u32 voltage_swing_level = link->phy_params.v_level; > @@ -1075,8 +1077,13 @@ static int msm_dp_ctrl_update_vx_px(struct > msm_dp_ctrl_private *ctrl) > > drm_dbg_dp(ctrl->drm_dev, "sink: p|v=0x%x\n", > voltage_swing_level | pre_emphasis_level); > - ret = drm_dp_dpcd_write(ctrl->aux, DP_TRAINING_LANE0_SET, > - buf, lane_cnt); > + > + if (dp_phy == DP_PHY_DPRX) > + reg = DP_TRAINING_LANE0_SET; > + else > + reg = DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy); > + > + ret = drm_dp_dpcd_write(ctrl->aux, reg, buf, lane_cnt); > if (ret == lane_cnt) > ret = 0; > > @@ -1084,9 +1091,10 @@ static int msm_dp_ctrl_update_vx_px(struct > msm_dp_ctrl_private *ctrl) > } > > static bool msm_dp_ctrl_train_pattern_set(struct msm_dp_ctrl_private *ctrl, > - u8 pattern) > + u8 pattern, enum drm_dp_phy dp_phy) > { > u8 buf; > + int reg; > int ret = 0; > > drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern); > @@ -1096,17 +1104,26 @@ static bool msm_dp_ctrl_train_pattern_set(struct > msm_dp_ctrl_private *ctrl, > if (pattern && pattern != DP_TRAINING_PATTERN_4) > buf |= DP_LINK_SCRAMBLING_DISABLE; > > - ret = drm_dp_dpcd_writeb(ctrl->aux, DP_TRAINING_PATTERN_SET, buf); > + if (dp_phy == DP_PHY_DPRX) > + reg = DP_TRAINING_PATTERN_SET; > + else > + reg = DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy); > + > + ret = drm_dp_dpcd_writeb(ctrl->aux, reg, buf); > return ret == 1; > } > > static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl, > - int *training_step) > + int *training_step, enum drm_dp_phy dp_phy) > { > + int delay_us; > int tries, old_v_level, ret = 0; > u8 link_status[DP_LINK_STATUS_SIZE]; > int const maximum_retries = 4; > > + delay_us = drm_dp_read_clock_recovery_delay(ctrl->aux, > + ctrl->panel->dpcd, > dp_phy, false); > + > msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); > > *training_step = DP_TRAINING_1; > @@ -1115,18 +1132,19 @@ static int msm_dp_ctrl_link_train_1(struct > msm_dp_ctrl_private *ctrl, > if (ret) > return ret; > msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 | > - DP_LINK_SCRAMBLING_DISABLE); > + DP_LINK_SCRAMBLING_DISABLE, dp_phy); > > - ret = msm_dp_ctrl_update_vx_px(ctrl); > + msm_dp_link_reset_phy_params_vx_px(ctrl->link); > + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy); > if (ret) > return ret; > > tries = 0; > old_v_level = ctrl->link->phy_params.v_level; > for (tries = 0; tries < maximum_retries; tries++) { > - drm_dp_link_train_clock_recovery_delay(ctrl->aux, > ctrl->panel->dpcd); > + fsleep(delay_us); > > - ret = drm_dp_dpcd_read_link_status(ctrl->aux, link_status); > + ret = drm_dp_dpcd_read_phy_link_status(ctrl->aux, dp_phy, > link_status); > if (ret) > return ret; > > @@ -1147,7 +1165,7 @@ static int msm_dp_ctrl_link_train_1(struct > msm_dp_ctrl_private *ctrl, > } > > msm_dp_link_adjust_levels(ctrl->link, link_status); > - ret = msm_dp_ctrl_update_vx_px(ctrl); > + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy); > if (ret) > return ret; > } > @@ -1199,21 +1217,31 @@ static int msm_dp_ctrl_link_lane_down_shift(struct > msm_dp_ctrl_private *ctrl) > return 0; > } > > -static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private > *ctrl) > +static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private > *ctrl, > + enum drm_dp_phy dp_phy) > { > - msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE); > - drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd); > + int delay_us; > + > + msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE, > dp_phy); > + > + delay_us = drm_dp_read_channel_eq_delay(ctrl->aux, > + ctrl->panel->dpcd, dp_phy, > false); > + fsleep(delay_us); > } > > static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl, > - int *training_step) > + int *training_step, enum drm_dp_phy dp_phy) > { > + int delay_us; > int tries = 0, ret = 0; > u8 pattern; > u32 state_ctrl_bit; > int const maximum_retries = 5; > u8 link_status[DP_LINK_STATUS_SIZE]; > > + delay_us = drm_dp_read_channel_eq_delay(ctrl->aux, > + ctrl->panel->dpcd, dp_phy, > false); > + > msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); > > *training_step = DP_TRAINING_2; > @@ -1233,12 +1261,12 @@ static int msm_dp_ctrl_link_train_2(struct > msm_dp_ctrl_private *ctrl, > if (ret) > return ret; > > - msm_dp_ctrl_train_pattern_set(ctrl, pattern); > + msm_dp_ctrl_train_pattern_set(ctrl, pattern, dp_phy); > > for (tries = 0; tries <= maximum_retries; tries++) { > - drm_dp_link_train_channel_eq_delay(ctrl->aux, > ctrl->panel->dpcd); > + fsleep(delay_us); > > - ret = drm_dp_dpcd_read_link_status(ctrl->aux, link_status); > + ret = drm_dp_dpcd_read_phy_link_status(ctrl->aux, dp_phy, > link_status); > if (ret) > return ret; > > @@ -1248,7 +1276,7 @@ static int msm_dp_ctrl_link_train_2(struct > msm_dp_ctrl_private *ctrl, > } > > msm_dp_link_adjust_levels(ctrl->link, link_status); > - ret = msm_dp_ctrl_update_vx_px(ctrl); > + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy); > if (ret) > return ret; > > @@ -1257,9 +1285,32 @@ static int msm_dp_ctrl_link_train_2(struct > msm_dp_ctrl_private *ctrl, > return -ETIMEDOUT; > } > > +static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl, > + int *training_step, enum drm_dp_phy > dp_phy) > +{ > + int ret; > + > + ret = msm_dp_ctrl_link_train_1(ctrl, training_step, dp_phy); > + if (ret) { > + DRM_ERROR("link training #1 on phy %d failed. ret=%d\n", > dp_phy, ret); > + return ret; > + } > + drm_dbg_dp(ctrl->drm_dev, "link training #1 on phy %d successful\n", > dp_phy); > + > + ret = msm_dp_ctrl_link_train_2(ctrl, training_step, dp_phy); > + if (ret) { > + DRM_ERROR("link training #2 on phy %d failed. ret=%d\n", > dp_phy, ret); > + return ret; > + } > + drm_dbg_dp(ctrl->drm_dev, "link training #2 on phy %d successful\n", > dp_phy); > + > + return 0; > +} > + > static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, > int *training_step) > { > + int i; > int ret = 0; > const u8 *dpcd = ctrl->panel->dpcd; > u8 encoding[] = { 0, DP_SET_ANSI_8B10B }; > @@ -1272,8 +1323,6 @@ static int msm_dp_ctrl_link_train(struct > msm_dp_ctrl_private *ctrl, > link_info.rate = ctrl->link->link_params.rate; > link_info.capabilities = DP_LINK_CAP_ENHANCED_FRAMING; > > - msm_dp_link_reset_phy_params_vx_px(ctrl->link); > - > msm_dp_aux_link_configure(ctrl->aux, &link_info); > > if (drm_dp_max_downspread(dpcd)) > @@ -1288,24 +1337,27 @@ static int msm_dp_ctrl_link_train(struct > msm_dp_ctrl_private *ctrl, > &assr, 1); > } > > - ret = msm_dp_ctrl_link_train_1(ctrl, training_step); > + for (i = ctrl->link->lttpr_count - 1; i >= 0; i--) { > + enum drm_dp_phy dp_phy = DP_PHY_LTTPR(i); > + > + ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, dp_phy); > + msm_dp_ctrl_clear_training_pattern(ctrl, dp_phy); > + > + if (ret) > + break; > + } > + > if (ret) { > - DRM_ERROR("link training #1 failed. ret=%d\n", ret); > + DRM_ERROR("link training of LTTPR(s) failed. ret=%d\n", ret); > goto end; > } > > - /* print success info as this is a result of user initiated action */ > - drm_dbg_dp(ctrl->drm_dev, "link training #1 successful\n"); > - > - ret = msm_dp_ctrl_link_train_2(ctrl, training_step); > + ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, DP_PHY_DPRX); > if (ret) { > - DRM_ERROR("link training #2 failed. ret=%d\n", ret); > + DRM_ERROR("link training on sink failed. ret=%d\n", ret); > goto end; > } > > - /* print success info as this is a result of user initiated action */ > - drm_dbg_dp(ctrl->drm_dev, "link training #2 successful\n"); > - > end: > msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); > > @@ -1622,7 +1674,7 @@ static int msm_dp_ctrl_link_maintenance(struct > msm_dp_ctrl_private *ctrl) > if (ret) > goto end; > > - msm_dp_ctrl_clear_training_pattern(ctrl); > + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); > > msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, > DP_STATE_CTRL_SEND_VIDEO); > > @@ -1646,7 +1698,7 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct > msm_dp_ctrl_private *ctrl) > return false; > } > msm_dp_catalog_ctrl_send_phy_pattern(ctrl->catalog, > pattern_requested); > - msm_dp_ctrl_update_vx_px(ctrl); > + msm_dp_ctrl_update_phy_vx_px(ctrl, DP_PHY_DPRX); > msm_dp_link_send_test_response(ctrl->link); > > pattern_sent = msm_dp_catalog_ctrl_read_phy_pattern(ctrl->catalog); > @@ -1888,7 +1940,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) > } > > /* stop link training before start re training */ > - msm_dp_ctrl_clear_training_pattern(ctrl); > + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); > } > > rc = msm_dp_ctrl_reinitialize_mainlink(ctrl); > @@ -1912,7 +1964,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) > * link training failed > * end txing train pattern here > */ > - msm_dp_ctrl_clear_training_pattern(ctrl); > + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); > > msm_dp_ctrl_deinitialize_mainlink(ctrl); > rc = -ECONNRESET; > @@ -1983,7 +2035,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl > *msm_dp_ctrl, bool force_link_train > msm_dp_ctrl_link_retrain(ctrl); > > /* stop txing train pattern to end link training */ > - msm_dp_ctrl_clear_training_pattern(ctrl); > + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); > > /* > * Set up transfer unit values and set controller state to send > -- > 2.45.2 >