Loading drivers/gpu/drm/tegra/dp.c +456 −0 Original line number Diff line number Diff line Loading @@ -334,6 +334,14 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) u8 values[2], value; int err; if (link->ops && link->ops->configure) { err = link->ops->configure(link); if (err < 0) { DRM_ERROR("failed to configure DP link: %d\n", err); return err; } } values[0] = drm_dp_link_rate_to_bw_code(link->rate); values[1] = link->lanes; Loading Loading @@ -416,3 +424,451 @@ int drm_dp_link_choose(struct drm_dp_link *link, return -ERANGE; } /** * DOC: Link training * * These functions contain common logic and helpers to implement DisplayPort * link training. */ /** * drm_dp_link_train_init() - initialize DisplayPort link training state * @train: DisplayPort link training state */ void drm_dp_link_train_init(struct drm_dp_link_train *train) { struct drm_dp_link_train_set *request = &train->request; struct drm_dp_link_train_set *adjust = &train->adjust; unsigned int i; for (i = 0; i < 4; i++) { request->voltage_swing[i] = 0; adjust->voltage_swing[i] = 0; request->pre_emphasis[i] = 0; adjust->pre_emphasis[i] = 0; request->post_cursor[i] = 0; adjust->post_cursor[i] = 0; } train->pattern = DP_TRAINING_PATTERN_DISABLE; train->clock_recovered = false; train->channel_equalized = false; } static bool drm_dp_link_train_valid(const struct drm_dp_link_train *train) { return train->clock_recovered && train->channel_equalized; } static int drm_dp_link_apply_training(struct drm_dp_link *link) { struct drm_dp_link_train_set *request = &link->train.request; unsigned int lanes = link->lanes, *vs, *pe, *pc, i; struct drm_dp_aux *aux = link->aux; u8 values[4], pattern = 0; int err; err = link->ops->apply_training(link); if (err < 0) { DRM_ERROR("failed to apply link training: %d\n", err); return err; } vs = request->voltage_swing; pe = request->pre_emphasis; pc = request->post_cursor; /* write currently selected voltage-swing and pre-emphasis levels */ for (i = 0; i < lanes; i++) values[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL(vs[i]) | DP_TRAIN_PRE_EMPHASIS_LEVEL(pe[i]); err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values, lanes); if (err < 0) { DRM_ERROR("failed to set training parameters: %d\n", err); return err; } /* write currently selected post-cursor level (if supported) */ if (link->revision >= 0x12 && link->rate == 540000) { values[0] = values[1] = 0; for (i = 0; i < lanes; i++) values[i / 2] |= DP_LANE_POST_CURSOR(i, pc[i]); err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_1_SET2, values, DIV_ROUND_UP(lanes, 2)); if (err < 0) { DRM_ERROR("failed to set post-cursor: %d\n", err); return err; } } /* write link pattern */ if (link->train.pattern != DP_TRAINING_PATTERN_DISABLE) pattern |= DP_LINK_SCRAMBLING_DISABLE; pattern |= link->train.pattern; err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern); if (err < 0) { DRM_ERROR("failed to set training pattern: %d\n", err); return err; } return 0; } static void drm_dp_link_train_wait(struct drm_dp_link *link) { unsigned long min = 0; switch (link->train.pattern) { case DP_TRAINING_PATTERN_1: min = link->aux_rd_interval.cr; break; case DP_TRAINING_PATTERN_2: case DP_TRAINING_PATTERN_3: min = link->aux_rd_interval.ce; break; default: break; } if (min > 0) usleep_range(min, 2 * min); } static void drm_dp_link_get_adjustments(struct drm_dp_link *link, u8 status[DP_LINK_STATUS_SIZE]) { struct drm_dp_link_train_set *adjust = &link->train.adjust; unsigned int i; for (i = 0; i < link->lanes; i++) { adjust->voltage_swing[i] = drm_dp_get_adjust_request_voltage(status, i) >> DP_TRAIN_VOLTAGE_SWING_SHIFT; adjust->pre_emphasis[i] = drm_dp_get_adjust_request_pre_emphasis(status, i) >> DP_TRAIN_PRE_EMPHASIS_SHIFT; adjust->post_cursor[i] = drm_dp_get_adjust_request_post_cursor(status, i); } } static void drm_dp_link_train_adjust(struct drm_dp_link_train *train) { struct drm_dp_link_train_set *request = &train->request; struct drm_dp_link_train_set *adjust = &train->adjust; unsigned int i; for (i = 0; i < 4; i++) if (request->voltage_swing[i] != adjust->voltage_swing[i]) request->voltage_swing[i] = adjust->voltage_swing[i]; for (i = 0; i < 4; i++) if (request->pre_emphasis[i] != adjust->pre_emphasis[i]) request->pre_emphasis[i] = adjust->pre_emphasis[i]; for (i = 0; i < 4; i++) if (request->post_cursor[i] != adjust->post_cursor[i]) request->post_cursor[i] = adjust->post_cursor[i]; } static int drm_dp_link_recover_clock(struct drm_dp_link *link) { u8 status[DP_LINK_STATUS_SIZE]; int err; err = drm_dp_link_apply_training(link); if (err < 0) return err; drm_dp_link_train_wait(link); err = drm_dp_dpcd_read_link_status(link->aux, status); if (err < 0) { DRM_ERROR("failed to read link status: %d\n", err); return err; } if (!drm_dp_clock_recovery_ok(status, link->lanes)) drm_dp_link_get_adjustments(link, status); else link->train.clock_recovered = true; return 0; } static int drm_dp_link_clock_recovery(struct drm_dp_link *link) { unsigned int repeat; int err; /* start clock recovery using training pattern 1 */ link->train.pattern = DP_TRAINING_PATTERN_1; for (repeat = 1; repeat < 5; repeat++) { err = drm_dp_link_recover_clock(link); if (err < 0) { DRM_ERROR("failed to recover clock: %d\n", err); return err; } drm_dp_link_train_adjust(&link->train); if (link->train.clock_recovered) break; } return 0; } static int drm_dp_link_equalize_channel(struct drm_dp_link *link) { struct drm_dp_aux *aux = link->aux; u8 status[DP_LINK_STATUS_SIZE]; int err; err = drm_dp_link_apply_training(link); if (err < 0) return err; drm_dp_link_train_wait(link); err = drm_dp_dpcd_read_link_status(aux, status); if (err < 0) { DRM_ERROR("failed to read link status: %d\n", err); return err; } if (!drm_dp_clock_recovery_ok(status, link->lanes)) { DRM_ERROR("clock recovery lost while equalizing channel\n"); link->train.clock_recovered = false; return 0; } if (!drm_dp_channel_eq_ok(status, link->lanes)) drm_dp_link_get_adjustments(link, status); else link->train.channel_equalized = true; return 0; } static int drm_dp_link_channel_equalization(struct drm_dp_link *link) { unsigned int repeat; int err; /* start channel equalization using pattern 2 or 3 */ if (link->caps.tps3_supported) link->train.pattern = DP_TRAINING_PATTERN_3; else link->train.pattern = DP_TRAINING_PATTERN_2; for (repeat = 1; repeat < 5; repeat++) { err = drm_dp_link_equalize_channel(link); if (err < 0) { DRM_ERROR("failed to equalize channel: %d\n", err); return err; } drm_dp_link_train_adjust(&link->train); if (link->train.channel_equalized) break; } return 0; } static int drm_dp_link_downgrade(struct drm_dp_link *link) { switch (link->rate) { case 162000: return -EINVAL; case 270000: link->rate = 162000; break; case 540000: link->rate = 270000; return 0; } return 0; } static void drm_dp_link_train_disable(struct drm_dp_link *link) { int err; link->train.pattern = DP_TRAINING_PATTERN_DISABLE; err = drm_dp_link_apply_training(link); if (err < 0) DRM_ERROR("failed to disable link training: %d\n", err); } static int drm_dp_link_train_full(struct drm_dp_link *link) { int err; retry: DRM_DEBUG_KMS("full-training link: %u lane%s at %u MHz\n", link->lanes, (link->lanes > 1) ? "s" : "", link->rate / 100); err = drm_dp_link_configure(link->aux, link); if (err < 0) { DRM_ERROR("failed to configure DP link: %d\n", err); return err; } err = drm_dp_link_clock_recovery(link); if (err < 0) { DRM_ERROR("clock recovery failed: %d\n", err); goto out; } if (!link->train.clock_recovered) { DRM_ERROR("clock recovery failed, downgrading link\n"); err = drm_dp_link_downgrade(link); if (err < 0) goto out; goto retry; } DRM_DEBUG_KMS("clock recovery succeeded\n"); err = drm_dp_link_channel_equalization(link); if (err < 0) { DRM_ERROR("channel equalization failed: %d\n", err); goto out; } if (!link->train.channel_equalized) { DRM_ERROR("channel equalization failed, downgrading link\n"); err = drm_dp_link_downgrade(link); if (err < 0) goto out; goto retry; } DRM_DEBUG_KMS("channel equalization succeeded\n"); out: drm_dp_link_train_disable(link); return err; } static int drm_dp_link_train_fast(struct drm_dp_link *link) { u8 status[DP_LINK_STATUS_SIZE]; int err; DRM_DEBUG_KMS("fast-training link: %u lane%s at %u MHz\n", link->lanes, (link->lanes > 1) ? "s" : "", link->rate / 100); err = drm_dp_link_configure(link->aux, link); if (err < 0) { DRM_ERROR("failed to configure DP link: %d\n", err); return err; } /* transmit training pattern 1 for 500 microseconds */ link->train.pattern = DP_TRAINING_PATTERN_1; err = drm_dp_link_apply_training(link); if (err < 0) goto out; usleep_range(500, 1000); /* transmit training pattern 2 or 3 for 500 microseconds */ if (link->caps.tps3_supported) link->train.pattern = DP_TRAINING_PATTERN_3; else link->train.pattern = DP_TRAINING_PATTERN_2; err = drm_dp_link_apply_training(link); if (err < 0) goto out; usleep_range(500, 1000); err = drm_dp_dpcd_read_link_status(link->aux, status); if (err < 0) { DRM_ERROR("failed to read link status: %d\n", err); goto out; } if (!drm_dp_clock_recovery_ok(status, link->lanes)) { DRM_ERROR("clock recovery failed\n"); err = -EIO; } if (!drm_dp_channel_eq_ok(status, link->lanes)) { DRM_ERROR("channel equalization failed\n"); err = -EIO; } out: drm_dp_link_train_disable(link); return err; } /** * drm_dp_link_train() - perform DisplayPort link training * @link: a DP link object * * Uses the context stored in the DP link object to perform link training. It * is expected that drivers will call drm_dp_link_probe() to obtain the link * capabilities before performing link training. * * If the sink supports fast link training (no AUX CH handshake) and valid * training settings are available, this function will try to perform fast * link training and fall back to full link training on failure. * * Returns: 0 on success or a negative error code on failure. */ int drm_dp_link_train(struct drm_dp_link *link) { int err; if (link->caps.fast_training) { if (drm_dp_link_train_valid(&link->train)) { err = drm_dp_link_train_fast(link); if (err < 0) DRM_ERROR("fast link training failed: %d\n", err); else return 0; } else { DRM_DEBUG_KMS("training parameters not available\n"); } } else { DRM_DEBUG_KMS("fast link training not supported\n"); } err = drm_dp_link_train_full(link); if (err < 0) DRM_ERROR("full link training failed: %d\n", err); return err; } drivers/gpu/drm/tegra/dp.h +68 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ struct drm_display_info; struct drm_display_mode; struct drm_dp_aux; struct drm_dp_link; /** * struct drm_dp_link_caps - DP link capabilities Loading Loading @@ -56,6 +57,55 @@ struct drm_dp_link_caps { void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, const struct drm_dp_link_caps *src); /** * struct drm_dp_link_ops - DP link operations */ struct drm_dp_link_ops { /** * @apply_training: */ int (*apply_training)(struct drm_dp_link *link); /** * @configure: */ int (*configure)(struct drm_dp_link *link); }; #define DP_TRAIN_VOLTAGE_SWING_LEVEL(x) ((x) << 0) #define DP_TRAIN_PRE_EMPHASIS_LEVEL(x) ((x) << 3) #define DP_LANE_POST_CURSOR(i, x) (((x) & 0x3) << (((i) & 1) << 2)) /** * struct drm_dp_link_train_set - link training settings * @voltage_swing: per-lane voltage swing * @pre_emphasis: per-lane pre-emphasis * @post_cursor: per-lane post-cursor */ struct drm_dp_link_train_set { unsigned int voltage_swing[4]; unsigned int pre_emphasis[4]; unsigned int post_cursor[4]; }; /** * struct drm_dp_link_train - link training state information * @request: currently requested settings * @adjust: adjustments requested by sink * @pattern: currently requested training pattern * @clock_recovered: flag to track if clock recovery has completed * @channel_equalized: flag to track if channel equalization has completed */ struct drm_dp_link_train { struct drm_dp_link_train_set request; struct drm_dp_link_train_set adjust; unsigned int pattern; bool clock_recovered; bool channel_equalized; }; /** * struct drm_dp_link - DP link capabilities and configuration * @revision: DP specification revision supported on the link Loading Loading @@ -92,6 +142,21 @@ struct drm_dp_link { unsigned long rates[DP_MAX_SUPPORTED_RATES]; unsigned int num_rates; /** * @ops: DP link operations */ const struct drm_dp_link_ops *ops; /** * @aux: DP AUX channel */ struct drm_dp_aux *aux; /** * @train: DP link training state */ struct drm_dp_link_train train; }; int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate); Loading @@ -106,4 +171,7 @@ int drm_dp_link_choose(struct drm_dp_link *link, const struct drm_display_mode *mode, const struct drm_display_info *info); void drm_dp_link_train_init(struct drm_dp_link_train *train); int drm_dp_link_train(struct drm_dp_link *link); #endif Loading
drivers/gpu/drm/tegra/dp.c +456 −0 Original line number Diff line number Diff line Loading @@ -334,6 +334,14 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) u8 values[2], value; int err; if (link->ops && link->ops->configure) { err = link->ops->configure(link); if (err < 0) { DRM_ERROR("failed to configure DP link: %d\n", err); return err; } } values[0] = drm_dp_link_rate_to_bw_code(link->rate); values[1] = link->lanes; Loading Loading @@ -416,3 +424,451 @@ int drm_dp_link_choose(struct drm_dp_link *link, return -ERANGE; } /** * DOC: Link training * * These functions contain common logic and helpers to implement DisplayPort * link training. */ /** * drm_dp_link_train_init() - initialize DisplayPort link training state * @train: DisplayPort link training state */ void drm_dp_link_train_init(struct drm_dp_link_train *train) { struct drm_dp_link_train_set *request = &train->request; struct drm_dp_link_train_set *adjust = &train->adjust; unsigned int i; for (i = 0; i < 4; i++) { request->voltage_swing[i] = 0; adjust->voltage_swing[i] = 0; request->pre_emphasis[i] = 0; adjust->pre_emphasis[i] = 0; request->post_cursor[i] = 0; adjust->post_cursor[i] = 0; } train->pattern = DP_TRAINING_PATTERN_DISABLE; train->clock_recovered = false; train->channel_equalized = false; } static bool drm_dp_link_train_valid(const struct drm_dp_link_train *train) { return train->clock_recovered && train->channel_equalized; } static int drm_dp_link_apply_training(struct drm_dp_link *link) { struct drm_dp_link_train_set *request = &link->train.request; unsigned int lanes = link->lanes, *vs, *pe, *pc, i; struct drm_dp_aux *aux = link->aux; u8 values[4], pattern = 0; int err; err = link->ops->apply_training(link); if (err < 0) { DRM_ERROR("failed to apply link training: %d\n", err); return err; } vs = request->voltage_swing; pe = request->pre_emphasis; pc = request->post_cursor; /* write currently selected voltage-swing and pre-emphasis levels */ for (i = 0; i < lanes; i++) values[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL(vs[i]) | DP_TRAIN_PRE_EMPHASIS_LEVEL(pe[i]); err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values, lanes); if (err < 0) { DRM_ERROR("failed to set training parameters: %d\n", err); return err; } /* write currently selected post-cursor level (if supported) */ if (link->revision >= 0x12 && link->rate == 540000) { values[0] = values[1] = 0; for (i = 0; i < lanes; i++) values[i / 2] |= DP_LANE_POST_CURSOR(i, pc[i]); err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_1_SET2, values, DIV_ROUND_UP(lanes, 2)); if (err < 0) { DRM_ERROR("failed to set post-cursor: %d\n", err); return err; } } /* write link pattern */ if (link->train.pattern != DP_TRAINING_PATTERN_DISABLE) pattern |= DP_LINK_SCRAMBLING_DISABLE; pattern |= link->train.pattern; err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern); if (err < 0) { DRM_ERROR("failed to set training pattern: %d\n", err); return err; } return 0; } static void drm_dp_link_train_wait(struct drm_dp_link *link) { unsigned long min = 0; switch (link->train.pattern) { case DP_TRAINING_PATTERN_1: min = link->aux_rd_interval.cr; break; case DP_TRAINING_PATTERN_2: case DP_TRAINING_PATTERN_3: min = link->aux_rd_interval.ce; break; default: break; } if (min > 0) usleep_range(min, 2 * min); } static void drm_dp_link_get_adjustments(struct drm_dp_link *link, u8 status[DP_LINK_STATUS_SIZE]) { struct drm_dp_link_train_set *adjust = &link->train.adjust; unsigned int i; for (i = 0; i < link->lanes; i++) { adjust->voltage_swing[i] = drm_dp_get_adjust_request_voltage(status, i) >> DP_TRAIN_VOLTAGE_SWING_SHIFT; adjust->pre_emphasis[i] = drm_dp_get_adjust_request_pre_emphasis(status, i) >> DP_TRAIN_PRE_EMPHASIS_SHIFT; adjust->post_cursor[i] = drm_dp_get_adjust_request_post_cursor(status, i); } } static void drm_dp_link_train_adjust(struct drm_dp_link_train *train) { struct drm_dp_link_train_set *request = &train->request; struct drm_dp_link_train_set *adjust = &train->adjust; unsigned int i; for (i = 0; i < 4; i++) if (request->voltage_swing[i] != adjust->voltage_swing[i]) request->voltage_swing[i] = adjust->voltage_swing[i]; for (i = 0; i < 4; i++) if (request->pre_emphasis[i] != adjust->pre_emphasis[i]) request->pre_emphasis[i] = adjust->pre_emphasis[i]; for (i = 0; i < 4; i++) if (request->post_cursor[i] != adjust->post_cursor[i]) request->post_cursor[i] = adjust->post_cursor[i]; } static int drm_dp_link_recover_clock(struct drm_dp_link *link) { u8 status[DP_LINK_STATUS_SIZE]; int err; err = drm_dp_link_apply_training(link); if (err < 0) return err; drm_dp_link_train_wait(link); err = drm_dp_dpcd_read_link_status(link->aux, status); if (err < 0) { DRM_ERROR("failed to read link status: %d\n", err); return err; } if (!drm_dp_clock_recovery_ok(status, link->lanes)) drm_dp_link_get_adjustments(link, status); else link->train.clock_recovered = true; return 0; } static int drm_dp_link_clock_recovery(struct drm_dp_link *link) { unsigned int repeat; int err; /* start clock recovery using training pattern 1 */ link->train.pattern = DP_TRAINING_PATTERN_1; for (repeat = 1; repeat < 5; repeat++) { err = drm_dp_link_recover_clock(link); if (err < 0) { DRM_ERROR("failed to recover clock: %d\n", err); return err; } drm_dp_link_train_adjust(&link->train); if (link->train.clock_recovered) break; } return 0; } static int drm_dp_link_equalize_channel(struct drm_dp_link *link) { struct drm_dp_aux *aux = link->aux; u8 status[DP_LINK_STATUS_SIZE]; int err; err = drm_dp_link_apply_training(link); if (err < 0) return err; drm_dp_link_train_wait(link); err = drm_dp_dpcd_read_link_status(aux, status); if (err < 0) { DRM_ERROR("failed to read link status: %d\n", err); return err; } if (!drm_dp_clock_recovery_ok(status, link->lanes)) { DRM_ERROR("clock recovery lost while equalizing channel\n"); link->train.clock_recovered = false; return 0; } if (!drm_dp_channel_eq_ok(status, link->lanes)) drm_dp_link_get_adjustments(link, status); else link->train.channel_equalized = true; return 0; } static int drm_dp_link_channel_equalization(struct drm_dp_link *link) { unsigned int repeat; int err; /* start channel equalization using pattern 2 or 3 */ if (link->caps.tps3_supported) link->train.pattern = DP_TRAINING_PATTERN_3; else link->train.pattern = DP_TRAINING_PATTERN_2; for (repeat = 1; repeat < 5; repeat++) { err = drm_dp_link_equalize_channel(link); if (err < 0) { DRM_ERROR("failed to equalize channel: %d\n", err); return err; } drm_dp_link_train_adjust(&link->train); if (link->train.channel_equalized) break; } return 0; } static int drm_dp_link_downgrade(struct drm_dp_link *link) { switch (link->rate) { case 162000: return -EINVAL; case 270000: link->rate = 162000; break; case 540000: link->rate = 270000; return 0; } return 0; } static void drm_dp_link_train_disable(struct drm_dp_link *link) { int err; link->train.pattern = DP_TRAINING_PATTERN_DISABLE; err = drm_dp_link_apply_training(link); if (err < 0) DRM_ERROR("failed to disable link training: %d\n", err); } static int drm_dp_link_train_full(struct drm_dp_link *link) { int err; retry: DRM_DEBUG_KMS("full-training link: %u lane%s at %u MHz\n", link->lanes, (link->lanes > 1) ? "s" : "", link->rate / 100); err = drm_dp_link_configure(link->aux, link); if (err < 0) { DRM_ERROR("failed to configure DP link: %d\n", err); return err; } err = drm_dp_link_clock_recovery(link); if (err < 0) { DRM_ERROR("clock recovery failed: %d\n", err); goto out; } if (!link->train.clock_recovered) { DRM_ERROR("clock recovery failed, downgrading link\n"); err = drm_dp_link_downgrade(link); if (err < 0) goto out; goto retry; } DRM_DEBUG_KMS("clock recovery succeeded\n"); err = drm_dp_link_channel_equalization(link); if (err < 0) { DRM_ERROR("channel equalization failed: %d\n", err); goto out; } if (!link->train.channel_equalized) { DRM_ERROR("channel equalization failed, downgrading link\n"); err = drm_dp_link_downgrade(link); if (err < 0) goto out; goto retry; } DRM_DEBUG_KMS("channel equalization succeeded\n"); out: drm_dp_link_train_disable(link); return err; } static int drm_dp_link_train_fast(struct drm_dp_link *link) { u8 status[DP_LINK_STATUS_SIZE]; int err; DRM_DEBUG_KMS("fast-training link: %u lane%s at %u MHz\n", link->lanes, (link->lanes > 1) ? "s" : "", link->rate / 100); err = drm_dp_link_configure(link->aux, link); if (err < 0) { DRM_ERROR("failed to configure DP link: %d\n", err); return err; } /* transmit training pattern 1 for 500 microseconds */ link->train.pattern = DP_TRAINING_PATTERN_1; err = drm_dp_link_apply_training(link); if (err < 0) goto out; usleep_range(500, 1000); /* transmit training pattern 2 or 3 for 500 microseconds */ if (link->caps.tps3_supported) link->train.pattern = DP_TRAINING_PATTERN_3; else link->train.pattern = DP_TRAINING_PATTERN_2; err = drm_dp_link_apply_training(link); if (err < 0) goto out; usleep_range(500, 1000); err = drm_dp_dpcd_read_link_status(link->aux, status); if (err < 0) { DRM_ERROR("failed to read link status: %d\n", err); goto out; } if (!drm_dp_clock_recovery_ok(status, link->lanes)) { DRM_ERROR("clock recovery failed\n"); err = -EIO; } if (!drm_dp_channel_eq_ok(status, link->lanes)) { DRM_ERROR("channel equalization failed\n"); err = -EIO; } out: drm_dp_link_train_disable(link); return err; } /** * drm_dp_link_train() - perform DisplayPort link training * @link: a DP link object * * Uses the context stored in the DP link object to perform link training. It * is expected that drivers will call drm_dp_link_probe() to obtain the link * capabilities before performing link training. * * If the sink supports fast link training (no AUX CH handshake) and valid * training settings are available, this function will try to perform fast * link training and fall back to full link training on failure. * * Returns: 0 on success or a negative error code on failure. */ int drm_dp_link_train(struct drm_dp_link *link) { int err; if (link->caps.fast_training) { if (drm_dp_link_train_valid(&link->train)) { err = drm_dp_link_train_fast(link); if (err < 0) DRM_ERROR("fast link training failed: %d\n", err); else return 0; } else { DRM_DEBUG_KMS("training parameters not available\n"); } } else { DRM_DEBUG_KMS("fast link training not supported\n"); } err = drm_dp_link_train_full(link); if (err < 0) DRM_ERROR("full link training failed: %d\n", err); return err; }
drivers/gpu/drm/tegra/dp.h +68 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ struct drm_display_info; struct drm_display_mode; struct drm_dp_aux; struct drm_dp_link; /** * struct drm_dp_link_caps - DP link capabilities Loading Loading @@ -56,6 +57,55 @@ struct drm_dp_link_caps { void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, const struct drm_dp_link_caps *src); /** * struct drm_dp_link_ops - DP link operations */ struct drm_dp_link_ops { /** * @apply_training: */ int (*apply_training)(struct drm_dp_link *link); /** * @configure: */ int (*configure)(struct drm_dp_link *link); }; #define DP_TRAIN_VOLTAGE_SWING_LEVEL(x) ((x) << 0) #define DP_TRAIN_PRE_EMPHASIS_LEVEL(x) ((x) << 3) #define DP_LANE_POST_CURSOR(i, x) (((x) & 0x3) << (((i) & 1) << 2)) /** * struct drm_dp_link_train_set - link training settings * @voltage_swing: per-lane voltage swing * @pre_emphasis: per-lane pre-emphasis * @post_cursor: per-lane post-cursor */ struct drm_dp_link_train_set { unsigned int voltage_swing[4]; unsigned int pre_emphasis[4]; unsigned int post_cursor[4]; }; /** * struct drm_dp_link_train - link training state information * @request: currently requested settings * @adjust: adjustments requested by sink * @pattern: currently requested training pattern * @clock_recovered: flag to track if clock recovery has completed * @channel_equalized: flag to track if channel equalization has completed */ struct drm_dp_link_train { struct drm_dp_link_train_set request; struct drm_dp_link_train_set adjust; unsigned int pattern; bool clock_recovered; bool channel_equalized; }; /** * struct drm_dp_link - DP link capabilities and configuration * @revision: DP specification revision supported on the link Loading Loading @@ -92,6 +142,21 @@ struct drm_dp_link { unsigned long rates[DP_MAX_SUPPORTED_RATES]; unsigned int num_rates; /** * @ops: DP link operations */ const struct drm_dp_link_ops *ops; /** * @aux: DP AUX channel */ struct drm_dp_aux *aux; /** * @train: DP link training state */ struct drm_dp_link_train train; }; int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate); Loading @@ -106,4 +171,7 @@ int drm_dp_link_choose(struct drm_dp_link *link, const struct drm_display_mode *mode, const struct drm_display_info *info); void drm_dp_link_train_init(struct drm_dp_link_train *train); int drm_dp_link_train(struct drm_dp_link *link); #endif