diff options
Diffstat (limited to 'drivers/media/cec')
-rw-r--r-- | drivers/media/cec/core/cec-pin-error-inj.c | 59 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-pin-priv.h | 8 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-pin.c | 31 | ||||
-rw-r--r-- | drivers/media/cec/platform/cec-gpio/cec-gpio.c | 58 | ||||
-rw-r--r-- | drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 5 | ||||
-rw-r--r-- | drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c | 4 | ||||
-rw-r--r-- | drivers/media/cec/usb/rainshadow/rainshadow-cec.c | 3 |
7 files changed, 135 insertions, 33 deletions
diff --git a/drivers/media/cec/core/cec-pin-error-inj.c b/drivers/media/cec/core/cec-pin-error-inj.c index 6e61a04b8168..d9e613c7ce3f 100644 --- a/drivers/media/cec/core/cec-pin-error-inj.c +++ b/drivers/media/cec/core/cec-pin-error-inj.c @@ -91,16 +91,22 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) if (!strcmp(token, "clear")) { memset(pin->error_inj, 0, sizeof(pin->error_inj)); pin->rx_toggle = pin->tx_toggle = false; + pin->rx_no_low_drive = false; pin->tx_ignore_nack_until_eom = false; pin->tx_custom_pulse = false; 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; + pin->tx_glitch_falling_edge = false; + pin->tx_glitch_rising_edge = false; return true; } if (!strcmp(token, "rx-clear")) { for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++) pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK; pin->rx_toggle = false; + pin->rx_no_low_drive = false; return true; } if (!strcmp(token, "tx-clear")) { @@ -111,6 +117,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) pin->tx_custom_pulse = false; 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; + pin->tx_glitch_falling_edge = false; + pin->tx_glitch_rising_edge = false; + return true; + } + if (!strcmp(token, "rx-no-low-drive")) { + pin->rx_no_low_drive = true; return true; } if (!strcmp(token, "tx-ignore-nack-until-eom")) { @@ -122,6 +136,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) cec_pin_start_timer(pin); return true; } + if (!strcmp(token, "tx-glitch-falling-edge")) { + pin->tx_glitch_falling_edge = true; + return true; + } + if (!strcmp(token, "tx-glitch-rising-edge")) { + pin->tx_glitch_rising_edge = true; + return true; + } if (!p) return false; @@ -139,7 +161,23 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) if (kstrtou32(p, 0, &usecs) || usecs > 10000000) return false; - pin->tx_custom_high_usecs = usecs; + pin->tx_glitch_high_usecs = usecs; + return true; + } + if (!strcmp(token, "tx-glitch-low-usecs")) { + u32 usecs; + + if (kstrtou32(p, 0, &usecs) || usecs > 100) + return false; + pin->tx_glitch_low_usecs = usecs; + return true; + } + if (!strcmp(token, "tx-glitch-high-usecs")) { + u32 usecs; + + if (kstrtou32(p, 0, &usecs) || usecs > 100) + return false; + pin->tx_glitch_high_usecs = usecs; return true; } @@ -273,6 +311,9 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf) seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n"); seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n"); seq_puts(sf, "#\n"); + seq_puts(sf, "# RX error injection settings:\n"); + seq_puts(sf, "# rx-no-low-drive do not generate low-drive pulses\n"); + seq_puts(sf, "#\n"); seq_puts(sf, "# RX error injection:\n"); seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n"); seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n"); @@ -285,6 +326,10 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf) seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n"); seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n"); seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n"); + seq_puts(sf, "# tx-glitch-low-usecs <usecs> define the 'low' time for the glitch pulse\n"); + seq_puts(sf, "# tx-glitch-high-usecs <usecs> define the 'high' time for the glitch pulse\n"); + seq_puts(sf, "# tx-glitch-falling-edge send the glitch pulse after every falling edge\n"); + seq_puts(sf, "# tx-glitch-rising-edge send the glitch pulse after every rising edge\n"); seq_puts(sf, "#\n"); seq_puts(sf, "# TX error injection:\n"); seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n"); @@ -332,8 +377,14 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf) } } + if (pin->rx_no_low_drive) + seq_puts(sf, "rx-no-low-drive\n"); if (pin->tx_ignore_nack_until_eom) seq_puts(sf, "tx-ignore-nack-until-eom\n"); + if (pin->tx_glitch_falling_edge) + seq_puts(sf, "tx-glitch-falling-edge\n"); + if (pin->tx_glitch_rising_edge) + seq_puts(sf, "tx-glitch-rising-edge\n"); if (pin->tx_custom_pulse) seq_puts(sf, "tx-custom-pulse\n"); if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT) @@ -342,5 +393,11 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf) if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT) seq_printf(sf, "tx-custom-high-usecs %u\n", pin->tx_custom_high_usecs); + if (pin->tx_glitch_low_usecs != CEC_TIM_GLITCH_DEFAULT) + seq_printf(sf, "tx-glitch-low-usecs %u\n", + pin->tx_glitch_low_usecs); + if (pin->tx_glitch_high_usecs != CEC_TIM_GLITCH_DEFAULT) + seq_printf(sf, "tx-glitch-high-usecs %u\n", + pin->tx_glitch_high_usecs); return 0; } diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h index 156a9f81be94..e7801be9adb9 100644 --- a/drivers/media/cec/core/cec-pin-priv.h +++ b/drivers/media/cec/core/cec-pin-priv.h @@ -164,6 +164,9 @@ enum cec_pin_state { /* The default for the low/high time of the custom pulse */ #define CEC_TIM_CUSTOM_DEFAULT 1000 +/* The default for the low/high time of the glitch pulse */ +#define CEC_TIM_GLITCH_DEFAULT 1 + #define CEC_NUM_PIN_EVENTS 128 #define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0) #define CEC_PIN_EVENT_FL_DROPPED (1 << 1) @@ -225,12 +228,17 @@ struct cec_pin { u32 timer_max_overrun; u32 timer_sum_overrun; + bool rx_no_low_drive; u32 tx_custom_low_usecs; u32 tx_custom_high_usecs; + u32 tx_glitch_low_usecs; + u32 tx_glitch_high_usecs; bool tx_ignore_nack_until_eom; bool tx_custom_pulse; bool tx_generated_poll; bool tx_post_eom; + bool tx_glitch_falling_edge; + bool tx_glitch_rising_edge; u8 tx_extra_bytes; u32 tx_low_drive_cnt; #ifdef CONFIG_CEC_PIN_ERROR_INJ diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 59ac12113f3a..4d7155281daa 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -142,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); } @@ -770,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; @@ -1350,6 +1377,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops, 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, diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c index 50cdc557c943..3c27789d8657 100644 --- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c @@ -61,49 +61,51 @@ static void cec_gpio_low(struct cec_adapter *adap) gpiod_set_value(cec->cec_gpio, 0); } -static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv) +static irqreturn_t cec_gpio_5v_irq_handler_thread(int irq, void *priv) { struct cec_gpio *cec = priv; + int val = gpiod_get_value_cansleep(cec->v5_gpio); + bool is_high = val > 0; - cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts); + if (val < 0 || is_high == cec->v5_is_high) + return IRQ_HANDLED; + + cec->v5_is_high = is_high; + cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts); return IRQ_HANDLED; } -static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv) +static irqreturn_t cec_gpio_5v_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; - int val = gpiod_get_value(cec->v5_gpio); - bool is_high = val > 0; - if (val < 0 || is_high == cec->v5_is_high) - return IRQ_HANDLED; cec->v5_ts = ktime_get(); - cec->v5_is_high = is_high; return IRQ_WAKE_THREAD; } -static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv) +static irqreturn_t cec_gpio_hpd_irq_handler_thread(int irq, void *priv) { struct cec_gpio *cec = priv; + int val = gpiod_get_value_cansleep(cec->hpd_gpio); + bool is_high = val > 0; - cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts); + if (val < 0 || is_high == cec->hpd_is_high) + return IRQ_HANDLED; + + cec->hpd_is_high = is_high; + cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts); return IRQ_HANDLED; } -static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv) +static irqreturn_t cec_gpio_hpd_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; - int val = gpiod_get_value(cec->hpd_gpio); - bool is_high = val > 0; - if (val < 0 || is_high == cec->hpd_is_high) - return IRQ_HANDLED; cec->hpd_ts = ktime_get(); - cec->hpd_is_high = is_high; return IRQ_WAKE_THREAD; } -static irqreturn_t cec_gpio_irq_handler(int irq, void *priv) +static irqreturn_t cec_gpio_cec_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; int val = gpiod_get_value(cec->cec_gpio); @@ -113,7 +115,7 @@ static irqreturn_t cec_gpio_irq_handler(int irq, void *priv) return IRQ_HANDLED; } -static bool cec_gpio_enable_irq(struct cec_adapter *adap) +static bool cec_gpio_cec_enable_irq(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); @@ -121,7 +123,7 @@ static bool cec_gpio_enable_irq(struct cec_adapter *adap) return true; } -static void cec_gpio_disable_irq(struct cec_adapter *adap) +static void cec_gpio_cec_disable_irq(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); @@ -148,7 +150,7 @@ static int cec_gpio_read_hpd(struct cec_adapter *adap) if (!cec->hpd_gpio) return -ENOTTY; - return gpiod_get_value(cec->hpd_gpio); + return gpiod_get_value_cansleep(cec->hpd_gpio); } static int cec_gpio_read_5v(struct cec_adapter *adap) @@ -157,15 +159,15 @@ static int cec_gpio_read_5v(struct cec_adapter *adap) if (!cec->v5_gpio) return -ENOTTY; - return gpiod_get_value(cec->v5_gpio); + return gpiod_get_value_cansleep(cec->v5_gpio); } static const struct cec_pin_ops cec_gpio_pin_ops = { .read = cec_gpio_read, .low = cec_gpio_low, .high = cec_gpio_high, - .enable_irq = cec_gpio_enable_irq, - .disable_irq = cec_gpio_disable_irq, + .enable_irq = cec_gpio_cec_enable_irq, + .disable_irq = cec_gpio_cec_disable_irq, .status = cec_gpio_status, .read_hpd = cec_gpio_read_hpd, .read_5v = cec_gpio_read_5v, @@ -209,7 +211,7 @@ static int cec_gpio_probe(struct platform_device *pdev) if (IS_ERR(cec->adap)) return PTR_ERR(cec->adap); - ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler, + ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_cec_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, cec->adap->name, cec); if (ret) @@ -218,8 +220,8 @@ static int cec_gpio_probe(struct platform_device *pdev) if (cec->hpd_gpio) { cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio); ret = devm_request_threaded_irq(dev, cec->hpd_irq, - cec_hpd_gpio_irq_handler, - cec_hpd_gpio_irq_handler_thread, + cec_gpio_hpd_irq_handler, + cec_gpio_hpd_irq_handler_thread, IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "hpd-gpio", cec); @@ -230,8 +232,8 @@ static int cec_gpio_probe(struct platform_device *pdev) if (cec->v5_gpio) { cec->v5_irq = gpiod_to_irq(cec->v5_gpio); ret = devm_request_threaded_irq(dev, cec->v5_irq, - cec_5v_gpio_irq_handler, - cec_5v_gpio_irq_handler_thread, + cec_gpio_5v_irq_handler, + cec_gpio_5v_irq_handler_thread, IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "v5-gpio", cec); diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 12b73ea0f31d..419b9a7abcce 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -298,6 +298,7 @@ struct cec_dmi_match { static const char *const port_b_conns[] = { "Port B", NULL }; static const char *const port_db_conns[] = { "Port D", "Port B", NULL }; static const char *const port_ba_conns[] = { "Port B", "Port A", NULL }; +static const char *const port_ab_conns[] = { "Port A", "Port B", NULL }; static const char *const port_d_conns[] = { "Port D", NULL }; static const struct cec_dmi_match cec_dmi_match_table[] = { @@ -329,6 +330,10 @@ static const struct cec_dmi_match cec_dmi_match_table[] = { { "Google", "Dexi", "0000:00:02.0", port_db_conns }, /* Google Dita */ { "Google", "Dita", "0000:00:02.0", port_db_conns }, + /* Google Dirks */ + { "Google", "Dirks", "0000:00:02.0", port_ab_conns }, + /* Google Moxie */ + { "Google", "Moxie", "0000:00:02.0", port_b_conns }, }; static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c index cfbfc4c1b2e6..41d019b01ec0 100644 --- a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c @@ -1002,8 +1002,8 @@ static int extron_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct extron_port *port = cec_get_drvdata(adap); - char buf[CEC_MAX_MSG_SIZE * 3 + 1]; - char cmd[CEC_MAX_MSG_SIZE * 3 + 13]; + char buf[(CEC_MAX_MSG_SIZE - 1) * 3 + 1]; + char cmd[sizeof(buf) + 14]; unsigned int i; if (port->disconnected) diff --git a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c index ee870ea1a886..6f8d6797c614 100644 --- a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c +++ b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c @@ -171,11 +171,12 @@ static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data, { struct rain *rain = serio_get_drvdata(serio); + spin_lock(&rain->buf_lock); if (rain->buf_len == DATA_SIZE) { + spin_unlock(&rain->buf_lock); dev_warn_once(rain->dev, "buffer overflow\n"); return IRQ_HANDLED; } - spin_lock(&rain->buf_lock); rain->buf_len++; rain->buf[rain->buf_wr_idx] = data; rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff; |