Commit 9b79d776 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab
Browse files

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: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 3b7dab49
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+20 −8
Original line number Diff line number Diff line
@@ -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);