The threaded IRQ is requested without IRQF_ONESHOT, allowing hardirq to
preempt the threaded handler. This creates a read-modify-write race on
HPD interrupt mask registers between the hardirq mute and thread unmute
operations.

Add IRQF_ONESHOT to keep the IRQ line masked during thread execution,
which eliminates the race and makes the per-event mute/unmute calls in
hardirq and thread handlers redundant. Move interrupt status clearing
to the beginning of the threaded handler, before
drm_helper_hpd_irq_event(), to avoid losing events that arrive during
the event handling.

Signed-off-by: Damon Ding <[email protected]>
---
 .../gpu/drm/bridge/analogix/analogix_dp_core.c  | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c 
b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 7ef8ef812b55..4fe248e66515 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -707,10 +707,8 @@ static irqreturn_t analogix_dp_hardirq(int irq, void *arg)
        u32 irq_type;
 
        irq_type = analogix_dp_get_irq_type(dp);
-       if (irq_type) {
-               analogix_dp_mute_hpd_interrupt(dp);
+       if (irq_type)
                ret = IRQ_WAKE_THREAD;
-       }
 
        return ret;
 }
@@ -721,6 +719,9 @@ static irqreturn_t analogix_dp_irq_thread(int irq, void 
*arg)
        u32 irq_type;
 
        irq_type = analogix_dp_get_irq_type(dp);
+       if (irq_type)
+               analogix_dp_clear_hotplug_interrupts(dp);
+
        if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN ||
            irq_type & DP_IRQ_TYPE_HP_CABLE_OUT) {
                dev_dbg(dp->dev, "Detected cable status changed!\n");
@@ -728,11 +729,6 @@ static irqreturn_t analogix_dp_irq_thread(int irq, void 
*arg)
                        drm_helper_hpd_irq_event(dp->drm_dev);
        }
 
-       if (irq_type) {
-               analogix_dp_clear_hotplug_interrupts(dp);
-               analogix_dp_unmute_hpd_interrupt(dp);
-       }
-
        return IRQ_HANDLED;
 }
 
@@ -1403,10 +1399,11 @@ analogix_dp_probe(struct device *dev, struct 
analogix_dp_plat_data *plat_data)
                 * that we can get the current state of the GPIO.
                 */
                dp->irq = gpiod_to_irq(dp->hpd_gpiod);
-               irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | 
IRQF_NO_AUTOEN;
+               irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | 
IRQF_NO_AUTOEN |
+                           IRQF_ONESHOT;
        } else {
                dp->irq = platform_get_irq(pdev, 0);
-               irq_flags = IRQF_NO_AUTOEN;
+               irq_flags = IRQF_NO_AUTOEN | IRQF_ONESHOT;
        }
 
        if (dp->irq == -ENXIO) {
-- 
2.34.1

Reply via email to