static int analogix_dp_link_start(struct analogix_dp_device *dp) { u8 buf[4]; int lane, lane_count, pll_tries, retval; lane_count = dp->link_train.lane_count; dp->link_train.lt_state = CLOCK_RECOVERY; dp->link_train.eq_loop = 0; for (lane = 0; lane < lane_count; lane++) dp->link_train.cr_loop[lane] = 0; /* Set link rate and count as you want to establish*/ analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate); analogix_dp_set_lane_count(dp, dp->link_train.lane_count); /* Setup RX configuration */ buf[0] = dp->link_train.link_rate; buf[1] = dp->link_train.lane_count; retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET, 2, buf); if (retval) return retval; /* Set TX pre-emphasis to minimum */ for (lane = 0; lane < lane_count; lane++) analogix_dp_set_lane_lane_pre_emphasis(dp, PRE_EMPHASIS_LEVEL_0, lane); /* Wait for PLL lock */ pll_tries = 0; while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { if (pll_tries == DP_TIMEOUT_LOOP_COUNT) { dev_err(dp->dev, "Wait for PLL lock timed out\n"); return -ETIMEDOUT; } pll_tries++; usleep_range(90, 120); } /* Set training pattern 1 */ analogix_dp_set_training_pattern(dp, TRAINING_PTN1); /* Set RX training pattern */ retval = analogix_dp_write_byte_to_dpcd(dp, DP_TRAINING_PATTERN_SET, DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1); if (retval) return retval; for (lane = 0; lane < lane_count; lane++) buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 | DP_TRAIN_VOLTAGE_SWING_LEVEL_0; retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET, lane_count, buf); return retval; }
static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp) { analogix_dp_set_training_pattern(dp, DP_NONE); drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); }
static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp) { analogix_dp_set_training_pattern(dp, DP_NONE); analogix_dp_write_byte_to_dpcd(dp, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); }
static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) { int lane, lane_count, retval; u8 voltage_swing, pre_emphasis, training_lane; u8 link_status[2], adjust_request[2]; usleep_range(100, 101); lane_count = dp->link_train.lane_count; retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); if (retval < 0) return retval; retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, adjust_request, 2); if (retval < 0) return retval; if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) { /* set training pattern 2 for EQ */ analogix_dp_set_training_pattern(dp, TRAINING_PTN2); retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2); if (retval < 0) return retval; dev_info(dp->dev, "Link Training Clock Recovery success\n"); dp->link_train.lt_state = EQUALIZER_TRAINING; } else { for (lane = 0; lane < lane_count; lane++) { training_lane = analogix_dp_get_lane_link_training( dp, lane); voltage_swing = analogix_dp_get_adjust_request_voltage( adjust_request, lane); pre_emphasis = analogix_dp_get_adjust_request_pre_emphasis( adjust_request, lane); if (DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing && DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis) dp->link_train.cr_loop[lane]++; if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP || voltage_swing == VOLTAGE_LEVEL_3 || pre_emphasis == PRE_EMPHASIS_LEVEL_3) { dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n", dp->link_train.cr_loop[lane], voltage_swing, pre_emphasis); analogix_dp_reduce_link_rate(dp); return -EIO; } } } analogix_dp_get_adjust_training_lane(dp, adjust_request); for (lane = 0; lane < lane_count; lane++) analogix_dp_set_lane_link_training(dp, dp->link_train.training_lane[lane], lane); retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link_train.training_lane, lane_count); if (retval < 0) return retval; return 0; }