diff options
Diffstat (limited to 'drivers/media/cec/core/cec-pin.c')
| -rw-r--r-- | drivers/media/cec/core/cec-pin.c | 80 |
1 files changed, 60 insertions, 20 deletions
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 68353c5dc501..4d7155281daa 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -4,8 +4,9 @@ */ #include <linux/delay.h> -#include <linux/slab.h> #include <linux/sched/types.h> +#include <linux/seq_file.h> +#include <linux/slab.h> #include <media/cec-pin.h> #include "cec-pin-priv.h" @@ -141,15 +142,42 @@ static bool cec_pin_read(struct cec_pin *pin) return v; } +static void cec_pin_insert_glitch(struct cec_pin *pin, bool rising_edge) +{ + /* + * Insert a short glitch after the falling or rising edge to + * simulate reflections on the CEC line. This can be used to + * test deglitch filters, which should be present in CEC devices + * to deal with noise on the line. + */ + if (!pin->tx_glitch_high_usecs || !pin->tx_glitch_low_usecs) + return; + if (rising_edge) { + udelay(pin->tx_glitch_high_usecs); + call_void_pin_op(pin, low); + udelay(pin->tx_glitch_low_usecs); + call_void_pin_op(pin, high); + } else { + udelay(pin->tx_glitch_low_usecs); + call_void_pin_op(pin, high); + udelay(pin->tx_glitch_high_usecs); + call_void_pin_op(pin, low); + } +} + static void cec_pin_low(struct cec_pin *pin) { call_void_pin_op(pin, low); + if (pin->tx_glitch_falling_edge && pin->adap->cec_pin_is_high) + cec_pin_insert_glitch(pin, false); cec_pin_update(pin, false, false); } static bool cec_pin_high(struct cec_pin *pin) { call_void_pin_op(pin, high); + if (pin->tx_glitch_rising_edge && !pin->adap->cec_pin_is_high) + cec_pin_insert_glitch(pin, true); return cec_pin_read(pin); } @@ -769,7 +797,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) * Go to low drive state when the total bit time is * too short. */ - if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN) { + if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN && !pin->rx_no_low_drive) { if (!pin->rx_data_bit_too_short_cnt++) { pin->rx_data_bit_too_short_ts = ktime_to_ns(pin->ts); pin->rx_data_bit_too_short_delta = delta; @@ -872,19 +900,19 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) if (pin->wait_usecs > 150) { pin->wait_usecs -= 100; pin->timer_ts = ktime_add_us(ts, 100); - hrtimer_forward_now(timer, ns_to_ktime(100000)); + hrtimer_forward_now(timer, us_to_ktime(100)); return HRTIMER_RESTART; } if (pin->wait_usecs > 100) { pin->wait_usecs /= 2; pin->timer_ts = ktime_add_us(ts, pin->wait_usecs); hrtimer_forward_now(timer, - ns_to_ktime(pin->wait_usecs * 1000)); + us_to_ktime(pin->wait_usecs)); return HRTIMER_RESTART; } pin->timer_ts = ktime_add_us(ts, pin->wait_usecs); hrtimer_forward_now(timer, - ns_to_ktime(pin->wait_usecs * 1000)); + us_to_ktime(pin->wait_usecs)); pin->wait_usecs = 0; return HRTIMER_RESTART; } @@ -982,7 +1010,7 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) } if (pin->state != CEC_ST_IDLE || pin->ops->enable_irq == NULL || pin->enable_irq_failed || adap->is_configuring || - adap->is_configured || adap->monitor_all_cnt) + adap->is_configured || adap->monitor_all_cnt || !adap->monitor_pin_cnt) break; /* Switch to interrupt mode */ atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_ENABLE); @@ -1019,13 +1047,12 @@ static enum hrtimer_restart cec_pin_timer(struct hrtimer *timer) if (!adap->monitor_pin_cnt || usecs <= 150) { pin->wait_usecs = 0; pin->timer_ts = ktime_add_us(ts, usecs); - hrtimer_forward_now(timer, - ns_to_ktime(usecs * 1000)); + hrtimer_forward_now(timer, us_to_ktime(usecs)); return HRTIMER_RESTART; } pin->wait_usecs = usecs - 100; pin->timer_ts = ktime_add_us(ts, 100); - hrtimer_forward_now(timer, ns_to_ktime(100000)); + hrtimer_forward_now(timer, us_to_ktime(100)); return HRTIMER_RESTART; } @@ -1033,8 +1060,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 +1116,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 +1129,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 +1252,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); @@ -1305,7 +1344,7 @@ void cec_pin_changed(struct cec_adapter *adap, bool value) cec_pin_update(pin, value, false); if (!value && (adap->is_configuring || adap->is_configured || - adap->monitor_all_cnt)) + adap->monitor_all_cnt || !adap->monitor_pin_cnt)) atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE); } EXPORT_SYMBOL_GPL(cec_pin_changed); @@ -1333,12 +1372,13 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops, if (pin == NULL) return ERR_PTR(-ENOMEM); pin->ops = pin_ops; - hrtimer_init(&pin->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); atomic_set(&pin->work_pin_num_events, 0); - pin->timer.function = cec_pin_timer; + hrtimer_setup(&pin->timer, cec_pin_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); init_waitqueue_head(&pin->kthread_waitq); pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT; pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT; + pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT; + pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT; adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name, caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN, |
