Move enable_irq()/disable_irq() from bridge enable/disable handlers into analogix_dp_bind()/analogix_dp_unbind(). This keeps the IRQ active throughout the device lifecycle rather than only during display activity, which is a prerequisite for supporting HPD detection while the display pipeline is inactive.
Make the interrupt fully threaded and hold a runtime PM reference in the threaded handler, so that no register is accessed while the controller is runtime suspended. Tested-by: Heiko Stuebner <[email protected]> Signed-off-by: Damon Ding <[email protected]> --- Changes in v3: - Move this patch after mute/unmute related modification and detail the commit msg. Changes in v4: - Add Tested-by tag. Changes in v5: - Use a pure threaded IRQ handler and hold a runtime PM reference in it, so that no MMIO happens on the unclocked controller after runtime suspend. (Sashiko) - Add prep patches to call analogix_dp_unbind() when drm_bridge_connector_init() fails, so that the IRQ enabled across the bind lifetime gets properly disabled on bind failures. (Sashiko) --- .../drm/bridge/analogix/analogix_dp_core.c | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index b14a8aa12394..50177d1ccb5c 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -700,24 +700,22 @@ static int analogix_dp_enable_scramble(struct analogix_dp_device *dp, return ret < 0 ? ret : 0; } -static irqreturn_t analogix_dp_hardirq(int irq, void *arg) -{ - struct analogix_dp_device *dp = arg; - irqreturn_t ret = IRQ_NONE; - u32 irq_type; - - irq_type = analogix_dp_get_irq_type(dp); - if (irq_type) - ret = IRQ_WAKE_THREAD; - - return ret; -} - static irqreturn_t analogix_dp_irq_thread(int irq, void *arg) { struct analogix_dp_device *dp = arg; u32 irq_type; bool hpd_detected; + int ret; + + /* + * The device may have been runtime suspended since the interrupt + * was raised. Hold a runtime PM reference before touching the + * registers, as the controller clocks are only guaranteed to be + * enabled while the device is runtime PM active. + */ + ret = pm_runtime_resume_and_get(dp->dev); + if (ret) + return IRQ_HANDLED; irq_type = analogix_dp_get_irq_type(dp); if (!dp->hpd_gpiod && irq_type) @@ -734,6 +732,9 @@ static irqreturn_t analogix_dp_irq_thread(int irq, void *arg) drm_helper_hpd_irq_event(dp->drm_dev); } + pm_runtime_mark_last_busy(dp->dev); + pm_runtime_put_autosuspend(dp->dev); + return IRQ_HANDLED; } @@ -1056,7 +1057,6 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp) goto out_dp_init; } - enable_irq(dp->irq); return 0; out_dp_init: @@ -1198,8 +1198,6 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) if (dp->dpms_mode != DRM_MODE_DPMS_ON) return; - disable_irq(dp->irq); - analogix_dp_set_analog_power_down(dp, POWER_ALL, 1); pm_runtime_put_sync(dp->dev); @@ -1527,7 +1525,7 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) } ret = devm_request_threaded_irq(&pdev->dev, dp->irq, - analogix_dp_hardirq, + NULL, analogix_dp_irq_thread, irq_flags, "analogix-dp", dp); if (ret) @@ -1613,6 +1611,8 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) goto err_unregister_aux; } + enable_irq(dp->irq); + return 0; err_unregister_aux: @@ -1624,6 +1624,7 @@ EXPORT_SYMBOL_GPL(analogix_dp_bind); void analogix_dp_unbind(struct analogix_dp_device *dp) { + disable_irq(dp->irq); drm_dp_aux_unregister(&dp->aux); } EXPORT_SYMBOL_GPL(analogix_dp_unbind); -- 2.34.1
