File Index
Files and directories
Sign in
FILE

link_dp_training_dpia.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_dpia.c C
   1/*
   2 * Copyright 2022 Advanced Micro Devices, Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: AMD
  23 *
  24 */
  25
  26/* FILE POLICY AND INTENDED USAGE:
  27 * This module implements functionality for training DPIA links.
  28 */
  29#include "link_dp_training_dpia.h"
  30#include "dc.h"
  31#include "inc/core_status.h"
  32#include "dpcd_defs.h"
  33
  34#include "link_dp_dpia.h"
  35#include "link_hwss.h"
  36#include "dm_helpers.h"
  37#include "dmub/inc/dmub_cmd.h"
  38#include "link_dpcd.h"
  39#include "link_dp_phy.h"
  40#include "link_dp_training_8b_10b.h"
  41#include "link_dp_capability.h"
  42#include "dc_dmub_srv.h"
  43#define DC_LOGGER \
  44	link->ctx->logger
  45
  46/* Extend interval between training status checks for manual testing. */
  47#define DPIA_DEBUG_EXTENDED_AUX_RD_INTERVAL_US 60000000
  48
  49#define TRAINING_AUX_RD_INTERVAL 100 //us
  50
  51/* SET_CONFIG message types sent by driver. */
  52enum dpia_set_config_type {
  53	DPIA_SET_CFG_SET_LINK = 0x01,
  54	DPIA_SET_CFG_SET_PHY_TEST_MODE = 0x05,
  55	DPIA_SET_CFG_SET_TRAINING = 0x18,
  56	DPIA_SET_CFG_SET_VSPE = 0x19
  57};
  58
  59/* Training stages (TS) in SET_CONFIG(SET_TRAINING) message. */
  60enum dpia_set_config_ts {
  61	DPIA_TS_DPRX_DONE = 0x00, /* Done training DPRX. */
  62	DPIA_TS_TPS1 = 0x01,
  63	DPIA_TS_TPS2 = 0x02,
  64	DPIA_TS_TPS3 = 0x03,
  65	DPIA_TS_TPS4 = 0x07,
  66	DPIA_TS_UFP_DONE = 0xff /* Done training DPTX-to-DPIA hop. */
  67};
  68
  69/* SET_CONFIG message data associated with messages sent by driver. */
  70union dpia_set_config_data {
  71	struct {
  72		uint8_t mode : 1;
  73		uint8_t reserved : 7;
  74	} set_link;
  75	struct {
  76		uint8_t stage;
  77	} set_training;
  78	struct {
  79		uint8_t swing : 2;
  80		uint8_t max_swing_reached : 1;
  81		uint8_t pre_emph : 2;
  82		uint8_t max_pre_emph_reached : 1;
  83		uint8_t reserved : 2;
  84	} set_vspe;
  85	uint8_t raw;
  86};
  87
  88
  89/* Configure link as prescribed in link_setting; set LTTPR mode; and
  90 * Initialize link training settings.
  91 * Abort link training if sink unplug detected.
  92 *
  93 * @param link DPIA link being trained.
  94 * @param[in] link_setting Lane count, link rate and downspread control.
  95 * @param[out] lt_settings Link settings and drive settings (voltage swing and pre-emphasis).
  96 */
  97static enum link_training_result dpia_configure_link(
  98		struct dc_link *link,
  99		const struct link_resource *link_res,
 100		const struct dc_link_settings *link_setting,
 101		struct link_training_settings *lt_settings)
 102{
 103	enum dc_status status;
 104	bool fec_enable;
 105
 106	DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) configuring\n - LTTPR mode(%d)\n",
 107		__func__,
 108		link->link_id.enum_id - ENUM_ID_1,
 109		lt_settings->lttpr_mode);
 110
 111	dp_decide_training_settings(
 112		link,
 113		link_res,
 114		link_setting,
 115		lt_settings);
 116
 117	dp_get_lttpr_mode_override(link, &lt_settings->lttpr_mode);
 118
 119	status = dpcd_configure_channel_coding(link, lt_settings);
 120	if (status != DC_OK && link->is_hpd_pending)
 121		return LINK_TRAINING_ABORT;
 122
 123	/* Configure lttpr mode */
 124	status = dpcd_configure_lttpr_mode(link, lt_settings);
 125	if (status != DC_OK && link->is_hpd_pending)
 126		return LINK_TRAINING_ABORT;
 127
 128	/* Set link rate, lane count and spread. */
 129	status = dpcd_set_link_settings(link, lt_settings);
 130	if (status != DC_OK && link->is_hpd_pending)
 131		return LINK_TRAINING_ABORT;
 132
 133	if (link_dp_get_encoding_format(link_setting) == DP_8b_10b_ENCODING) {
 134		if (link->preferred_training_settings.fec_enable != NULL)
 135			fec_enable = *link->preferred_training_settings.fec_enable;
 136		else
 137			fec_enable = true;
 138		status = dp_set_fec_ready(link, link_res, fec_enable);
 139	}
 140
 141	if (status != DC_OK && link->is_hpd_pending)
 142		return LINK_TRAINING_ABORT;
 143
 144	return LINK_TRAINING_SUCCESS;
 145}
 146
 147static enum dc_status core_link_send_set_config(
 148	struct dc_link *link,
 149	uint8_t msg_type,
 150	uint8_t msg_data)
 151{
 152	struct set_config_cmd_payload payload;
 153	enum set_config_status set_config_result = SET_CONFIG_PENDING;
 154
 155	/* prepare set_config payload */
 156	payload.msg_type = msg_type;
 157	payload.msg_data = msg_data;
 158
 159	if (!link->ddc->ddc_pin && !link->aux_access_disabled &&
 160			(dm_helpers_dmub_set_config_sync(link->ctx,
 161			link, &payload, &set_config_result) == -1)) {
 162		return DC_ERROR_UNEXPECTED;
 163	}
 164
 165	/* set_config should return ACK if successful */
 166	return (set_config_result == SET_CONFIG_ACK_RECEIVED) ? DC_OK : DC_ERROR_UNEXPECTED;
 167}
 168
 169/* Build SET_CONFIG message data payload for specified message type. */
 170static uint8_t dpia_build_set_config_data(
 171		enum dpia_set_config_type type,
 172		struct dc_link *link,
 173		struct link_training_settings *lt_settings)
 174{
 175	union dpia_set_config_data data;
 176
 177	data.raw = 0;
 178
 179	switch (type) {
 180	case DPIA_SET_CFG_SET_LINK:
 181		data.set_link.mode = lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT ? 1 : 0;
 182		break;
 183	case DPIA_SET_CFG_SET_PHY_TEST_MODE:
 184		break;
 185	case DPIA_SET_CFG_SET_VSPE:
 186		/* Assume all lanes have same drive settings. */
 187		data.set_vspe.swing = lt_settings->hw_lane_settings[0].VOLTAGE_SWING;
 188		data.set_vspe.pre_emph = lt_settings->hw_lane_settings[0].PRE_EMPHASIS;
 189		data.set_vspe.max_swing_reached =
 190				lt_settings->hw_lane_settings[0].VOLTAGE_SWING == VOLTAGE_SWING_MAX_LEVEL ? 1 : 0;
 191		data.set_vspe.max_pre_emph_reached =
 192				lt_settings->hw_lane_settings[0].PRE_EMPHASIS == PRE_EMPHASIS_MAX_LEVEL ? 1 : 0;
 193		break;
 194	default:
 195		ASSERT(false); /* Message type not supported by helper function. */
 196		break;
 197	}
 198
 199	return data.raw;
 200}
 201
 202/* Convert DC training pattern to DPIA training stage. */
 203static enum dc_status convert_trng_ptn_to_trng_stg(enum dc_dp_training_pattern tps, enum dpia_set_config_ts *ts)
 204{
 205	enum dc_status status = DC_OK;
 206
 207	switch (tps) {
 208	case DP_TRAINING_PATTERN_SEQUENCE_1:
 209		*ts = DPIA_TS_TPS1;
 210		break;
 211	case DP_TRAINING_PATTERN_SEQUENCE_2:
 212		*ts = DPIA_TS_TPS2;
 213		break;
 214	case DP_TRAINING_PATTERN_SEQUENCE_3:
 215		*ts = DPIA_TS_TPS3;
 216		break;
 217	case DP_TRAINING_PATTERN_SEQUENCE_4:
 218		*ts = DPIA_TS_TPS4;
 219		break;
 220	case DP_TRAINING_PATTERN_VIDEOIDLE:
 221		*ts = DPIA_TS_DPRX_DONE;
 222		break;
 223	default: /* TPS not supported by helper function. */
 224		ASSERT(false);
 225		*ts = DPIA_TS_DPRX_DONE;
 226		status = DC_UNSUPPORTED_VALUE;
 227		break;
 228	}
 229
 230	return status;
 231}
 232
 233/* Write training pattern to DPCD. */
 234static enum dc_status dpcd_set_lt_pattern(
 235	struct dc_link *link,
 236	enum dc_dp_training_pattern pattern,
 237	uint32_t hop)
 238{
 239	union dpcd_training_pattern dpcd_pattern = {0};
 240	uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET;
 241	enum dc_status status;
 242
 243	if (hop != DPRX)
 244		dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
 245			((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1));
 246
 247	/* DpcdAddress_TrainingPatternSet */
 248	dpcd_pattern.v1_4.TRAINING_PATTERN_SET =
 249		dp_training_pattern_to_dpcd_training_pattern(link, pattern);
 250
 251	dpcd_pattern.v1_4.SCRAMBLING_DISABLE =
 252		dp_initialize_scrambling_data_symbols(link, pattern);
 253
 254	if (hop != DPRX) {
 255		DC_LOG_HW_LINK_TRAINING("%s\n LTTPR Repeater ID: %d\n 0x%X pattern = %x\n",
 256			__func__,
 257			hop,
 258			dpcd_tps_offset,
 259			dpcd_pattern.v1_4.TRAINING_PATTERN_SET);
 260	} else {
 261		DC_LOG_HW_LINK_TRAINING("%s\n 0x%X pattern = %x\n",
 262			__func__,
 263			dpcd_tps_offset,
 264			dpcd_pattern.v1_4.TRAINING_PATTERN_SET);
 265	}
 266
 267	status = core_link_write_dpcd(
 268			link,
 269			dpcd_tps_offset,
 270			&dpcd_pattern.raw,
 271			sizeof(dpcd_pattern.raw));
 272
 273	return status;
 274}
 275
 276/* Execute clock recovery phase of link training for specified hop in display
 277 * path.in non-transparent mode:
 278 * - Driver issues both DPCD and SET_CONFIG transactions.
 279 * - TPS1 is transmitted for any hops downstream of DPOA.
 280 * - Drive (VS/PE) only transmitted for the hop immediately downstream of DPOA.
 281 * - CR for the first hop (DPTX-to-DPIA) is assumed to be successful.
 282 *
 283 * @param link DPIA link being trained.
 284 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
 285 * @param hop Hop in display path. DPRX = 0.
 286 */
 287static enum link_training_result dpia_training_cr_non_transparent(
 288		struct dc_link *link,
 289		const struct link_resource *link_res,
 290		struct link_training_settings *lt_settings,
 291		uint32_t hop)
 292{
 293	enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0;
 294	uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
 295	enum dc_status status = DC_ERROR_UNEXPECTED;
 296	uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */
 297	uint32_t retry_count = 0;
 298	uint32_t wait_time_microsec = TRAINING_AUX_RD_INTERVAL; /* From DP spec, CR read interval is always 100us. */
 299	enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
 300	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
 301	union lane_align_status_updated dpcd_lane_status_updated = {0};
 302	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
 303	uint8_t set_cfg_data;
 304	enum dpia_set_config_ts ts;
 305
 306	repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
 307
 308	/* Cap of LINK_TRAINING_MAX_CR_RETRY attempts at clock recovery.
 309	 * Fix inherited from perform_clock_recovery_sequence() -
 310	 * the DP equivalent of this function:
 311	 * Required for Synaptics MST hub which can put the LT in
 312	 * infinite loop by switching the VS between level 0 and level 1
 313	 * continuously.
 314	 */
 315	while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
 316			(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
 317
 318		/* DPTX-to-DPIA */
 319		if (hop == repeater_cnt) {
 320			/* Send SET_CONFIG(SET_LINK:LC,LR,LTTPR) to notify DPOA that
 321			 * non-transparent link training has started.
 322			 * This also enables the transmission of clk_sync packets.
 323			 */
 324			set_cfg_data = dpia_build_set_config_data(
 325					DPIA_SET_CFG_SET_LINK,
 326					link,
 327					lt_settings);
 328			status = core_link_send_set_config(
 329					link,
 330					DPIA_SET_CFG_SET_LINK,
 331					set_cfg_data);
 332			/* CR for this hop is considered successful as long as
 333			 * SET_CONFIG message is acknowledged by DPOA.
 334			 */
 335			if (status == DC_OK)
 336				result = LINK_TRAINING_SUCCESS;
 337			else
 338				result = LINK_TRAINING_ABORT;
 339			break;
 340		}
 341
 342		/* DPOA-to-x */
 343		/* Instruct DPOA to transmit TPS1 then update DPCD. */
 344		if (retry_count == 0) {
 345			status = convert_trng_ptn_to_trng_stg(lt_settings->pattern_for_cr, &ts);
 346			if (status != DC_OK) {
 347				result = LINK_TRAINING_ABORT;
 348				break;
 349			}
 350			status = core_link_send_set_config(
 351					link,
 352					DPIA_SET_CFG_SET_TRAINING,
 353					ts);
 354			if (status != DC_OK) {
 355				result = LINK_TRAINING_ABORT;
 356				break;
 357			}
 358			status = dpcd_set_lt_pattern(link, lt_settings->pattern_for_cr, hop);
 359			if (status != DC_OK) {
 360				result = LINK_TRAINING_ABORT;
 361				break;
 362			}
 363		}
 364
 365		/* Update DPOA drive settings then DPCD. DPOA does only adjusts
 366		 * drive settings for hops immediately downstream.
 367		 */
 368		if (hop == repeater_cnt - 1) {
 369			set_cfg_data = dpia_build_set_config_data(
 370					DPIA_SET_CFG_SET_VSPE,
 371					link,
 372					lt_settings);
 373			status = core_link_send_set_config(
 374					link,
 375					DPIA_SET_CFG_SET_VSPE,
 376					set_cfg_data);
 377			if (status != DC_OK) {
 378				result = LINK_TRAINING_ABORT;
 379				break;
 380			}
 381		}
 382		status = dpcd_set_lane_settings(link, lt_settings, hop);
 383		if (status != DC_OK) {
 384			result = LINK_TRAINING_ABORT;
 385			break;
 386		}
 387
 388		dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
 389
 390		/* Read status and adjustment requests from DPCD. */
 391		status = dp_get_lane_status_and_lane_adjust(
 392				link,
 393				lt_settings,
 394				dpcd_lane_status,
 395				&dpcd_lane_status_updated,
 396				dpcd_lane_adjust,
 397				hop);
 398		if (status != DC_OK) {
 399			result = LINK_TRAINING_ABORT;
 400			break;
 401		}
 402
 403		/* Check if clock recovery successful. */
 404		if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
 405			DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__);
 406			result = LINK_TRAINING_SUCCESS;
 407			break;
 408		}
 409
 410		result = dp_get_cr_failure(lane_count, dpcd_lane_status);
 411
 412		if (dp_is_max_vs_reached(lt_settings))
 413			break;
 414
 415		/* Count number of attempts with same drive settings.
 416		 * Note: settings are the same for all lanes,
 417		 * so comparing first lane is sufficient.
 418		 */
 419		if ((lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
 420				dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
 421				&& (lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET ==
 422						dpcd_lane_adjust[0].bits.PRE_EMPHASIS_LANE))
 423			retries_cr++;
 424		else
 425			retries_cr = 0;
 426
 427		/* Update VS/PE. */
 428		dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
 429				lt_settings->hw_lane_settings,
 430				lt_settings->dpcd_lane_settings);
 431		retry_count++;
 432	}
 433
 434	DC_LOG_HW_LINK_TRAINING(
 435		"%s\n DPIA(%d) clock recovery\n -hop(%d)\n - result(%d)\n - retries(%d)\n - status(%d)\n",
 436		__func__,
 437		link->link_id.enum_id - ENUM_ID_1,
 438		hop,
 439		result,
 440		retry_count,
 441		status);
 442
 443	return result;
 444}
 445
 446/* Execute clock recovery phase of link training in transparent LTTPR mode:
 447 * - Driver only issues DPCD transactions and leaves USB4 tunneling (SET_CONFIG) messages to DPIA.
 448 * - Driver writes TPS1 to DPCD to kick off training.
 449 * - Clock recovery (CR) for link is handled by DPOA, which reports result to DPIA on completion.
 450 * - DPIA communicates result to driver by updating CR status when driver reads DPCD.
 451 *
 452 * @param link DPIA link being trained.
 453 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
 454 */
 455static enum link_training_result dpia_training_cr_transparent(
 456		struct dc_link *link,
 457		const struct link_resource *link_res,
 458		struct link_training_settings *lt_settings)
 459{
 460	enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0;
 461	enum dc_status status;
 462	uint32_t retries_cr = 0; /* Number of consecutive attempts with same VS or PE. */
 463	uint32_t retry_count = 0;
 464	uint32_t wait_time_microsec = lt_settings->cr_pattern_time;
 465	enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
 466	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
 467	union lane_align_status_updated dpcd_lane_status_updated = {0};
 468	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
 469
 470	/* Cap of LINK_TRAINING_MAX_CR_RETRY attempts at clock recovery.
 471	 * Fix inherited from perform_clock_recovery_sequence() -
 472	 * the DP equivalent of this function:
 473	 * Required for Synaptics MST hub which can put the LT in
 474	 * infinite loop by switching the VS between level 0 and level 1
 475	 * continuously.
 476	 */
 477	while ((retries_cr < LINK_TRAINING_MAX_RETRY_COUNT) &&
 478			(retry_count < LINK_TRAINING_MAX_CR_RETRY)) {
 479
 480		/* Write TPS1 (not VS or PE) to DPCD to start CR phase.
 481		 * DPIA sends SET_CONFIG(SET_LINK) to notify DPOA to
 482		 * start link training.
 483		 */
 484		if (retry_count == 0) {
 485			status = dpcd_set_lt_pattern(link, lt_settings->pattern_for_cr, DPRX);
 486			if (status != DC_OK) {
 487				result = LINK_TRAINING_ABORT;
 488				break;
 489			}
 490		}
 491
 492		dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
 493
 494		/* Read status and adjustment requests from DPCD. */
 495		status = dp_get_lane_status_and_lane_adjust(
 496				link,
 497				lt_settings,
 498				dpcd_lane_status,
 499				&dpcd_lane_status_updated,
 500				dpcd_lane_adjust,
 501				DPRX);
 502		if (status != DC_OK) {
 503			result = LINK_TRAINING_ABORT;
 504			break;
 505		}
 506
 507		/* Check if clock recovery successful. */
 508		if (dp_is_cr_done(lane_count, dpcd_lane_status)) {
 509			DC_LOG_HW_LINK_TRAINING("%s: Clock recovery OK\n", __func__);
 510			result = LINK_TRAINING_SUCCESS;
 511			break;
 512		}
 513
 514		result = dp_get_cr_failure(lane_count, dpcd_lane_status);
 515
 516		if (dp_is_max_vs_reached(lt_settings))
 517			break;
 518
 519		/* Count number of attempts with same drive settings.
 520		 * Note: settings are the same for all lanes,
 521		 * so comparing first lane is sufficient.
 522		 */
 523		if ((lt_settings->dpcd_lane_settings[0].bits.VOLTAGE_SWING_SET ==
 524				dpcd_lane_adjust[0].bits.VOLTAGE_SWING_LANE)
 525				&& (lt_settings->dpcd_lane_settings[0].bits.PRE_EMPHASIS_SET ==
 526						dpcd_lane_adjust[0].bits.PRE_EMPHASIS_LANE))
 527			retries_cr++;
 528		else
 529			retries_cr = 0;
 530
 531		/* Update VS/PE. */
 532		dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
 533				lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
 534		retry_count++;
 535	}
 536
 537	DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) clock recovery\n -hop(%d)\n - result(%d)\n - retries(%d)\n",
 538		__func__,
 539		link->link_id.enum_id - ENUM_ID_1,
 540		DPRX,
 541		result,
 542		retry_count);
 543
 544	return result;
 545}
 546
 547/* Execute clock recovery phase of link training for specified hop in display
 548 * path.
 549 *
 550 * @param link DPIA link being trained.
 551 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
 552 * @param hop Hop in display path. DPRX = 0.
 553 */
 554static enum link_training_result dpia_training_cr_phase(
 555		struct dc_link *link,
 556		const struct link_resource *link_res,
 557		struct link_training_settings *lt_settings,
 558		uint32_t hop)
 559{
 560	enum link_training_result result = LINK_TRAINING_CR_FAIL_LANE0;
 561
 562	if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
 563		result = dpia_training_cr_non_transparent(link, link_res, lt_settings, hop);
 564	else
 565		result = dpia_training_cr_transparent(link, link_res, lt_settings);
 566
 567	return result;
 568}
 569
 570/* Execute equalization phase of link training for specified hop in display
 571 * path in non-transparent mode:
 572 * - driver issues both DPCD and SET_CONFIG transactions.
 573 * - TPSx is transmitted for any hops downstream of DPOA.
 574 * - Drive (VS/PE) only transmitted for the hop immediately downstream of DPOA.
 575 * - EQ for the first hop (DPTX-to-DPIA) is assumed to be successful.
 576 * - DPRX EQ only reported successful when both DPRX and DPIA requirements (clk sync packets sent) fulfilled.
 577 *
 578 * @param link DPIA link being trained.
 579 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
 580 * @param hop Hop in display path. DPRX = 0.
 581 */
 582static enum link_training_result dpia_training_eq_non_transparent(
 583		struct dc_link *link,
 584		const struct link_resource *link_res,
 585		struct link_training_settings *lt_settings,
 586		uint32_t hop)
 587{
 588	enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ;
 589	uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
 590	uint32_t retries_eq = 0;
 591	enum dc_status status = DC_ERROR_UNEXPECTED;
 592	enum dc_dp_training_pattern tr_pattern;
 593	uint32_t wait_time_microsec = 0;
 594	enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
 595	union lane_align_status_updated dpcd_lane_status_updated = {0};
 596	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
 597	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
 598	uint8_t set_cfg_data;
 599	enum dpia_set_config_ts ts;
 600
 601	/* Training pattern is TPS4 for repeater;
 602	 * TPS2/3/4 for DPRX depending on what it supports.
 603	 */
 604	if (hop == DPRX)
 605		tr_pattern = lt_settings->pattern_for_eq;
 606	else
 607		tr_pattern = DP_TRAINING_PATTERN_SEQUENCE_4;
 608
 609	repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
 610
 611	for (retries_eq = 0; retries_eq < LINK_TRAINING_MAX_RETRY_COUNT; retries_eq++) {
 612
 613		/* DPTX-to-DPIA equalization always successful. */
 614		if (hop == repeater_cnt) {
 615			result = LINK_TRAINING_SUCCESS;
 616			break;
 617		}
 618
 619		/* Instruct DPOA to transmit TPSn then update DPCD. */
 620		if (retries_eq == 0) {
 621			status = convert_trng_ptn_to_trng_stg(tr_pattern, &ts);
 622			if (status != DC_OK) {
 623				result = LINK_TRAINING_ABORT;
 624				break;
 625			}
 626			status = core_link_send_set_config(
 627					link,
 628					DPIA_SET_CFG_SET_TRAINING,
 629					ts);
 630			if (status != DC_OK) {
 631				result = LINK_TRAINING_ABORT;
 632				break;
 633			}
 634			status = dpcd_set_lt_pattern(link, tr_pattern, hop);
 635			if (status != DC_OK) {
 636				result = LINK_TRAINING_ABORT;
 637				break;
 638			}
 639		}
 640
 641		/* Update DPOA drive settings then DPCD. DPOA only adjusts
 642		 * drive settings for hop immediately downstream.
 643		 */
 644		if (hop == repeater_cnt - 1) {
 645			set_cfg_data = dpia_build_set_config_data(
 646					DPIA_SET_CFG_SET_VSPE,
 647					link,
 648					lt_settings);
 649			status = core_link_send_set_config(
 650					link,
 651					DPIA_SET_CFG_SET_VSPE,
 652					set_cfg_data);
 653			if (status != DC_OK) {
 654				result = LINK_TRAINING_ABORT;
 655				break;
 656			}
 657		}
 658		status = dpcd_set_lane_settings(link, lt_settings, hop);
 659		if (status != DC_OK) {
 660			result = LINK_TRAINING_ABORT;
 661			break;
 662		}
 663
 664		/* Extend wait time on second equalisation attempt on final hop to
 665		 * ensure clock sync packets have been sent.
 666		 */
 667		if (hop == DPRX && retries_eq == 1)
 668			wait_time_microsec = max(wait_time_microsec, (uint32_t) DPIA_CLK_SYNC_DELAY);
 669		else
 670			wait_time_microsec = dpia_get_eq_aux_rd_interval(link, lt_settings, hop);
 671
 672		dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
 673
 674		/* Read status and adjustment requests from DPCD. */
 675		status = dp_get_lane_status_and_lane_adjust(
 676				link,
 677				lt_settings,
 678				dpcd_lane_status,
 679				&dpcd_lane_status_updated,
 680				dpcd_lane_adjust,
 681				hop);
 682		if (status != DC_OK) {
 683			result = LINK_TRAINING_ABORT;
 684			break;
 685		}
 686
 687		/* CR can still fail during EQ phase. Fail training if CR fails. */
 688		if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
 689			result = LINK_TRAINING_EQ_FAIL_CR;
 690			break;
 691		}
 692
 693		if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
 694				dp_is_symbol_locked(link->cur_link_settings.lane_count, dpcd_lane_status) &&
 695				dp_is_interlane_aligned(dpcd_lane_status_updated)) {
 696			result =  LINK_TRAINING_SUCCESS;
 697			break;
 698		}
 699
 700		/* Update VS/PE. */
 701		dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
 702				lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
 703	}
 704
 705	DC_LOG_HW_LINK_TRAINING(
 706		"%s\n DPIA(%d) equalization\n - hop(%d)\n - result(%d)\n - retries(%d)\n - status(%d)\n",
 707		__func__,
 708		link->link_id.enum_id - ENUM_ID_1,
 709		hop,
 710		result,
 711		retries_eq,
 712		status);
 713
 714	return result;
 715}
 716
 717/* Execute equalization phase of link training for specified hop in display
 718 * path in transparent LTTPR mode:
 719 * - driver only issues DPCD transactions leaves USB4 tunneling (SET_CONFIG) messages to DPIA.
 720 * - driver writes TPSx to DPCD to notify DPIA that is in equalization phase.
 721 * - equalization (EQ) for link is handled by DPOA, which reports result to DPIA on completion.
 722 * - DPIA communicates result to driver by updating EQ status when driver reads DPCD.
 723 *
 724 * @param link DPIA link being trained.
 725 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
 726 * @param hop Hop in display path. DPRX = 0.
 727 */
 728static enum link_training_result dpia_training_eq_transparent(
 729		struct dc_link *link,
 730		const struct link_resource *link_res,
 731		struct link_training_settings *lt_settings)
 732{
 733	enum link_training_result result = LINK_TRAINING_EQ_FAIL_EQ;
 734	uint32_t retries_eq = 0;
 735	enum dc_status status;
 736	enum dc_dp_training_pattern tr_pattern = lt_settings->pattern_for_eq;
 737	uint32_t wait_time_microsec;
 738	enum dc_lane_count lane_count = lt_settings->link_settings.lane_count;
 739	union lane_align_status_updated dpcd_lane_status_updated = {0};
 740	union lane_status dpcd_lane_status[LANE_COUNT_DP_MAX] = {0};
 741	union lane_adjust dpcd_lane_adjust[LANE_COUNT_DP_MAX] = {0};
 742
 743	wait_time_microsec = dpia_get_eq_aux_rd_interval(link, lt_settings, DPRX);
 744
 745	for (retries_eq = 0; retries_eq < LINK_TRAINING_MAX_RETRY_COUNT; retries_eq++) {
 746
 747		if (retries_eq == 0) {
 748			status = dpcd_set_lt_pattern(link, tr_pattern, DPRX);
 749			if (status != DC_OK) {
 750				result = LINK_TRAINING_ABORT;
 751				break;
 752			}
 753		}
 754
 755		dp_wait_for_training_aux_rd_interval(link, wait_time_microsec);
 756
 757		/* Read status and adjustment requests from DPCD. */
 758		status = dp_get_lane_status_and_lane_adjust(
 759				link,
 760				lt_settings,
 761				dpcd_lane_status,
 762				&dpcd_lane_status_updated,
 763				dpcd_lane_adjust,
 764				DPRX);
 765		if (status != DC_OK) {
 766			result = LINK_TRAINING_ABORT;
 767			break;
 768		}
 769
 770		/* CR can still fail during EQ phase. Fail training if CR fails. */
 771		if (!dp_is_cr_done(lane_count, dpcd_lane_status)) {
 772			result = LINK_TRAINING_EQ_FAIL_CR;
 773			break;
 774		}
 775
 776		if (dp_is_ch_eq_done(lane_count, dpcd_lane_status) &&
 777				dp_is_symbol_locked(link->cur_link_settings.lane_count, dpcd_lane_status)) {
 778			/* Take into consideration corner case for DP 1.4a LL Compliance CTS as USB4
 779			 * has to share encoders unlike DP and USBC
 780			 */
 781			if (dp_is_interlane_aligned(dpcd_lane_status_updated) || (link->skip_fallback_on_link_loss && retries_eq)) {
 782				result =  LINK_TRAINING_SUCCESS;
 783				break;
 784			}
 785		}
 786
 787		/* Update VS/PE. */
 788		dp_decide_lane_settings(lt_settings, dpcd_lane_adjust,
 789				lt_settings->hw_lane_settings, lt_settings->dpcd_lane_settings);
 790	}
 791
 792	DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) equalization\n - hop(%d)\n - result(%d)\n - retries(%d)\n",
 793		__func__,
 794		link->link_id.enum_id - ENUM_ID_1,
 795		DPRX,
 796		result,
 797		retries_eq);
 798
 799	return result;
 800}
 801
 802/* Execute equalization phase of link training for specified hop in display
 803 * path.
 804 *
 805 * @param link DPIA link being trained.
 806 * @param lt_settings link_setting and drive settings (voltage swing and pre-emphasis).
 807 * @param hop Hop in display path. DPRX = 0.
 808 */
 809static enum link_training_result dpia_training_eq_phase(
 810		struct dc_link *link,
 811		const struct link_resource *link_res,
 812		struct link_training_settings *lt_settings,
 813		uint32_t hop)
 814{
 815	enum link_training_result result;
 816
 817	if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
 818		result = dpia_training_eq_non_transparent(link, link_res, lt_settings, hop);
 819	else
 820		result = dpia_training_eq_transparent(link, link_res, lt_settings);
 821
 822	return result;
 823}
 824
 825/* End training of specified hop in display path. */
 826static enum dc_status dpcd_clear_lt_pattern(
 827	struct dc_link *link,
 828	uint32_t hop)
 829{
 830	union dpcd_training_pattern dpcd_pattern = {0};
 831	uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET;
 832	enum dc_status status;
 833
 834	if (hop != DPRX)
 835		dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
 836			((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1));
 837
 838	status = core_link_write_dpcd(
 839			link,
 840			dpcd_tps_offset,
 841			&dpcd_pattern.raw,
 842			sizeof(dpcd_pattern.raw));
 843
 844	return status;
 845}
 846
 847/* End training of specified hop in display path.
 848 *
 849 * In transparent LTTPR mode:
 850 * - driver clears training pattern for the specified hop in DPCD.
 851 * In non-transparent LTTPR mode:
 852 * - in addition to clearing training pattern, driver issues USB4 tunneling
 853 * (SET_CONFIG) messages to notify DPOA when training is done for first hop
 854 * (DPTX-to-DPIA) and last hop (DPRX).
 855 *
 856 * @param link DPIA link being trained.
 857 * @param hop Hop in display path. DPRX = 0.
 858 */
 859static enum link_training_result dpia_training_end(
 860		struct dc_link *link,
 861		struct link_training_settings *lt_settings,
 862		uint32_t hop)
 863{
 864	enum link_training_result result = LINK_TRAINING_SUCCESS;
 865	uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
 866	enum dc_status status;
 867
 868	if (lt_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) {
 869
 870		repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
 871
 872		if (hop == repeater_cnt) { /* DPTX-to-DPIA */
 873			/* Send SET_CONFIG(SET_TRAINING:0xff) to notify DPOA that
 874			 * DPTX-to-DPIA hop trained. No DPCD write needed for first hop.
 875			 */
 876			status = core_link_send_set_config(
 877					link,
 878					DPIA_SET_CFG_SET_TRAINING,
 879					DPIA_TS_UFP_DONE);
 880			if (status != DC_OK)
 881				result = LINK_TRAINING_ABORT;
 882		} else { /* DPOA-to-x */
 883			/* Write 0x0 to TRAINING_PATTERN_SET */
 884			status = dpcd_clear_lt_pattern(link, hop);
 885			if (status != DC_OK)
 886				result = LINK_TRAINING_ABORT;
 887		}
 888
 889		/* Notify DPOA that non-transparent link training of DPRX done. */
 890		if (hop == DPRX && result != LINK_TRAINING_ABORT) {
 891			status = core_link_send_set_config(
 892					link,
 893					DPIA_SET_CFG_SET_TRAINING,
 894					DPIA_TS_DPRX_DONE);
 895			if (status != DC_OK)
 896				result = LINK_TRAINING_ABORT;
 897		}
 898
 899	} else { /* non-LTTPR or transparent LTTPR. */
 900
 901		/* Write 0x0 to TRAINING_PATTERN_SET */
 902		status = dpcd_clear_lt_pattern(link, hop);
 903		if (status != DC_OK)
 904			result = LINK_TRAINING_ABORT;
 905
 906	}
 907
 908	DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) end\n - hop(%d)\n - result(%d)\n - LTTPR mode(%d)\n",
 909		__func__,
 910		link->link_id.enum_id - ENUM_ID_1,
 911		hop,
 912		result,
 913		lt_settings->lttpr_mode);
 914
 915	return result;
 916}
 917
 918/* Return status read interval during equalization phase. */
 919uint32_t dpia_get_eq_aux_rd_interval(
 920		const struct dc_link *link,
 921		const struct link_training_settings *lt_settings,
 922		uint32_t hop)
 923{
 924	/* Check debug option for extending aux read interval. */
 925	if (link->dc->debug.dpia_debug.bits.extend_aux_rd_interval)
 926		return DPIA_DEBUG_EXTENDED_AUX_RD_INTERVAL_US;
 927	else if (hop == DPRX)
 928		return lt_settings->eq_pattern_time;
 929	else
 930		return dp_translate_training_aux_read_interval(
 931					link->dpcd_caps.lttpr_caps.aux_rd_interval[hop - 1]);
 932}
 933
 934/* When aborting training of specified hop in display path, clean up by:
 935 * - Attempting to clear DPCD TRAINING_PATTERN_SET, LINK_BW_SET and LANE_COUNT_SET.
 936 * - Sending SET_CONFIG(SET_LINK) with lane count and link rate set to 0.
 937 *
 938 * @param link DPIA link being trained.
 939 * @param hop Hop in display path. DPRX = 0.
 940 */
 941void dpia_training_abort(
 942		struct dc_link *link,
 943		struct link_training_settings *lt_settings,
 944		uint32_t hop)
 945{
 946	uint8_t data = 0;
 947	uint32_t dpcd_tps_offset = DP_TRAINING_PATTERN_SET;
 948
 949	DC_LOG_HW_LINK_TRAINING("%s\n DPIA(%d) aborting\n - LTTPR mode(%d)\n - HPD(%d)\n",
 950		__func__,
 951		link->link_id.enum_id - ENUM_ID_1,
 952		lt_settings->lttpr_mode,
 953		link->is_hpd_pending);
 954
 955	/* Abandon clean-up if sink unplugged. */
 956	if (link->is_hpd_pending)
 957		return;
 958
 959	if (hop != DPRX)
 960		dpcd_tps_offset = DP_TRAINING_PATTERN_SET_PHY_REPEATER1 +
 961			((DP_REPEATER_CONFIGURATION_AND_STATUS_SIZE) * (hop - 1));
 962
 963	core_link_write_dpcd(link, dpcd_tps_offset, &data, 1);
 964	core_link_write_dpcd(link, DP_LINK_BW_SET, &data, 1);
 965	core_link_write_dpcd(link, DP_LANE_COUNT_SET, &data, 1);
 966
 967	if (!link->dc->config.consolidated_dpia_dp_lt)
 968		core_link_send_set_config(link, DPIA_SET_CFG_SET_LINK, data);
 969}
 970
 971void dpia_set_tps_notification(
 972		struct dc_link *link,
 973		const struct link_training_settings *lt_settings,
 974		uint8_t pattern,
 975		uint32_t hop)
 976{
 977	uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
 978
 979	if (lt_settings->lttpr_mode != LTTPR_MODE_NON_TRANSPARENT || pattern == DPCD_TRAINING_PATTERN_VIDEOIDLE)
 980		return;
 981
 982	repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
 983
 984	if (hop != repeater_cnt)
 985		dc_process_dmub_dpia_set_tps_notification(link->ctx->dc, link->link_index, pattern);
 986}
 987
 988enum link_training_result dpia_perform_link_training(
 989	struct dc_link *link,
 990	const struct link_resource *link_res,
 991	const struct dc_link_settings *link_setting,
 992	bool skip_video_pattern)
 993{
 994	enum link_training_result result;
 995	struct link_training_settings lt_settings = {0};
 996	uint8_t repeater_cnt = 0; /* Number of hops/repeaters in display path. */
 997	int8_t repeater_id; /* Current hop. */
 998
 999	struct dc_link_settings link_settings = *link_setting; // non-const copy to pass in
1000
1001	lt_settings.lttpr_mode = dp_decide_lttpr_mode(link, &link_settings);
1002
1003	/* Configure link as prescribed in link_setting and set LTTPR mode. */
1004	result = dpia_configure_link(link, link_res, link_setting, &lt_settings);
1005	if (result != LINK_TRAINING_SUCCESS)
1006		return result;
1007
1008	if (lt_settings.lttpr_mode == LTTPR_MODE_NON_TRANSPARENT)
1009		repeater_cnt = dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt);
1010
1011	/* Train each hop in turn starting with the one closest to DPTX.
1012	 * In transparent or non-LTTPR mode, train only the final hop (DPRX).
1013	 */
1014	for (repeater_id = repeater_cnt; repeater_id >= 0; repeater_id--) {
1015		/* Clock recovery. */
1016		result = dpia_training_cr_phase(link, link_res, &lt_settings, repeater_id);
1017		if (result != LINK_TRAINING_SUCCESS)
1018			break;
1019
1020		/* Equalization. */
1021		result = dpia_training_eq_phase(link, link_res, &lt_settings, repeater_id);
1022		if (result != LINK_TRAINING_SUCCESS)
1023			break;
1024
1025		/* Stop training hop. */
1026		result = dpia_training_end(link, &lt_settings, repeater_id);
1027		if (result != LINK_TRAINING_SUCCESS)
1028			break;
1029	}
1030
1031	/* Double-check link status if training successful; gracefully abort
1032	 * training of current hop if training failed due to message tunneling
1033	 * failure; end training of hop if training ended conventionally and
1034	 * falling back to lower bandwidth settings possible.
1035	 */
1036	if (result == LINK_TRAINING_SUCCESS) {
1037		fsleep(5000);
1038		if (!link->skip_fallback_on_link_loss)
1039			result = dp_check_link_loss_status(link, &lt_settings);
1040	} else if (result == LINK_TRAINING_ABORT)
1041		dpia_training_abort(link, &lt_settings, repeater_id);
1042	else
1043		dpia_training_end(link, &lt_settings, repeater_id);
1044
1045	return result;
1046}
~/ Read-only file preview.