Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/msm/dp: Introduce link training per-segment for LTTPRs

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

Since at least some LTTPRs (eg. Parade PS8830) do not correctly report
voltage-swing, pre-emphasis level 3 support, always assume level 3 is
supported. This is permitted under DP 2.1(a) section 3.6.7.2 stating
that LTTPR shall set its transmitter levels as close as possible to
those requested by the DPTX, if the DPTX sets the voltage swing or
pre-emphasis to a level that the LTTPR does not support. It shall be
noted that LTTPR’s level choosing is implementation-specific.

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).

Fixes: 72d0af4accd9 ("drm/msm/dp: Add support for LTTPR handling")
Tested-by: Jessica Zhang <quic_jesszhan@quicinc.com> # SA8775P
Tested-by: Johan Hovold <johan+linaro@kernel.org>
Tested-by: Rob Clark <robdclark@gmail.com>
Tested-by: Stefan Schmidt <stefan.schmidt@linaro.org>
Signed-off-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com>
Reviewed-by: Abel Vesa <abel.vesa@linaro.org>
Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Patchwork: https://patchwork.freedesktop.org/patch/652305/
Link: https://lore.kernel.org/r/20250507230113.14270-5-alex.vinarskis@gmail.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

authored by

Aleksandrs Vinarskis and committed by
Dmitry Baryshkov
5a0436e9 7513ccb8

