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
;
22 struct gpio_desc
*hpd_gpio
;
27 struct gpio_desc
*v5_gpio
;
33 static bool cec_gpio_read(struct cec_adapter
*adap
)
35 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
39 return gpiod_get_value(cec
->cec_gpio
);
42 static void cec_gpio_high(struct cec_adapter
*adap
)
44 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
48 cec
->cec_is_low
= false;
49 gpiod_set_value(cec
->cec_gpio
, 1);
52 static void cec_gpio_low(struct cec_adapter
*adap
)
54 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
58 if (WARN_ON_ONCE(cec
->cec_have_irq
))
59 free_irq(cec
->cec_irq
, cec
);
60 cec
->cec_have_irq
= false;
61 cec
->cec_is_low
= true;
62 gpiod_set_value(cec
->cec_gpio
, 0);
65 static irqreturn_t
cec_hpd_gpio_irq_handler_thread(int irq
, void *priv
)
67 struct cec_gpio
*cec
= priv
;
69 cec_queue_pin_hpd_event(cec
->adap
, cec
->hpd_is_high
, cec
->hpd_ts
);
73 static irqreturn_t
cec_5v_gpio_irq_handler(int irq
, void *priv
)
75 struct cec_gpio
*cec
= priv
;
76 bool is_high
= gpiod_get_value(cec
->v5_gpio
);
78 if (is_high
== cec
->v5_is_high
)
80 cec
->v5_ts
= ktime_get();
81 cec
->v5_is_high
= is_high
;
82 return IRQ_WAKE_THREAD
;
85 static irqreturn_t
cec_5v_gpio_irq_handler_thread(int irq
, void *priv
)
87 struct cec_gpio
*cec
= priv
;
89 cec_queue_pin_5v_event(cec
->adap
, cec
->v5_is_high
, cec
->v5_ts
);
93 static irqreturn_t
cec_hpd_gpio_irq_handler(int irq
, void *priv
)
95 struct cec_gpio
*cec
= priv
;
96 bool is_high
= gpiod_get_value(cec
->hpd_gpio
);
98 if (is_high
== cec
->hpd_is_high
)
100 cec
->hpd_ts
= ktime_get();
101 cec
->hpd_is_high
= is_high
;
102 return IRQ_WAKE_THREAD
;
105 static irqreturn_t
cec_gpio_irq_handler(int irq
, void *priv
)
107 struct cec_gpio
*cec
= priv
;
109 cec_pin_changed(cec
->adap
, gpiod_get_value(cec
->cec_gpio
));
113 static bool cec_gpio_enable_irq(struct cec_adapter
*adap
)
115 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
117 if (cec
->cec_have_irq
)
120 if (request_irq(cec
->cec_irq
, cec_gpio_irq_handler
,
121 IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
,
124 cec
->cec_have_irq
= true;
128 static void cec_gpio_disable_irq(struct cec_adapter
*adap
)
130 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
132 if (cec
->cec_have_irq
)
133 free_irq(cec
->cec_irq
, cec
);
134 cec
->cec_have_irq
= false;
137 static void cec_gpio_status(struct cec_adapter
*adap
, struct seq_file
*file
)
139 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
141 seq_printf(file
, "mode: %s\n", cec
->cec_is_low
? "low-drive" : "read");
142 if (cec
->cec_have_irq
)
143 seq_printf(file
, "using irq: %d\n", cec
->cec_irq
);
145 seq_printf(file
, "hpd: %s\n",
146 cec
->hpd_is_high
? "high" : "low");
148 seq_printf(file
, "5V: %s\n",
149 cec
->v5_is_high
? "high" : "low");
152 static int cec_gpio_read_hpd(struct cec_adapter
*adap
)
154 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
158 return gpiod_get_value(cec
->hpd_gpio
);
161 static int cec_gpio_read_5v(struct cec_adapter
*adap
)
163 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
167 return gpiod_get_value(cec
->v5_gpio
);
170 static void cec_gpio_free(struct cec_adapter
*adap
)
172 cec_gpio_disable_irq(adap
);
175 static const struct cec_pin_ops cec_gpio_pin_ops
= {
176 .read
= cec_gpio_read
,
178 .high
= cec_gpio_high
,
179 .enable_irq
= cec_gpio_enable_irq
,
180 .disable_irq
= cec_gpio_disable_irq
,
181 .status
= cec_gpio_status
,
182 .free
= cec_gpio_free
,
183 .read_hpd
= cec_gpio_read_hpd
,
184 .read_5v
= cec_gpio_read_5v
,
187 static int cec_gpio_probe(struct platform_device
*pdev
)
189 struct device
*dev
= &pdev
->dev
;
190 struct cec_gpio
*cec
;
193 cec
= devm_kzalloc(dev
, sizeof(*cec
), GFP_KERNEL
);
199 cec
->cec_gpio
= devm_gpiod_get(dev
, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN
);
200 if (IS_ERR(cec
->cec_gpio
))
201 return PTR_ERR(cec
->cec_gpio
);
202 cec
->cec_irq
= gpiod_to_irq(cec
->cec_gpio
);
204 cec
->hpd_gpio
= devm_gpiod_get_optional(dev
, "hpd", GPIOD_IN
);
205 if (IS_ERR(cec
->hpd_gpio
))
206 return PTR_ERR(cec
->hpd_gpio
);
208 cec
->v5_gpio
= devm_gpiod_get_optional(dev
, "v5", GPIOD_IN
);
209 if (IS_ERR(cec
->v5_gpio
))
210 return PTR_ERR(cec
->v5_gpio
);
212 cec
->adap
= cec_pin_allocate_adapter(&cec_gpio_pin_ops
,
213 cec
, pdev
->name
, CEC_CAP_DEFAULTS
| CEC_CAP_PHYS_ADDR
|
214 CEC_CAP_MONITOR_ALL
| CEC_CAP_MONITOR_PIN
);
215 if (IS_ERR(cec
->adap
))
216 return PTR_ERR(cec
->adap
);
219 cec
->hpd_irq
= gpiod_to_irq(cec
->hpd_gpio
);
220 ret
= devm_request_threaded_irq(dev
, cec
->hpd_irq
,
221 cec_hpd_gpio_irq_handler
,
222 cec_hpd_gpio_irq_handler_thread
,
224 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
,
231 cec
->v5_irq
= gpiod_to_irq(cec
->v5_gpio
);
232 ret
= devm_request_threaded_irq(dev
, cec
->v5_irq
,
233 cec_5v_gpio_irq_handler
,
234 cec_5v_gpio_irq_handler_thread
,
236 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
,
242 ret
= cec_register_adapter(cec
->adap
, &pdev
->dev
);
244 cec_delete_adapter(cec
->adap
);
248 platform_set_drvdata(pdev
, cec
);
252 static int cec_gpio_remove(struct platform_device
*pdev
)
254 struct cec_gpio
*cec
= platform_get_drvdata(pdev
);
256 cec_unregister_adapter(cec
->adap
);
260 static const struct of_device_id cec_gpio_match
[] = {
262 .compatible
= "cec-gpio",
266 MODULE_DEVICE_TABLE(of
, cec_gpio_match
);
268 static struct platform_driver cec_gpio_pdrv
= {
269 .probe
= cec_gpio_probe
,
270 .remove
= cec_gpio_remove
,
273 .of_match_table
= cec_gpio_match
,
277 module_platform_driver(cec_gpio_pdrv
);
279 MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
280 MODULE_LICENSE("GPL v2");
281 MODULE_DESCRIPTION("CEC GPIO driver");