From: Ilya Bakoulin <[email protected]>

[Why & How]
The FFE electrical compliance test behavior is not correct. The driver
doesn't properly iterate over the FFE levels and select PS/DE based on
test request and/or cable-ID.

Fix the initialization of the link state so that when the polling loop
takes over, the test proceeds as described in the spec.

Reviewed-by: Relja Vojvodic <[email protected]>
Signed-off-by: Ilya Bakoulin <[email protected]>
Signed-off-by: Ray Wu <[email protected]>
---
 .../gpu/drm/amd/display/dc/dc_hdmi_types.h    |   4 +-
 .../display/dc/link/hwss/link_hwss_hpo_frl.c  |   3 +
 .../gpu/drm/amd/display/dc/link/link_dpms.c   |   2 +
 .../display/dc/link/protocols/link_hdmi_frl.c | 127 +++++++++++++-----
 4 files changed, 104 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h 
b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
index eb6e7f4043fd..9e0fe41f28a5 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
@@ -235,7 +235,8 @@ enum link_result {
        LINK_RESULT_SUCCESS,
        LINK_RESULT_LOWER_LINKRATE,
        LINK_RESULT_TIMEOUT,
-       LINK_RESULT_FALLBACK
+       LINK_RESULT_FALLBACK,
+       LINK_RESULT_NO_TIMEOUT
 };
 
 enum hdmi_frl_link_rate {
@@ -274,6 +275,7 @@ struct dc_hdmi_frl_flags {
        bool force_frl_dsc;
        bool force_frl_max;
        bool apply_vsdb_rcc_wa;
+       bool no_timeout;
 };
 
 struct dc_hdmi_frl_link_training_overrides {
diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c 
b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
index a2c0fa21ee94..62288133cd55 100644
--- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
@@ -59,6 +59,9 @@ static void disable_hpo_frl_link_output(struct dc_link *link,
                enum signal_type signal)
 {
        (void)link_res;
+       if (link->frl_flags.no_timeout)
+               return;
+
        if (dc_is_hdmi_frl_signal(signal))
                
link->hpo_frl_link_enc->funcs->disable_link_encoder(link->hpo_frl_link_enc);
        link->link_enc->funcs->disable_output(link->link_enc, signal);
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c 
b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
index 42a47d12d1f3..cde531dd1626 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -2090,6 +2090,8 @@ static enum dc_status enable_link_hdmi_frl(struct 
pipe_ctx *pipe_ctx)
                pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_enable(
                        pipe_ctx->stream_res.hpo_frl_stream_enc,
                        pipe_ctx->stream_res.tg->inst);
+       } else if (link_stat == LINK_RESULT_NO_TIMEOUT) {
+               return status;
        } else {
                status = DC_FAIL_HDMI_FRL_LINK_TRAINING;
                stream->link->frl_link_settings.frl_link_rate = 0;
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 5e0979ac3dcf..52a22b3e78af 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
@@ -451,11 +451,37 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
        uint8_t FFE_Levels = hdmi_frl_get_max_ffe_level(ddc_service->link, 
link_settings);
        uint8_t current_FFE = 0;
        bool override_FFE = false;
-       bool flt_no_timeout = false;
 
        DC_LOGGER_INIT(ddc_service->link->ctx->logger);
        FRL_INFO("FRL LINK TRAINING:  Starting FRL Link Training.\n");
 
+       /*Test Condition - FLT_no_timeout avoid link training*/
+       offset = HDMI_SCDC_SOURCE_TEST_REQ;
+       link_query_ddc_data(ddc_service, slave_address, &offset,
+                       sizeof(offset), &test_req.byte, sizeof(test_req.byte));
+       FRL_INFO("FRL TEST REQ:  FLT_no_timeout = %d \n", 
test_req.fields.FLT_NO_TIMEOUT);
+
+       if (test_req.fields.FLT_NO_TIMEOUT && 
ddc_service->link->frl_flags.no_timeout) {
+               bool de_emphasis_only = false;
+               bool pre_shoot_only = false;
+               bool no_ffe = false;
+
+               read_idcc_data(ddc_service, HDMI_IDCC_SCOPE_RW_CA,
+                       ddc_service->link->hdmi_cable_id.raw, 0,
+                       sizeof(ddc_service->link->hdmi_cable_id.raw));
+
+               hdmi_return_preeshoot_and_deemphasis(ddc_service->link, 
&test_req,
+                       &de_emphasis_only, &pre_shoot_only, &no_ffe);
+
+               dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0,
+                       de_emphasis_only,
+                       pre_shoot_only,
+                       no_ffe,
+                       link_settings);
+
+               return LINK_RESULT_NO_TIMEOUT;
+       }
+
        hdmi_frl_LTS_clear_Link_Setting(ddc_service);
 
        offset = HDMI_SCDC_SINK_VERSION;
@@ -511,13 +537,68 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                }
        } while (elapsed < poll_timeout_ns);
 
