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_gpio_5v_irq_handler_thread(int irq, void *priv) 65 { 66 struct cec_gpio *cec = priv; 67 int val = gpiod_get_value_cansleep(cec->v5_gpio); 68 bool is_high = val > 0; 69 70 if (val < 0 || is_high == cec->v5_is_high) 71 return IRQ_HANDLED; 72 73 cec->v5_is_high = is_high; 74 cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts); 75 return IRQ_HANDLED; 76 } 77 78 static irqreturn_t cec_gpio_5v_irq_handler(int irq, void *priv) 79 { 80 struct cec_gpio *cec = priv; 81 82 cec->v5_ts = ktime_get(); 83 return IRQ_WAKE_THREAD; 84 } 85 86 static irqreturn_t cec_gpio_hpd_irq_handler_thread(int irq, void *priv) 87 { 88 struct cec_gpio *cec = priv; 89 int val = gpiod_get_value_cansleep(cec->hpd_gpio); 90 bool is_high = val > 0; 91 92 if (val < 0 || is_high == cec->hpd_is_high) 93 return IRQ_HANDLED; 94 95 cec->hpd_is_high = is_high; 96 cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts); 97 return IRQ_HANDLED; 98 } 99 100 static irqreturn_t cec_gpio_hpd_irq_handler(int irq, void *priv) 101 { 102 struct cec_gpio *cec = priv; 103 104 cec->hpd_ts = ktime_get(); 105 return IRQ_WAKE_THREAD; 106 } 107 108 static irqreturn_t cec_gpio_cec_irq_handler(int irq, void *priv) 109 { 110 struct cec_gpio *cec = priv; 111 int val = gpiod_get_value(cec->cec_gpio); 112 113 if (val >= 0) 114 cec_pin_changed(cec->adap, val > 0); 115 return IRQ_HANDLED; 116 } 117 118 static bool cec_gpio_cec_enable_irq(struct cec_adapter *adap) 119 { 120 struct cec_gpio *cec = cec_get_drvdata(adap); 121 122 enable_irq(cec->cec_irq); 123 return true; 124 } 125 126 static void cec_gpio_cec_disable_irq(struct cec_adapter *adap) 127 { 128 struct cec_gpio *cec = cec_get_drvdata(adap); 129 130 disable_irq(cec->cec_irq); 131 } 132 133 static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) 134 { 135 struct cec_gpio *cec = cec_get_drvdata(adap); 136 137 seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read"); 138 seq_printf(file, "using irq: %d\n", cec->cec_irq); 139 if (cec->hpd_gpio) 140 seq_printf(file, "hpd: %s\n", 141 cec->hpd_is_high ? "high" : "low"); 142 if (cec->v5_gpio) 143 seq_printf(file, "5V: %s\n", 144 cec->v5_is_high ? "high" : "low"); 145 } 146 147 static int cec_gpio_read_hpd(struct cec_adapter *adap) 148 { 149 struct cec_gpio *cec = cec_get_drvdata(adap); 150 151 if (!cec->hpd_gpio) 152 return -ENOTTY; 153 return gpiod_get_value_cansleep(cec->hpd_gpio); 154 } 155 156 static int cec_gpio_read_5v(struct cec_adapter *adap) 157 { 158 struct cec_gpio *cec = cec_get_drvdata(adap); 159 160 if (!cec->v5_gpio) 161 return -ENOTTY; 162 return gpiod_get_value_cansleep(cec->v5_gpio); 163 } 164 165 static const struct cec_pin_ops cec_gpio_pin_ops = { 166 .read = cec_gpio_read, 167 .low = cec_gpio_low, 168 .high = cec_gpio_high, 169 .enable_irq = cec_gpio_cec_enable_irq, 170 .disable_irq = cec_gpio_cec_disable_irq, 171 .status = cec_gpio_status, 172 .read_hpd = cec_gpio_read_hpd, 173 .read_5v = cec_gpio_read_5v, 174 }; 175 176 static int cec_gpio_probe(struct platform_device *pdev) 177 { 178 struct device *dev = &pdev->dev; 179 struct device *hdmi_dev; 180 struct cec_gpio *cec; 181 u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN; 182 int ret; 183 184 hdmi_dev = cec_notifier_parse_hdmi_phandle(dev); 185 if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER) 186 return PTR_ERR(hdmi_dev); 187 if (IS_ERR(hdmi_dev)) 188 caps |= CEC_CAP_PHYS_ADDR; 189 190 cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); 191 if (!cec) 192 return -ENOMEM; 193 194 cec->dev = dev; 195 196 cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN); 197 if (IS_ERR(cec->cec_gpio)) 198 return PTR_ERR(cec->cec_gpio); 199 cec->cec_irq = gpiod_to_irq(cec->cec_gpio); 200 201 cec->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); 202 if (IS_ERR(cec->hpd_gpio)) 203 return PTR_ERR(cec->hpd_gpio); 204 205 cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN); 206 if (IS_ERR(cec->v5_gpio)) 207 return PTR_ERR(cec->v5_gpio); 208 209 cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops, 210 cec, pdev->name, caps); 211 if (IS_ERR(cec->adap)) 212 return PTR_ERR(cec->adap); 213 214 ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_cec_irq_handler, 215 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, 216 cec->adap->name, cec); 217 if (ret) 218 goto del_adap; 219 220 if (cec->hpd_gpio) { 221 cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio); 222 ret = devm_request_threaded_irq(dev, cec->hpd_irq, 223 cec_gpio_hpd_irq_handler, 224 cec_gpio_hpd_irq_handler_thread, 225 IRQF_ONESHOT | 226 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 227 "hpd-gpio", cec); 228 if (ret) 229 goto del_adap; 230 } 231 232 if (cec->v5_gpio) { 233 cec->v5_irq = gpiod_to_irq(cec->v5_gpio); 234 ret = devm_request_threaded_irq(dev, cec->v5_irq, 235 cec_gpio_5v_irq_handler, 236 cec_gpio_5v_irq_handler_thread, 237 IRQF_ONESHOT | 238 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 239 "v5-gpio", cec); 240 if (ret) 241 goto del_adap; 242 } 243 244 if (!IS_ERR(hdmi_dev)) { 245 cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL, 246 cec->adap); 247 if (!cec->notifier) { 248 ret = -ENOMEM; 249 goto del_adap; 250 } 251 } 252 253 ret = cec_register_adapter(cec->adap, &pdev->dev); 254 if (ret) 255 goto unreg_notifier; 256 257 platform_set_drvdata(pdev, cec); 258 return 0; 259 260 unreg_notifier: 261 cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); 262 del_adap: 263 cec_delete_adapter(cec->adap); 264 return ret; 265 } 266 267 static void cec_gpio_remove(struct platform_device *pdev) 268 { 269 struct cec_gpio *cec = platform_get_drvdata(pdev); 270 271 cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); 272 cec_unregister_adapter(cec->adap); 273 } 274 275 static const struct of_device_id cec_gpio_match[] = { 276 { 277 .compatible = "cec-gpio", 278 }, 279 {}, 280 }; 281 MODULE_DEVICE_TABLE(of, cec_gpio_match); 282 283 static struct platform_driver cec_gpio_pdrv = { 284 .probe = cec_gpio_probe, 285 .remove = cec_gpio_remove, 286 .driver = { 287 .name = "cec-gpio", 288 .of_match_table = cec_gpio_match, 289 }, 290 }; 291 292 module_platform_driver(cec_gpio_pdrv); 293 294 MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>"); 295 MODULE_LICENSE("GPL v2"); 296 MODULE_DESCRIPTION("CEC GPIO driver"); 297