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;
 
        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;
 
 }
 
 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);
        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;
        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;
 
                }
 
                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;
        }
        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;
        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;
 
                }
 
                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;
 
        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 };
        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))
                                &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);
 
        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);
 
                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);
                        }
 
                        /* 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);
                 * 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;
                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