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-notifier.h>
12 #include <media/cec-pin.h>
15 struct cec_adapter
*adap
;
16 struct cec_notifier
*notifier
;
19 struct gpio_desc
*cec_gpio
;
23 struct gpio_desc
*hpd_gpio
;
28 struct gpio_desc
*v5_gpio
;
34 static bool cec_gpio_read(struct cec_adapter
*adap
)
36 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
40 return gpiod_get_value(cec
->cec_gpio
);
43 static void cec_gpio_high(struct cec_adapter
*adap
)
45 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
49 cec
->cec_is_low
= false;
50 gpiod_set_value(cec
->cec_gpio
, 1);
53 static void cec_gpio_low(struct cec_adapter
*adap
)
55 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
59 cec
->cec_is_low
= true;
60 gpiod_set_value(cec
->cec_gpio
, 0);
63 static irqreturn_t
cec_hpd_gpio_irq_handler_thread(int irq
, void *priv
)
65 struct cec_gpio
*cec
= priv
;
67 cec_queue_pin_hpd_event(cec
->adap
, cec
->hpd_is_high
, cec
->hpd_ts
);
71 static irqreturn_t
cec_5v_gpio_irq_handler(int irq
, void *priv
)
73 struct cec_gpio
*cec
= priv
;
74 bool is_high
= gpiod_get_value(cec
->v5_gpio
);
76 if (is_high
== cec
->v5_is_high
)
78 cec
->v5_ts
= ktime_get();
79 cec
->v5_is_high
= is_high
;
80 return IRQ_WAKE_THREAD
;
83 static irqreturn_t
cec_5v_gpio_irq_handler_thread(int irq
, void *priv
)
85 struct cec_gpio
*cec
= priv
;
87 cec_queue_pin_5v_event(cec
->adap
, cec
->v5_is_high
, cec
->v5_ts
);
91 static irqreturn_t
cec_hpd_gpio_irq_handler(int irq
, void *priv
)
93 struct cec_gpio
*cec
= priv
;
94 bool is_high
= gpiod_get_value(cec
->hpd_gpio
);
96 if (is_high
== cec
->hpd_is_high
)
98 cec
->hpd_ts
= ktime_get();
99 cec
->hpd_is_high
= is_high
;
100 return IRQ_WAKE_THREAD
;
103 static irqreturn_t
cec_gpio_irq_handler(int irq
, void *priv
)
105 struct cec_gpio
*cec
= priv
;
107 cec_pin_changed(cec
->adap
, gpiod_get_value(cec
->cec_gpio
));
111 static bool cec_gpio_enable_irq(struct cec_adapter
*adap
)
113 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
115 enable_irq(cec
->cec_irq
);
119 static void cec_gpio_disable_irq(struct cec_adapter
*adap
)
121 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
123 disable_irq(cec
->cec_irq
);
126 static void cec_gpio_status(struct cec_adapter
*adap
, struct seq_file
*file
)
128 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
130 seq_printf(file
, "mode: %s\n", cec
->cec_is_low
? "low-drive" : "read");
131 seq_printf(file
, "using irq: %d\n", cec
->cec_irq
);
133 seq_printf(file
, "hpd: %s\n",
134 cec
->hpd_is_high
? "high" : "low");
136 seq_printf(file
, "5V: %s\n",
137 cec
->v5_is_high
? "high" : "low");
140 static int cec_gpio_read_hpd(struct cec_adapter
*adap
)
142 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
146 return gpiod_get_value(cec
->hpd_gpio
);
149 static int cec_gpio_read_5v(struct cec_adapter
*adap
)
151 struct cec_gpio
*cec
= cec_get_drvdata(adap
);
155 return gpiod_get_value(cec
->v5_gpio
);
158 static void cec_gpio_free(struct cec_adapter
*adap
)
160 cec_gpio_disable_irq(adap
);
163 static const struct cec_pin_ops cec_gpio_pin_ops
= {
164 .read
= cec_gpio_read
,
166 .high
= cec_gpio_high
,
167 .enable_irq
= cec_gpio_enable_irq
,
168 .disable_irq
= cec_gpio_disable_irq
,
169 .status
= cec_gpio_status
,
170 .free
= cec_gpio_free
,
171 .read_hpd
= cec_gpio_read_hpd
,
172 .read_5v
= cec_gpio_read_5v
,
175 static int cec_gpio_probe(struct platform_device
*pdev
)
177 struct device
*dev
= &pdev
->dev
;
178 struct device
*hdmi_dev
;
179 struct cec_gpio
*cec
;
180 u32 caps
= CEC_CAP_DEFAULTS
| CEC_CAP_MONITOR_ALL
| CEC_CAP_MONITOR_PIN
;
183 hdmi_dev
= cec_notifier_parse_hdmi_phandle(dev
);
184 if (PTR_ERR(hdmi_dev
) == -EPROBE_DEFER
)
185 return PTR_ERR(hdmi_dev
);
186 if (IS_ERR(hdmi_dev
))
187 caps
|= CEC_CAP_PHYS_ADDR
;
189 cec
= devm_kzalloc(dev
, sizeof(*cec
), GFP_KERNEL
);
195 cec
->cec_gpio
= devm_gpiod_get(dev
, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN
);
196 if (IS_ERR(cec
->cec_gpio
))
197 return PTR_ERR(cec
->cec_gpio
);
198 cec
->cec_irq
= gpiod_to_irq(cec
->cec_gpio
);
200 cec
->hpd_gpio
= devm_gpiod_get_optional(dev
, "hpd", GPIOD_IN
);
201 if (IS_ERR(cec
->hpd_gpio
))
202 return PTR_ERR(cec
->hpd_gpio
);
204 cec
->v5_gpio
= devm_gpiod_get_optional(dev
, "v5", GPIOD_IN
);
205 if (IS_ERR(cec
->v5_gpio
))
206 return PTR_ERR(cec
->v5_gpio
);
208 cec
->adap
= cec_pin_allocate_adapter(&cec_gpio_pin_ops
,
209 cec
, pdev
->name
, caps
);
210 if (IS_ERR(cec
->adap
))
211 return PTR_ERR(cec
->adap
);
213 ret
= devm_request_irq(dev
, cec
->cec_irq
, cec_gpio_irq_handler
,
214 IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
,
215 cec
->adap
->name
, cec
);
219 cec_gpio_disable_irq(cec
->adap
);
222 cec
->hpd_irq
= gpiod_to_irq(cec
->hpd_gpio
);
223 ret
= devm_request_threaded_irq(dev
, cec
->hpd_irq
,
224 cec_hpd_gpio_irq_handler
,
225 cec_hpd_gpio_irq_handler_thread
,
227 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
,
234 cec
->v5_irq
= gpiod_to_irq(cec
->v5_gpio
);
235 ret
= devm_request_threaded_irq(dev
, cec
->v5_irq
,
236 cec_5v_gpio_irq_handler
,
237 cec_5v_gpio_irq_handler_thread
,
239 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
,
245 if (!IS_ERR(hdmi_dev
)) {
246 cec
->notifier
= cec_notifier_cec_adap_register(hdmi_dev
, NULL
,
248 if (!cec
->notifier
) {
254 ret
= cec_register_adapter(cec
->adap
, &pdev
->dev
);
258 platform_set_drvdata(pdev
, cec
);
262 cec_notifier_cec_adap_unregister(cec
->notifier
, cec
->adap
);
264 cec_delete_adapter(cec
->adap
);
268 static int cec_gpio_remove(struct platform_device
*pdev
)
270 struct cec_gpio
*cec
= platform_get_drvdata(pdev
);
272 cec_notifier_cec_adap_unregister(cec
->notifier
, cec
->adap
);
273 cec_unregister_adapter(cec
->adap
);
277 static const struct of_device_id cec_gpio_match
[] = {
279 .compatible
= "cec-gpio",
283 MODULE_DEVICE_TABLE(of
, cec_gpio_match
);
285 static struct platform_driver cec_gpio_pdrv
= {
286 .probe
= cec_gpio_probe
,
287 .remove
= cec_gpio_remove
,
290 .of_match_table
= cec_gpio_match
,
294 module_platform_driver(cec_gpio_pdrv
);
296 MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
297 MODULE_LICENSE("GPL v2");
298 MODULE_DESCRIPTION("CEC GPIO driver");