-       /*Test Condition - FLT_no_timeout avoid link training*/
-       offset = HDMI_SCDC_SOURCE_TEST_REQ;
-       link_query_ddc_data(ddc_service, slave_address, &offset,
-                       sizeof(offset), &test_req.byte, sizeof(test_req.byte));
-       FRL_INFO("FRL TEST REQ:  FLT_no_timeout = %d \n", 
test_req.fields.FLT_NO_TIMEOUT);
-       if (test_req.fields.FLT_NO_TIMEOUT)
-               flt_no_timeout = true;
+       if (test_req.fields.FLT_NO_TIMEOUT) {
+               ddc_service->link->frl_flags.no_timeout = true;
+
+               /*Specify FRL rate*/
+               write_buffer[0] = HDMI_SCDC_CONFIG_1;
+               /*FRL_RATE*/
+               write_buffer[1] = (uint8_t)(link_settings->frl_link_rate | 
(FFE_Levels << 4));
+               link_query_ddc_data(ddc_service, slave_address,
+                               write_buffer, sizeof(write_buffer), NULL, 0);
+               FRL_INFO("FRL LINK TRAINING:  Write link rate = %d.  Max 
FFE_Levels = %d\n",
+                               link_settings->frl_link_rate, FFE_Levels);
+
+               offset = HDMI_SCDC_UPDATE_0;
+               /*Check FLT_update flag*/
+               link_query_ddc_data(ddc_service, slave_address,
+                                               &offset, sizeof(offset), 
&scdc_update.byte[0],
+                                               sizeof(scdc_update.byte[0]));
+
+               offset = HDMI_SCDC_LTP_REQ;
+               link_query_ddc_data(ddc_service, slave_address,
+                                               &offset, sizeof(offset), 
ltp_req.byte,
+                                               sizeof(ltp_req.byte));
+
+               ln0_pattern = ltp_req.fields.LN0_LTP_REQ;
+               ln1_pattern = ltp_req.fields.LN1_LTP_REQ;
+               ln2_pattern = ltp_req.fields.LN2_LTP_REQ;
+               ln3_pattern = ltp_req.fields.LN3_LTP_REQ;
+
+               hpo_frl_link_enc->funcs->set_hdmi_training_pattern(
+                                       hpo_frl_link_enc,
+                                       ln0_pattern - 1,
+                                       ln1_pattern - 1,
+                                       ln2_pattern - 1,
+                                       ln3_pattern - 1);
+
+               read_idcc_data(ddc_service, HDMI_IDCC_SCOPE_RW_CA,
+                               ddc_service->link->hdmi_cable_id.raw, 0,
+                               sizeof(ddc_service->link->hdmi_cable_id.raw));
+
+               bool de_emphasis_only = false;
+               bool pre_shoot_only = false;
+               bool no_ffe = false;
+
+               hdmi_return_preeshoot_and_deemphasis(ddc_service->link, 
&test_req,
+                       &de_emphasis_only, &pre_shoot_only, &no_ffe);
+
+               dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0,
+                       de_emphasis_only,
+                       pre_shoot_only,
+                       no_ffe,
+                       link_settings);
+
+               write_buffer[0] = HDMI_SCDC_UPDATE_0;
+               /*FLT_update - bit 5*/
+               write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5);
+               link_query_ddc_data(ddc_service, slave_address,
+                               write_buffer, sizeof(write_buffer), NULL, 0);
+
+               return LINK_RESULT_NO_TIMEOUT;
+       }
+
+       ddc_service->link->frl_flags.no_timeout = false;
 
        if (status_data.fields.FLT_READY) {
                /*Specify FRL rate*/
@@ -534,9 +615,7 @@ 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)
-                       poll_timeout_ns = 300 * 1000 * 1000;
-               if (flt_no_timeout)
-                       poll_timeout_ns = 1000 * 1000 * 1000;
+                       poll_timeout_ns = 320 * 1000 * 1000;
 
                flt_poll_start = dm_get_timestamp(ddc_service->ctx);
                uint8_t prev_flt_update = 0;
