Instead of reusing the polling code for hpd handling, split them up. This has a few consequences: - Don't touch HPD capable connectors in the poll loop. - Only touch HPD capable connectors in drm_helper_hpd_irq_event. - Run the HPD handling directly instead of going through a work item - all callers already call drm_helper_hpd_irq_event from process context without holding the mode_config mutex. The ultimate goal is that drivers grow some smarts about which connectors have received a hotplug event and only call the detect code of that connector. But that's a second step. v2: s/hdp/hpd/, noticed by Adam Jackson. I can't type. Signed-Off-by: Daniel Vetter <daniel.vetter at ffwll.ch> --- drivers/gpu/drm/drm_crtc_helper.c | 39 ++++++++++++++++++++++++++++-------- 1 files changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 909a85c..5461ee6 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -942,9 +942,9 @@ static void output_poll_execute(struct work_struct *work) mutex_lock(&dev->mode_config.mutex); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - /* if this is HPD or polled don't check it - - TV out for instance */ - if (!connector->polled) + /* Ignore HPD capable connectors and connectors where we don't + * want any hotplug detection at all for polling. */ + if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD) continue; else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) @@ -954,8 +954,7 @@ static void output_poll_execute(struct work_struct *work) /* if we are connected and don't want to poll for disconnect skip it */ if (old_status == connector_status_connected && - !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && - !(connector->polled & DRM_CONNECTOR_POLL_HPD)) + !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT)) continue; connector->status = connector->funcs->detect(connector, false); @@ -1019,12 +1018,34 @@ EXPORT_SYMBOL(drm_kms_helper_poll_fini); void drm_helper_hpd_irq_event(struct drm_device *dev) { + struct drm_connector *connector; + enum drm_connector_status old_status; + bool changed = false; + if (!dev->mode_config.poll_enabled) return; - /* kill timer and schedule immediate execution, this doesn't block */ - cancel_delayed_work(&dev->mode_config.output_poll_work); - if (drm_kms_helper_poll) - queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, 0); + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + + /* Only handle HPD capable connectors. */ + if (connector->polled != DRM_CONNECTOR_POLL_HPD) + continue; + + old_status = connector->status; + + connector->status = connector->funcs->detect(connector, false); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", + connector->base.id, + drm_get_connector_name(connector), + old_status, connector->status); + if (old_status != connector->status) + changed = true; + } + + mutex_unlock(&dev->mode_config.mutex); + + if (changed) + drm_kms_helper_hotplug_event(dev); } EXPORT_SYMBOL(drm_helper_hpd_irq_event); -- 1.7.7.6