@@ -3859,6 +3859,97 @@ void amdgpu_dm_update_connector_after_detect(
38593859 update_subconnector_property (aconnector );
38603860}
38613861
3862+ static bool are_sinks_equal (const struct dc_sink * sink1 , const struct dc_sink * sink2 )
3863+ {
3864+ if (!sink1 || !sink2 )
3865+ return false;
3866+ if (sink1 -> sink_signal != sink2 -> sink_signal )
3867+ return false;
3868+
3869+ if (sink1 -> dc_edid .length != sink2 -> dc_edid .length )
3870+ return false;
3871+
3872+ if (memcmp (sink1 -> dc_edid .raw_edid , sink2 -> dc_edid .raw_edid ,
3873+ sink1 -> dc_edid .length ) != 0 )
3874+ return false;
3875+ return true;
3876+ }
3877+
3878+
3879+ /**
3880+ * DOC: hdmi_hpd_debounce_work
3881+ *
3882+ * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD
3883+ * (such as during power save transitions), this delay determines how long to
3884+ * wait before processing the HPD event. This allows distinguishing between a
3885+ * physical unplug (>hdmi_hpd_debounce_delay)
3886+ * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay).
3887+ *
3888+ * If the toggle is less than this delay, the driver compares sink capabilities
3889+ * and permits a hotplug event if they changed.
3890+ *
3891+ * The default value of 1500ms was chosen based on experimental testing with
3892+ * various monitors that exhibit spontaneous HPD toggling behavior.
3893+ */
3894+ static void hdmi_hpd_debounce_work (struct work_struct * work )
3895+ {
3896+ struct amdgpu_dm_connector * aconnector =
3897+ container_of (to_delayed_work (work ), struct amdgpu_dm_connector ,
3898+ hdmi_hpd_debounce_work );
3899+ struct drm_connector * connector = & aconnector -> base ;
3900+ struct drm_device * dev = connector -> dev ;
3901+ struct amdgpu_device * adev = drm_to_adev (dev );
3902+ struct dc * dc = aconnector -> dc_link -> ctx -> dc ;
3903+ bool fake_reconnect = false;
3904+ bool reallow_idle = false;
3905+ bool ret = false;
3906+ guard (mutex )(& aconnector -> hpd_lock );
3907+
3908+ /* Re-detect the display */
3909+ scoped_guard (mutex , & adev -> dm .dc_lock ) {
3910+ if (dc -> caps .ips_support && dc -> ctx -> dmub_srv -> idle_allowed ) {
3911+ dc_allow_idle_optimizations (dc , false);
3912+ reallow_idle = true;
3913+ }
3914+ ret = dc_link_detect (aconnector -> dc_link , DETECT_REASON_HPD );
3915+ }
3916+
3917+ if (ret ) {
3918+ /* Apply workaround delay for certain panels */
3919+ apply_delay_after_dpcd_poweroff (adev , aconnector -> dc_sink );
3920+ /* Compare sinks to determine if this was a spontaneous HPD toggle */
3921+ if (are_sinks_equal (aconnector -> dc_link -> local_sink , aconnector -> hdmi_prev_sink )) {
3922+ /*
3923+ * Sinks match - this was a spontaneous HDMI HPD toggle.
3924+ */
3925+ drm_dbg_kms (dev , "HDMI HPD: Sink unchanged after debounce, internal re-enable\n" );
3926+ fake_reconnect = true;
3927+ }
3928+
3929+ /* Update connector state */
3930+ amdgpu_dm_update_connector_after_detect (aconnector );
3931+
3932+ drm_modeset_lock_all (dev );
3933+ dm_restore_drm_connector_state (dev , connector );
3934+ drm_modeset_unlock_all (dev );
3935+
3936+ /* Only notify OS if sink actually changed */
3937+ if (!fake_reconnect && aconnector -> base .force == DRM_FORCE_UNSPECIFIED )
3938+ drm_kms_helper_hotplug_event (dev );
3939+ }
3940+
3941+ /* Release the cached sink reference */
3942+ if (aconnector -> hdmi_prev_sink ) {
3943+ dc_sink_release (aconnector -> hdmi_prev_sink );
3944+ aconnector -> hdmi_prev_sink = NULL ;
3945+ }
3946+
3947+ scoped_guard (mutex , & adev -> dm .dc_lock ) {
3948+ if (reallow_idle && dc -> caps .ips_support )
3949+ dc_allow_idle_optimizations (dc , true);
3950+ }
3951+ }
3952+
38623953static void handle_hpd_irq_helper (struct amdgpu_dm_connector * aconnector )
38633954{
38643955 struct drm_connector * connector = & aconnector -> base ;
@@ -3868,6 +3959,7 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
38683959 struct dm_connector_state * dm_con_state = to_dm_connector_state (connector -> state );
38693960 struct dc * dc = aconnector -> dc_link -> ctx -> dc ;
38703961 bool ret = false;
3962+ bool debounce_required = false;
38713963
38723964 if (adev -> dm .disable_hpd_irq )
38733965 return ;
@@ -3890,6 +3982,14 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
38903982 if (!dc_link_detect_connection_type (aconnector -> dc_link , & new_connection_type ))
38913983 drm_err (adev_to_drm (adev ), "KMS: Failed to detect connector\n" );
38923984
3985+ /*
3986+ * Check for HDMI disconnect with debounce enabled.
3987+ */
3988+ debounce_required = (aconnector -> hdmi_hpd_debounce_delay_ms > 0 &&
3989+ dc_is_hdmi_signal (aconnector -> dc_link -> connector_signal ) &&
3990+ new_connection_type == dc_connection_none &&
3991+ aconnector -> dc_link -> local_sink != NULL );
3992+
38933993 if (aconnector -> base .force && new_connection_type == dc_connection_none ) {
38943994 emulated_link_detect (aconnector -> dc_link );
38953995
@@ -3899,7 +3999,34 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
38993999
39004000 if (aconnector -> base .force == DRM_FORCE_UNSPECIFIED )
39014001 drm_kms_helper_connector_hotplug_event (connector );
4002+ } else if (debounce_required ) {
4003+ /*
4004+ * HDMI disconnect detected - schedule delayed work instead of
4005+ * processing immediately. This allows us to coalesce spurious
4006+ * HDMI signals from physical unplugs.
4007+ */
4008+ drm_dbg_kms (dev , "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n" ,
4009+ aconnector -> hdmi_hpd_debounce_delay_ms );
4010+
4011+ /* Cache the current sink for later comparison */
4012+ if (aconnector -> hdmi_prev_sink )
4013+ dc_sink_release (aconnector -> hdmi_prev_sink );
4014+ aconnector -> hdmi_prev_sink = aconnector -> dc_link -> local_sink ;
4015+ if (aconnector -> hdmi_prev_sink )
4016+ dc_sink_retain (aconnector -> hdmi_prev_sink );
4017+
4018+ /* Schedule delayed detection. */
4019+ if (mod_delayed_work (system_wq ,
4020+ & aconnector -> hdmi_hpd_debounce_work ,
4021+ msecs_to_jiffies (aconnector -> hdmi_hpd_debounce_delay_ms )))
4022+ drm_dbg_kms (dev , "HDMI HPD: Re-scheduled debounce work\n" );
4023+
39024024 } else {
4025+
4026+ /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */
4027+ if (delayed_work_pending (& aconnector -> hdmi_hpd_debounce_work ))
4028+ return ;
4029+
39034030 scoped_guard (mutex , & adev -> dm .dc_lock ) {
39044031 dc_exit_ips_for_hw_access (dc );
39054032 ret = dc_link_detect (aconnector -> dc_link , DETECT_REASON_HPD );
@@ -7388,6 +7515,13 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
73887515 if (aconnector -> mst_mgr .dev )
73897516 drm_dp_mst_topology_mgr_destroy (& aconnector -> mst_mgr );
73907517
7518+ /* Cancel and flush any pending HDMI HPD debounce work */
7519+ cancel_delayed_work_sync (& aconnector -> hdmi_hpd_debounce_work );
7520+ if (aconnector -> hdmi_prev_sink ) {
7521+ dc_sink_release (aconnector -> hdmi_prev_sink );
7522+ aconnector -> hdmi_prev_sink = NULL ;
7523+ }
7524+
73917525 if (aconnector -> bl_idx != -1 ) {
73927526 backlight_device_unregister (dm -> backlight_dev [aconnector -> bl_idx ]);
73937527 dm -> backlight_dev [aconnector -> bl_idx ] = NULL ;
@@ -8549,6 +8683,10 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
85498683 mutex_init (& aconnector -> hpd_lock );
85508684 mutex_init (& aconnector -> handle_mst_msg_ready );
85518685
8686+ aconnector -> hdmi_hpd_debounce_delay_ms = AMDGPU_DM_HDMI_HPD_DEBOUNCE_MS ;
8687+ INIT_DELAYED_WORK (& aconnector -> hdmi_hpd_debounce_work , hdmi_hpd_debounce_work );
8688+ aconnector -> hdmi_prev_sink = NULL ;
8689+
85528690 /*
85538691 * configure support HPD hot plug connector_>polled default value is 0
85548692 * which means HPD hot plug not supported
0 commit comments