From 9499d3bc41a2c4e6a1badfb55f943b9d0c34f034 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 26 May 2026 14:39:11 +0200 Subject: [PATCH] drm: Send per-connector hotplug events Try to send per-connector hotplug events as often as possible, rather than connector-less global hotplug events. This does result in more hotplug events if multiple connectors changed at the same time, but give userspace more actionable information. Since the hotplug event needs to be sent outside of the mode_config mutex to avoid a deadlock, the drm_client_dev_hotplug() call is split off from the drm_sysfs_(connector_)?hotplug_event calls. Co-developed-by: Marius Vlad Signed-off-by: Marius Vlad Signed-off-by: Nicolas Frattaroli Reviewed-by: Daniel Stone Link: https://patch.msgid.link/20260526-hot-plug-passup-v10-2-f62351a9ea3e@collabora.com Signed-off-by: Daniel Stone --- drivers/gpu/drm/drm_probe_helper.c | 53 ++++++++++++++++-------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index d4dc8cb45bce..e26ead5b5e58 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -760,27 +760,32 @@ static void output_poll_execute(struct work_struct *work) struct drm_connector *connector; struct drm_connector_list_iter conn_iter; enum drm_connector_status old_status; - bool repoll = false, changed; + bool repoll = false, changed = false; u64 old_epoch_counter; if (!dev->mode_config.poll_enabled) return; - /* Pick up any changes detected by the probe functions. */ - changed = dev->mode_config.delayed_event; - dev->mode_config.delayed_event = false; - if (!drm_kms_helper_poll) { if (dev->mode_config.poll_running) { drm_kms_helper_disable_hpd(dev); dev->mode_config.poll_running = false; } - goto out; + + scoped_guard(mutex, &dev->mode_config.mutex) { + changed = dev->mode_config.delayed_event; + dev->mode_config.delayed_event = false; + } + + if (changed) + drm_kms_helper_hotplug_event(dev); + + return; } if (!mutex_trylock(&dev->mode_config.mutex)) { - repoll = true; - goto out; + schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); + return; } drm_connector_list_iter_begin(dev, &conn_iter); @@ -836,16 +841,23 @@ static void output_poll_execute(struct work_struct *work) connector->base.id, connector->name, old_epoch_counter, connector->epoch_counter); + drm_sysfs_connector_hotplug_event(connector); changed = true; } } drm_connector_list_iter_end(&conn_iter); + /* Pick up any changes detected by the probe functions. */ + if (dev->mode_config.delayed_event) { + dev->mode_config.delayed_event = false; + changed = true; + drm_sysfs_hotplug_event(dev); + } + mutex_unlock(&dev->mode_config.mutex); -out: if (changed) - drm_kms_helper_hotplug_event(dev); + drm_client_dev_hotplug(dev); if (repoll) schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); @@ -1081,9 +1093,9 @@ EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event); */ bool drm_helper_hpd_irq_event(struct drm_device *dev) { - struct drm_connector *connector, *first_changed_connector = NULL; struct drm_connector_list_iter conn_iter; - int changed = 0; + struct drm_connector *connector; + bool changed = false; if (!dev->mode_config.poll_enabled) return false; @@ -1096,24 +1108,15 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) continue; if (check_connector_changed(connector)) { - if (!first_changed_connector) { - drm_connector_get(connector); - first_changed_connector = connector; - } - - changed++; + changed = true; + drm_sysfs_connector_hotplug_event(connector); } } drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); - if (changed == 1) - drm_kms_helper_connector_hotplug_event(first_changed_connector); - else if (changed > 0) - drm_kms_helper_hotplug_event(dev); - - if (first_changed_connector) - drm_connector_put(first_changed_connector); + if (changed) + drm_client_dev_hotplug(dev); return changed; }