diff options
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.19.8/0967-drm-amd-display-verify-lane-status-before-exiting-ve.patch')
-rw-r--r-- | common/recipes-kernel/linux/linux-yocto-4.19.8/0967-drm-amd-display-verify-lane-status-before-exiting-ve.patch | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.19.8/0967-drm-amd-display-verify-lane-status-before-exiting-ve.patch b/common/recipes-kernel/linux/linux-yocto-4.19.8/0967-drm-amd-display-verify-lane-status-before-exiting-ve.patch new file mode 100644 index 00000000..04e5ac18 --- /dev/null +++ b/common/recipes-kernel/linux/linux-yocto-4.19.8/0967-drm-amd-display-verify-lane-status-before-exiting-ve.patch @@ -0,0 +1,335 @@ +From d081894629b9c3fc134b4641a76ce0e632f1e571 Mon Sep 17 00:00:00 2001 +From: Wenjing Liu <Wenjing.Liu@amd.com> +Date: Mon, 3 Dec 2018 17:26:15 -0500 +Subject: [PATCH 0967/2940] drm/amd/display: verify lane status before exiting + verify link cap + +[why] +DP LL CTS1.4 4.3.2.1 test failure. + +[how] +The failure is caused by not handling DP link loss +hpd short pusle during set mode. The change is to read link status +before set mode link training. If link is lost, re-verify link caps. +Signed-off-by: Wenjing Liu <Wenjing.Liu@amd.com> +Reviewed-by: Jun Lei <Jun.Lei@amd.com> +Acked-by: Leo Li <sunpeng.li@amd.com> +Signed-off-by: Alex Deucher <alexander.deucher@amd.com> +--- + .../gpu/drm/amd/display/dc/core/dc_link_dp.c | 243 +++++++++--------- + .../drm/amd/display/dc/core/dc_link_hwss.c | 3 +- + 2 files changed, 127 insertions(+), 119 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +index 849a3a3032f7..0999102e7130 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +@@ -1089,6 +1089,121 @@ static struct dc_link_settings get_max_link_cap(struct dc_link *link) + return max_link_cap; + } + ++static enum dc_status read_hpd_rx_irq_data( ++ struct dc_link *link, ++ union hpd_irq_data *irq_data) ++{ ++ static enum dc_status retval; ++ ++ /* The HW reads 16 bytes from 200h on HPD, ++ * but if we get an AUX_DEFER, the HW cannot retry ++ * and this causes the CTS tests 4.3.2.1 - 3.2.4 to ++ * fail, so we now explicitly read 6 bytes which is ++ * the req from the above mentioned test cases. ++ * ++ * For DP 1.4 we need to read those from 2002h range. ++ */ ++ if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) ++ retval = core_link_read_dpcd( ++ link, ++ DP_SINK_COUNT, ++ irq_data->raw, ++ sizeof(union hpd_irq_data)); ++ else { ++ /* Read 14 bytes in a single read and then copy only the required fields. ++ * This is more efficient than doing it in two separate AUX reads. */ ++ ++ uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1]; ++ ++ retval = core_link_read_dpcd( ++ link, ++ DP_SINK_COUNT_ESI, ++ tmp, ++ sizeof(tmp)); ++ ++ if (retval != DC_OK) ++ return retval; ++ ++ irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI]; ++ irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; ++ irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI]; ++ irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; ++ irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; ++ irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; ++ } ++ ++ return retval; ++} ++ ++static bool hpd_rx_irq_check_link_loss_status( ++ struct dc_link *link, ++ union hpd_irq_data *hpd_irq_dpcd_data) ++{ ++ uint8_t irq_reg_rx_power_state = 0; ++ enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; ++ union lane_status lane_status; ++ uint32_t lane; ++ bool sink_status_changed; ++ bool return_code; ++ ++ sink_status_changed = false; ++ return_code = false; ++ ++ if (link->cur_link_settings.lane_count == 0) ++ return return_code; ++ ++ /*1. Check that Link Status changed, before re-training.*/ ++ ++ /*parse lane status*/ ++ for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) { ++ /* check status of lanes 0,1 ++ * changed DpcdAddress_Lane01Status (0x202) ++ */ ++ lane_status.raw = get_nibble_at_index( ++ &hpd_irq_dpcd_data->bytes.lane01_status.raw, ++ lane); ++ ++ if (!lane_status.bits.CHANNEL_EQ_DONE_0 || ++ !lane_status.bits.CR_DONE_0 || ++ !lane_status.bits.SYMBOL_LOCKED_0) { ++ /* if one of the channel equalization, clock ++ * recovery or symbol lock is dropped ++ * consider it as (link has been ++ * dropped) dp sink status has changed ++ */ ++ sink_status_changed = true; ++ break; ++ } ++ } ++ ++ /* Check interlane align.*/ ++ if (sink_status_changed || ++ !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { ++ ++ DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); ++ ++ return_code = true; ++ ++ /*2. Check that we can handle interrupt: Not in FS DOS, ++ * Not in "Display Timeout" state, Link is trained. ++ */ ++ dpcd_result = core_link_read_dpcd(link, ++ DP_SET_POWER, ++ &irq_reg_rx_power_state, ++ sizeof(irq_reg_rx_power_state)); ++ ++ if (dpcd_result != DC_OK) { ++ DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n", ++ __func__); ++ } else { ++ if (irq_reg_rx_power_state != DP_SET_POWER_D0) ++ return_code = false; ++ } ++ } ++ ++ return return_code; ++} ++ + bool dp_verify_link_cap( + struct dc_link *link, + struct dc_link_settings *known_limit_link_setting, +@@ -1104,12 +1219,14 @@ bool dp_verify_link_cap( + struct clock_source *dp_cs; + enum clock_source_id dp_cs_id = CLOCK_SOURCE_ID_EXTERNAL; + enum link_training_result status; ++ union hpd_irq_data irq_data; + + if (link->dc->debug.skip_detection_link_training) { + link->verified_link_cap = *known_limit_link_setting; + return true; + } + ++ memset(&irq_data, 0, sizeof(irq_data)); + success = false; + skip_link_training = false; + +@@ -1168,9 +1285,15 @@ bool dp_verify_link_cap( + (*fail_count)++; + } + +- if (success) ++ if (success) { + link->verified_link_cap = *cur; +- ++ udelay(1000); ++ if (read_hpd_rx_irq_data(link, &irq_data) == DC_OK) ++ if (hpd_rx_irq_check_link_loss_status( ++ link, ++ &irq_data)) ++ (*fail_count)++; ++ } + /* always disable the link before trying another + * setting or before returning we'll enable it later + * based on the actual mode we're driving +@@ -1572,122 +1695,6 @@ void decide_link_settings(struct dc_stream_state *stream, + } + + /*************************Short Pulse IRQ***************************/ +- +-static bool hpd_rx_irq_check_link_loss_status( +- struct dc_link *link, +- union hpd_irq_data *hpd_irq_dpcd_data) +-{ +- uint8_t irq_reg_rx_power_state = 0; +- enum dc_status dpcd_result = DC_ERROR_UNEXPECTED; +- union lane_status lane_status; +- uint32_t lane; +- bool sink_status_changed; +- bool return_code; +- +- sink_status_changed = false; +- return_code = false; +- +- if (link->cur_link_settings.lane_count == 0) +- return return_code; +- +- /*1. Check that Link Status changed, before re-training.*/ +- +- /*parse lane status*/ +- for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) { +- /* check status of lanes 0,1 +- * changed DpcdAddress_Lane01Status (0x202) +- */ +- lane_status.raw = get_nibble_at_index( +- &hpd_irq_dpcd_data->bytes.lane01_status.raw, +- lane); +- +- if (!lane_status.bits.CHANNEL_EQ_DONE_0 || +- !lane_status.bits.CR_DONE_0 || +- !lane_status.bits.SYMBOL_LOCKED_0) { +- /* if one of the channel equalization, clock +- * recovery or symbol lock is dropped +- * consider it as (link has been +- * dropped) dp sink status has changed +- */ +- sink_status_changed = true; +- break; +- } +- } +- +- /* Check interlane align.*/ +- if (sink_status_changed || +- !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { +- +- DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); +- +- return_code = true; +- +- /*2. Check that we can handle interrupt: Not in FS DOS, +- * Not in "Display Timeout" state, Link is trained. +- */ +- dpcd_result = core_link_read_dpcd(link, +- DP_SET_POWER, +- &irq_reg_rx_power_state, +- sizeof(irq_reg_rx_power_state)); +- +- if (dpcd_result != DC_OK) { +- DC_LOG_HW_HPD_IRQ("%s: DPCD read failed to obtain power state.\n", +- __func__); +- } else { +- if (irq_reg_rx_power_state != DP_SET_POWER_D0) +- return_code = false; +- } +- } +- +- return return_code; +-} +- +-static enum dc_status read_hpd_rx_irq_data( +- struct dc_link *link, +- union hpd_irq_data *irq_data) +-{ +- static enum dc_status retval; +- +- /* The HW reads 16 bytes from 200h on HPD, +- * but if we get an AUX_DEFER, the HW cannot retry +- * and this causes the CTS tests 4.3.2.1 - 3.2.4 to +- * fail, so we now explicitly read 6 bytes which is +- * the req from the above mentioned test cases. +- * +- * For DP 1.4 we need to read those from 2002h range. +- */ +- if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14) +- retval = core_link_read_dpcd( +- link, +- DP_SINK_COUNT, +- irq_data->raw, +- sizeof(union hpd_irq_data)); +- else { +- /* Read 14 bytes in a single read and then copy only the required fields. +- * This is more efficient than doing it in two separate AUX reads. */ +- +- uint8_t tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI + 1]; +- +- retval = core_link_read_dpcd( +- link, +- DP_SINK_COUNT_ESI, +- tmp, +- sizeof(tmp)); +- +- if (retval != DC_OK) +- return retval; +- +- irq_data->bytes.sink_cnt.raw = tmp[DP_SINK_COUNT_ESI - DP_SINK_COUNT_ESI]; +- irq_data->bytes.device_service_irq.raw = tmp[DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 - DP_SINK_COUNT_ESI]; +- irq_data->bytes.lane01_status.raw = tmp[DP_LANE0_1_STATUS_ESI - DP_SINK_COUNT_ESI]; +- irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; +- irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; +- irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; +- } +- +- return retval; +-} +- + static bool allow_hpd_rx_irq(const struct dc_link *link) + { + /* +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +index 82cd1d6e6e59..0065ec7d5330 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_hwss.c +@@ -96,6 +96,7 @@ void dp_enable_link_phy( + link_settings, + clock_source); + } ++ link->cur_link_settings = *link_settings; + + dp_receiver_power_ctrl(link, true); + } +@@ -307,6 +308,7 @@ void dp_retrain_link_dp_test(struct dc_link *link, + link->link_enc, + link_setting, + pipes[i].clock_source->id); ++ link->cur_link_settings = *link_setting; + + dp_receiver_power_ctrl(link, true); + +@@ -316,7 +318,6 @@ void dp_retrain_link_dp_test(struct dc_link *link, + skip_video_pattern, + LINK_TRAINING_ATTEMPTS); + +- link->cur_link_settings = *link_setting; + + link->dc->hwss.enable_stream(&pipes[i]); + +-- +2.17.1 + |