xe_irq_suspend() resets all display interrupt registers, including the HPD enable/mask registers. When runtime suspending to D3hot with PME armed, this leaves the display unable to generate the hotplug event that would wake the device, so a plug goes unnoticed until something else resumes it.
As we are going to support PME and avoid HPD polling during the runtime suspend, we need a way to preserve the HPD related interrupt registers. A keep_hpd flag is used for that purpose and the HPD related interrupt registers are reset / preserved based on this flag. But we need to plug this keep_hpd in the existing IRQ suspend / reset mechanism. At this point this flag is set to false until the PME capability is implemented. Assisted-by: GitHub_Copilot:claude-opus-5 Signed-off-by: Vinod Govindapillai <[email protected]> --- .../gpu/drm/i915/display/intel_display_irq.c | 46 +++++++++++++------ .../gpu/drm/i915/display/intel_display_irq.h | 2 +- drivers/gpu/drm/i915/i915_irq.c | 16 +++---- drivers/gpu/drm/xe/display/xe_display.c | 4 +- drivers/gpu/drm/xe/display/xe_display.h | 4 +- drivers/gpu/drm/xe/xe_device.c | 2 +- drivers/gpu/drm/xe/xe_irq.c | 18 +++++--- drivers/gpu/drm/xe/xe_irq.h | 2 +- drivers/gpu/drm/xe/xe_pm.c | 4 +- 9 files changed, 59 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index a59b75830bd1..26fc90ff7299 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1976,16 +1976,20 @@ static void _vlv_display_irq_reset(struct intel_display *display) display->irq.vlv_imr_mask = ~0u; } -static void vlv_display_irq_reset(struct intel_display *display) +static void vlv_display_irq_reset(struct intel_display *display, bool keep_hpd) { + drm_WARN_ON(display->drm, keep_hpd); + spin_lock_irq(&display->irq.lock); if (display->irq.vlv_display_irqs_enabled) _vlv_display_irq_reset(display); spin_unlock_irq(&display->irq.lock); } -static void i9xx_display_irq_reset(struct intel_display *display) +static void i9xx_display_irq_reset(struct intel_display *display, bool keep_hpd) { + drm_WARN_ON(display->drm, keep_hpd); + if (HAS_HOTPLUG(display)) { i915_hotplug_interrupt_update(display, 0xffffffff, 0); intel_de_rmw(display, PORT_HOTPLUG_STAT(display), 0, 0); @@ -2177,8 +2181,10 @@ static void ibx_display_irq_reset(struct intel_display *display) intel_de_write(display, SERR_INT, 0xffffffff); } -static void ilk_display_irq_reset(struct intel_display *display) +static void ilk_display_irq_reset(struct intel_display *display, bool keep_hpd) { + drm_WARN_ON(display->drm, keep_hpd); + irq_reset(display, DE_IRQ_REGS); display->irq.ilk_de_imr_mask = ~0u; @@ -2193,10 +2199,12 @@ static void ilk_display_irq_reset(struct intel_display *display) ibx_display_irq_reset(display); } -static void gen8_display_irq_reset(struct intel_display *display) +static void gen8_display_irq_reset(struct intel_display *display, bool keep_hpd) { enum pipe pipe; + drm_WARN_ON(display->drm, keep_hpd); + intel_de_write(display, EDP_PSR_IMR, 0xffffffff); intel_de_write(display, EDP_PSR_IIR, 0xffffffff); @@ -2212,13 +2220,14 @@ static void gen8_display_irq_reset(struct intel_display *display) ibx_display_irq_reset(display); } -static void gen11_display_irq_reset(struct intel_display *display) +static void gen11_display_irq_reset(struct intel_display *display, bool keep_hpd) { enum pipe pipe; u32 trans_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | BIT(TRANSCODER_C) | BIT(TRANSCODER_D); - intel_de_write(display, GEN11_DISPLAY_INT_CTL, 0); + if (!keep_hpd) + intel_de_write(display, GEN11_DISPLAY_INT_CTL, 0); if (DISPLAY_VER(display) >= 12) { enum transcoder trans; @@ -2250,13 +2259,20 @@ static void gen11_display_irq_reset(struct intel_display *display) irq_reset(display, GEN8_DE_PORT_IRQ_REGS); irq_reset(display, GEN8_DE_MISC_IRQ_REGS); - if (DISPLAY_VER(display) >= 14) - irq_reset(display, PICAINTERRUPT_IRQ_REGS); - else - irq_reset(display, GEN11_DE_HPD_IRQ_REGS); + /* + * When runtime suspending to D3hot with PME armed, leave the HPD + * interrupt registers programmed so that a hotplug can still + * generate the wake event. Everything else is reset as usual. + */ + if (!keep_hpd) { + if (DISPLAY_VER(display) >= 14) + irq_reset(display, PICAINTERRUPT_IRQ_REGS); + else + irq_reset(display, GEN11_DE_HPD_IRQ_REGS); - if (INTEL_PCH_TYPE(display) >= PCH_ICP) - irq_reset(display, SDE_IRQ_REGS); + if (INTEL_PCH_TYPE(display) >= PCH_ICP) + irq_reset(display, SDE_IRQ_REGS); + } } void gen8_irq_power_well_post_enable(struct intel_display *display, @@ -2542,7 +2558,7 @@ static void gen11_de_irq_postinstall(struct intel_display *display) } struct intel_display_irq_funcs { - void (*reset)(struct intel_display *display); + void (*reset)(struct intel_display *display, bool keep_hpd); void (*postinstall)(struct intel_display *display); void (*ack)(struct intel_display *display, struct intel_display_irq_state *state); bool (*handler)(struct intel_display *display, const struct intel_display_irq_state *state); @@ -2587,12 +2603,12 @@ static const struct intel_display_irq_funcs i915_display_irq_funcs = { .handler = i915_display_irq_handler, }; -void intel_display_irq_reset(struct intel_display *display) +void intel_display_irq_reset(struct intel_display *display, bool keep_hpd) { if (!HAS_DISPLAY(display)) return; - display->irq.funcs->reset(display); + display->irq.funcs->reset(display, keep_hpd); } void intel_display_irq_postinstall(struct intel_display *display) diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.h b/drivers/gpu/drm/i915/display/intel_display_irq.h index 84446bf53401..106f6d979f71 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.h +++ b/drivers/gpu/drm/i915/display/intel_display_irq.h @@ -64,7 +64,7 @@ struct intel_display_irq_state { u32 pipe_stats[I915_MAX_PIPES]; }; -void intel_display_irq_reset(struct intel_display *display); +void intel_display_irq_reset(struct intel_display *display, bool keep_hpd); void intel_display_irq_postinstall(struct intel_display *display); void intel_display_irq_ack(struct intel_display *display, struct intel_display_irq_state *state); bool intel_display_irq_handler(struct intel_display *display, const struct intel_display_irq_state *state); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 932409b943ad..db53b6e82d8e 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -599,7 +599,7 @@ static void ilk_irq_reset(struct drm_i915_private *dev_priv) struct intel_display *display = dev_priv->display; /* The master interrupt enable is in DEIER, reset display irq first */ - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); gen5_gt_irq_reset(to_gt(dev_priv)); } @@ -612,7 +612,7 @@ static void valleyview_irq_reset(struct drm_i915_private *dev_priv) gen5_gt_irq_reset(to_gt(dev_priv)); - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); } static void gen8_irq_reset(struct drm_i915_private *dev_priv) @@ -623,7 +623,7 @@ static void gen8_irq_reset(struct drm_i915_private *dev_priv) gen8_master_intr_disable(intel_uncore_regs(uncore)); gen8_gt_irq_reset(to_gt(dev_priv)); - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); gen2_irq_reset(uncore, GEN8_PCU_IRQ_REGS); } @@ -636,7 +636,7 @@ static void gen11_irq_reset(struct drm_i915_private *dev_priv) gen11_master_intr_disable(intel_uncore_regs(&dev_priv->uncore)); gen11_gt_irq_reset(gt); - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); gen2_irq_reset(uncore, GEN11_GU_MISC_IRQ_REGS); gen2_irq_reset(uncore, GEN8_PCU_IRQ_REGS); @@ -654,7 +654,7 @@ static void dg1_irq_reset(struct drm_i915_private *dev_priv) for_each_gt(gt, dev_priv, i) gen11_gt_irq_reset(gt); - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); gen2_irq_reset(uncore, GEN11_GU_MISC_IRQ_REGS); gen2_irq_reset(uncore, GEN8_PCU_IRQ_REGS); @@ -674,7 +674,7 @@ static void cherryview_irq_reset(struct drm_i915_private *dev_priv) gen2_irq_reset(uncore, GEN8_PCU_IRQ_REGS); - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); } static void ilk_irq_postinstall(struct drm_i915_private *dev_priv) @@ -823,7 +823,7 @@ static void i915_irq_reset(struct drm_i915_private *dev_priv) struct intel_display *display = dev_priv->display; struct intel_uncore *uncore = &dev_priv->uncore; - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); gen2_error_reset(uncore, GEN2_ERROR_REGS); gen2_irq_reset(uncore, GEN2_IRQ_REGS); @@ -900,7 +900,7 @@ static void i965_irq_reset(struct drm_i915_private *dev_priv) struct intel_display *display = dev_priv->display; struct intel_uncore *uncore = &dev_priv->uncore; - intel_display_irq_reset(display); + intel_display_irq_reset(display, false); gen2_error_reset(uncore, GEN2_ERROR_REGS); gen2_irq_reset(uncore, GEN2_IRQ_REGS); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 7b25c0814674..6f1f8e0b25b5 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -216,14 +216,14 @@ void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) intel_opregion_asle_intr(display); } -void xe_display_irq_reset(struct xe_device *xe) +void xe_display_irq_reset(struct xe_device *xe, bool keep_hpd) { struct intel_display *display = xe->display; if (!xe->info.probe_display) return; - intel_display_irq_reset(display); + intel_display_irq_reset(display, keep_hpd); } void xe_display_irq_postinstall(struct xe_device *xe) diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h index 0babb50bfc77..19f313eba6fb 100644 --- a/drivers/gpu/drm/xe/display/xe_display.h +++ b/drivers/gpu/drm/xe/display/xe_display.h @@ -34,7 +34,7 @@ void xe_display_shutdown_late(struct xe_device *xe); void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl); void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir); -void xe_display_irq_reset(struct xe_device *xe); +void xe_display_irq_reset(struct xe_device *xe, bool keep_hpd); void xe_display_irq_postinstall(struct xe_device *xe); void xe_display_pm_suspend(struct xe_device *xe); @@ -72,7 +72,7 @@ static inline void xe_display_shutdown_late(struct xe_device *xe) {} static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {} static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {} -static inline void xe_display_irq_reset(struct xe_device *xe) {} +static inline void xe_display_irq_reset(struct xe_device *xe, bool keep_hpd) {} static inline void xe_display_irq_postinstall(struct xe_device *xe) {} static inline void xe_display_pm_suspend(struct xe_device *xe) {} diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 74d566693dfd..2c586dd3e187 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1190,7 +1190,7 @@ void xe_device_shutdown(struct xe_device *xe) xe_display_shutdown(xe); - xe_irq_suspend(xe); + xe_irq_suspend(xe, false); for_each_gt(gt, xe, id) xe_gt_shutdown(gt); diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c index 9e49e2241da4..eb240615455c 100644 --- a/drivers/gpu/drm/xe/xe_irq.c +++ b/drivers/gpu/drm/xe/xe_irq.c @@ -644,7 +644,7 @@ static void vf_irq_reset(struct xe_device *xe) } } -static void xe_irq_reset(struct xe_device *xe) +static void xe_irq_reset(struct xe_device *xe, bool keep_hpd) { struct xe_tile *tile; u8 id; @@ -666,7 +666,7 @@ static void xe_irq_reset(struct xe_device *xe) tile = xe_device_get_root_tile(xe); mask_and_disable(tile, GU_MISC_IRQ_OFFSET); - xe_display_irq_reset(xe); + xe_display_irq_reset(xe, keep_hpd); xe_i2c_irq_reset(xe); /* @@ -789,7 +789,7 @@ static void irq_uninstall(void *arg) if (!atomic_xchg(&xe->irq.enabled, 0)) return; - xe_irq_reset(xe); + xe_irq_reset(xe, false); if (xe_device_has_msix(xe)) xe_irq_msix_free(xe); @@ -813,7 +813,7 @@ int xe_irq_install(struct xe_device *xe) xe_hw_error_init(xe); - xe_irq_reset(xe); + xe_irq_reset(xe, false); if (xe_device_has_msix(xe)) { nvec = xe->irq.msix.nvec; @@ -843,7 +843,7 @@ static void xe_irq_msi_synchronize_irq(struct xe_device *xe) synchronize_irq(to_pci_dev(xe->drm.dev)->irq); } -void xe_irq_suspend(struct xe_device *xe) +void xe_irq_suspend(struct xe_device *xe, bool keep_hpd) { atomic_set(&xe->irq.enabled, 0); /* no new irqs */ @@ -852,7 +852,7 @@ void xe_irq_suspend(struct xe_device *xe) xe_irq_msix_synchronize_irq(xe); else xe_irq_msi_synchronize_irq(xe); - xe_irq_reset(xe); /* turn irqs off */ + xe_irq_reset(xe, keep_hpd); /* turn irqs off */ } void xe_irq_resume(struct xe_device *xe) @@ -866,7 +866,11 @@ void xe_irq_resume(struct xe_device *xe) * 2. display is not yet resumed */ atomic_set(&xe->irq.enabled, 1); - xe_irq_reset(xe); + /* + * Always a full reset here: once awake, HPD is delivered as a normal + * interrupt again and the postinstall below re-arms that path. + */ + xe_irq_reset(xe, false); xe_irq_postinstall(xe); /* turn irqs on */ for_each_gt(gt, xe, id) diff --git a/drivers/gpu/drm/xe/xe_irq.h b/drivers/gpu/drm/xe/xe_irq.h index a28bd577ba52..eb9eb2060ff5 100644 --- a/drivers/gpu/drm/xe/xe_irq.h +++ b/drivers/gpu/drm/xe/xe_irq.h @@ -16,7 +16,7 @@ struct xe_gt; int xe_irq_init(struct xe_device *xe); int xe_irq_install(struct xe_device *xe); -void xe_irq_suspend(struct xe_device *xe); +void xe_irq_suspend(struct xe_device *xe, bool keep_hpd); void xe_irq_resume(struct xe_device *xe); void xe_irq_enable_hwe(struct xe_gt *gt); int xe_irq_msix_request_irq(struct xe_device *xe, irq_handler_t handler, void *irq_buf, diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c index f517bf453b54..1488c4dfcc05 100644 --- a/drivers/gpu/drm/xe/xe_pm.c +++ b/drivers/gpu/drm/xe/xe_pm.c @@ -202,7 +202,7 @@ int xe_pm_suspend(struct xe_device *xe) goto err_display; } - xe_irq_suspend(xe); + xe_irq_suspend(xe, false); xe_display_pm_suspend_late(xe); @@ -639,7 +639,7 @@ int xe_pm_runtime_suspend(struct xe_device *xe) goto out_resume; } - xe_irq_suspend(xe); + xe_irq_suspend(xe, false); xe_display_pm_runtime_suspend_late(xe); -- 2.43.0
