From: Hans Verkuil hans.verkuil@cisco.com
This driver adds support for CEC implementations that use a pull-up GPIO pin. While SoCs exist that do this, the primary use-case is to turn a single-board computer into a cheap CEC debugger.
Together with 'cec-ctl --monitor-pin' you can do low-level CEC bus monitoring and do protocol analysis. And error injection is also planned for the future.
Here is an example using the Raspberry Pi 3:
https://hverkuil.home.xs4all.nl/rpi3-cec.jpg
While this example is for the Rpi, this driver will work for any SoC with a pull-up GPIO pin.
In addition the cec-gpio driver can optionally monitor the HPD pin. The state of the HPD pin influences the CEC behavior so it is very useful to be able to monitor both.
And some HDMI sinks are known to quickly toggle the HPD when e.g. switching between inputs. So it is useful to be able to see an event when the HPD changes value.
The first two patches add support for the new HPD events. The last three patches are for the cec-gpio driver itself.
Regards,
Hans
Changes since v2:
- Add support for HPD events. - Switch from pin BCM4 to pin BCM7 in the bindings example
Changes since v1:
- Updated the bindings doc to not refer to the driver, instead refer to the hardware.
Hans Verkuil (5): cec: add CEC_EVENT_PIN_HPD_LOW/HIGH events cec-ioc-dqevent.rst: document new CEC_EVENT_PIN_HPD_LOW/HIGH events dt-bindings: document the CEC GPIO bindings cec-gpio: add HDMI CEC GPIO driver MAINTAINERS: add cec-gpio entry
.../devicetree/bindings/media/cec-gpio.txt | 22 ++ Documentation/media/uapi/cec/cec-ioc-dqevent.rst | 18 ++ MAINTAINERS | 9 + drivers/media/cec/cec-adap.c | 18 +- drivers/media/cec/cec-api.c | 18 +- drivers/media/platform/Kconfig | 9 + drivers/media/platform/Makefile | 2 + drivers/media/platform/cec-gpio/Makefile | 1 + drivers/media/platform/cec-gpio/cec-gpio.c | 237 +++++++++++++++++++++ include/media/cec-pin.h | 4 + include/media/cec.h | 12 +- include/uapi/linux/cec.h | 2 + 12 files changed, 346 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/cec-gpio.txt create mode 100644 drivers/media/platform/cec-gpio/Makefile create mode 100644 drivers/media/platform/cec-gpio/cec-gpio.c
From: Hans Verkuil hans.verkuil@cisco.com
Add support for two new low-level events: PIN_HPD_LOW and PIN_HPD_HIGH.
This is specifically meant for use with the upcoming cec-gpio driver and makes it possible to trace when the HPD pin changes. Some HDMI sinks do strange things with the HPD and this makes it easy to debug this.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- drivers/media/cec/cec-adap.c | 18 +++++++++++++++++- drivers/media/cec/cec-api.c | 18 ++++++++++++++---- include/media/cec-pin.h | 4 ++++ include/media/cec.h | 12 +++++++++++- include/uapi/linux/cec.h | 2 ++ 5 files changed, 48 insertions(+), 6 deletions(-)
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index dd769e40416f..eb904a71609a 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -86,7 +86,7 @@ void cec_queue_event_fh(struct cec_fh *fh, const struct cec_event *new_ev, u64 ts) { static const u8 max_events[CEC_NUM_EVENTS] = { - 1, 1, 64, 64, + 1, 1, 64, 64, 8, 8, }; struct cec_event_entry *entry; unsigned int ev_idx = new_ev->event - 1; @@ -170,6 +170,22 @@ void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts) } EXPORT_SYMBOL_GPL(cec_queue_pin_cec_event);
+/* Notify userspace that the HPD pin changed state at the given time. */ +void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts) +{ + struct cec_event ev = { + .event = is_high ? CEC_EVENT_PIN_HPD_HIGH : + CEC_EVENT_PIN_HPD_LOW, + }; + struct cec_fh *fh; + + mutex_lock(&adap->devnode.lock); + list_for_each_entry(fh, &adap->devnode.fhs, list) + cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); + mutex_unlock(&adap->devnode.lock); +} +EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event); + /* * Queue a new message for this filehandle. * diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index a079f7fe018c..465bb3ec21f6 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -529,7 +529,7 @@ static int cec_open(struct inode *inode, struct file *filp) * Initial events that are automatically sent when the cec device is * opened. */ - struct cec_event ev_state = { + struct cec_event ev = { .event = CEC_EVENT_STATE_CHANGE, .flags = CEC_EVENT_FL_INITIAL_STATE, }; @@ -569,9 +569,19 @@ static int cec_open(struct inode *inode, struct file *filp) filp->private_data = fh;
/* Queue up initial state events */ - ev_state.state_change.phys_addr = adap->phys_addr; - ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; - cec_queue_event_fh(fh, &ev_state, 0); + ev.state_change.phys_addr = adap->phys_addr; + ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; + cec_queue_event_fh(fh, &ev, 0); +#ifdef CONFIG_CEC_PIN + if (adap->pin && adap->pin->ops->read_hpd) { + err = adap->pin->ops->read_hpd(adap); + if (err >= 0) { + ev.event = err ? CEC_EVENT_PIN_HPD_HIGH : + CEC_EVENT_PIN_HPD_LOW; + cec_queue_event_fh(fh, &ev, 0); + } + } +#endif
list_add(&fh->list, &devnode->fhs); mutex_unlock(&devnode->lock); diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h index f09cc9579d53..ea84b9c9e0c3 100644 --- a/include/media/cec-pin.h +++ b/include/media/cec-pin.h @@ -97,6 +97,9 @@ enum cec_pin_state { * @free: optional. Free any allocated resources. Called when the * adapter is deleted. * @status: optional, log status information. + * @read_hpd: read the HPD pin. Return true if high, false if low or + * an error if negative. If NULL or -ENOTTY is returned, + * then this is not supported. * * These operations are used by the cec pin framework to manipulate * the CEC pin. @@ -109,6 +112,7 @@ struct cec_pin_ops { void (*disable_irq)(struct cec_adapter *adap); void (*free)(struct cec_adapter *adap); void (*status)(struct cec_adapter *adap, struct seq_file *file); + int (*read_hpd)(struct cec_adapter *adap); };
#define CEC_NUM_PIN_EVENTS 128 diff --git a/include/media/cec.h b/include/media/cec.h index df6b3bd31284..9d0f983faea9 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -91,7 +91,7 @@ struct cec_event_entry { };
#define CEC_NUM_CORE_EVENTS 2 -#define CEC_NUM_EVENTS CEC_EVENT_PIN_CEC_HIGH +#define CEC_NUM_EVENTS CEC_EVENT_PIN_HPD_HIGH
struct cec_fh { struct list_head list; @@ -296,6 +296,16 @@ static inline void cec_received_msg(struct cec_adapter *adap, void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts);
+/** + * cec_queue_pin_hpd_event() - queue a pin event with a given timestamp. + * + * @adap: pointer to the cec adapter + * @is_high: when true the HPD pin is high, otherwise it is low + * @ts: the timestamp for this event + * + */ +void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts); + /** * cec_get_edid_phys_addr() - find and return the physical address * diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 4351c3481aea..b9f8df3a0477 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -410,6 +410,8 @@ struct cec_log_addrs { #define CEC_EVENT_LOST_MSGS 2 #define CEC_EVENT_PIN_CEC_LOW 3 #define CEC_EVENT_PIN_CEC_HIGH 4 +#define CEC_EVENT_PIN_HPD_LOW 5 +#define CEC_EVENT_PIN_HPD_HIGH 6
#define CEC_EVENT_FL_INITIAL_STATE (1 << 0) #define CEC_EVENT_FL_DROPPED_EVENTS (1 << 1)
From: Hans Verkuil hans.verkuil@cisco.com
Document these new CEC events.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- Documentation/media/uapi/cec/cec-ioc-dqevent.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst index db615e3405c0..32b47763f5a6 100644 --- a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst +++ b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst @@ -160,6 +160,24 @@ it is guaranteed that the state did change in between the two events. - Generated if the CEC pin goes from a low voltage to a high voltage. Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` capability set. + * .. _`CEC-EVENT-PIN-HPD-LOW`: + + - ``CEC_EVENT_PIN_HPD_LOW`` + - 5 + - Generated if the HPD pin goes from a high voltage to a low voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. When open() is called, the HPD pin can be read and + the HPD is low, then an initial event will be generated for that + filehandle. + * .. _`CEC-EVENT-PIN-HPD-HIGH`: + + - ``CEC_EVENT_PIN_HPD_HIGH`` + - 6 + - Generated if the HPD pin goes from a low voltage to a high voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. When open() is called, the HPD pin can be read and + the HPD is high, then an initial event will be generated for that + filehandle.
.. tabularcolumns:: |p{6.0cm}|p{0.6cm}|p{10.9cm}|
From: Hans Verkuil hans.verkuil@cisco.com
Document the bindings for the cec-gpio module for hardware where the CEC pin and optionally the HPD pin are connected to GPIO pins.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- .../devicetree/bindings/media/cec-gpio.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/cec-gpio.txt
diff --git a/Documentation/devicetree/bindings/media/cec-gpio.txt b/Documentation/devicetree/bindings/media/cec-gpio.txt new file mode 100644 index 000000000000..d1df742af0ac --- /dev/null +++ b/Documentation/devicetree/bindings/media/cec-gpio.txt @@ -0,0 +1,22 @@ +* HDMI CEC GPIO driver + +The HDMI CEC GPIO module supports CEC implementations where the CEC pin +is hooked up to a pull-up GPIO pin and - optionally - the HPD pin is +hooked up to a pull-down GPIO pin. + +Required properties: + - compatible: value must be "cec-gpio" + - cec-gpio: gpio that the CEC line is connected to + +Optional property: + - hpd-gpio: gpio that the HPD line is connected to + +Example for the Raspberry Pi 3 where the CEC line is connected to +pin 26 aka BCM7 aka CE1 on the GPIO pin header and the HPD line is +connected to pin 11 aka BCM17: + +cec-gpio@7 { + compatible = "cec-gpio"; + cec-gpio = <&gpio 7 GPIO_ACTIVE_HIGH>; + hpd-gpio = <&gpio 17 GPIO_ACTIVE_HIGH>; +};
On Wed, Aug 30, 2017 at 6:10 PM, Hans Verkuil hverkuil@xs4all.nl wrote:
From: Hans Verkuil hans.verkuil@cisco.com
Document the bindings for the cec-gpio module for hardware where the CEC pin and optionally the HPD pin are connected to GPIO pins.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com
I usually refer to GPIO "lines" rather than "pins" for clarity. It's because some systems also have pin control, and then it becomes a bit muddy what is a pin.
+* HDMI CEC GPIO driver
+The HDMI CEC GPIO module supports CEC implementations where the CEC pin +is hooked up to a pull-up GPIO pin and - optionally - the HPD pin is +hooked up to a pull-down GPIO pin.
+Required properties:
- compatible: value must be "cec-gpio"
- cec-gpio: gpio that the CEC line is connected to
+Optional property:
- hpd-gpio: gpio that the HPD line is connected to
+Example for the Raspberry Pi 3 where the CEC line is connected to +pin 26 aka BCM7 aka CE1 on the GPIO pin header and the HPD line is +connected to pin 11 aka BCM17:
+cec-gpio@7 {
compatible = "cec-gpio";
cec-gpio = <&gpio 7 GPIO_ACTIVE_HIGH>;
hpd-gpio = <&gpio 17 GPIO_ACTIVE_HIGH>;
+};
So what I understood from the driver is that the cec-gpio is maybe actually an open drain output line, so in that case it should be stated in the docs and cec-gpio = <&gpio 7 GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN> or GPIO_LINE_OPEN_DRAIN if it is not also single-ended.
Yours, Linus Walleij
On 31/08/17 11:20, Linus Walleij wrote:
On Wed, Aug 30, 2017 at 6:10 PM, Hans Verkuil hverkuil@xs4all.nl wrote:
From: Hans Verkuil hans.verkuil@cisco.com
Document the bindings for the cec-gpio module for hardware where the CEC pin and optionally the HPD pin are connected to GPIO pins.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com
I usually refer to GPIO "lines" rather than "pins" for clarity. It's because some systems also have pin control, and then it becomes a bit muddy what is a pin.
I'll change the terminology.
+* HDMI CEC GPIO driver
+The HDMI CEC GPIO module supports CEC implementations where the CEC pin +is hooked up to a pull-up GPIO pin and - optionally - the HPD pin is +hooked up to a pull-down GPIO pin.
+Required properties:
- compatible: value must be "cec-gpio"
- cec-gpio: gpio that the CEC line is connected to
+Optional property:
- hpd-gpio: gpio that the HPD line is connected to
+Example for the Raspberry Pi 3 where the CEC line is connected to +pin 26 aka BCM7 aka CE1 on the GPIO pin header and the HPD line is +connected to pin 11 aka BCM17:
+cec-gpio@7 {
compatible = "cec-gpio";
cec-gpio = <&gpio 7 GPIO_ACTIVE_HIGH>;
hpd-gpio = <&gpio 17 GPIO_ACTIVE_HIGH>;
+};
So what I understood from the driver is that the cec-gpio is maybe actually an open drain output line, so in that case it should be stated in the docs and cec-gpio = <&gpio 7 GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN> or GPIO_LINE_OPEN_DRAIN if it is not also single-ended.
Yes, I agree, it's open drain. I'm not sure whether or not it is single-ended. I'm not sure what the difference is, and I have no electronics background.
Looking at fwnode_get_named_gpiod() it seems that it has to be single-ended for GPIO_OPEN_DRAIN to be set, so I am going with that. It works, so it is probably right :-)
Regards,
Hans
From: Hans Verkuil hans.verkuil@cisco.com
Add a simple HDMI CEC GPIO driver that sits on top of the cec-pin framework.
While I have heard of SoCs that use the GPIO pin for CEC (apparently an early RockChip SoC used that), the main use-case of this driver is to function as a debugging tool.
By connecting the CEC line to a GPIO pin on a Raspberry Pi 3 for example it turns it into a CEC debugger and protocol analyzer.
With 'cec-ctl --monitor-pin' the CEC traffic can be analyzed.
But of course it can also be used with any hardware project where the HDMI CEC pin is hooked up to a pull-up gpio pin.
In addition this has (optional) support for tracing HPD changes if the HPD is connected to a GPIO.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- drivers/media/platform/Kconfig | 9 ++ drivers/media/platform/Makefile | 2 + drivers/media/platform/cec-gpio/Makefile | 1 + drivers/media/platform/cec-gpio/cec-gpio.c | 237 +++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 drivers/media/platform/cec-gpio/Makefile create mode 100644 drivers/media/platform/cec-gpio/cec-gpio.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 7e7cc49b8674..d95b0ee367d6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -553,6 +553,15 @@ config VIDEO_MESON_AO_CEC This is a driver for Amlogic Meson SoCs AO CEC interface. It uses the generic CEC framework interface. CEC bus is present in the HDMI connector and enables communication + +config CEC_GPIO + tristate "Generic GPIO-based CEC driver" + depends on PREEMPT + select CEC_CORE + select CEC_PIN + ---help--- + This is a generic GPIO-based CEC driver. + The CEC bus is present in the HDMI connector and enables communication between compatible devices.
config VIDEO_SAMSUNG_S5P_CEC diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index c1ef946bf032..9bf48f118537 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_VIDEO_CODA) += coda/
obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
+obj-$(CONFIG_CEC_GPIO) += cec-gpio/ + obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
obj-$(CONFIG_VIDEO_MUX) += video-mux.o diff --git a/drivers/media/platform/cec-gpio/Makefile b/drivers/media/platform/cec-gpio/Makefile new file mode 100644 index 000000000000..e82b258afa55 --- /dev/null +++ b/drivers/media/platform/cec-gpio/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CEC_GPIO) += cec-gpio.o diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c new file mode 100644 index 000000000000..a582cf91bf87 --- /dev/null +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -0,0 +1,237 @@ +/* + * 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. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/of_gpio.h> +#include <media/cec-pin.h> + +struct cec_gpio { + struct cec_adapter *adap; + struct device *dev; + int gpio; + int hpd_gpio; + int irq; + int hpd_irq; + bool hpd_is_high; + ktime_t hpd_ts; + bool is_low; + bool have_irq; +}; + +static bool cec_gpio_read(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (cec->is_low) + return false; + return gpio_get_value(cec->gpio); +} + +static void cec_gpio_high(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (!cec->is_low) + return; + cec->is_low = false; + gpio_direction_input(cec->gpio); +} + +static void cec_gpio_low(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (cec->is_low) + return; + if (WARN_ON_ONCE(cec->have_irq)) + free_irq(cec->irq, cec); + cec->have_irq = false; + cec->is_low = true; + gpio_direction_output(cec->gpio, 0); +} + +static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv) +{ + struct cec_gpio *cec = priv; + + 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) +{ + struct cec_gpio *cec = priv; + + cec->hpd_ts = ktime_get(); + cec->hpd_is_high = gpio_get_value(cec->hpd_gpio); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t cec_gpio_irq_handler(int irq, void *priv) +{ + struct cec_gpio *cec = priv; + + cec_pin_changed(cec->adap, gpio_get_value(cec->gpio)); + return IRQ_HANDLED; +} + +static bool cec_gpio_enable_irq(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (cec->have_irq) + return true; + + if (request_irq(cec->irq, cec_gpio_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + adap->name, cec)) + return false; + cec->have_irq = true; + return true; +} + +static void cec_gpio_disable_irq(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (cec->have_irq) + free_irq(cec->irq, cec); + cec->have_irq = false; +} + +static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + seq_printf(file, "mode: %s\n", cec->is_low ? "low-drive" : "read"); + if (cec->have_irq) + seq_printf(file, "using irq: %d\n", cec->irq); + if (cec->hpd_gpio >= 0) + seq_printf(file, "hpd: %s\n", + cec->hpd_is_high ? "high" : "low"); +} + +static int cec_gpio_read_hpd(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (cec->hpd_gpio < 0) + return -ENOTTY; + return gpio_get_value(cec->hpd_gpio); +} + +static void cec_gpio_free(struct cec_adapter *adap) +{ + cec_gpio_disable_irq(adap); +} + +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, + .status = cec_gpio_status, + .free = cec_gpio_free, + .read_hpd = cec_gpio_read_hpd, +}; + +static int cec_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + enum of_gpio_flags hpd_gpio_flags; + struct cec_gpio *cec; + int ret; + + cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); + if (!cec) + return -ENOMEM; + + cec->gpio = of_get_named_gpio_flags(dev->of_node, + "cec-gpio", 0, &hpd_gpio_flags); + if (cec->gpio < 0) + return cec->gpio; + + cec->hpd_gpio = of_get_named_gpio_flags(dev->of_node, + "hpd-gpio", 0, &hpd_gpio_flags); + cec->irq = gpio_to_irq(cec->gpio); + gpio_direction_input(cec->gpio); + cec->dev = dev; + + if (cec->hpd_gpio >= 0) { + cec->hpd_irq = gpio_to_irq(cec->hpd_gpio); + gpio_direction_input(cec->hpd_gpio); + if (devm_request_threaded_irq(dev, cec->hpd_irq, + cec_hpd_gpio_irq_handler, + cec_hpd_gpio_irq_handler_thread, + IRQF_ONESHOT | + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "hpd-gpio", cec)) + cec->hpd_gpio = -1; + } + + cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops, + cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | + CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN); + if (IS_ERR(cec->adap)) + return PTR_ERR(cec->adap); + + ret = cec_register_adapter(cec->adap, &pdev->dev); + if (ret) { + cec_delete_adapter(cec->adap); + return ret; + } + + platform_set_drvdata(pdev, cec); + return 0; +} + +static int cec_gpio_remove(struct platform_device *pdev) +{ + struct cec_gpio *cec = platform_get_drvdata(pdev); + + cec_unregister_adapter(cec->adap); + return 0; +} + +static const struct of_device_id cec_gpio_match[] = { + { + .compatible = "cec-gpio", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cec_gpio_match); + +static struct platform_driver cec_gpio_pdrv = { + .probe = cec_gpio_probe, + .remove = cec_gpio_remove, + .driver = { + .name = "cec-gpio", + .of_match_table = cec_gpio_match, + }, +}; + +module_platform_driver(cec_gpio_pdrv); + +MODULE_AUTHOR("Hans Verkuil hans.verkuil@cisco.com"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("CEC GPIO driver");
On Wed, Aug 30, 2017 at 6:10 PM, Hans Verkuil hverkuil@xs4all.nl wrote:
From: Hans Verkuil hans.verkuil@cisco.com
Add a simple HDMI CEC GPIO driver that sits on top of the cec-pin framework.
While I have heard of SoCs that use the GPIO pin for CEC (apparently an early RockChip SoC used that), the main use-case of this driver is to function as a debugging tool.
By connecting the CEC line to a GPIO pin on a Raspberry Pi 3 for example it turns it into a CEC debugger and protocol analyzer.
With 'cec-ctl --monitor-pin' the CEC traffic can be analyzed.
But of course it can also be used with any hardware project where the HDMI CEC pin is hooked up to a pull-up gpio pin.
In addition this has (optional) support for tracing HPD changes if the HPD is connected to a GPIO.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com
Pretty cool stuff!
+config CEC_GPIO
tristate "Generic GPIO-based CEC driver"
depends on PREEMPT
depends on GPIOLIB
or
select GPIOLIB
your pick.
+#include <linux/gpio.h>
This should not be needed in new code.
+#include <linux/gpio/consumer.h>
This should be enough.
+#include <linux/of_gpio.h>
Your should not need this either.
+struct cec_gpio {
struct cec_adapter *adap;
struct device *dev;
int gpio;
int hpd_gpio;
Think this should be:
struct gpio_desc *gpio; struct gpio_desc *hpd_gpio;
int irq;
int hpd_irq;
bool hpd_is_high;
ktime_t hpd_ts;
bool is_low;
bool have_irq;
+};
+static bool cec_gpio_read(struct cec_adapter *adap) +{
struct cec_gpio *cec = cec_get_drvdata(adap);
if (cec->is_low)
return false;
return gpio_get_value(cec->gpio);
Use descriptor accessors
gpiod_get_value()
+static void cec_gpio_high(struct cec_adapter *adap) +{
struct cec_gpio *cec = cec_get_drvdata(adap);
if (!cec->is_low)
return;
cec->is_low = false;
gpio_direction_input(cec->gpio);
Are you setting the GPIO high by setting it as input? That sounds like you should be requesting it as open drain in the DTS file, see Documentation/gpio/driver.txt for details about open drain, and use GPIO_LINE_OPEN_DRAIN from <dt-bindings/gpio/gpio.h>
+static void cec_gpio_low(struct cec_adapter *adap) +{
struct cec_gpio *cec = cec_get_drvdata(adap);
if (cec->is_low)
return;
if (WARN_ON_ONCE(cec->have_irq))
free_irq(cec->irq, cec);
cec->have_irq = false;
cec->is_low = true;
gpio_direction_output(cec->gpio, 0);
Yeah this looks like you're doing open drain. gpiod_direction_output() etc.
+static int cec_gpio_read_hpd(struct cec_adapter *adap) +{
struct cec_gpio *cec = cec_get_drvdata(adap);
if (cec->hpd_gpio < 0)
return -ENOTTY;
return gpio_get_value(cec->hpd_gpio);
gpiod_get_value()
+static int cec_gpio_probe(struct platform_device *pdev) +{
struct device *dev = &pdev->dev;
enum of_gpio_flags hpd_gpio_flags;
struct cec_gpio *cec;
int ret;
cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
if (!cec)
return -ENOMEM;
cec->gpio = of_get_named_gpio_flags(dev->of_node,
"cec-gpio", 0, &hpd_gpio_flags);
if (cec->gpio < 0)
return cec->gpio;
cec->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
"hpd-gpio", 0, &hpd_gpio_flags);
This is a proper device so don't use all this trouble.
cec->gpio = gpiod_get(dev, "cec-gpio", GPIOD_IN);
or similar (grep for examples!)
Same for hdp_gpio.
cec->irq = gpio_to_irq(cec->gpio);
gpiod_to_irq()
gpio_direction_input(cec->gpio);
This is not needed with the above IN flag.
But as said above, maybe you are looking for an open drain output actually.
if (cec->hpd_gpio >= 0) {
cec->hpd_irq = gpio_to_irq(cec->hpd_gpio);
gpio_direction_input(cec->hpd_gpio);
Already done if you pass the right flag. This should be IN for sure.
Use gpiod_to_irq().
Please include me on subsequent posts, I want to try to use descriptors as much as possible for new drivers.
Yours, Linus Walleij
From: Hans Verkuil hans.verkuil@cisco.com
Add an entry for the CEC GPIO driver.
Signed-off-by: Hans Verkuil hans.verkuil@cisco.com --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index eb930ebecfcb..5ef0d34ef502 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3205,6 +3205,15 @@ F: include/uapi/linux/cec.h F: include/uapi/linux/cec-funcs.h F: Documentation/devicetree/bindings/media/cec.txt
+CEC GPIO DRIVER +M: Hans Verkuil hans.verkuil@cisco.com +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Supported +F: drivers/media/platform/cec-gpio/ +F: Documentation/devicetree/bindings/media/cec-gpio.txt + CELL BROADBAND ENGINE ARCHITECTURE M: Arnd Bergmann arnd@arndb.de L: linuxppc-dev@lists.ozlabs.org
dri-devel@lists.freedesktop.org