1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 4 */ 5 6 #include <linux/delay.h> 7 #include <linux/gpio/consumer.h> 8 #include <linux/interrupt.h> 9 #include <linux/module.h> 10 #include <linux/platform_device.h> 11 #include <linux/seq_file.h> 12 #include <media/cec-notifier.h> 13 #include <media/cec-pin.h> 14 15 struct cec_gpio { 16 struct cec_adapter *adap; 17 struct cec_notifier *notifier; 18 struct device *dev; 19 20 struct gpio_desc *cec_gpio; 21 int cec_irq; 22 bool cec_is_low; 23 24 struct gpio_desc *hpd_gpio; 25 int hpd_irq; 26 bool hpd_is_high; 27 ktime_t hpd_ts; 28 29 struct gpio_desc *v5_gpio; 30 int v5_irq; 31 bool v5_is_high; 32 ktime_t v5_ts; 33 }; 34 35 static int cec_gpio_read(struct cec_adapter *adap) 36 { 37 struct cec_gpio *cec = cec_get_drvdata(adap); 38 39 if (cec->cec_is_low) 40 return 0; 41 return gpiod_get_value(cec->cec_gpio); 42 } 43 44 static void cec_gpio_high(struct cec_adapter *adap) 45 { 46 struct cec_gpio *cec = cec_get_drvdata(adap); 47 48 if (!cec->cec_is_low) 49 return; 50 cec->cec_is_low = false; 51 gpiod_set_value(cec->cec_gpio, 1); 52 } 53 54 static void cec_gpio_low(struct cec_adapter *adap) 55 { 56 struct cec_gpio *cec = cec_get_drvdata(adap); 57 58 if (cec->cec_is_low) 59 return; 60 cec->cec_is_low = true; 61 gpiod_set_value(cec->cec_gpio, 0); 62 } 63 64 static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv) 65 { 66 struct cec_gpio *cec = priv; 67 68 cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts); 69 return IRQ_HANDLED; 70 } 71 72 static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv) 73 { 74 struct cec_gpio *cec = priv; 75 int val = gpiod_get_value(cec->v5_gpio); 76 bool is_high = val > 0; 77 78 if (val < 0 || is_high == cec->v5_is_high) 79 return IRQ_HANDLED; 80 cec->v5_ts = ktime_get(); 81 cec->v5_is_high = is_high; 82 return IRQ_WAKE_THREAD; 83 } 84 85 static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv) 86 { 87 struct cec_gpio *cec = priv; 88 89 cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts); 90 return IRQ_HANDLED; 91 } 92 93 static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv) 94 { 95 struct cec_gpio *cec = priv; 96 int val = gpiod_get_value(cec->hpd_gpio); 97 bool is_high = val > 0; 98 99 if (val < 0 || is_high == cec->hpd_is_high) 100 return IRQ_HANDLED; 101 cec->hpd_ts = ktime_get(); 102 cec->hpd_is_high = is_high; 103 return IRQ_WAKE_THREAD; 104 } 105 106 static irqreturn_t cec_gpio_irq_handler(int irq, void *priv) 107 { 108 struct cec_gpio *cec = priv; 109 int val = gpiod_get_value(cec->cec_gpio); 110 111 if (val >= 0) 112 cec_pin_changed(cec->adap, val > 0); 113 return IRQ_HANDLED; 114 } 115 116 static bool cec_gpio_enable_irq(struct cec_adapter *adap) 117 { 118 struct cec_gpio *cec = cec_get_drvdata(adap); 119 120 enable_irq(cec->cec_irq); 121 return true; 122 } 123 124 static void cec_gpio_disable_irq(struct cec_adapter *adap) 125 { 126 struct cec_gpio *cec = cec_get_drvdata(adap); 127 128 disable_irq(cec->cec_irq); 129 } 130 131 static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) 132 { 133 struct cec_gpio *cec = cec_get_drvdata(adap); 134 135 seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read"); 136 seq_printf(file, "using irq: %d\n", cec->cec_irq); 137 if (cec->hpd_gpio) 138 seq_printf(file, "hpd: %s\n", 139 cec->hpd_is_high ? "high" : "low"); 140 if (cec->v5_gpio) 141 seq_printf(file, "5V: %s\n", 142 cec->v5_is_high ? "high" : "low"); 143 } 144 145 static int cec_gpio_read_hpd(struct cec_adapter *adap) 146 { 147 struct cec_gpio *cec = cec_get_drvdata(adap); 148 149 if (!cec->hpd_gpio) 150 return -ENOTTY; 151 return gpiod_get_value(cec->hpd_gpio); 152 } 153 154 static int cec_gpio_read_5v(struct cec_adapter *adap) 155 { 156 struct cec_gpio *cec = cec_get_drvdata(adap); 157 158 if (!cec->v5_gpio) 159 return -ENOTTY; 160 return gpiod_get_value(cec->v5_gpio); 161 } 162 163 static const struct cec_pin_ops cec_gpio_pin_ops = { 164 .read = cec_gpio_read, 165 .low = cec_gpio_low, 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 .read_hpd = cec_gpio_read_hpd, 171 .read_5v = cec_gpio_read_5v, 172 }; 173 174 static int cec_gpio_probe(struct platform_device *pdev) 175 { 176 struct device *dev = &pdev->dev; 177 struct device *hdmi_dev; 178 struct cec_gpio *cec; 179 u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; 180 int ret; 181 182 hdmi_dev = cec_notifier_parse_hdmi_phandle(dev); 183 if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER) 184 return PTR_ERR(hdmi_dev); 185 if (IS_ERR(hdmi_dev)) 186 caps |= CEC_CAP_PHYS_ADDR; 187 188 cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); 189 if (!cec) 190 return -ENOMEM; 191 192 cec->dev = dev; 193 194 cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN); 195 if (IS_ERR(cec->cec_gpio)) 196 return PTR_ERR(cec->cec_gpio); 197 cec->cec_irq = gpiod_to_irq(cec->cec_gpio); 198 199 cec->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); 200 if (IS_ERR(cec->hpd_gpio)) 201 return PTR_ERR(cec->hpd_gpio); 202 203 cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN); 204 if (IS_ERR(cec->v5_gpio)) 205 return PTR_ERR(cec->v5_gpio); 206 207 cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops, 208 cec, pdev->name, caps); 209 if (IS_ERR(cec->adap)) 210 return PTR_ERR(cec->adap); 211 212 ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler, 213 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, 214 cec->adap->name, cec); 215 if (ret) 216 goto del_adap; 217 218 if (cec->hpd_gpio) { 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, 223 IRQF_ONESHOT | 224 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 225 "hpd-gpio", cec); 226 if (ret) 227 goto del_adap; 228 } 229 230 if (cec->v5_gpio) { 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, 235 IRQF_ONESHOT | 236 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 237 "v5-gpio", cec); 238 if (ret) 239 goto del_adap; 240 } 241 242 if (!IS_ERR(hdmi_dev)) { 243 cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL, 244 cec->adap); 245 if (!cec->notifier) { 246 ret = -ENOMEM; 247 goto del_adap; 248 } 249 } 250 251 ret = cec_register_adapter(cec->adap, &pdev->dev); 252 if (ret) 253 goto unreg_notifier; 254 255 platform_set_drvdata(pdev, cec); 256 return 0; 257 258 unreg_notifier: 259 cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); 260 del_adap: 261 cec_delete_adapter(cec->adap); 262 return ret; 263 } 264 265 static void cec_gpio_remove(struct platform_device *pdev) 266 { 267 struct cec_gpio *cec = platform_get_drvdata(pdev); 268 269 cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); 270 cec_unregister_adapter(cec->adap); 271 } 272 273 static const struct of_device_id cec_gpio_match[] = { 274 { 275 .compatible = "cec-gpio", 276 }, 277 {}, 278 }; 279 MODULE_DEVICE_TABLE(of, cec_gpio_match); 280 281 static struct platform_driver cec_gpio_pdrv = { 282 .probe = cec_gpio_probe, 283 .remove = cec_gpio_remove, 284 .driver = { 285 .name = "cec-gpio", 286 .of_match_table = cec_gpio_match, 287 }, 288 }; 289 290 module_platform_driver(cec_gpio_pdrv); 291 292 MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>"); 293 MODULE_LICENSE("GPL v2"); 294 MODULE_DESCRIPTION("CEC GPIO driver"); 295