drm_atomic_helper_shutdown() disables all CRTCs but leaves output polling and IRQ-driven hot-plug detection running. On reboot, a late DP hot-plug-detect (HPD) IRQ can fire after apps_smmu has already disabled translation for the display subsystem, causing the HPD thread to kick off a new modeset that drives DPU/DP hardware and DMA through a stale IOMMU mapping.
drm_atomic_helper_shutdown() disables all CRTCs first, but a pending HPD IRQ thread wakes up afterwards, reads the DPCD, and fires an unsolicited hotplug event that triggers a second atomic commit turning the display back on -- right as the IOMMU is disabling translation: systemd-shutdown[1]: Rebooting. msm_dpu: drm_atomic_commit: committing (shutdown disabling CRTCs) arm-smmu 3da0000.iommu: disabling translation msm_dpu: drm_dp_read_dpcd_caps (late HPD IRQ thread wakes up) msm_dpu: drm_sysfs_connector_hotplug_event: DP-1 hotplug event msm_dpu: drm_client_modeset_probe: DP-1 found preferred mode msm_dpu: drm_atomic_commit: committing (unsolicited, re-enables display) dpu_crtc_commit_kickoff: crtc94 first commit arm-smmu 15200000.iommu: disabling translation Call drm_kms_helper_poll_disable() to tear this down: it stops the output poll worker and calls each connector's &drm_connector_helper_funcs.disable_hpd, which for HPD-capable bridges masks the interrupt in hardware and then waits for an in-flight HPD handler under bridge->hpd_mutex. Suspend the in-kernel clients as well, so that a hotplug event which still gets through is recorded in client->hotplug_pending instead of being probed and committed. Reported on Qualcomm platforms such as lemans-evk and monaco-evk during reboot stress testing. Assisted-by: LLM Signed-off-by: Mahadevan P <[email protected]> --- Changes in v3: - Use drm_kms_helper_poll_disable() instead of drm_kms_helper_poll_fini(), guarded on mode_config.poll_enabled. (Sashiko AI review) - Also suspend the in-kernel clients. Masking HPD is not sufficient on its own: it only covers connectors notifying through drm_bridge_hpd_notify(), and a forced GETCONNECTOR re-arms HPD via drm_helper_probe_single_connector_modes() while poll_enabled is still set. drm_client_dev_suspend() keeps the resulting event from reaching a commit, which is the drm_client_modeset_probe() path. (Sashiko AI review) - Reworded the kerneldoc; v2 claimed only that polling and HPD were torn down via drm_kms_helper_poll_fini(). - Link to v2: https://lore.kernel.org/r/[email protected] Changes in v2: - Dropped the drm/msm/dp local .shutdown patch (disable_irq()/ synchronize_irq() in dp_display.c)., fixed this at the DRM core level instead so every driver benefits, not just msm_dp.(Dmitry). - Link to v1: https://lore.kernel.org/r/[email protected] --- drivers/gpu/drm/drm_atomic_helper.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9d006f98413a..3a23777aa00f 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -35,6 +35,7 @@ #include <drm/drm_atomic_uapi.h> #include <drm/drm_blend.h> #include <drm/drm_bridge.h> +#include <drm/drm_client_event.h> #include <drm/drm_colorop.h> #include <drm/drm_damage_helper.h> #include <drm/drm_device.h> @@ -42,6 +43,7 @@ #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> #include <drm/drm_self_refresh_helper.h> #include <drm/drm_vblank.h> #include <drm/drm_writeback.h> @@ -3676,6 +3678,11 @@ EXPORT_SYMBOL(drm_atomic_helper_reset_crtc); * * This is just a convenience wrapper around drm_atomic_helper_disable_all(), * and it is the atomic version of drm_helper_force_disable_all(). + * + * Before disabling the CRTCs this stops output polling, masks hot-plug + * detection and suspends the in-kernel clients, so that a hotplug event cannot + * probe and commit a new mode once the CRTCs are down. Polling is disabled but + * not finalized; drivers must still call drm_kms_helper_poll_fini(). */ void drm_atomic_helper_shutdown(struct drm_device *dev) { @@ -3685,6 +3692,14 @@ void drm_atomic_helper_shutdown(struct drm_device *dev) if (dev == NULL) return; + /* + * Don't disable polling if it was never initialized + */ + if (dev->mode_config.poll_enabled) + drm_kms_helper_poll_disable(dev); + + drm_client_dev_suspend(dev); + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); ret = drm_atomic_helper_disable_all(dev, &ctx); --- base-commit: 3f2425f5b5bbbdd991ca9cdfd5502e68d8895998 change-id: 20260717-dpshutdown-047912fcf60b Best regards, -- Mahadevan P <[email protected]>
