14be5e864SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0+ 24be5e864SMauro Carvalho Chehab /* 34be5e864SMauro Carvalho Chehab * CEC driver for ChromeOS Embedded Controller 44be5e864SMauro Carvalho Chehab * 54be5e864SMauro Carvalho Chehab * Copyright (c) 2018 BayLibre, SAS 64be5e864SMauro Carvalho Chehab * Author: Neil Armstrong <narmstrong@baylibre.com> 74be5e864SMauro Carvalho Chehab */ 84be5e864SMauro Carvalho Chehab 94be5e864SMauro Carvalho Chehab #include <linux/kernel.h> 104be5e864SMauro Carvalho Chehab #include <linux/module.h> 11*50a0844bSTzung-Bi Shih #include <linux/mod_devicetable.h> 124be5e864SMauro Carvalho Chehab #include <linux/platform_device.h> 134be5e864SMauro Carvalho Chehab #include <linux/dmi.h> 144be5e864SMauro Carvalho Chehab #include <linux/pci.h> 154be5e864SMauro Carvalho Chehab #include <linux/cec.h> 164be5e864SMauro Carvalho Chehab #include <linux/slab.h> 174be5e864SMauro Carvalho Chehab #include <linux/interrupt.h> 184be5e864SMauro Carvalho Chehab #include <linux/platform_data/cros_ec_commands.h> 194be5e864SMauro Carvalho Chehab #include <linux/platform_data/cros_ec_proto.h> 204be5e864SMauro Carvalho Chehab #include <media/cec.h> 214be5e864SMauro Carvalho Chehab #include <media/cec-notifier.h> 224be5e864SMauro Carvalho Chehab 234be5e864SMauro Carvalho Chehab #define DRV_NAME "cros-ec-cec" 244be5e864SMauro Carvalho Chehab 254d0e179aSReka Norman /** 264d0e179aSReka Norman * struct cros_ec_cec_port - Driver data for a single EC CEC port 274d0e179aSReka Norman * 284d0e179aSReka Norman * @port_num: port number 294d0e179aSReka Norman * @adap: CEC adapter 304d0e179aSReka Norman * @notify: CEC notifier pointer 314d0e179aSReka Norman * @rx_msg: storage for a received message 324d0e179aSReka Norman * @cros_ec_cec: pointer to the parent struct 334d0e179aSReka Norman */ 344d0e179aSReka Norman struct cros_ec_cec_port { 354d0e179aSReka Norman int port_num; 364d0e179aSReka Norman struct cec_adapter *adap; 374d0e179aSReka Norman struct cec_notifier *notify; 384d0e179aSReka Norman struct cec_msg rx_msg; 394d0e179aSReka Norman struct cros_ec_cec *cros_ec_cec; 404d0e179aSReka Norman }; 414d0e179aSReka Norman 424be5e864SMauro Carvalho Chehab /** 434be5e864SMauro Carvalho Chehab * struct cros_ec_cec - Driver data for EC CEC 444be5e864SMauro Carvalho Chehab * 454be5e864SMauro Carvalho Chehab * @cros_ec: Pointer to EC device 464be5e864SMauro Carvalho Chehab * @notifier: Notifier info for responding to EC events 47adbfc747SReka Norman * @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG. 484d0e179aSReka Norman * @num_ports: Number of CEC ports 494d0e179aSReka Norman * @ports: Array of ports 504be5e864SMauro Carvalho Chehab */ 514be5e864SMauro Carvalho Chehab struct cros_ec_cec { 524be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec; 534be5e864SMauro Carvalho Chehab struct notifier_block notifier; 54adbfc747SReka Norman int write_cmd_version; 554d0e179aSReka Norman int num_ports; 564d0e179aSReka Norman struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS]; 574be5e864SMauro Carvalho Chehab }; 584be5e864SMauro Carvalho Chehab 59425d2051SReka Norman static void cros_ec_cec_received_message(struct cros_ec_cec_port *port, 60425d2051SReka Norman uint8_t *msg, uint8_t len) 61425d2051SReka Norman { 62425d2051SReka Norman if (len > CEC_MAX_MSG_SIZE) 63425d2051SReka Norman len = CEC_MAX_MSG_SIZE; 64425d2051SReka Norman 65425d2051SReka Norman port->rx_msg.len = len; 66425d2051SReka Norman memcpy(port->rx_msg.msg, msg, len); 67425d2051SReka Norman 68425d2051SReka Norman cec_received_msg(port->adap, &port->rx_msg); 69425d2051SReka Norman } 70425d2051SReka Norman 714be5e864SMauro Carvalho Chehab static void handle_cec_message(struct cros_ec_cec *cros_ec_cec) 724be5e864SMauro Carvalho Chehab { 734be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 744be5e864SMauro Carvalho Chehab uint8_t *cec_message = cros_ec->event_data.data.cec_message; 754be5e864SMauro Carvalho Chehab unsigned int len = cros_ec->event_size; 76425d2051SReka Norman struct cros_ec_cec_port *port; 77425d2051SReka Norman /* 78425d2051SReka Norman * There are two ways of receiving CEC messages: 79425d2051SReka Norman * 1. Old EC firmware which only supports one port sends the data in a 80425d2051SReka Norman * cec_message MKBP event. 81425d2051SReka Norman * 2. New EC firmware which supports multiple ports uses 82425d2051SReka Norman * EC_MKBP_CEC_HAVE_DATA to notify that data is ready and 83425d2051SReka Norman * EC_CMD_CEC_READ_MSG to read it. 84425d2051SReka Norman * Check that the EC only has one CEC port, and then we can assume the 85425d2051SReka Norman * message is from port 0. 86425d2051SReka Norman */ 87425d2051SReka Norman if (cros_ec_cec->num_ports != 1) { 88425d2051SReka Norman dev_err(cros_ec->dev, 89425d2051SReka Norman "received cec_message on device with %d ports\n", 90425d2051SReka Norman cros_ec_cec->num_ports); 91425d2051SReka Norman return; 92425d2051SReka Norman } 93425d2051SReka Norman port = cros_ec_cec->ports[0]; 944be5e864SMauro Carvalho Chehab 95425d2051SReka Norman cros_ec_cec_received_message(port, cec_message, len); 96425d2051SReka Norman } 974be5e864SMauro Carvalho Chehab 98425d2051SReka Norman static void cros_ec_cec_read_message(struct cros_ec_cec_port *port) 99425d2051SReka Norman { 100425d2051SReka Norman struct cros_ec_device *cros_ec = port->cros_ec_cec->cros_ec; 101425d2051SReka Norman struct ec_params_cec_read params = { 102425d2051SReka Norman .port = port->port_num, 103425d2051SReka Norman }; 104425d2051SReka Norman struct ec_response_cec_read response; 105425d2051SReka Norman int ret; 106425d2051SReka Norman 107425d2051SReka Norman ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_READ_MSG, ¶ms, 108425d2051SReka Norman sizeof(params), &response, sizeof(response)); 109425d2051SReka Norman if (ret < 0) { 110425d2051SReka Norman dev_err(cros_ec->dev, 111425d2051SReka Norman "error reading CEC message on EC: %d\n", ret); 112425d2051SReka Norman return; 113425d2051SReka Norman } 114425d2051SReka Norman 115425d2051SReka Norman cros_ec_cec_received_message(port, response.msg, response.msg_len); 1164be5e864SMauro Carvalho Chehab } 1174be5e864SMauro Carvalho Chehab 1184be5e864SMauro Carvalho Chehab static void handle_cec_event(struct cros_ec_cec *cros_ec_cec) 1194be5e864SMauro Carvalho Chehab { 1204be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 1211cabf526SReka Norman uint32_t cec_events = cros_ec->event_data.data.cec_events; 1221cabf526SReka Norman uint32_t port_num = EC_MKBP_EVENT_CEC_GET_PORT(cec_events); 1231cabf526SReka Norman uint32_t events = EC_MKBP_EVENT_CEC_GET_EVENTS(cec_events); 1241cabf526SReka Norman struct cros_ec_cec_port *port; 1251cabf526SReka Norman 1261cabf526SReka Norman if (port_num >= cros_ec_cec->num_ports) { 1271cabf526SReka Norman dev_err(cros_ec->dev, 1281cabf526SReka Norman "received CEC event for invalid port %d\n", port_num); 1291cabf526SReka Norman return; 1301cabf526SReka Norman } 1311cabf526SReka Norman port = cros_ec_cec->ports[port_num]; 1324be5e864SMauro Carvalho Chehab 1334be5e864SMauro Carvalho Chehab if (events & EC_MKBP_CEC_SEND_OK) 1344d0e179aSReka Norman cec_transmit_attempt_done(port->adap, CEC_TX_STATUS_OK); 1354be5e864SMauro Carvalho Chehab 1364be5e864SMauro Carvalho Chehab /* FW takes care of all retries, tell core to avoid more retries */ 1374be5e864SMauro Carvalho Chehab if (events & EC_MKBP_CEC_SEND_FAILED) 1384d0e179aSReka Norman cec_transmit_attempt_done(port->adap, 1394be5e864SMauro Carvalho Chehab CEC_TX_STATUS_MAX_RETRIES | 1404be5e864SMauro Carvalho Chehab CEC_TX_STATUS_NACK); 141425d2051SReka Norman 142425d2051SReka Norman if (events & EC_MKBP_CEC_HAVE_DATA) 143425d2051SReka Norman cros_ec_cec_read_message(port); 1444be5e864SMauro Carvalho Chehab } 1454be5e864SMauro Carvalho Chehab 1464be5e864SMauro Carvalho Chehab static int cros_ec_cec_event(struct notifier_block *nb, 1474be5e864SMauro Carvalho Chehab unsigned long queued_during_suspend, 1484be5e864SMauro Carvalho Chehab void *_notify) 1494be5e864SMauro Carvalho Chehab { 1504be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec; 1514be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec; 1524be5e864SMauro Carvalho Chehab 1534be5e864SMauro Carvalho Chehab cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier); 1544be5e864SMauro Carvalho Chehab cros_ec = cros_ec_cec->cros_ec; 1554be5e864SMauro Carvalho Chehab 1564be5e864SMauro Carvalho Chehab if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) { 1574be5e864SMauro Carvalho Chehab handle_cec_event(cros_ec_cec); 1584be5e864SMauro Carvalho Chehab return NOTIFY_OK; 1594be5e864SMauro Carvalho Chehab } 1604be5e864SMauro Carvalho Chehab 1614be5e864SMauro Carvalho Chehab if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) { 1624be5e864SMauro Carvalho Chehab handle_cec_message(cros_ec_cec); 1634be5e864SMauro Carvalho Chehab return NOTIFY_OK; 1644be5e864SMauro Carvalho Chehab } 1654be5e864SMauro Carvalho Chehab 1664be5e864SMauro Carvalho Chehab return NOTIFY_DONE; 1674be5e864SMauro Carvalho Chehab } 1684be5e864SMauro Carvalho Chehab 1694be5e864SMauro Carvalho Chehab static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr) 1704be5e864SMauro Carvalho Chehab { 1714d0e179aSReka Norman struct cros_ec_cec_port *port = adap->priv; 1724d0e179aSReka Norman struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec; 1734be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 174afca12e3SReka Norman struct ec_params_cec_set params = { 175afca12e3SReka Norman .cmd = CEC_CMD_LOGICAL_ADDRESS, 176e90bd1feSReka Norman .port = port->port_num, 177afca12e3SReka Norman .val = logical_addr, 178afca12e3SReka Norman }; 1794be5e864SMauro Carvalho Chehab int ret; 1804be5e864SMauro Carvalho Chehab 181afca12e3SReka Norman ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, ¶ms, sizeof(params), 182afca12e3SReka Norman NULL, 0); 1834be5e864SMauro Carvalho Chehab if (ret < 0) { 1844be5e864SMauro Carvalho Chehab dev_err(cros_ec->dev, 1854be5e864SMauro Carvalho Chehab "error setting CEC logical address on EC: %d\n", ret); 1864be5e864SMauro Carvalho Chehab return ret; 1874be5e864SMauro Carvalho Chehab } 1884be5e864SMauro Carvalho Chehab 1894be5e864SMauro Carvalho Chehab return 0; 1904be5e864SMauro Carvalho Chehab } 1914be5e864SMauro Carvalho Chehab 1924be5e864SMauro Carvalho Chehab static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts, 1934be5e864SMauro Carvalho Chehab u32 signal_free_time, struct cec_msg *cec_msg) 1944be5e864SMauro Carvalho Chehab { 1954d0e179aSReka Norman struct cros_ec_cec_port *port = adap->priv; 1964d0e179aSReka Norman struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec; 1974be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 198afca12e3SReka Norman struct ec_params_cec_write params; 199adbfc747SReka Norman struct ec_params_cec_write_v1 params_v1; 2004be5e864SMauro Carvalho Chehab int ret; 2014be5e864SMauro Carvalho Chehab 202adbfc747SReka Norman if (cros_ec_cec->write_cmd_version == 0) { 203afca12e3SReka Norman memcpy(params.msg, cec_msg->msg, cec_msg->len); 204afca12e3SReka Norman ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_WRITE_MSG, ¶ms, 205afca12e3SReka Norman cec_msg->len, NULL, 0); 206adbfc747SReka Norman } else { 207adbfc747SReka Norman params_v1.port = port->port_num; 208adbfc747SReka Norman params_v1.msg_len = cec_msg->len; 209adbfc747SReka Norman memcpy(params_v1.msg, cec_msg->msg, cec_msg->len); 210adbfc747SReka Norman ret = cros_ec_cmd(cros_ec, cros_ec_cec->write_cmd_version, 211adbfc747SReka Norman EC_CMD_CEC_WRITE_MSG, ¶ms_v1, 212adbfc747SReka Norman sizeof(params_v1), NULL, 0); 213adbfc747SReka Norman } 214adbfc747SReka Norman 2154be5e864SMauro Carvalho Chehab if (ret < 0) { 2164be5e864SMauro Carvalho Chehab dev_err(cros_ec->dev, 2174be5e864SMauro Carvalho Chehab "error writing CEC msg on EC: %d\n", ret); 2184be5e864SMauro Carvalho Chehab return ret; 2194be5e864SMauro Carvalho Chehab } 2204be5e864SMauro Carvalho Chehab 2214be5e864SMauro Carvalho Chehab return 0; 2224be5e864SMauro Carvalho Chehab } 2234be5e864SMauro Carvalho Chehab 2244be5e864SMauro Carvalho Chehab static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable) 2254be5e864SMauro Carvalho Chehab { 2264d0e179aSReka Norman struct cros_ec_cec_port *port = adap->priv; 2274d0e179aSReka Norman struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec; 2284be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 229afca12e3SReka Norman struct ec_params_cec_set params = { 230afca12e3SReka Norman .cmd = CEC_CMD_ENABLE, 231e90bd1feSReka Norman .port = port->port_num, 232afca12e3SReka Norman .val = enable, 233afca12e3SReka Norman }; 2344be5e864SMauro Carvalho Chehab int ret; 2354be5e864SMauro Carvalho Chehab 236afca12e3SReka Norman ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, ¶ms, sizeof(params), 237afca12e3SReka Norman NULL, 0); 2384be5e864SMauro Carvalho Chehab if (ret < 0) { 2394be5e864SMauro Carvalho Chehab dev_err(cros_ec->dev, 2404be5e864SMauro Carvalho Chehab "error %sabling CEC on EC: %d\n", 2414be5e864SMauro Carvalho Chehab (enable ? "en" : "dis"), ret); 2424be5e864SMauro Carvalho Chehab return ret; 2434be5e864SMauro Carvalho Chehab } 2444be5e864SMauro Carvalho Chehab 2454be5e864SMauro Carvalho Chehab return 0; 2464be5e864SMauro Carvalho Chehab } 2474be5e864SMauro Carvalho Chehab 2484be5e864SMauro Carvalho Chehab static const struct cec_adap_ops cros_ec_cec_ops = { 2494be5e864SMauro Carvalho Chehab .adap_enable = cros_ec_cec_adap_enable, 2504be5e864SMauro Carvalho Chehab .adap_log_addr = cros_ec_cec_set_log_addr, 2514be5e864SMauro Carvalho Chehab .adap_transmit = cros_ec_cec_transmit, 2524be5e864SMauro Carvalho Chehab }; 2534be5e864SMauro Carvalho Chehab 2544be5e864SMauro Carvalho Chehab #ifdef CONFIG_PM_SLEEP 2554be5e864SMauro Carvalho Chehab static int cros_ec_cec_suspend(struct device *dev) 2564be5e864SMauro Carvalho Chehab { 2574be5e864SMauro Carvalho Chehab struct platform_device *pdev = to_platform_device(dev); 2584be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev); 2594be5e864SMauro Carvalho Chehab 2604be5e864SMauro Carvalho Chehab if (device_may_wakeup(dev)) 2614be5e864SMauro Carvalho Chehab enable_irq_wake(cros_ec_cec->cros_ec->irq); 2624be5e864SMauro Carvalho Chehab 2634be5e864SMauro Carvalho Chehab return 0; 2644be5e864SMauro Carvalho Chehab } 2654be5e864SMauro Carvalho Chehab 2664be5e864SMauro Carvalho Chehab static int cros_ec_cec_resume(struct device *dev) 2674be5e864SMauro Carvalho Chehab { 2684be5e864SMauro Carvalho Chehab struct platform_device *pdev = to_platform_device(dev); 2694be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev); 2704be5e864SMauro Carvalho Chehab 2714be5e864SMauro Carvalho Chehab if (device_may_wakeup(dev)) 2724be5e864SMauro Carvalho Chehab disable_irq_wake(cros_ec_cec->cros_ec->irq); 2734be5e864SMauro Carvalho Chehab 2744be5e864SMauro Carvalho Chehab return 0; 2754be5e864SMauro Carvalho Chehab } 2764be5e864SMauro Carvalho Chehab #endif 2774be5e864SMauro Carvalho Chehab 2784be5e864SMauro Carvalho Chehab static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops, 2794be5e864SMauro Carvalho Chehab cros_ec_cec_suspend, cros_ec_cec_resume); 2804be5e864SMauro Carvalho Chehab 2814be5e864SMauro Carvalho Chehab #if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) 2824be5e864SMauro Carvalho Chehab 2834be5e864SMauro Carvalho Chehab /* 284e7885b9cSReka Norman * Specify the DRM device name handling the HDMI output and the HDMI connector 285e7885b9cSReka Norman * corresponding to each CEC port. The order of connectors must match the order 286e7885b9cSReka Norman * in the EC (first connector is EC port 0, ...), and the number of connectors 287e7885b9cSReka Norman * must match the number of ports in the EC (which can be queried using the 288e7885b9cSReka Norman * EC_CMD_CEC_PORT_COUNT host command). 2894be5e864SMauro Carvalho Chehab */ 2904be5e864SMauro Carvalho Chehab 2914be5e864SMauro Carvalho Chehab struct cec_dmi_match { 2924be5e864SMauro Carvalho Chehab const char *sys_vendor; 2934be5e864SMauro Carvalho Chehab const char *product_name; 2944be5e864SMauro Carvalho Chehab const char *devname; 295e7885b9cSReka Norman const char *const *conns; 2964be5e864SMauro Carvalho Chehab }; 2974be5e864SMauro Carvalho Chehab 298678e8d80SKen Lin static const char *const port_b_conns[] = { "Port B", NULL }; 299678e8d80SKen Lin static const char *const port_db_conns[] = { "Port D", "Port B", NULL }; 300678e8d80SKen Lin static const char *const port_ba_conns[] = { "Port B", "Port A", NULL }; 301678e8d80SKen Lin static const char *const port_d_conns[] = { "Port D", NULL }; 302e7885b9cSReka Norman 3034be5e864SMauro Carvalho Chehab static const struct cec_dmi_match cec_dmi_match_table[] = { 3044be5e864SMauro Carvalho Chehab /* Google Fizz */ 305678e8d80SKen Lin { "Google", "Fizz", "0000:00:02.0", port_b_conns }, 30697733180SZhuohao Lee /* Google Brask */ 307678e8d80SKen Lin { "Google", "Brask", "0000:00:02.0", port_b_conns }, 308a1a9b71eSScott Chao /* Google Moli */ 309678e8d80SKen Lin { "Google", "Moli", "0000:00:02.0", port_b_conns }, 310f5d48ba2SAjye Huang /* Google Kinox */ 311678e8d80SKen Lin { "Google", "Kinox", "0000:00:02.0", port_b_conns }, 312594b6bddSRory Liu /* Google Kuldax */ 313678e8d80SKen Lin { "Google", "Kuldax", "0000:00:02.0", port_b_conns }, 31446ff24efSZoey Wu /* Google Aurash */ 315678e8d80SKen Lin { "Google", "Aurash", "0000:00:02.0", port_b_conns }, 3166f8cdfdfSKevin Chiu /* Google Gladios */ 317678e8d80SKen Lin { "Google", "Gladios", "0000:00:02.0", port_b_conns }, 3186f8cdfdfSKevin Chiu /* Google Lisbon */ 319678e8d80SKen Lin { "Google", "Lisbon", "0000:00:02.0", port_b_conns }, 3208d3e6030SReka Norman /* Google Dibbi */ 321678e8d80SKen Lin { "Google", "Dibbi", "0000:00:02.0", port_db_conns }, 3225bc2de5fSStefan Adolfsson /* Google Constitution */ 323678e8d80SKen Lin { "Google", "Constitution", "0000:00:02.0", port_ba_conns }, 324beeefd75Srasheed.hsueh /* Google Boxy */ 325678e8d80SKen Lin { "Google", "Boxy", "0000:00:02.0", port_d_conns }, 326cd5c11d5SKen Lin /* Google Taranza */ 327cd5c11d5SKen Lin { "Google", "Taranza", "0000:00:02.0", port_db_conns }, 328ebc733e5SKen Lin /* Google Dexi */ 329ebc733e5SKen Lin { "Google", "Dexi", "0000:00:02.0", port_db_conns }, 33076f623d2SKells Ping /* Google Dita */ 33176f623d2SKells Ping { "Google", "Dita", "0000:00:02.0", port_db_conns }, 3324be5e864SMauro Carvalho Chehab }; 3334be5e864SMauro Carvalho Chehab 3344be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, 335e7885b9cSReka Norman const char * const **conns) 3364be5e864SMauro Carvalho Chehab { 3374be5e864SMauro Carvalho Chehab int i; 3384be5e864SMauro Carvalho Chehab 3394be5e864SMauro Carvalho Chehab for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) { 3404be5e864SMauro Carvalho Chehab const struct cec_dmi_match *m = &cec_dmi_match_table[i]; 3414be5e864SMauro Carvalho Chehab 3424be5e864SMauro Carvalho Chehab if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) && 3434be5e864SMauro Carvalho Chehab dmi_match(DMI_PRODUCT_NAME, m->product_name)) { 3444be5e864SMauro Carvalho Chehab struct device *d; 3454be5e864SMauro Carvalho Chehab 3464be5e864SMauro Carvalho Chehab /* Find the device, bail out if not yet registered */ 3474be5e864SMauro Carvalho Chehab d = bus_find_device_by_name(&pci_bus_type, NULL, 3484be5e864SMauro Carvalho Chehab m->devname); 3494be5e864SMauro Carvalho Chehab if (!d) 3504be5e864SMauro Carvalho Chehab return ERR_PTR(-EPROBE_DEFER); 3514be5e864SMauro Carvalho Chehab put_device(d); 352e7885b9cSReka Norman *conns = m->conns; 3534be5e864SMauro Carvalho Chehab return d; 3544be5e864SMauro Carvalho Chehab } 3554be5e864SMauro Carvalho Chehab } 3564be5e864SMauro Carvalho Chehab 3574be5e864SMauro Carvalho Chehab /* Hardware support must be added in the cec_dmi_match_table */ 3584be5e864SMauro Carvalho Chehab dev_warn(dev, "CEC notifier not configured for this hardware\n"); 3594be5e864SMauro Carvalho Chehab 3604be5e864SMauro Carvalho Chehab return ERR_PTR(-ENODEV); 3614be5e864SMauro Carvalho Chehab } 3624be5e864SMauro Carvalho Chehab 3634be5e864SMauro Carvalho Chehab #else 3644be5e864SMauro Carvalho Chehab 3654be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev, 366e7885b9cSReka Norman const char * const **conns) 3674be5e864SMauro Carvalho Chehab { 3684be5e864SMauro Carvalho Chehab return ERR_PTR(-ENODEV); 3694be5e864SMauro Carvalho Chehab } 3704be5e864SMauro Carvalho Chehab 3714be5e864SMauro Carvalho Chehab #endif 3724be5e864SMauro Carvalho Chehab 3735d227f02SReka Norman static int cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec) 3745d227f02SReka Norman { 3755d227f02SReka Norman struct ec_response_cec_port_count response; 3765d227f02SReka Norman int ret; 3775d227f02SReka Norman 3785d227f02SReka Norman ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL, 3795d227f02SReka Norman 0, &response, sizeof(response)); 3805d227f02SReka Norman if (ret < 0) { 3815d227f02SReka Norman /* 3825d227f02SReka Norman * Old EC firmware only supports one port and does not support 3835d227f02SReka Norman * the port count command, so fall back to assuming one port. 3845d227f02SReka Norman */ 3855d227f02SReka Norman cros_ec_cec->num_ports = 1; 3865d227f02SReka Norman return 0; 3875d227f02SReka Norman } 3885d227f02SReka Norman 3895d227f02SReka Norman if (response.port_count == 0) { 3905d227f02SReka Norman dev_err(cros_ec_cec->cros_ec->dev, 3915d227f02SReka Norman "EC reports 0 CEC ports\n"); 3925d227f02SReka Norman return -ENODEV; 3935d227f02SReka Norman } 3945d227f02SReka Norman 3955d227f02SReka Norman if (response.port_count > EC_CEC_MAX_PORTS) { 3965d227f02SReka Norman dev_err(cros_ec_cec->cros_ec->dev, 3975d227f02SReka Norman "EC reports too many ports: %d\n", response.port_count); 3985d227f02SReka Norman return -EINVAL; 3995d227f02SReka Norman } 4005d227f02SReka Norman 4015d227f02SReka Norman cros_ec_cec->num_ports = response.port_count; 4025d227f02SReka Norman return 0; 4035d227f02SReka Norman } 4045d227f02SReka Norman 405adbfc747SReka Norman static int cros_ec_cec_get_write_cmd_version(struct cros_ec_cec *cros_ec_cec) 406adbfc747SReka Norman { 407adbfc747SReka Norman struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; 408adbfc747SReka Norman struct ec_params_get_cmd_versions_v1 params = { 409adbfc747SReka Norman .cmd = EC_CMD_CEC_WRITE_MSG, 410adbfc747SReka Norman }; 411adbfc747SReka Norman struct ec_response_get_cmd_versions response; 412adbfc747SReka Norman int ret; 413adbfc747SReka Norman 414adbfc747SReka Norman ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GET_CMD_VERSIONS, ¶ms, 415adbfc747SReka Norman sizeof(params), &response, sizeof(response)); 416adbfc747SReka Norman if (ret < 0) { 417adbfc747SReka Norman dev_err(cros_ec->dev, 418adbfc747SReka Norman "error getting CEC write command version: %d\n", ret); 419adbfc747SReka Norman return ret; 420adbfc747SReka Norman } 421adbfc747SReka Norman 422adbfc747SReka Norman if (response.version_mask & EC_VER_MASK(1)) { 423adbfc747SReka Norman cros_ec_cec->write_cmd_version = 1; 424adbfc747SReka Norman } else { 425adbfc747SReka Norman if (cros_ec_cec->num_ports != 1) { 426adbfc747SReka Norman dev_err(cros_ec->dev, 427adbfc747SReka Norman "v0 write command only supports 1 port, %d reported\n", 428adbfc747SReka Norman cros_ec_cec->num_ports); 429adbfc747SReka Norman return -EINVAL; 430adbfc747SReka Norman } 431adbfc747SReka Norman cros_ec_cec->write_cmd_version = 0; 432adbfc747SReka Norman } 433adbfc747SReka Norman 434adbfc747SReka Norman return 0; 435adbfc747SReka Norman } 436adbfc747SReka Norman 4374d0e179aSReka Norman static int cros_ec_cec_init_port(struct device *dev, 4384d0e179aSReka Norman struct cros_ec_cec *cros_ec_cec, 4394d0e179aSReka Norman int port_num, struct device *hdmi_dev, 440e7885b9cSReka Norman const char * const *conns) 4414d0e179aSReka Norman { 4424d0e179aSReka Norman struct cros_ec_cec_port *port; 4434d0e179aSReka Norman int ret; 4444d0e179aSReka Norman 4454d0e179aSReka Norman port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); 4464d0e179aSReka Norman if (!port) 4474d0e179aSReka Norman return -ENOMEM; 4484d0e179aSReka Norman 4494d0e179aSReka Norman port->cros_ec_cec = cros_ec_cec; 4504d0e179aSReka Norman port->port_num = port_num; 4514d0e179aSReka Norman 4524d0e179aSReka Norman port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME, 4534d0e179aSReka Norman CEC_CAP_DEFAULTS | 4544d0e179aSReka Norman CEC_CAP_CONNECTOR_INFO, 1); 4554d0e179aSReka Norman if (IS_ERR(port->adap)) 4564d0e179aSReka Norman return PTR_ERR(port->adap); 4574d0e179aSReka Norman 458e7885b9cSReka Norman if (!conns[port_num]) { 459e7885b9cSReka Norman dev_err(dev, "no conn for port %d\n", port_num); 460e7885b9cSReka Norman ret = -ENODEV; 461e7885b9cSReka Norman goto out_probe_adapter; 462e7885b9cSReka Norman } 463e7885b9cSReka Norman 464e7885b9cSReka Norman port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num], 4654d0e179aSReka Norman port->adap); 4664d0e179aSReka Norman if (!port->notify) { 4674d0e179aSReka Norman ret = -ENOMEM; 4684d0e179aSReka Norman goto out_probe_adapter; 4694d0e179aSReka Norman } 4704d0e179aSReka Norman 4714d0e179aSReka Norman ret = cec_register_adapter(port->adap, dev); 4724d0e179aSReka Norman if (ret < 0) 4734d0e179aSReka Norman goto out_probe_notify; 4744d0e179aSReka Norman 4754d0e179aSReka Norman cros_ec_cec->ports[port_num] = port; 4764d0e179aSReka Norman 4774d0e179aSReka Norman return 0; 4784d0e179aSReka Norman 4794d0e179aSReka Norman out_probe_notify: 4804d0e179aSReka Norman cec_notifier_cec_adap_unregister(port->notify, port->adap); 4814d0e179aSReka Norman out_probe_adapter: 4824d0e179aSReka Norman cec_delete_adapter(port->adap); 4834d0e179aSReka Norman return ret; 4844d0e179aSReka Norman } 4854d0e179aSReka Norman 4864be5e864SMauro Carvalho Chehab static int cros_ec_cec_probe(struct platform_device *pdev) 4874be5e864SMauro Carvalho Chehab { 4884be5e864SMauro Carvalho Chehab struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); 4894be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = ec_dev->ec_dev; 4904be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec; 4914d0e179aSReka Norman struct cros_ec_cec_port *port; 4924be5e864SMauro Carvalho Chehab struct device *hdmi_dev; 493e7885b9cSReka Norman const char * const *conns = NULL; 4944be5e864SMauro Carvalho Chehab int ret; 4954be5e864SMauro Carvalho Chehab 496e7885b9cSReka Norman hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conns); 4974be5e864SMauro Carvalho Chehab if (IS_ERR(hdmi_dev)) 4984be5e864SMauro Carvalho Chehab return PTR_ERR(hdmi_dev); 4994be5e864SMauro Carvalho Chehab 5004be5e864SMauro Carvalho Chehab cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec), 5014be5e864SMauro Carvalho Chehab GFP_KERNEL); 5024be5e864SMauro Carvalho Chehab if (!cros_ec_cec) 5034be5e864SMauro Carvalho Chehab return -ENOMEM; 5044be5e864SMauro Carvalho Chehab 5054be5e864SMauro Carvalho Chehab platform_set_drvdata(pdev, cros_ec_cec); 5064be5e864SMauro Carvalho Chehab cros_ec_cec->cros_ec = cros_ec; 5074be5e864SMauro Carvalho Chehab 5086f01dfb7SDariusz Marcinkiewicz device_init_wakeup(&pdev->dev, 1); 5094be5e864SMauro Carvalho Chehab 5105d227f02SReka Norman ret = cros_ec_cec_get_num_ports(cros_ec_cec); 5115d227f02SReka Norman if (ret) 5125d227f02SReka Norman return ret; 5134be5e864SMauro Carvalho Chehab 514adbfc747SReka Norman ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec); 515adbfc747SReka Norman if (ret) 516adbfc747SReka Norman return ret; 517adbfc747SReka Norman 5184d0e179aSReka Norman for (int i = 0; i < cros_ec_cec->num_ports; i++) { 5194d0e179aSReka Norman ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i, 520e7885b9cSReka Norman hdmi_dev, conns); 5214d0e179aSReka Norman if (ret) 5224d0e179aSReka Norman goto unregister_ports; 5234be5e864SMauro Carvalho Chehab } 5244be5e864SMauro Carvalho Chehab 5254be5e864SMauro Carvalho Chehab /* Get CEC events from the EC. */ 5264be5e864SMauro Carvalho Chehab cros_ec_cec->notifier.notifier_call = cros_ec_cec_event; 5274be5e864SMauro Carvalho Chehab ret = blocking_notifier_chain_register(&cros_ec->event_notifier, 5284be5e864SMauro Carvalho Chehab &cros_ec_cec->notifier); 5294be5e864SMauro Carvalho Chehab if (ret) { 5304be5e864SMauro Carvalho Chehab dev_err(&pdev->dev, "failed to register notifier\n"); 5314d0e179aSReka Norman goto unregister_ports; 5324be5e864SMauro Carvalho Chehab } 5334be5e864SMauro Carvalho Chehab 5344be5e864SMauro Carvalho Chehab return 0; 5354be5e864SMauro Carvalho Chehab 5364d0e179aSReka Norman unregister_ports: 5374d0e179aSReka Norman /* 5384d0e179aSReka Norman * Unregister any adapters which have been registered. We don't add the 5394d0e179aSReka Norman * port to the array until the adapter has been registered successfully, 5404d0e179aSReka Norman * so any non-NULL ports must have been registered. 5414d0e179aSReka Norman */ 5424d0e179aSReka Norman for (int i = 0; i < cros_ec_cec->num_ports; i++) { 5434d0e179aSReka Norman port = cros_ec_cec->ports[i]; 5444d0e179aSReka Norman if (!port) 5454d0e179aSReka Norman break; 5464d0e179aSReka Norman cec_notifier_cec_adap_unregister(port->notify, port->adap); 5474d0e179aSReka Norman cec_unregister_adapter(port->adap); 5484d0e179aSReka Norman } 5494be5e864SMauro Carvalho Chehab return ret; 5504be5e864SMauro Carvalho Chehab } 5514be5e864SMauro Carvalho Chehab 55245848b28SUwe Kleine-König static void cros_ec_cec_remove(struct platform_device *pdev) 5534be5e864SMauro Carvalho Chehab { 5544be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev); 5554be5e864SMauro Carvalho Chehab struct device *dev = &pdev->dev; 5564d0e179aSReka Norman struct cros_ec_cec_port *port; 5574be5e864SMauro Carvalho Chehab int ret; 5584be5e864SMauro Carvalho Chehab 5590ff7aee2SUwe Kleine-König /* 5600ff7aee2SUwe Kleine-König * blocking_notifier_chain_unregister() only fails if the notifier isn't 5610ff7aee2SUwe Kleine-König * in the list. We know it was added to it by .probe(), so there should 5620ff7aee2SUwe Kleine-König * be no need for error checking. Be cautious and still check. 5630ff7aee2SUwe Kleine-König */ 5644be5e864SMauro Carvalho Chehab ret = blocking_notifier_chain_unregister( 5654be5e864SMauro Carvalho Chehab &cros_ec_cec->cros_ec->event_notifier, 5664be5e864SMauro Carvalho Chehab &cros_ec_cec->notifier); 5670ff7aee2SUwe Kleine-König if (ret) 5684be5e864SMauro Carvalho Chehab dev_err(dev, "failed to unregister notifier\n"); 5694be5e864SMauro Carvalho Chehab 5704d0e179aSReka Norman for (int i = 0; i < cros_ec_cec->num_ports; i++) { 5714d0e179aSReka Norman port = cros_ec_cec->ports[i]; 5724d0e179aSReka Norman cec_notifier_cec_adap_unregister(port->notify, port->adap); 5734d0e179aSReka Norman cec_unregister_adapter(port->adap); 5744d0e179aSReka Norman } 5754be5e864SMauro Carvalho Chehab } 5764be5e864SMauro Carvalho Chehab 577*50a0844bSTzung-Bi Shih static const struct platform_device_id cros_ec_cec_id[] = { 578*50a0844bSTzung-Bi Shih { DRV_NAME, 0 }, 579*50a0844bSTzung-Bi Shih {} 580*50a0844bSTzung-Bi Shih }; 581*50a0844bSTzung-Bi Shih MODULE_DEVICE_TABLE(platform, cros_ec_cec_id); 582*50a0844bSTzung-Bi Shih 5834be5e864SMauro Carvalho Chehab static struct platform_driver cros_ec_cec_driver = { 5844be5e864SMauro Carvalho Chehab .probe = cros_ec_cec_probe, 58545848b28SUwe Kleine-König .remove_new = cros_ec_cec_remove, 5864be5e864SMauro Carvalho Chehab .driver = { 5874be5e864SMauro Carvalho Chehab .name = DRV_NAME, 5884be5e864SMauro Carvalho Chehab .pm = &cros_ec_cec_pm_ops, 5894be5e864SMauro Carvalho Chehab }, 590*50a0844bSTzung-Bi Shih .id_table = cros_ec_cec_id, 5914be5e864SMauro Carvalho Chehab }; 5924be5e864SMauro Carvalho Chehab 5934be5e864SMauro Carvalho Chehab module_platform_driver(cros_ec_cec_driver); 5944be5e864SMauro Carvalho Chehab 5954be5e864SMauro Carvalho Chehab MODULE_DESCRIPTION("CEC driver for ChromeOS ECs"); 5964be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 5974be5e864SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 598