1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * CEC driver for ChromeOS Embedded Controller 4 * 5 * Copyright (c) 2018 BayLibre, SAS 6 * Author: Neil Armstrong <narmstrong@baylibre.com> 7 */ 8 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/platform_device.h> 12#include <linux/dmi.h> 13#include <linux/pci.h> 14#include <linux/cec.h> 15#include <linux/slab.h> 16#include <linux/interrupt.h> 17#include <linux/platform_data/cros_ec_commands.h> 18#include <linux/platform_data/cros_ec_proto.h> 19#include <media/cec.h> 20#include <media/cec-notifier.h> 21 22#define DRV_NAME "cros-ec-cec" 23 24/** 25 * struct cros_ec_cec - Driver data for EC CEC 26 * 27 * @cros_ec: Pointer to EC device 28 * @notifier: Notifier info for responding to EC events 29 * @adap: CEC adapter 30 * @notify: CEC notifier pointer 31 * @rx_msg: storage for a received message 32 */ 33struct cros_ec_cec { 34 struct cros_ec_device *cros_ec; 35 struct notifier_block notifier; 36 struct cec_adapter *adap; 37 struct cec_notifier *notify; 38 struct cec_msg rx_msg; 39}; 40 41static void handle_cec_message(struct cros_ec_cec *cros_ec_cec) 42{ 43 struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 44 uint8_t *cec_message = cros_ec->event_data.data.cec_message; 45 unsigned int len = cros_ec->event_size; 46 47 if (len > CEC_MAX_MSG_SIZE) 48 len = CEC_MAX_MSG_SIZE; 49 cros_ec_cec->rx_msg.len = len; 50 memcpy(cros_ec_cec->rx_msg.msg, cec_message, len); 51 52 cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg); 53} 54 55static void handle_cec_event(struct cros_ec_cec *cros_ec_cec) 56{ 57 struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 58 uint32_t events = cros_ec->event_data.data.cec_events; 59 60 if (events & EC_MKBP_CEC_SEND_OK) 61 cec_transmit_attempt_done(cros_ec_cec->adap, 62 CEC_TX_STATUS_OK); 63 64 /* FW takes care of all retries, tell core to avoid more retries */ 65 if (events & EC_MKBP_CEC_SEND_FAILED) 66 cec_transmit_attempt_done(cros_ec_cec->adap, 67 CEC_TX_STATUS_MAX_RETRIES | 68 CEC_TX_STATUS_NACK); 69} 70 71static int cros_ec_cec_event(struct notifier_block *nb, 72 unsigned long queued_during_suspend, 73 void *_notify) 74{ 75 struct cros_ec_cec *cros_ec_cec; 76 struct cros_ec_device *cros_ec; 77 78 cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier); 79 cros_ec = cros_ec_cec->cros_ec; 80 81 if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) { 82 handle_cec_event(cros_ec_cec); 83 return NOTIFY_OK; 84 } 85 86 if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) { 87 handle_cec_message(cros_ec_cec); 88 return NOTIFY_OK; 89 } 90 91 return NOTIFY_DONE; 92} 93 94static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr) 95{ 96 struct cros_ec_cec *cros_ec_cec = adap->priv; 97 struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 98 struct { 99 struct cros_ec_command msg; 100 struct ec_params_cec_set data; 101 } __packed msg = {}; 102 int ret; 103 104 msg.msg.command = EC_CMD_CEC_SET; 105 msg.msg.outsize = sizeof(msg.data); 106 msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS; 107 msg.data.val = logical_addr; 108 109 ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); 110 if (ret < 0) { 111 dev_err(cros_ec->dev, 112 "error setting CEC logical address on EC: %d\n", ret); 113 return ret; 114 } 115 116 return 0; 117} 118 119static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts, 120 u32 signal_free_time, struct cec_msg *cec_msg) 121{ 122 struct cros_ec_cec *cros_ec_cec = adap->priv; 123 struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 124 struct { 125 struct cros_ec_command msg; 126 struct ec_params_cec_write data; 127 } __packed msg = {}; 128 int ret; 129 130 msg.msg.command = EC_CMD_CEC_WRITE_MSG; 131 msg.msg.outsize = cec_msg->len; 132 memcpy(msg.data.msg, cec_msg->msg, cec_msg->len); 133 134 ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); 135 if (ret < 0) { 136 dev_err(cros_ec->dev, 137 "error writing CEC msg on EC: %d\n", ret); 138 return ret; 139 } 140 141 return 0; 142} 143 144static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable) 145{ 146 struct cros_ec_cec *cros_ec_cec = adap->priv; 147 struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 148 struct { 149 struct cros_ec_command msg; 150 struct ec_params_cec_set data; 151 } __packed msg = {}; 152 int ret; 153 154 msg.msg.command = EC_CMD_CEC_SET; 155 msg.msg.outsize = sizeof(msg.data); 156 msg.data.cmd = CEC_CMD_ENABLE; 157 msg.data.val = enable; 158 159 ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); 160 if (ret < 0) { 161 dev_err(cros_ec->dev, 162 "error %sabling CEC on EC: %d\n", 163 (enable ? "en" : "dis"), ret); 164 return ret; 165 } 166 167 return 0; 168} 169 170static const struct cec_adap_ops cros_ec_cec_ops = { 171 .adap_enable = cros_ec_cec_adap_enable, 172 .adap_log_addr = cros_ec_cec_set_log_addr, 173 .adap_transmit = cros_ec_cec_transmit, 174}; 175 176#ifdef CONFIG_PM_SLEEP 177static int cros_ec_cec_suspend(struct device *dev) 178{ 179 struct platform_device *pdev = to_platform_device(dev); 180 struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev); 181 182 if (device_may_wakeup(dev)) 183 enable_irq_wake(cros_ec_cec->cros_ec->irq); 184 185 return 0; 186} 187 188static int cros_ec_cec_resume(struct device *dev) 189{ 190 struct platform_device *pdev = to_platform_device(dev); 191 struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev); 192 193 if (device_may_wakeup(dev)) 194 disable_irq_wake(cros_ec_cec->cros_ec->irq); 195 196 return 0; 197} 198#endif 199 200static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops, 201 cros_ec_cec_suspend, cros_ec_cec_resume); 202 203#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) 204 205/* 206 * The Firmware only handles a single CEC interface tied to a single HDMI 207 * connector we specify along with the DRM device name handling the HDMI output 208 */ 209 210struct cec_dmi_match { 211 const char *sys_vendor; 212 const char *product_name; 213 const char *devname; 214 const char *conn; 215}; 216 217static const struct cec_dmi_match cec_dmi_match_table[] = { 218 /* Google Fizz */ 219 { "Google", "Fizz", "0000:00:02.0", "Port B" }, 220}; 221 222static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, 223 const char **conn) 224{ 225 int i; 226 227 for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) { 228 const struct cec_dmi_match *m = &cec_dmi_match_table[i]; 229 230 if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) && 231 dmi_match(DMI_PRODUCT_NAME, m->product_name)) { 232 struct device *d; 233 234 /* Find the device, bail out if not yet registered */ 235 d = bus_find_device_by_name(&pci_bus_type, NULL, 236 m->devname); 237 if (!d) 238 return ERR_PTR(-EPROBE_DEFER); 239 put_device(d); 240 *conn = m->conn; 241 return d; 242 } 243 } 244 245 /* Hardware support must be added in the cec_dmi_match_table */ 246 dev_warn(dev, "CEC notifier not configured for this hardware\n"); 247 248 return ERR_PTR(-ENODEV); 249} 250 251#else 252 253static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, 254 const char **conn) 255{ 256 return ERR_PTR(-ENODEV); 257} 258 259#endif 260 261static int cros_ec_cec_probe(struct platform_device *pdev) 262{ 263 struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); 264 struct cros_ec_device *cros_ec = ec_dev->ec_dev; 265 struct cros_ec_cec *cros_ec_cec; 266 struct device *hdmi_dev; 267 const char *conn = NULL; 268 int ret; 269 270 hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conn); 271 if (IS_ERR(hdmi_dev)) 272 return PTR_ERR(hdmi_dev); 273 274 cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec), 275 GFP_KERNEL); 276 if (!cros_ec_cec) 277 return -ENOMEM; 278 279 platform_set_drvdata(pdev, cros_ec_cec); 280 cros_ec_cec->cros_ec = cros_ec; 281 282 device_init_wakeup(&pdev->dev, 1); 283 284 cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec, 285 DRV_NAME, 286 CEC_CAP_DEFAULTS | 287 CEC_CAP_CONNECTOR_INFO, 1); 288 if (IS_ERR(cros_ec_cec->adap)) 289 return PTR_ERR(cros_ec_cec->adap); 290 291 cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn, 292 cros_ec_cec->adap); 293 if (!cros_ec_cec->notify) { 294 ret = -ENOMEM; 295 goto out_probe_adapter; 296 } 297 298 /* Get CEC events from the EC. */ 299 cros_ec_cec->notifier.notifier_call = cros_ec_cec_event; 300 ret = blocking_notifier_chain_register(&cros_ec->event_notifier, 301 &cros_ec_cec->notifier); 302 if (ret) { 303 dev_err(&pdev->dev, "failed to register notifier\n"); 304 goto out_probe_notify; 305 } 306 307 ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev); 308 if (ret < 0) 309 goto out_probe_notify; 310 311 return 0; 312 313out_probe_notify: 314 cec_notifier_cec_adap_unregister(cros_ec_cec->notify, 315 cros_ec_cec->adap); 316out_probe_adapter: 317 cec_delete_adapter(cros_ec_cec->adap); 318 return ret; 319} 320 321static int cros_ec_cec_remove(struct platform_device *pdev) 322{ 323 struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev); 324 struct device *dev = &pdev->dev; 325 int ret; 326 327 ret = blocking_notifier_chain_unregister( 328 &cros_ec_cec->cros_ec->event_notifier, 329 &cros_ec_cec->notifier); 330 331 if (ret) { 332 dev_err(dev, "failed to unregister notifier\n"); 333 return ret; 334 } 335 336 cec_notifier_cec_adap_unregister(cros_ec_cec->notify, 337 cros_ec_cec->adap); 338 cec_unregister_adapter(cros_ec_cec->adap); 339 340 return 0; 341} 342 343static struct platform_driver cros_ec_cec_driver = { 344 .probe = cros_ec_cec_probe, 345 .remove = cros_ec_cec_remove, 346 .driver = { 347 .name = DRV_NAME, 348 .pm = &cros_ec_cec_pm_ops, 349 }, 350}; 351 352module_platform_driver(cros_ec_cec_driver); 353 354MODULE_DESCRIPTION("CEC driver for ChromeOS ECs"); 355MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 356MODULE_LICENSE("GPL"); 357MODULE_ALIAS("platform:" DRV_NAME); 358