[WHY]
The HDMI FRL status polling work can run concurrently with link detect
after an unplug/replug. Both paths access the same link through DDC and may
perform FRL link training, while the polling path also reads link state
without holding dc_lock.

[HOW]
Use mutex_trylock() to serialize the complete link walk, FRL status
polling, and retraining sequence with dc_lock. If the lock is busy,
defer the background poll to the next 200ms cycle instead of blocking
an active detect, modeset, or flip.

Signed-off-by: Fangzhi Zuo <[email protected]>
Reviewed-by: Harry Wentland <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_connector.c   | 35 +++++++++++--------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index 7ebf7216b499..3255f5b2dde6 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -3109,26 +3109,33 @@ void hdmi_frl_status_polling_work(struct work_struct 
*work)
        struct dc *dc = dm->dc;
        struct dc_link *dc_link;
        bool link_update = false;
+       bool link_detected;
 
-       for (int i = 0; i < MAX_LINKS; i++) {
-               dc_link = dc->links[i];
+       /* Defer to the next cycle rather than block on a busy dc_lock. */
+       if (mutex_trylock(&dm->dc_lock)) {
+               for (int i = 0; i < MAX_LINKS; i++) {
+                       dc_link = dc->links[i];
 
+                       if (!dc_link || !dc_link->local_sink)
+                               continue;
 
-               if (!dc_link || !dc_link->local_sink)
-                       continue;
-
-               if (!dc_is_hdmi_signal(dc_link->connector_signal))
-                       continue;
+                       if (!dc_is_hdmi_signal(dc_link->connector_signal))
+                               continue;
 
-               if (dc_link->frl_link_settings.frl_link_rate == 0)
-                       continue;
+                       if (dc_link->frl_link_settings.frl_link_rate == 0)
+                               continue;
 
-               link_update = dc_link_frl_poll_status_flag(dc_link);
-               if (link_update) {
-                       mutex_lock(&dm->dc_lock);
-                       dc_link_detect(dc_link, DETECT_REASON_RETRAIN);
-                       mutex_unlock(&dm->dc_lock);
+               link_update =
+                       dc_link_frl_poll_status_flag(dc_link);
+                       if (link_update) {
+                               link_detected =
+                                       dc_link_detect(
+                                               dc_link, DETECT_REASON_RETRAIN);
+                               if (!link_detected)
+                                       DRM_ERROR("HDMI FRL retrain failed\n");
+                       }
                }
+               mutex_unlock(&dm->dc_lock);
        }
 
        queue_delayed_work(dm->hdmi_frl_status_polling_wq,
-- 
2.53.0

Reply via email to