@@ -561,21 +640,9 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                                prev_flt_update = scdc_update.fields.FLT_UPDATE;
                        }
 
-                       if (flt_no_timeout) {
-                               offset = HDMI_SCDC_SOURCE_TEST_REQ;
-                               link_query_ddc_data(ddc_service, slave_address, 
&offset,
-                                               sizeof(offset), &test_req.byte, 
sizeof(test_req.byte));
-                               if (test_req.fields.FLT_NO_TIMEOUT == 0) {
-                                       FRL_INFO("FRL TEST REQ:  FLT_no_timeout 
= %d \n", test_req.fields.FLT_NO_TIMEOUT);
-                                       return LINK_RESULT_SUCCESS;
-                               }
-                       }
-
                        /*Set TxFFE = TxFFE0*/
                        /*Program FFE_Levels - scdc_config has this field at 0 
*/
                        if (override_FFE) {
-                               if (flt_no_timeout)
-                                       current_FFE = 0;
                                if (current_FFE == 0)
                                        
dio_link_enc->funcs->prog_eq_setting(dio_link_enc, current_FFE,
                                                        
test_req.fields.TXFFE_DEEMPHASIS,
@@ -626,8 +693,7 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
 
                                if (ln0_pattern == 0x03 || ln1_pattern == 0x03 
||
                                                ln2_pattern == 0x03 || 
ln3_pattern == 0x03)
-                                       if (!flt_no_timeout)
-                                               continue;
+                                       continue;
 
                                if (link_settings->frl_num_lanes == 3) {
                                        ln3_pattern = 1;
@@ -651,8 +717,6 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                                                override_FFE = true;
                                                if (current_FFE > FFE_Levels)
                                                        current_FFE = 0;
-                                               if (flt_no_timeout)
-                                                       current_FFE = 0;
                                        }
                                } else {
                                        if (!ln0_pattern && !ln1_pattern && 
!ln2_pattern && !ln3_pattern) {
@@ -677,8 +741,6 @@ static enum link_result 
hdmi_frl_perform_link_training(struct ddc_service *ddc_s
                                                override_FFE = true;
                                                if (current_FFE > FFE_Levels)
                                                        current_FFE = 0;
-                                               if (flt_no_timeout)
-                                                       current_FFE = 0;
                                        }
                                }
 
@@ -733,7 +795,7 @@ enum link_result 
hdmi_frl_perform_link_training_with_retries(
 
        /* FRL Link Training */
        while (status != LINK_RESULT_FALLBACK) {
-               if (status == LINK_RESULT_SUCCESS) {
+               if (status == LINK_RESULT_SUCCESS || status == 
LINK_RESULT_NO_TIMEOUT) {
                        break;
                } else if (status == LINK_RESULT_LOWER_LINKRATE) {
                        FRL_INFO("FRL LINK TRAINING:  Sink requested lower link 
rate during link enable. \n");
@@ -780,6 +842,9 @@ enum link_result 
hdmi_frl_perform_link_training_with_fallback(
                status = hdmi_frl_perform_link_training(link->ddc,
                                &link->frl_link_settings);
 
+               if (status == LINK_RESULT_NO_TIMEOUT)
+                       break;
+
                link->dc->hwss.disable_link_output(link, link_res, 
SIGNAL_TYPE_HDMI_FRL);
 
                if (status == LINK_RESULT_SUCCESS)
@@ -892,7 +957,7 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
 
        status = hdmi_frl_perform_link_training_with_fallback(link, &link_res, 
frl_phy_clock_source_id);
 
-       if (status == LINK_RESULT_SUCCESS) {
+       if (status == LINK_RESULT_SUCCESS || status == LINK_RESULT_NO_TIMEOUT) {
                cur->frl_link_rate = link->frl_link_settings.frl_link_rate;
                cur->frl_num_lanes = link->frl_link_settings.frl_num_lanes;
                success = true;
-- 
2.43.0

Reply via email to