+89 -37
+89 -37
drivers/gpu/drm/msm/dp/dp_ctrl.c
··· 1034 1034 return 0; 1035 1035 } 1036 1036 1037 - static int msm_dp_ctrl_update_vx_px(struct msm_dp_ctrl_private *ctrl) 1037 + static int msm_dp_ctrl_update_phy_vx_px(struct msm_dp_ctrl_private *ctrl, 1038 + enum drm_dp_phy dp_phy) 1038 1039 { 1039 1040 struct msm_dp_link *link = ctrl->link; 1040 - int ret = 0, lane, lane_cnt; 1041 + int lane, lane_cnt, reg; 1042 + int ret = 0; 1041 1043 u8 buf[4]; 1042 1044 u32 max_level_reached = 0; 1043 1045 u32 voltage_swing_level = link->phy_params.v_level; ··· 1077 1075 1078 1076 drm_dbg_dp(ctrl->drm_dev, "sink: p|v=0x%x\n", 1079 1077 voltage_swing_level | pre_emphasis_level); 1080 - ret = drm_dp_dpcd_write(ctrl->aux, DP_TRAINING_LANE0_SET, 1081 - buf, lane_cnt); 1078 + 1079 + if (dp_phy == DP_PHY_DPRX) 1080 + reg = DP_TRAINING_LANE0_SET; 1081 + else 1082 + reg = DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy); 1083 + 1084 + ret = drm_dp_dpcd_write(ctrl->aux, reg, buf, lane_cnt); 1082 1085 if (ret == lane_cnt) 1083 1086 ret = 0; 1084 1087 ··· 1091 1084 } 1092 1085 1093 1086 static bool msm_dp_ctrl_train_pattern_set(struct msm_dp_ctrl_private *ctrl, 1094 - u8 pattern) 1087 + u8 pattern, enum drm_dp_phy dp_phy) 1095 1088 { 1096 1089 u8 buf; 1090 + int reg; 1097 1091 int ret = 0; 1098 1092 1099 1093 drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern); ··· 1104 1096 if (pattern && pattern != DP_TRAINING_PATTERN_4) 1105 1097 buf |= DP_LINK_SCRAMBLING_DISABLE; 1106 1098 1107 - ret = drm_dp_dpcd_writeb(ctrl->aux, DP_TRAINING_PATTERN_SET, buf); 1099 + if (dp_phy == DP_PHY_DPRX) 1100 + reg = DP_TRAINING_PATTERN_SET; 1101 + else 1102 + reg = DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy); 1103 + 1104 + ret = drm_dp_dpcd_writeb(ctrl->aux, reg, buf); 1108 1105 return ret == 1; 1109 1106 } 1110 1107 1111 1108 static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl, 1112 - int *training_step) 1109 + int *training_step, enum drm_dp_phy dp_phy) 1113 1110 { 1111 + int delay_us; 1114 1112 int tries, old_v_level, ret = 0; 1115 1113 u8 link_status[DP_LINK_STATUS_SIZE]; 1116 1114 int const maximum_retries = 4; 1115 + 1116 + delay_us = drm_dp_read_clock_recovery_delay(ctrl->aux, 1117 + ctrl->panel->dpcd, dp_phy, false); 1117 1118 1118 1119 msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); 1119 1120 ··· 1132 1115 if (ret) 1133 1116 return ret; 1134 1117 msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 | 1135 - DP_LINK_SCRAMBLING_DISABLE); 1118 + DP_LINK_SCRAMBLING_DISABLE, dp_phy); 1136 1119 1137 - ret = msm_dp_ctrl_update_vx_px(ctrl); 1120 + msm_dp_link_reset_phy_params_vx_px(ctrl->link); 1121 + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy); 1138 1122 if (ret) 1139 1123 return ret; 1140 1124 1141 1125 tries = 0; 1142 1126 old_v_level = ctrl->link->phy_params.v_level; 1143 1127 for (tries = 0; tries < maximum_retries; tries++) { 1144 - drm_dp_link_train_clock_recovery_delay(ctrl->aux, ctrl->panel->dpcd); 1128 + fsleep(delay_us); 1145 1129 1146 - ret = drm_dp_dpcd_read_link_status(ctrl->aux, link_status); 1130 + ret = drm_dp_dpcd_read_phy_link_status(ctrl->aux, dp_phy, link_status); 1147 1131 if (ret) 1148 1132 return ret; 1149 1133 ··· 1165 1147 } 1166 1148 1167 1149 msm_dp_link_adjust_levels(ctrl->link, link_status); 1168 - ret = msm_dp_ctrl_update_vx_px(ctrl); 1150 + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy); 1169 1151 if (ret) 1170 1152 return ret; 1171 1153 } ··· 1217 1199 return 0; 1218 1200 } 1219 1201 1220 - static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl) 1202 + static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl, 1203 + enum drm_dp_phy dp_phy) 1221 1204 { 1222 - msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE); 1223 - drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd); 1205 + int delay_us; 1206 + 1207 + msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE, dp_phy); 1208 + 1209 + delay_us = drm_dp_read_channel_eq_delay(ctrl->aux, 1210 + ctrl->panel->dpcd, dp_phy, false); 1211 + fsleep(delay_us); 1224 1212 } 1225 1213 1226 1214 static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl, 1227 - int *training_step) 1215 + int *training_step, enum drm_dp_phy dp_phy) 1228 1216 { 1217 + int delay_us; 1229 1218 int tries = 0, ret = 0; 1230 1219 u8 pattern; 1231 1220 u32 state_ctrl_bit; 1232 1221 int const maximum_retries = 5; 1233 1222 u8 link_status[DP_LINK_STATUS_SIZE]; 1223 + 1224 + delay_us = drm_dp_read_channel_eq_delay(ctrl->aux, 1225 + ctrl->panel->dpcd, dp_phy, false); 1234 1226 1235 1227 msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); 1236 1228 ··· 1261 1233 if (ret) 1262 1234 return ret; 1263 1235 1264 - msm_dp_ctrl_train_pattern_set(ctrl, pattern); 1236 + msm_dp_ctrl_train_pattern_set(ctrl, pattern, dp_phy); 1265 1237 1266 1238 for (tries = 0; tries <= maximum_retries; tries++) { 1267 - drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd); 1239 + fsleep(delay_us); 1268 1240 1269 - ret = drm_dp_dpcd_read_link_status(ctrl->aux, link_status); 1241 + ret = drm_dp_dpcd_read_phy_link_status(ctrl->aux, dp_phy, link_status); 1270 1242 if (ret) 1271 1243 return ret; 1272 1244 ··· 1276 1248 } 1277 1249 1278 1250 msm_dp_link_adjust_levels(ctrl->link, link_status); 1279 - ret = msm_dp_ctrl_update_vx_px(ctrl); 1251 + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy); 1280 1252 if (ret) 1281 1253 return ret; 1282 1254 ··· 1285 1257 return -ETIMEDOUT; 1286 1258 } 1287 1259 1260 + static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl, 1261 + int *training_step, enum drm_dp_phy dp_phy) 1262 + { 1263 + int ret; 1264 + 1265 + ret = msm_dp_ctrl_link_train_1(ctrl, training_step, dp_phy); 1266 + if (ret) { 1267 + DRM_ERROR("link training #1 on phy %d failed. ret=%d\n", dp_phy, ret); 1268 + return ret; 1269 + } 1270 + drm_dbg_dp(ctrl->drm_dev, "link training #1 on phy %d successful\n", dp_phy); 1271 + 1272 + ret = msm_dp_ctrl_link_train_2(ctrl, training_step, dp_phy); 1273 + if (ret) { 1274 + DRM_ERROR("link training #2 on phy %d failed. ret=%d\n", dp_phy, ret); 1275 + return ret; 1276 + } 1277 + drm_dbg_dp(ctrl->drm_dev, "link training #2 on phy %d successful\n", dp_phy); 1278 + 1279 + return 0; 1280 + } 1281 + 1288 1282 static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, 1289 1283 int *training_step) 1290 1284 { 1285 + int i; 1291 1286 int ret = 0; 1292 1287 const u8 *dpcd = ctrl->panel->dpcd; 1293 1288 u8 encoding[] = { 0, DP_SET_ANSI_8B10B }; ··· 1322 1271 link_info.num_lanes = ctrl->link->link_params.num_lanes; 1323 1272 link_info.rate = ctrl->link->link_params.rate; 1324 1273 link_info.capabilities = DP_LINK_CAP_ENHANCED_FRAMING; 1325 - 1326 - msm_dp_link_reset_phy_params_vx_px(ctrl->link); 1327 1274 1328 1275 msm_dp_aux_link_configure(ctrl->aux, &link_info); 1329 1276 ··· 1337 1288 &assr, 1); 1338 1289 } 1339 1290 1340 - ret = msm_dp_ctrl_link_train_1(ctrl, training_step); 1291 + for (i = ctrl->link->lttpr_count - 1; i >= 0; i--) { 1292 + enum drm_dp_phy dp_phy = DP_PHY_LTTPR(i); 1293 + 1294 + ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, dp_phy); 1295 + msm_dp_ctrl_clear_training_pattern(ctrl, dp_phy); 1296 + 1297 + if (ret) 1298 + break; 1299 + } 1300 + 1341 1301 if (ret) { 1342 - DRM_ERROR("link training #1 failed. ret=%d\n", ret); 1302 + DRM_ERROR("link training of LTTPR(s) failed. ret=%d\n", ret); 1343 1303 goto end; 1344 1304 } 1345 1305 1346 - /* print success info as this is a result of user initiated action */ 1347 - drm_dbg_dp(ctrl->drm_dev, "link training #1 successful\n"); 1348 - 1349 - ret = msm_dp_ctrl_link_train_2(ctrl, training_step); 1306 + ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, DP_PHY_DPRX); 1350 1307 if (ret) { 1351 - DRM_ERROR("link training #2 failed. ret=%d\n", ret); 1308 + DRM_ERROR("link training on sink failed. ret=%d\n", ret); 1352 1309 goto end; 1353 1310 } 1354 - 1355 - /* print success info as this is a result of user initiated action */ 1356 - drm_dbg_dp(ctrl->drm_dev, "link training #2 successful\n"); 1357 1311 1358 1312 end: 1359 1313 msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); ··· 1674 1622 if (ret) 1675 1623 goto end; 1676 1624 1677 - msm_dp_ctrl_clear_training_pattern(ctrl); 1625 + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); 1678 1626 1679 1627 msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO); 1680 1628 ··· 1698 1646 return false; 1699 1647 } 1700 1648 msm_dp_catalog_ctrl_send_phy_pattern(ctrl->catalog, pattern_requested); 1701 - msm_dp_ctrl_update_vx_px(ctrl); 1649 + msm_dp_ctrl_update_phy_vx_px(ctrl, DP_PHY_DPRX); 1702 1650 msm_dp_link_send_test_response(ctrl->link); 1703 1651 1704 1652 pattern_sent = msm_dp_catalog_ctrl_read_phy_pattern(ctrl->catalog); ··· 1940 1888 } 1941 1889 1942 1890 /* stop link training before start re training */ 1943 - msm_dp_ctrl_clear_training_pattern(ctrl); 1891 + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); 1944 1892 } 1945 1893 1946 1894 rc = msm_dp_ctrl_reinitialize_mainlink(ctrl); ··· 1964 1912 * link training failed 1965 1913 * end txing train pattern here 1966 1914 */ 1967 - msm_dp_ctrl_clear_training_pattern(ctrl); 1915 + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); 1968 1916 1969 1917 msm_dp_ctrl_deinitialize_mainlink(ctrl); 1970 1918 rc = -ECONNRESET; ··· 2035 1983 msm_dp_ctrl_link_retrain(ctrl); 2036 1984 2037 1985 /* stop txing train pattern to end link training */ 2038 - msm_dp_ctrl_clear_training_pattern(ctrl); 1986 + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); 2039 1987 2040 1988 /* 2041 1989 * Set up transfer unit values and set controller state to send