media: cec-pin: improve interrupt handling
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Fri, 7 Jul 2023 11:26:39 +0000 (13:26 +0200)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Thu, 10 Aug 2023 05:58:32 +0000 (07:58 +0200)
The CEC pin framework needs a bit more control over the interrupt
handling: make sure that the disable_irq op is called even if the
device node is unregistered, log the state of the interrupt in
debugfs, and disable the interrupt when the kernel thread is stopped.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/media/cec/core/cec-pin-priv.h
drivers/media/cec/core/cec-pin.c

index 8eb5819..156a9f8 100644 (file)
@@ -183,6 +183,7 @@ struct cec_pin {
        u16                             la_mask;
        bool                            monitor_all;
        bool                            rx_eom;
+       bool                            enabled_irq;
        bool                            enable_irq_failed;
        enum cec_pin_state              state;
        struct cec_msg                  tx_msg;
index 68353c5..8a3921f 100644 (file)
@@ -1033,8 +1033,9 @@ static int cec_pin_thread_func(void *_adap)
 {
        struct cec_adapter *adap = _adap;
        struct cec_pin *pin = adap->pin;
-       bool irq_enabled = false;
 
+       pin->enabled_irq = false;
+       pin->enable_irq_failed = false;
        for (;;) {
                wait_event_interruptible(pin->kthread_waitq,
                                         kthread_should_stop() ||
@@ -1088,9 +1089,10 @@ static int cec_pin_thread_func(void *_adap)
                switch (atomic_xchg(&pin->work_irq_change,
                                    CEC_PIN_IRQ_UNCHANGED)) {
                case CEC_PIN_IRQ_DISABLE:
-                       if (irq_enabled) {
-                               call_void_pin_op(pin, disable_irq);
-                               irq_enabled = false;
+                       if (pin->enabled_irq) {
+                               pin->ops->disable_irq(adap);
+                               pin->enabled_irq = false;
+                               pin->enable_irq_failed = false;
                        }
                        cec_pin_high(pin);
                        if (pin->state == CEC_ST_OFF)
@@ -1100,21 +1102,29 @@ static int cec_pin_thread_func(void *_adap)
                                      HRTIMER_MODE_REL);
                        break;
                case CEC_PIN_IRQ_ENABLE:
-                       if (irq_enabled)
+                       if (pin->enabled_irq || !pin->ops->enable_irq ||
+                           pin->adap->devnode.unregistered)
                                break;
-                       pin->enable_irq_failed = !call_pin_op(pin, enable_irq);
+                       pin->enable_irq_failed = !pin->ops->enable_irq(adap);
                        if (pin->enable_irq_failed) {
                                cec_pin_to_idle(pin);
                                hrtimer_start(&pin->timer, ns_to_ktime(0),
                                              HRTIMER_MODE_REL);
                        } else {
-                               irq_enabled = true;
+                               pin->enabled_irq = true;
                        }
                        break;
                default:
                        break;
                }
        }
+
+       if (pin->enabled_irq) {
+               pin->ops->disable_irq(pin->adap);
+               pin->enabled_irq = false;
+               pin->enable_irq_failed = false;
+               cec_pin_high(pin);
+       }
        return 0;
 }
 
@@ -1215,7 +1225,9 @@ static void cec_pin_adap_status(struct cec_adapter *adap,
        seq_printf(file, "cec pin: %d\n", call_pin_op(pin, read));
        seq_printf(file, "cec pin events dropped: %u\n",
                   pin->work_pin_events_dropped_cnt);
-       seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed);
+       if (pin->ops->enable_irq)
+               seq_printf(file, "irq %s\n", pin->enabled_irq ? "enabled" :
+                          (pin->enable_irq_failed ? "failed" : "disabled"));
        if (pin->timer_100us_overruns) {
                seq_printf(file, "timer overruns > 100us: %u of %u\n",
                           pin->timer_100us_overruns, pin->timer_cnt);