/** * xilinx_drm_dp_link_train_cr - Train clock recovery * @dp: DisplayPort IP core structure * * Return: 0 if clock recovery train is done successfully, or corresponding * error code. */ static int xilinx_drm_dp_link_train_cr(struct xilinx_drm_dp *dp) { u8 link_status[DP_LINK_STATUS_SIZE]; u8 lane_cnt = dp->mode.lane_cnt; u8 vs = 0, tries = 0; u16 max_tries, i; bool cr_done; int ret; ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE); if (ret < 0) return ret; xilinx_drm_writel(dp->iomem, XILINX_DP_TX_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_1); /* 256 loops should be maximum iterations for 4 lanes and 4 values. * So, This loop should exit before 512 iterations */ for (max_tries = 0; max_tries < 512; max_tries++) { ret = xilinx_drm_dp_update_vs_emph(dp); if (ret) return ret; drm_dp_link_train_clock_recovery_delay(dp->dpcd); ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status); if (ret < 0) return ret; cr_done = drm_dp_clock_recovery_ok(link_status, lane_cnt); if (cr_done) break; for (i = 0; i < lane_cnt; i++) if (!(dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED)) break; if (i == lane_cnt) break; if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == vs) tries++; else tries = 0; if (tries == DP_MAX_TRAINING_TRIES) break; vs = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; xilinx_drm_dp_adjust_train(dp, link_status); } if (!cr_done) return -EIO; return 0; }
static int edp_start_link_train_1(struct edp_ctrl *ctrl) { u8 link_status[DP_LINK_STATUS_SIZE]; u8 old_v_level; int tries; int ret; int rlen; DBG(""); edp_host_train_set(ctrl, DP_TRAINING_PATTERN_1); ret = edp_voltage_pre_emphasise_set(ctrl); if (ret) return ret; ret = edp_train_pattern_set_write(ctrl, DP_TRAINING_PATTERN_1 | DP_RECOVERED_CLOCK_OUT_EN); if (ret) return ret; tries = 0; old_v_level = ctrl->v_level; while (1) { drm_dp_link_train_clock_recovery_delay(ctrl->dpcd); rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status); if (rlen < DP_LINK_STATUS_SIZE) { pr_err("%s: read link status failed\n", __func__); return -ENOLINK; } if (drm_dp_clock_recovery_ok(link_status, ctrl->lane_cnt)) { ret = 0; break; } if (ctrl->v_level == DPCD_LINK_VOLTAGE_MAX) { ret = -1; break; } if (old_v_level == ctrl->v_level) { tries++; if (tries >= 5) { ret = -1; break; } } else { tries = 0; old_v_level = ctrl->v_level; } edp_sink_train_set_adjust(ctrl, link_status); ret = edp_voltage_pre_emphasise_set(ctrl); if (ret) return ret; } return ret; }
static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info) { bool clock_recovery; u8 voltage; int i; radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1); memset(dp_info->train_set, 0, 4); radeon_dp_update_vs_emph(dp_info); DRM_UDELAY(400); /* clock recovery loop */ clock_recovery = false; dp_info->tries = 0; voltage = 0xff; while (1) { drm_dp_link_train_clock_recovery_delay(dp_info->dpcd); if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) { DRM_ERROR("displayport link status failed\n"); break; } if (drm_dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) { clock_recovery = true; break; } for (i = 0; i < dp_info->dp_lane_count; i++) { if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; } if (i == dp_info->dp_lane_count) { DRM_ERROR("clock recovery reached max voltage\n"); break; } if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { ++dp_info->tries; if (dp_info->tries == 5) { DRM_ERROR("clock recovery tried 5 times\n"); break; } } else dp_info->tries = 0; voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Compute new train_set as requested by sink */ dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set); radeon_dp_update_vs_emph(dp_info); } if (!clock_recovery) { DRM_ERROR("clock recovery failed\n"); return -1; } else { DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n", dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT); return 0; } }
static void intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) { bool channel_eq = false; int tries, cr_tries; u32 training_pattern; training_pattern = intel_dp_training_pattern(intel_dp); /* channel equalization */ if (!intel_dp_set_link_train(intel_dp, training_pattern | DP_LINK_SCRAMBLING_DISABLE)) { DRM_ERROR("failed to start channel equalization\n"); return; } tries = 0; cr_tries = 0; channel_eq = false; for (;;) { uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { DRM_ERROR("failed to train DP, aborting\n"); intel_dp_dump_link_status(link_status); break; } drm_dp_link_train_channel_eq_delay(intel_dp->dpcd); if (!intel_dp_get_link_status(intel_dp, link_status)) { DRM_ERROR("failed to get link status\n"); break; } /* Make sure clock is still ok */ if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { intel_dp_link_training_clock_recovery(intel_dp); intel_dp_set_link_train(intel_dp, training_pattern | DP_LINK_SCRAMBLING_DISABLE); cr_tries++; continue; } if (drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { channel_eq = true; break; } /* Try 5 times, then try clock recovery if that fails */ if (tries > 5) { intel_dp_link_training_clock_recovery(intel_dp); intel_dp_set_link_train(intel_dp, training_pattern | DP_LINK_SCRAMBLING_DISABLE); tries = 0; cr_tries++; continue; } /* Update training set as requested by target */ intel_get_adjust_train(intel_dp, link_status); if (!intel_dp_update_link_train(intel_dp)) { DRM_ERROR("failed to update link training\n"); break; } ++tries; } intel_dp_set_idle_link_train(intel_dp); if (channel_eq) DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n"); }
/* Enable corresponding port and start training pattern 1 */ static void intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) { int i; uint8_t voltage; int voltage_tries, loop_tries; uint8_t link_config[2]; uint8_t link_bw, rate_select; if (intel_dp->prepare_link_retrain) intel_dp->prepare_link_retrain(intel_dp); intel_dp_compute_rate(intel_dp, intel_dp->link_rate, &link_bw, &rate_select); /* Write the link configuration data */ link_config[0] = link_bw; link_config[1] = intel_dp->lane_count; if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); if (intel_dp->num_sink_rates) drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET, &rate_select, 1); link_config[0] = 0; link_config[1] = DP_SET_ANSI_8B10B; drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2); intel_dp->DP |= DP_PORT_EN; /* clock recovery */ if (!intel_dp_reset_link_train(intel_dp, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE)) { DRM_ERROR("failed to enable link training\n"); return; } voltage = 0xff; voltage_tries = 0; loop_tries = 0; for (;;) { uint8_t link_status[DP_LINK_STATUS_SIZE]; drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd); if (!intel_dp_get_link_status(intel_dp, link_status)) { DRM_ERROR("failed to get link status\n"); break; } if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); break; } /* Check to see if we've tried the max voltage */ for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; if (i == intel_dp->lane_count) { ++loop_tries; if (loop_tries == 5) { DRM_ERROR("too many full retries, give up\n"); intel_dp_dump_link_status(link_status); break; } intel_dp_reset_link_train(intel_dp, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE); voltage_tries = 0; continue; } /* Check to see if we've tried the same voltage 5 times */ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { ++voltage_tries; if (voltage_tries == 5) { DRM_ERROR("too many voltage retries, give up\n"); break; } } else voltage_tries = 0; voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Update training set as requested by target */ intel_get_adjust_train(intel_dp, link_status); if (!intel_dp_update_link_train(intel_dp)) { DRM_ERROR("failed to update link training\n"); break; } } }