From: Ilya Bakoulin <[email protected]>

[Why/How]
Current method of keeping track of the LT timeout can cause the actual
elapsed time to creep past the spec-defined limits.

Adjust the behavior to make timeouts more consistent.

Signed-off-by: Ilya Bakoulin <[email protected]>
Signed-off-by: Fangzhi Zuo <[email protected]>
Reviewed-by: Chris Park <[email protected]>
Reviewed-by: Charlene Liu <[email protected]>
---
 .../display/dc/link/protocols/link_hdmi_frl.c | 58 +++++++++++--------
 1 file changed, 33 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c 
b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
index 7f930097da18..2f1b8c0bc8f8 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -442,9 +442,9 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
        union hdmi_scdc_status_flags_data status_data = {0};
        union hdmi_scdc_source_test_req test_req = {0};
        union hdmi_scdc_LTP_req_data ltp_req = {0};
-       uint16_t num_polls = 0;
-       uint16_t max_polls = 105;
-       unsigned long long wait_time_ns = 2000000;
+       unsigned long long poll_timeout_ns = 200 * 1000 * 1000;
+       unsigned long long wait_time_ns = 2 * 1000 * 1000;
+       unsigned long long num_polls = 0;
        struct hpo_frl_link_encoder *hpo_frl_link_enc = 
ddc_service->link->hpo_frl_link_enc;
        struct link_encoder *dio_link_enc = ddc_service->link->link_enc;
        uint8_t sink_version = 0;
@@ -452,7 +452,6 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
        uint8_t current_FFE = 0;
        bool override_FFE = false;
        bool flt_no_timeout = false;
-       unsigned long long flt_poll_cur_time = 0, flt_poll_last_time = 0, 
flt_poll_elapsed_time_ns = 0;
 
        DC_LOGGER_INIT(ddc_service->link->ctx->logger);
        FRL_INFO("FRL LINK TRAINING:  Starting FRL Link Training.\n");
@@ -483,18 +482,22 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
        offset = HDMI_SCDC_STATUS_FLAGS;
 
        /*LTS:2: Check FLT Ready, poll for 200ms */
-       while (num_polls < max_polls) {
-               flt_poll_cur_time = dm_get_timestamp(ddc_service->ctx);
-               flt_poll_elapsed_time_ns = 
dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_cur_time, 
flt_poll_last_time);
-               if (flt_poll_elapsed_time_ns < wait_time_ns)
+       unsigned long long flt_poll_start = dm_get_timestamp(ddc_service->ctx);
+       unsigned long long flt_poll_curr = flt_poll_start;
+       unsigned long long elapsed = 0;
+
+       do {
+               flt_poll_curr = dm_get_timestamp(ddc_service->ctx);
+                                       elapsed = 
dm_get_elapse_time_in_ns(ddc_service->ctx,
+                                               flt_poll_curr, flt_poll_start);
+               if (elapsed < (num_polls * wait_time_ns))
                        continue;
-               flt_poll_last_time = dm_get_timestamp(ddc_service->ctx);
                num_polls++;
 
                link_query_ddc_data(ddc_service, slave_address,
                                &offset, sizeof(offset), &status_data.byte,
                                sizeof(status_data.byte));
-               FRL_INFO("FRL LINK TRAINING:  Read FLT_READY = %d.  num_polls = 
%d\n",
+               FRL_INFO("FRL LINK TRAINING:  Read FLT_READY = %d.  num_polls = 
%llu\n",
                                status_data.fields.FLT_READY, num_polls);
                if (status_data.fields.FLT_READY) {
                        /* Spec recommends to clear update flag, but QD980 has 
problem */
@@ -506,7 +509,7 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                                        link_settings);
                        break;
                }
-       }
+       } while (elapsed < poll_timeout_ns);
 
        /*Test Condition - FLT_no_timeout avoid link training*/
        offset = HDMI_SCDC_SOURCE_TEST_REQ;
@@ -531,15 +534,19 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                /*Start FLT Timer = 200 ms, or 300ms if link rate >= 16Gbps*/
                num_polls = 0;
                if (link_settings->frl_link_rate >= HDMI_FRL_LINK_RATE_16GBPS)
-                       max_polls = 155;
-
-               while (num_polls < max_polls) {
-                       flt_poll_cur_time = dm_get_timestamp(ddc_service->ctx);
-                       flt_poll_elapsed_time_ns = 
dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_cur_time, 
flt_poll_last_time);
-                       if (flt_poll_elapsed_time_ns < wait_time_ns)
+                       poll_timeout_ns = 300 * 1000 * 1000;
+               if (flt_no_timeout)
+                       poll_timeout_ns = 1000 * 1000 * 1000;
+
+               flt_poll_start = dm_get_timestamp(ddc_service->ctx);
+               uint8_t prev_flt_update = 0;
+
+               do {
+                       flt_poll_curr = dm_get_timestamp(ddc_service->ctx);
+                                       elapsed = 
dm_get_elapse_time_in_ns(ddc_service->ctx,
+                                               flt_poll_curr, flt_poll_start);
+                       if (elapsed < (num_polls * wait_time_ns))
                                continue;
-                       flt_poll_last_time = flt_poll_cur_time;
-
                        num_polls++;
 
                        offset = HDMI_SCDC_UPDATE_0;
@@ -548,8 +555,12 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                                                        &offset, 
sizeof(offset), &scdc_update.byte[0],
                                                        
sizeof(scdc_update.byte[0]));
 
-                       FRL_INFO("FRL LINK TRAINING:  Read FLT_UPDATE = %d.  
num_polls = %d\n",
-                                       scdc_update.fields.FLT_UPDATE, 
num_polls);
+                       if (prev_flt_update != scdc_update.fields.FLT_UPDATE) {
+                               FRL_INFO("FRL LINK TRAINING: FLT_UPDATE=%d 
polls=%llu\n",
+                                               scdc_update.fields.FLT_UPDATE, 
num_polls);
+                               prev_flt_update = scdc_update.fields.FLT_UPDATE;
+                       }
+
                        /*Set TxFFE = TxFFE0*/
                        /*Program FFE_Levels - scdc_config has this field at 0 
*/
                        if (override_FFE) {
@@ -680,11 +691,8 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                                /* Workaround for DEDCN3AG-111
                                 * HDMI-FRL Incorrect Serialization Order for 
LTP4
                                 */
-                               if (flt_no_timeout) {
-                                       return LINK_RESULT_SUCCESS;
-                               }
                        }
-               }
+               } while (elapsed < poll_timeout_ns);
                FRL_INFO("FRL LINK TRAINING:  FAILED - Timeout waiting for 
FLT_UPDATE to be set by sink.\n");
                write_buffer[0] = HDMI_SCDC_CONFIG_1;
                /*FRL_RATE*/
-- 
2.53.0

Reply via email to