1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 #include <linux/module.h>
7 #include <linux/interrupt.h>
8 #include <linux/delay.h>
9 #include <linux/platform_device.h>
10 #include <linux/gpio/consumer.h>
11 #include <media/cec-pin.h>
14 struct cec_adapter *adap;
17 struct gpio_desc *cec_gpio;
21 struct gpio_desc *hpd_gpio;
26 struct gpio_desc *v5_gpio;
32 static bool cec_gpio_read(struct cec_adapter *adap)
34 struct cec_gpio *cec = cec_get_drvdata(adap);
38 return gpiod_get_value(cec->cec_gpio);
41 static void cec_gpio_high(struct cec_adapter *adap)
43 struct cec_gpio *cec = cec_get_drvdata(adap);
47 cec->cec_is_low = false;
48 gpiod_set_value(cec->cec_gpio, 1);
51 static void cec_gpio_low(struct cec_adapter *adap)
53 struct cec_gpio *cec = cec_get_drvdata(adap);
57 cec->cec_is_low = true;
58 gpiod_set_value(cec->cec_gpio, 0);
61 static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv)
63 struct cec_gpio *cec = priv;
65 cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
69 static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv)
71 struct cec_gpio *cec = priv;
72 bool is_high = gpiod_get_value(cec->v5_gpio);
74 if (is_high == cec->v5_is_high)
76 cec->v5_ts = ktime_get();
77 cec->v5_is_high = is_high;
78 return IRQ_WAKE_THREAD;
81 static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv)
83 struct cec_gpio *cec = priv;
85 cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
89 static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv)
91 struct cec_gpio *cec = priv;
92 bool is_high = gpiod_get_value(cec->hpd_gpio);
94 if (is_high == cec->hpd_is_high)
96 cec->hpd_ts = ktime_get();
97 cec->hpd_is_high = is_high;
98 return IRQ_WAKE_THREAD;
101 static irqreturn_t cec_gpio_irq_handler(int irq, void *priv)
103 struct cec_gpio *cec = priv;
105 cec_pin_changed(cec->adap, gpiod_get_value(cec->cec_gpio));
109 static bool cec_gpio_enable_irq(struct cec_adapter *adap)
111 struct cec_gpio *cec = cec_get_drvdata(adap);
113 enable_irq(cec->cec_irq);
117 static void cec_gpio_disable_irq(struct cec_adapter *adap)
119 struct cec_gpio *cec = cec_get_drvdata(adap);
121 disable_irq(cec->cec_irq);
124 static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file)
126 struct cec_gpio *cec = cec_get_drvdata(adap);
128 seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read");
129 seq_printf(file, "using irq: %d\n", cec->cec_irq);
131 seq_printf(file, "hpd: %s\n",
132 cec->hpd_is_high ? "high" : "low");
134 seq_printf(file, "5V: %s\n",
135 cec->v5_is_high ? "high" : "low");
138 static int cec_gpio_read_hpd(struct cec_adapter *adap)
140 struct cec_gpio *cec = cec_get_drvdata(adap);
144 return gpiod_get_value(cec->hpd_gpio);
147 static int cec_gpio_read_5v(struct cec_adapter *adap)
149 struct cec_gpio *cec = cec_get_drvdata(adap);
153 return gpiod_get_value(cec->v5_gpio);
156 static void cec_gpio_free(struct cec_adapter *adap)
158 cec_gpio_disable_irq(adap);
161 static const struct cec_pin_ops cec_gpio_pin_ops = {
162 .read = cec_gpio_read,
164 .high = cec_gpio_high,
165 .enable_irq = cec_gpio_enable_irq,
166 .disable_irq = cec_gpio_disable_irq,
167 .status = cec_gpio_status,
168 .free = cec_gpio_free,
169 .read_hpd = cec_gpio_read_hpd,
170 .read_5v = cec_gpio_read_5v,
173 static int cec_gpio_probe(struct platform_device *pdev)
175 struct device *dev = &pdev->dev;
176 struct cec_gpio *cec;
179 cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
185 cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN);
186 if (IS_ERR(cec->cec_gpio))
187 return PTR_ERR(cec->cec_gpio);
188 cec->cec_irq = gpiod_to_irq(cec->cec_gpio);
190 cec->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
191 if (IS_ERR(cec->hpd_gpio))
192 return PTR_ERR(cec->hpd_gpio);
194 cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN);
195 if (IS_ERR(cec->v5_gpio))
196 return PTR_ERR(cec->v5_gpio);
198 cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops,
199 cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR |
200 CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN);
201 if (IS_ERR(cec->adap))
202 return PTR_ERR(cec->adap);
204 ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler,
205 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
206 cec->adap->name, cec);
210 cec_gpio_disable_irq(cec->adap);
213 cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio);
214 ret = devm_request_threaded_irq(dev, cec->hpd_irq,
215 cec_hpd_gpio_irq_handler,
216 cec_hpd_gpio_irq_handler_thread,
218 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
225 cec->v5_irq = gpiod_to_irq(cec->v5_gpio);
226 ret = devm_request_threaded_irq(dev, cec->v5_irq,
227 cec_5v_gpio_irq_handler,
228 cec_5v_gpio_irq_handler_thread,
230 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
236 ret = cec_register_adapter(cec->adap, &pdev->dev);
238 cec_delete_adapter(cec->adap);
242 platform_set_drvdata(pdev, cec);
246 static int cec_gpio_remove(struct platform_device *pdev)
248 struct cec_gpio *cec = platform_get_drvdata(pdev);
250 cec_unregister_adapter(cec->adap);
254 static const struct of_device_id cec_gpio_match[] = {
256 .compatible = "cec-gpio",
260 MODULE_DEVICE_TABLE(of, cec_gpio_match);
262 static struct platform_driver cec_gpio_pdrv = {
263 .probe = cec_gpio_probe,
264 .remove = cec_gpio_remove,
267 .of_match_table = cec_gpio_match,
271 module_platform_driver(cec_gpio_pdrv);
273 MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
274 MODULE_LICENSE("GPL v2");
275 MODULE_DESCRIPTION("CEC GPIO driver");