summaryrefslogtreecommitdiffstats
path: root/include/media
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2017-07-11 08:30:42 +0200
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-07-18 17:57:18 +0200
commitea5c8ef296681b53480ebeeffd06083bb60e693d (patch)
tree672a2ad5ce04c93d6e5a6afff7eeb290147bf3f1 /include/media
parentmedia: cec: add core support for low-level CEC pin monitoring (diff)
downloadlinux-ea5c8ef296681b53480ebeeffd06083bb60e693d.tar.xz
linux-ea5c8ef296681b53480ebeeffd06083bb60e693d.zip
media: cec-pin: add low-level pin hardware support
Add support for CEC hardware that relies on low-level pin polling or GPIO interrupts. One example is the Allwinner SoC. But any GPIO-based CEC implementation can use this as well. A GPIO implementation is very suitable as well for debugging: it can use interrupts to detect state changes and report it. Userspace can then verify if the bus traffic is correct. This also makes error injection possible. The disadvantage is that it is hard to get the timings right since linux isn't a hard realtime system. In general on an idle system it works quite well, but under load the timer will miss its mark every so often. The debugfs file /sys/kernel/debug/cec/cecX/status gives some statistics with respect to the timer overruns. When the adapter is unconfigured and the low-level driver supports interrupts, then the interrupt will be used to detect changes. This should be quite accurate. But when the adapter is configured a hrtimer has to be used. The hrtimer implements a state machine where for each state the code will read the bus or drive the bus and go on to the next state. It will re-arm the timer with a delay based on the next state. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Reviewed-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'include/media')
-rw-r--r--include/media/cec-pin.h183
-rw-r--r--include/media/cec.h4
2 files changed, 187 insertions, 0 deletions
diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h
new file mode 100644
index 000000000000..44b82d29d480
--- /dev/null
+++ b/include/media/cec-pin.h
@@ -0,0 +1,183 @@
+/*
+ * cec-pin.h - low-level CEC pin control
+ *
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef LINUX_CEC_PIN_H
+#define LINUX_CEC_PIN_H
+
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <media/cec.h>
+
+enum cec_pin_state {
+ /* CEC is off */
+ CEC_ST_OFF,
+ /* CEC is idle, waiting for Rx or Tx */
+ CEC_ST_IDLE,
+
+ /* Tx states */
+
+ /* Pending Tx, waiting for Signal Free Time to expire */
+ CEC_ST_TX_WAIT,
+ /* Low-drive was detected, wait for bus to go high */
+ CEC_ST_TX_WAIT_FOR_HIGH,
+ /* Drive CEC low for the start bit */
+ CEC_ST_TX_START_BIT_LOW,
+ /* Drive CEC high for the start bit */
+ CEC_ST_TX_START_BIT_HIGH,
+ /* Drive CEC low for the 0 bit */
+ CEC_ST_TX_DATA_BIT_0_LOW,
+ /* Drive CEC high for the 0 bit */
+ CEC_ST_TX_DATA_BIT_0_HIGH,
+ /* Drive CEC low for the 1 bit */
+ CEC_ST_TX_DATA_BIT_1_LOW,
+ /* Drive CEC high for the 1 bit */
+ CEC_ST_TX_DATA_BIT_1_HIGH,
+ /*
+ * Wait for start of sample time to check for Ack bit or first
+ * four initiator bits to check for Arbitration Lost.
+ */
+ CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE,
+ /* Wait for end of bit period after sampling */
+ CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE,
+
+ /* Rx states */
+
+ /* Start bit low detected */
+ CEC_ST_RX_START_BIT_LOW,
+ /* Start bit high detected */
+ CEC_ST_RX_START_BIT_HIGH,
+ /* Wait for bit sample time */
+ CEC_ST_RX_DATA_SAMPLE,
+ /* Wait for earliest end of bit period after sampling */
+ CEC_ST_RX_DATA_POST_SAMPLE,
+ /* Wait for CEC to go high (i.e. end of bit period */
+ CEC_ST_RX_DATA_HIGH,
+ /* Drive CEC low to send 0 Ack bit */
+ CEC_ST_RX_ACK_LOW,
+ /* End of 0 Ack time, wait for earliest end of bit period */
+ CEC_ST_RX_ACK_LOW_POST,
+ /* Wait for CEC to go high (i.e. end of bit period */
+ CEC_ST_RX_ACK_HIGH_POST,
+ /* Wait for earliest end of bit period and end of message */
+ CEC_ST_RX_ACK_FINISH,
+
+ /* Start low drive */
+ CEC_ST_LOW_DRIVE,
+ /* Monitor pin using interrupts */
+ CEC_ST_RX_IRQ,
+
+ /* Total number of pin states */
+ CEC_PIN_STATES
+};
+
+/**
+ * struct cec_pin_ops - low-level CEC pin operations
+ * @read: read the CEC pin. Return true if high, false if low.
+ * @low: drive the CEC pin low.
+ * @high: stop driving the CEC pin. The pull-up will drive the pin
+ * high, unless someone else is driving the pin low.
+ * @enable_irq: optional, enable the interrupt to detect pin voltage changes.
+ * @disable_irq: optional, disable the interrupt.
+ * @free: optional. Free any allocated resources. Called when the
+ * adapter is deleted.
+ * @status: optional, log status information.
+ *
+ * These operations are used by the cec pin framework to manipulate
+ * the CEC pin.
+ */
+struct cec_pin_ops {
+ bool (*read)(struct cec_adapter *adap);
+ void (*low)(struct cec_adapter *adap);
+ void (*high)(struct cec_adapter *adap);
+ bool (*enable_irq)(struct cec_adapter *adap);
+ void (*disable_irq)(struct cec_adapter *adap);
+ void (*free)(struct cec_adapter *adap);
+ void (*status)(struct cec_adapter *adap, struct seq_file *file);
+};
+
+#define CEC_NUM_PIN_EVENTS 128
+
+struct cec_pin {
+ struct cec_adapter *adap;
+ const struct cec_pin_ops *ops;
+ struct task_struct *kthread;
+ wait_queue_head_t kthread_waitq;
+ struct hrtimer timer;
+ ktime_t ts;
+ unsigned int wait_usecs;
+ u16 la_mask;
+ bool enabled;
+ bool monitor_all;
+ bool cur_value;
+ bool rx_eom;
+ bool enable_irq_failed;
+ enum cec_pin_state state;
+ struct cec_msg tx_msg;
+ u32 tx_bit;
+ bool tx_nacked;
+ u32 tx_signal_free_time;
+ struct cec_msg rx_msg;
+ u32 rx_bit;
+
+ struct cec_msg work_rx_msg;
+ u8 work_tx_status;
+ bool work_enable_irq;
+ ktime_t work_tx_ts;
+ atomic_t work_pin_events;
+ unsigned int work_pin_events_wr;
+ unsigned int work_pin_events_rd;
+ ktime_t work_pin_ts[CEC_NUM_PIN_EVENTS];
+ bool work_pin_is_high[CEC_NUM_PIN_EVENTS];
+ ktime_t timer_ts;
+ u32 timer_cnt;
+ u32 timer_100ms_overruns;
+ u32 timer_300ms_overruns;
+ u32 timer_max_overrun;
+ u32 timer_sum_overrun;
+};
+
+/**
+ * cec_pin_changed() - update pin state from interrupt
+ *
+ * @adap: pointer to the cec adapter
+ * @value: when true the pin is high, otherwise it is low
+ *
+ * If changes of the CEC voltage are detected via an interrupt, then
+ * cec_pin_changed is called from the interrupt with the new value.
+ */
+void cec_pin_changed(struct cec_adapter *adap, bool value);
+
+/**
+ * cec_pin_allocate_adapter() - allocate a pin-based cec adapter
+ *
+ * @pin_ops: low-level pin operations
+ * @priv: will be stored in adap->priv and can be used by the adapter ops.
+ * Use cec_get_drvdata(adap) to get the priv pointer.
+ * @name: the name of the CEC adapter. Note: this name will be copied.
+ * @caps: capabilities of the CEC adapter. This will be ORed with
+ * CEC_CAP_MONITOR_ALL and CEC_CAP_MONITOR_PIN.
+ *
+ * Allocate a cec adapter using the cec pin framework.
+ *
+ * Return: a pointer to the cec adapter or an error pointer
+ */
+struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
+ void *priv, const char *name, u32 caps);
+
+#endif
diff --git a/include/media/cec.h b/include/media/cec.h
index d983960b37ad..f9cab1a9f912 100644
--- a/include/media/cec.h
+++ b/include/media/cec.h
@@ -61,6 +61,7 @@ struct cec_devnode {
struct cec_adapter;
struct cec_data;
+struct cec_pin;
struct cec_data {
struct list_head list;
@@ -189,6 +190,9 @@ struct cec_adapter {
#ifdef CONFIG_CEC_NOTIFIER
struct cec_notifier *notifier;
#endif
+#ifdef CONFIG_CEC_PIN
+ struct cec_pin *pin;
+#endif
struct dentry *cec_dir;
struct dentry *status_file;