Commit b473bd5f authored by Lewis Huang's avatar Lewis Huang Committed by Alex Deucher

drm/amd/display: refine wake up aux in retrieve link caps

[Why]
Read set_power_state dpcd after HPD cause USB4 CTS 4.2.1.1

[How]
Read LTTPR caps first. If aux channel not ready, wake up aux channel.
If wake up aux channel return pass, retrieve lttpr caps again. If wake
up aux channel return false, register a detection retry timer.
Tested-by: default avatarMark Broadworth <mark.broadworth@amd.com>
Reviewed-by: default avatarWenjing Liu <Wenjing.Liu@amd.com>
Acked-by: default avatarRodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: default avatarLewis Huang <Lewis.Huang@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent f1943a51
......@@ -5031,7 +5031,7 @@ static bool dpcd_read_sink_ext_caps(struct dc_link *link)
return true;
}
bool dp_retrieve_lttpr_cap(struct dc_link *link)
enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link)
{
uint8_t lttpr_dpcd_data[8];
enum dc_status status = DC_ERROR_UNEXPECTED;
......@@ -5099,7 +5099,7 @@ bool dp_retrieve_lttpr_cap(struct dc_link *link)
CONN_DATA_DETECT(link, lttpr_dpcd_data, sizeof(lttpr_dpcd_data), "LTTPR Caps: ");
DC_LOG_DC("is_lttpr_present = %d\n", is_lttpr_present);
return is_lttpr_present;
return status;
}
bool dp_is_lttpr_present(struct dc_link *link)
......@@ -5227,76 +5227,45 @@ static void retrieve_cable_id(struct dc_link *link)
&link->dpcd_caps.cable_id, &usbc_cable_id);
}
/* DPRX may take some time to respond to AUX messages after HPD asserted.
* If AUX read unsuccessful, try to wake unresponsive DPRX by toggling DPCD SET_POWER (0x600).
*/
static enum dc_status wa_try_to_wake_dprx(struct dc_link *link, uint64_t timeout_ms)
enum dc_status wake_up_aux_channel(struct dc_link *link)
{
enum dc_status status = DC_ERROR_UNEXPECTED;
uint8_t dpcd_data = 0;
uint64_t start_ts = 0;
uint64_t current_ts = 0;
uint64_t time_taken_ms = 0;
enum dc_connection_type type = dc_connection_none;
bool lttpr_present;
bool vbios_lttpr_interop = link->dc->caps.vbios_lttpr_aware;
uint32_t aux_channel_retry_cnt = 0;
uint8_t dpcd_power_state = '\0';
lttpr_present = dp_is_lttpr_present(link) ||
(!vbios_lttpr_interop || !link->dc->caps.extended_aux_timeout_support);
DC_LOG_DC("lttpr_present = %d.\n", lttpr_present ? 1 : 0);
while (status != DC_OK && aux_channel_retry_cnt < 10) {
status = core_link_read_dpcd(link, DP_SET_POWER,
&dpcd_power_state, sizeof(dpcd_power_state));
/* Issue an AUX read to test DPRX responsiveness. If LTTPR is supported the first read is expected to
* be to determine LTTPR capabilities. Otherwise trying to read power state should be an innocuous AUX read.
*/
if (lttpr_present)
status = core_link_read_dpcd(
link,
DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
&dpcd_data,
sizeof(dpcd_data));
else
status = core_link_read_dpcd(
link,
DP_SET_POWER,
&dpcd_data,
sizeof(dpcd_data));
/* Delay 1 ms if AUX CH is in power down state. Based on spec
* section 2.3.1.2, if AUX CH may be powered down due to
* write to DPCD 600h = 2. Sink AUX CH is monitoring differential
* signal and may need up to 1 ms before being able to reply.
*/
if (status != DC_OK || dpcd_power_state == DP_SET_POWER_D3) {
udelay(1000);
aux_channel_retry_cnt++;
}
}
if (status != DC_OK) {
DC_LOG_WARNING("%s: Read DPCD LTTPR_CAP failed - try to toggle DPCD SET_POWER for %lld ms.",
__func__,
timeout_ms);
start_ts = dm_get_timestamp(link->ctx);
do {
if (!dc_link_detect_sink(link, &type) || type == dc_connection_none)
break;
dpcd_data = DP_SET_POWER_D3;
status = core_link_write_dpcd(
link,
DP_SET_POWER,
&dpcd_data,
sizeof(dpcd_data));
dpcd_data = DP_SET_POWER_D0;
status = core_link_write_dpcd(
link,
DP_SET_POWER,
&dpcd_data,
sizeof(dpcd_data));
current_ts = dm_get_timestamp(link->ctx);
time_taken_ms = div_u64(dm_get_elapse_time_in_ns(link->ctx, current_ts, start_ts), 1000000);
} while (status != DC_OK && time_taken_ms < timeout_ms);
dpcd_power_state = DP_SET_POWER_D0;
status = core_link_write_dpcd(
link,
DP_SET_POWER,
&dpcd_power_state,
sizeof(dpcd_power_state));
DC_LOG_WARNING("%s: DPCD SET_POWER %s after %lld ms%s",
__func__,
(status == DC_OK) ? "succeeded" : "failed",
time_taken_ms,
(type == dc_connection_none) ? ". Unplugged." : ".");
dpcd_power_state = DP_SET_POWER_D3;
status = core_link_write_dpcd(
link,
DP_SET_POWER,
&dpcd_power_state,
sizeof(dpcd_power_state));
return DC_ERROR_UNEXPECTED;
}
return status;
return DC_OK;
}
static bool retrieve_link_cap(struct dc_link *link)
......@@ -5308,7 +5277,6 @@ static bool retrieve_link_cap(struct dc_link *link)
/*Only need to read 1 byte starting from DP_DPRX_FEATURE_ENUMERATION_LIST.
*/
uint8_t dpcd_dprx_data = '\0';
uint8_t dpcd_power_state = '\0';
struct dp_device_vendor_id sink_id;
union down_stream_port_count down_strm_port_count;
......@@ -5316,11 +5284,9 @@ static bool retrieve_link_cap(struct dc_link *link)
union dp_downstream_port_present ds_port = { 0 };
enum dc_status status = DC_ERROR_UNEXPECTED;
uint32_t read_dpcd_retry_cnt = 3;
uint32_t aux_channel_retry_cnt = 0;
int i;
struct dp_sink_hw_fw_revision dp_hw_fw_revision;
const uint32_t post_oui_delay = 30; // 30ms
bool is_lttpr_present = false;
memset(dpcd_data, '\0', sizeof(dpcd_data));
memset(&down_strm_port_count,
......@@ -5335,51 +5301,17 @@ static bool retrieve_link_cap(struct dc_link *link)
dc_link_aux_try_to_configure_timeout(link->ddc,
LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD);
/* Try to ensure AUX channel active before proceeding. */
if (link->dc->debug.aux_wake_wa.bits.enable_wa) {
uint64_t timeout_ms = link->dc->debug.aux_wake_wa.bits.timeout_ms;
if (link->dc->debug.aux_wake_wa.bits.use_default_timeout)
timeout_ms = LINK_AUX_WAKE_TIMEOUT_MS;
status = wa_try_to_wake_dprx(link, timeout_ms);
}
while (status != DC_OK && aux_channel_retry_cnt < 10) {
status = core_link_read_dpcd(link, DP_SET_POWER,
&dpcd_power_state, sizeof(dpcd_power_state));
/* Delay 1 ms if AUX CH is in power down state. Based on spec
* section 2.3.1.2, if AUX CH may be powered down due to
* write to DPCD 600h = 2. Sink AUX CH is monitoring differential
* signal and may need up to 1 ms before being able to reply.
*/
if (status != DC_OK || dpcd_power_state == DP_SET_POWER_D3) {
udelay(1000);
aux_channel_retry_cnt++;
}
}
status = dp_retrieve_lttpr_cap(link);
/* If aux channel is not active, return false and trigger another detect*/
if (status != DC_OK) {
dpcd_power_state = DP_SET_POWER_D0;
status = core_link_write_dpcd(
link,
DP_SET_POWER,
&dpcd_power_state,
sizeof(dpcd_power_state));
dpcd_power_state = DP_SET_POWER_D3;
status = core_link_write_dpcd(
link,
DP_SET_POWER,
&dpcd_power_state,
sizeof(dpcd_power_state));
return false;
status = wake_up_aux_channel(link);
if (status == DC_OK)
dp_retrieve_lttpr_cap(link);
else
return false;
}
is_lttpr_present = dp_retrieve_lttpr_cap(link);
if (is_lttpr_present)
if (dp_is_lttpr_present(link))
configure_lttpr_mode_transparent(link);
/* Read DP tunneling information. */
......@@ -5406,7 +5338,7 @@ static bool retrieve_link_cap(struct dc_link *link)
return false;
}
if (!is_lttpr_present)
if (!dp_is_lttpr_present(link))
dc_link_aux_try_to_configure_timeout(link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD);
{
......
......@@ -193,7 +193,7 @@ enum dc_status dpcd_configure_lttpr_mode(
struct link_training_settings *lt_settings);
enum dp_link_encoding dp_get_link_encoding_format(const struct dc_link_settings *link_settings);
bool dp_retrieve_lttpr_cap(struct dc_link *link);
enum dc_status dp_retrieve_lttpr_cap(struct dc_link *link);
bool dp_is_lttpr_present(struct dc_link *link);
enum lttpr_mode dp_decide_lttpr_mode(struct dc_link *link, struct dc_link_settings *link_setting);
void dp_get_lttpr_mode_override(struct dc_link *link, enum lttpr_mode *override);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment