media: cec-pin: improve interrupt handling

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>
This commit is contained in:
Hans Verkuil 2023-07-07 13:26:39 +02:00 committed by Mauro Carvalho Chehab
parent 3b7dab49c4
commit 9b79d776a2
2 changed files with 21 additions and 8 deletions

View File

@ -183,6 +183,7 @@ struct cec_pin {
u16 la_mask; u16 la_mask;
bool monitor_all; bool monitor_all;
bool rx_eom; bool rx_eom;
bool enabled_irq;
bool enable_irq_failed; bool enable_irq_failed;
enum cec_pin_state state; enum cec_pin_state state;
struct cec_msg tx_msg; struct cec_msg tx_msg;

View File

@ -1033,8 +1033,9 @@ static int cec_pin_thread_func(void *_adap)
{ {
struct cec_adapter *adap = _adap; struct cec_adapter *adap = _adap;
struct cec_pin *pin = adap->pin; struct cec_pin *pin = adap->pin;
bool irq_enabled = false;
pin->enabled_irq = false;
pin->enable_irq_failed = false;
for (;;) { for (;;) {
wait_event_interruptible(pin->kthread_waitq, wait_event_interruptible(pin->kthread_waitq,
kthread_should_stop() || kthread_should_stop() ||
@ -1088,9 +1089,10 @@ static int cec_pin_thread_func(void *_adap)
switch (atomic_xchg(&pin->work_irq_change, switch (atomic_xchg(&pin->work_irq_change,
CEC_PIN_IRQ_UNCHANGED)) { CEC_PIN_IRQ_UNCHANGED)) {
case CEC_PIN_IRQ_DISABLE: case CEC_PIN_IRQ_DISABLE:
if (irq_enabled) { if (pin->enabled_irq) {
call_void_pin_op(pin, disable_irq); pin->ops->disable_irq(adap);
irq_enabled = false; pin->enabled_irq = false;
pin->enable_irq_failed = false;
} }
cec_pin_high(pin); cec_pin_high(pin);
if (pin->state == CEC_ST_OFF) if (pin->state == CEC_ST_OFF)
@ -1100,21 +1102,29 @@ static int cec_pin_thread_func(void *_adap)
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
break; break;
case CEC_PIN_IRQ_ENABLE: case CEC_PIN_IRQ_ENABLE:
if (irq_enabled) if (pin->enabled_irq || !pin->ops->enable_irq ||
pin->adap->devnode.unregistered)
break; 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) { if (pin->enable_irq_failed) {
cec_pin_to_idle(pin); cec_pin_to_idle(pin);
hrtimer_start(&pin->timer, ns_to_ktime(0), hrtimer_start(&pin->timer, ns_to_ktime(0),
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
} else { } else {
irq_enabled = true; pin->enabled_irq = true;
} }
break; break;
default: default:
break; 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; 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: %d\n", call_pin_op(pin, read));
seq_printf(file, "cec pin events dropped: %u\n", seq_printf(file, "cec pin events dropped: %u\n",
pin->work_pin_events_dropped_cnt); 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) { if (pin->timer_100us_overruns) {
seq_printf(file, "timer overruns > 100us: %u of %u\n", seq_printf(file, "timer overruns > 100us: %u of %u\n",
pin->timer_100us_overruns, pin->timer_cnt); pin->timer_100us_overruns, pin->timer_cnt);