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>
114be5e864SMauro Carvalho Chehab #include <linux/platform_device.h>
124be5e864SMauro Carvalho Chehab #include <linux/dmi.h>
134be5e864SMauro Carvalho Chehab #include <linux/pci.h>
144be5e864SMauro Carvalho Chehab #include <linux/cec.h>
154be5e864SMauro Carvalho Chehab #include <linux/slab.h>
164be5e864SMauro Carvalho Chehab #include <linux/interrupt.h>
174be5e864SMauro Carvalho Chehab #include <linux/platform_data/cros_ec_commands.h>
184be5e864SMauro Carvalho Chehab #include <linux/platform_data/cros_ec_proto.h>
194be5e864SMauro Carvalho Chehab #include <media/cec.h>
204be5e864SMauro Carvalho Chehab #include <media/cec-notifier.h>
214be5e864SMauro Carvalho Chehab
224be5e864SMauro Carvalho Chehab #define DRV_NAME "cros-ec-cec"
234be5e864SMauro Carvalho Chehab
244be5e864SMauro Carvalho Chehab /**
254be5e864SMauro Carvalho Chehab * struct cros_ec_cec - Driver data for EC CEC
264be5e864SMauro Carvalho Chehab *
274be5e864SMauro Carvalho Chehab * @cros_ec: Pointer to EC device
284be5e864SMauro Carvalho Chehab * @notifier: Notifier info for responding to EC events
294be5e864SMauro Carvalho Chehab * @adap: CEC adapter
304be5e864SMauro Carvalho Chehab * @notify: CEC notifier pointer
314be5e864SMauro Carvalho Chehab * @rx_msg: storage for a received message
324be5e864SMauro Carvalho Chehab */
334be5e864SMauro Carvalho Chehab struct cros_ec_cec {
344be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec;
354be5e864SMauro Carvalho Chehab struct notifier_block notifier;
364be5e864SMauro Carvalho Chehab struct cec_adapter *adap;
374be5e864SMauro Carvalho Chehab struct cec_notifier *notify;
384be5e864SMauro Carvalho Chehab struct cec_msg rx_msg;
394be5e864SMauro Carvalho Chehab };
404be5e864SMauro Carvalho Chehab
handle_cec_message(struct cros_ec_cec * cros_ec_cec)414be5e864SMauro Carvalho Chehab static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
424be5e864SMauro Carvalho Chehab {
434be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
444be5e864SMauro Carvalho Chehab uint8_t *cec_message = cros_ec->event_data.data.cec_message;
454be5e864SMauro Carvalho Chehab unsigned int len = cros_ec->event_size;
464be5e864SMauro Carvalho Chehab
472dc73b48SHans Verkuil if (len > CEC_MAX_MSG_SIZE)
482dc73b48SHans Verkuil len = CEC_MAX_MSG_SIZE;
494be5e864SMauro Carvalho Chehab cros_ec_cec->rx_msg.len = len;
504be5e864SMauro Carvalho Chehab memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
514be5e864SMauro Carvalho Chehab
524be5e864SMauro Carvalho Chehab cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg);
534be5e864SMauro Carvalho Chehab }
544be5e864SMauro Carvalho Chehab
handle_cec_event(struct cros_ec_cec * cros_ec_cec)554be5e864SMauro Carvalho Chehab static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
564be5e864SMauro Carvalho Chehab {
574be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
584be5e864SMauro Carvalho Chehab uint32_t events = cros_ec->event_data.data.cec_events;
594be5e864SMauro Carvalho Chehab
604be5e864SMauro Carvalho Chehab if (events & EC_MKBP_CEC_SEND_OK)
614be5e864SMauro Carvalho Chehab cec_transmit_attempt_done(cros_ec_cec->adap,
624be5e864SMauro Carvalho Chehab CEC_TX_STATUS_OK);
634be5e864SMauro Carvalho Chehab
644be5e864SMauro Carvalho Chehab /* FW takes care of all retries, tell core to avoid more retries */
654be5e864SMauro Carvalho Chehab if (events & EC_MKBP_CEC_SEND_FAILED)
664be5e864SMauro Carvalho Chehab cec_transmit_attempt_done(cros_ec_cec->adap,
674be5e864SMauro Carvalho Chehab CEC_TX_STATUS_MAX_RETRIES |
684be5e864SMauro Carvalho Chehab CEC_TX_STATUS_NACK);
694be5e864SMauro Carvalho Chehab }
704be5e864SMauro Carvalho Chehab
cros_ec_cec_event(struct notifier_block * nb,unsigned long queued_during_suspend,void * _notify)714be5e864SMauro Carvalho Chehab static int cros_ec_cec_event(struct notifier_block *nb,
724be5e864SMauro Carvalho Chehab unsigned long queued_during_suspend,
734be5e864SMauro Carvalho Chehab void *_notify)
744be5e864SMauro Carvalho Chehab {
754be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec;
764be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec;
774be5e864SMauro Carvalho Chehab
784be5e864SMauro Carvalho Chehab cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
794be5e864SMauro Carvalho Chehab cros_ec = cros_ec_cec->cros_ec;
804be5e864SMauro Carvalho Chehab
814be5e864SMauro Carvalho Chehab if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
824be5e864SMauro Carvalho Chehab handle_cec_event(cros_ec_cec);
834be5e864SMauro Carvalho Chehab return NOTIFY_OK;
844be5e864SMauro Carvalho Chehab }
854be5e864SMauro Carvalho Chehab
864be5e864SMauro Carvalho Chehab if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
874be5e864SMauro Carvalho Chehab handle_cec_message(cros_ec_cec);
884be5e864SMauro Carvalho Chehab return NOTIFY_OK;
894be5e864SMauro Carvalho Chehab }
904be5e864SMauro Carvalho Chehab
914be5e864SMauro Carvalho Chehab return NOTIFY_DONE;
924be5e864SMauro Carvalho Chehab }
934be5e864SMauro Carvalho Chehab
cros_ec_cec_set_log_addr(struct cec_adapter * adap,u8 logical_addr)944be5e864SMauro Carvalho Chehab static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
954be5e864SMauro Carvalho Chehab {
964be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = adap->priv;
974be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
984be5e864SMauro Carvalho Chehab struct {
994be5e864SMauro Carvalho Chehab struct cros_ec_command msg;
1004be5e864SMauro Carvalho Chehab struct ec_params_cec_set data;
1014be5e864SMauro Carvalho Chehab } __packed msg = {};
1024be5e864SMauro Carvalho Chehab int ret;
1034be5e864SMauro Carvalho Chehab
1044be5e864SMauro Carvalho Chehab msg.msg.command = EC_CMD_CEC_SET;
1054be5e864SMauro Carvalho Chehab msg.msg.outsize = sizeof(msg.data);
1064be5e864SMauro Carvalho Chehab msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS;
1074be5e864SMauro Carvalho Chehab msg.data.val = logical_addr;
1084be5e864SMauro Carvalho Chehab
1094be5e864SMauro Carvalho Chehab ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
1104be5e864SMauro Carvalho Chehab if (ret < 0) {
1114be5e864SMauro Carvalho Chehab dev_err(cros_ec->dev,
1124be5e864SMauro Carvalho Chehab "error setting CEC logical address on EC: %d\n", ret);
1134be5e864SMauro Carvalho Chehab return ret;
1144be5e864SMauro Carvalho Chehab }
1154be5e864SMauro Carvalho Chehab
1164be5e864SMauro Carvalho Chehab return 0;
1174be5e864SMauro Carvalho Chehab }
1184be5e864SMauro Carvalho Chehab
cros_ec_cec_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * cec_msg)1194be5e864SMauro Carvalho Chehab static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
1204be5e864SMauro Carvalho Chehab u32 signal_free_time, struct cec_msg *cec_msg)
1214be5e864SMauro Carvalho Chehab {
1224be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = adap->priv;
1234be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
1244be5e864SMauro Carvalho Chehab struct {
1254be5e864SMauro Carvalho Chehab struct cros_ec_command msg;
1264be5e864SMauro Carvalho Chehab struct ec_params_cec_write data;
1274be5e864SMauro Carvalho Chehab } __packed msg = {};
1284be5e864SMauro Carvalho Chehab int ret;
1294be5e864SMauro Carvalho Chehab
1304be5e864SMauro Carvalho Chehab msg.msg.command = EC_CMD_CEC_WRITE_MSG;
1314be5e864SMauro Carvalho Chehab msg.msg.outsize = cec_msg->len;
1324be5e864SMauro Carvalho Chehab memcpy(msg.data.msg, cec_msg->msg, cec_msg->len);
1334be5e864SMauro Carvalho Chehab
1344be5e864SMauro Carvalho Chehab ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
1354be5e864SMauro Carvalho Chehab if (ret < 0) {
1364be5e864SMauro Carvalho Chehab dev_err(cros_ec->dev,
1374be5e864SMauro Carvalho Chehab "error writing CEC msg on EC: %d\n", ret);
1384be5e864SMauro Carvalho Chehab return ret;
1394be5e864SMauro Carvalho Chehab }
1404be5e864SMauro Carvalho Chehab
1414be5e864SMauro Carvalho Chehab return 0;
1424be5e864SMauro Carvalho Chehab }
1434be5e864SMauro Carvalho Chehab
cros_ec_cec_adap_enable(struct cec_adapter * adap,bool enable)1444be5e864SMauro Carvalho Chehab static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
1454be5e864SMauro Carvalho Chehab {
1464be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = adap->priv;
1474be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
1484be5e864SMauro Carvalho Chehab struct {
1494be5e864SMauro Carvalho Chehab struct cros_ec_command msg;
1504be5e864SMauro Carvalho Chehab struct ec_params_cec_set data;
1514be5e864SMauro Carvalho Chehab } __packed msg = {};
1524be5e864SMauro Carvalho Chehab int ret;
1534be5e864SMauro Carvalho Chehab
1544be5e864SMauro Carvalho Chehab msg.msg.command = EC_CMD_CEC_SET;
1554be5e864SMauro Carvalho Chehab msg.msg.outsize = sizeof(msg.data);
1564be5e864SMauro Carvalho Chehab msg.data.cmd = CEC_CMD_ENABLE;
1574be5e864SMauro Carvalho Chehab msg.data.val = enable;
1584be5e864SMauro Carvalho Chehab
1594be5e864SMauro Carvalho Chehab ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
1604be5e864SMauro Carvalho Chehab if (ret < 0) {
1614be5e864SMauro Carvalho Chehab dev_err(cros_ec->dev,
1624be5e864SMauro Carvalho Chehab "error %sabling CEC on EC: %d\n",
1634be5e864SMauro Carvalho Chehab (enable ? "en" : "dis"), ret);
1644be5e864SMauro Carvalho Chehab return ret;
1654be5e864SMauro Carvalho Chehab }
1664be5e864SMauro Carvalho Chehab
1674be5e864SMauro Carvalho Chehab return 0;
1684be5e864SMauro Carvalho Chehab }
1694be5e864SMauro Carvalho Chehab
1704be5e864SMauro Carvalho Chehab static const struct cec_adap_ops cros_ec_cec_ops = {
1714be5e864SMauro Carvalho Chehab .adap_enable = cros_ec_cec_adap_enable,
1724be5e864SMauro Carvalho Chehab .adap_log_addr = cros_ec_cec_set_log_addr,
1734be5e864SMauro Carvalho Chehab .adap_transmit = cros_ec_cec_transmit,
1744be5e864SMauro Carvalho Chehab };
1754be5e864SMauro Carvalho Chehab
1764be5e864SMauro Carvalho Chehab #ifdef CONFIG_PM_SLEEP
cros_ec_cec_suspend(struct device * dev)1774be5e864SMauro Carvalho Chehab static int cros_ec_cec_suspend(struct device *dev)
1784be5e864SMauro Carvalho Chehab {
1794be5e864SMauro Carvalho Chehab struct platform_device *pdev = to_platform_device(dev);
1804be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
1814be5e864SMauro Carvalho Chehab
1824be5e864SMauro Carvalho Chehab if (device_may_wakeup(dev))
1834be5e864SMauro Carvalho Chehab enable_irq_wake(cros_ec_cec->cros_ec->irq);
1844be5e864SMauro Carvalho Chehab
1854be5e864SMauro Carvalho Chehab return 0;
1864be5e864SMauro Carvalho Chehab }
1874be5e864SMauro Carvalho Chehab
cros_ec_cec_resume(struct device * dev)1884be5e864SMauro Carvalho Chehab static int cros_ec_cec_resume(struct device *dev)
1894be5e864SMauro Carvalho Chehab {
1904be5e864SMauro Carvalho Chehab struct platform_device *pdev = to_platform_device(dev);
1914be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
1924be5e864SMauro Carvalho Chehab
1934be5e864SMauro Carvalho Chehab if (device_may_wakeup(dev))
1944be5e864SMauro Carvalho Chehab disable_irq_wake(cros_ec_cec->cros_ec->irq);
1954be5e864SMauro Carvalho Chehab
1964be5e864SMauro Carvalho Chehab return 0;
1974be5e864SMauro Carvalho Chehab }
1984be5e864SMauro Carvalho Chehab #endif
1994be5e864SMauro Carvalho Chehab
2004be5e864SMauro Carvalho Chehab static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
2014be5e864SMauro Carvalho Chehab cros_ec_cec_suspend, cros_ec_cec_resume);
2024be5e864SMauro Carvalho Chehab
2034be5e864SMauro Carvalho Chehab #if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
2044be5e864SMauro Carvalho Chehab
2054be5e864SMauro Carvalho Chehab /*
2064be5e864SMauro Carvalho Chehab * The Firmware only handles a single CEC interface tied to a single HDMI
2074be5e864SMauro Carvalho Chehab * connector we specify along with the DRM device name handling the HDMI output
2084be5e864SMauro Carvalho Chehab */
2094be5e864SMauro Carvalho Chehab
2104be5e864SMauro Carvalho Chehab struct cec_dmi_match {
2114be5e864SMauro Carvalho Chehab const char *sys_vendor;
2124be5e864SMauro Carvalho Chehab const char *product_name;
2134be5e864SMauro Carvalho Chehab const char *devname;
2144be5e864SMauro Carvalho Chehab const char *conn;
2154be5e864SMauro Carvalho Chehab };
2164be5e864SMauro Carvalho Chehab
2174be5e864SMauro Carvalho Chehab static const struct cec_dmi_match cec_dmi_match_table[] = {
2184be5e864SMauro Carvalho Chehab /* Google Fizz */
2194be5e864SMauro Carvalho Chehab { "Google", "Fizz", "0000:00:02.0", "Port B" },
22097733180SZhuohao Lee /* Google Brask */
22197733180SZhuohao Lee { "Google", "Brask", "0000:00:02.0", "Port B" },
222a1a9b71eSScott Chao /* Google Moli */
223a1a9b71eSScott Chao { "Google", "Moli", "0000:00:02.0", "Port B" },
224f5d48ba2SAjye Huang /* Google Kinox */
225f5d48ba2SAjye Huang { "Google", "Kinox", "0000:00:02.0", "Port B" },
226594b6bddSRory Liu /* Google Kuldax */
227594b6bddSRory Liu { "Google", "Kuldax", "0000:00:02.0", "Port B" },
22846ff24efSZoey Wu /* Google Aurash */
22946ff24efSZoey Wu { "Google", "Aurash", "0000:00:02.0", "Port B" },
2306f8cdfdfSKevin Chiu /* Google Gladios */
2316f8cdfdfSKevin Chiu { "Google", "Gladios", "0000:00:02.0", "Port B" },
2326f8cdfdfSKevin Chiu /* Google Lisbon */
2336f8cdfdfSKevin Chiu { "Google", "Lisbon", "0000:00:02.0", "Port B" },
2344be5e864SMauro Carvalho Chehab };
2354be5e864SMauro Carvalho Chehab
cros_ec_cec_find_hdmi_dev(struct device * dev,const char ** conn)2364be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
2374be5e864SMauro Carvalho Chehab const char **conn)
2384be5e864SMauro Carvalho Chehab {
2394be5e864SMauro Carvalho Chehab int i;
2404be5e864SMauro Carvalho Chehab
2414be5e864SMauro Carvalho Chehab for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
2424be5e864SMauro Carvalho Chehab const struct cec_dmi_match *m = &cec_dmi_match_table[i];
2434be5e864SMauro Carvalho Chehab
2444be5e864SMauro Carvalho Chehab if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
2454be5e864SMauro Carvalho Chehab dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
2464be5e864SMauro Carvalho Chehab struct device *d;
2474be5e864SMauro Carvalho Chehab
2484be5e864SMauro Carvalho Chehab /* Find the device, bail out if not yet registered */
2494be5e864SMauro Carvalho Chehab d = bus_find_device_by_name(&pci_bus_type, NULL,
2504be5e864SMauro Carvalho Chehab m->devname);
2514be5e864SMauro Carvalho Chehab if (!d)
2524be5e864SMauro Carvalho Chehab return ERR_PTR(-EPROBE_DEFER);
2534be5e864SMauro Carvalho Chehab put_device(d);
2544be5e864SMauro Carvalho Chehab *conn = m->conn;
2554be5e864SMauro Carvalho Chehab return d;
2564be5e864SMauro Carvalho Chehab }
2574be5e864SMauro Carvalho Chehab }
2584be5e864SMauro Carvalho Chehab
2594be5e864SMauro Carvalho Chehab /* Hardware support must be added in the cec_dmi_match_table */
2604be5e864SMauro Carvalho Chehab dev_warn(dev, "CEC notifier not configured for this hardware\n");
2614be5e864SMauro Carvalho Chehab
2624be5e864SMauro Carvalho Chehab return ERR_PTR(-ENODEV);
2634be5e864SMauro Carvalho Chehab }
2644be5e864SMauro Carvalho Chehab
2654be5e864SMauro Carvalho Chehab #else
2664be5e864SMauro Carvalho Chehab
cros_ec_cec_find_hdmi_dev(struct device * dev,const char ** conn)2674be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
2684be5e864SMauro Carvalho Chehab const char **conn)
2694be5e864SMauro Carvalho Chehab {
2704be5e864SMauro Carvalho Chehab return ERR_PTR(-ENODEV);
2714be5e864SMauro Carvalho Chehab }
2724be5e864SMauro Carvalho Chehab
2734be5e864SMauro Carvalho Chehab #endif
2744be5e864SMauro Carvalho Chehab
cros_ec_cec_probe(struct platform_device * pdev)2754be5e864SMauro Carvalho Chehab static int cros_ec_cec_probe(struct platform_device *pdev)
2764be5e864SMauro Carvalho Chehab {
2774be5e864SMauro Carvalho Chehab struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
2784be5e864SMauro Carvalho Chehab struct cros_ec_device *cros_ec = ec_dev->ec_dev;
2794be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec;
2804be5e864SMauro Carvalho Chehab struct device *hdmi_dev;
2814be5e864SMauro Carvalho Chehab const char *conn = NULL;
2824be5e864SMauro Carvalho Chehab int ret;
2834be5e864SMauro Carvalho Chehab
2844be5e864SMauro Carvalho Chehab hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conn);
2854be5e864SMauro Carvalho Chehab if (IS_ERR(hdmi_dev))
2864be5e864SMauro Carvalho Chehab return PTR_ERR(hdmi_dev);
2874be5e864SMauro Carvalho Chehab
2884be5e864SMauro Carvalho Chehab cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
2894be5e864SMauro Carvalho Chehab GFP_KERNEL);
2904be5e864SMauro Carvalho Chehab if (!cros_ec_cec)
2914be5e864SMauro Carvalho Chehab return -ENOMEM;
2924be5e864SMauro Carvalho Chehab
2934be5e864SMauro Carvalho Chehab platform_set_drvdata(pdev, cros_ec_cec);
2944be5e864SMauro Carvalho Chehab cros_ec_cec->cros_ec = cros_ec;
2954be5e864SMauro Carvalho Chehab
2966f01dfb7SDariusz Marcinkiewicz device_init_wakeup(&pdev->dev, 1);
2974be5e864SMauro Carvalho Chehab
2984be5e864SMauro Carvalho Chehab cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
2994be5e864SMauro Carvalho Chehab DRV_NAME,
3004be5e864SMauro Carvalho Chehab CEC_CAP_DEFAULTS |
3014be5e864SMauro Carvalho Chehab CEC_CAP_CONNECTOR_INFO, 1);
3024be5e864SMauro Carvalho Chehab if (IS_ERR(cros_ec_cec->adap))
3034be5e864SMauro Carvalho Chehab return PTR_ERR(cros_ec_cec->adap);
3044be5e864SMauro Carvalho Chehab
3054be5e864SMauro Carvalho Chehab cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
3064be5e864SMauro Carvalho Chehab cros_ec_cec->adap);
3074be5e864SMauro Carvalho Chehab if (!cros_ec_cec->notify) {
3084be5e864SMauro Carvalho Chehab ret = -ENOMEM;
3094be5e864SMauro Carvalho Chehab goto out_probe_adapter;
3104be5e864SMauro Carvalho Chehab }
3114be5e864SMauro Carvalho Chehab
3124be5e864SMauro Carvalho Chehab /* Get CEC events from the EC. */
3134be5e864SMauro Carvalho Chehab cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
3144be5e864SMauro Carvalho Chehab ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
3154be5e864SMauro Carvalho Chehab &cros_ec_cec->notifier);
3164be5e864SMauro Carvalho Chehab if (ret) {
3174be5e864SMauro Carvalho Chehab dev_err(&pdev->dev, "failed to register notifier\n");
3184be5e864SMauro Carvalho Chehab goto out_probe_notify;
3194be5e864SMauro Carvalho Chehab }
3204be5e864SMauro Carvalho Chehab
3214be5e864SMauro Carvalho Chehab ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
3224be5e864SMauro Carvalho Chehab if (ret < 0)
3234be5e864SMauro Carvalho Chehab goto out_probe_notify;
3244be5e864SMauro Carvalho Chehab
3254be5e864SMauro Carvalho Chehab return 0;
3264be5e864SMauro Carvalho Chehab
3274be5e864SMauro Carvalho Chehab out_probe_notify:
3284be5e864SMauro Carvalho Chehab cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
3294be5e864SMauro Carvalho Chehab cros_ec_cec->adap);
3304be5e864SMauro Carvalho Chehab out_probe_adapter:
3314be5e864SMauro Carvalho Chehab cec_delete_adapter(cros_ec_cec->adap);
3324be5e864SMauro Carvalho Chehab return ret;
3334be5e864SMauro Carvalho Chehab }
3344be5e864SMauro Carvalho Chehab
cros_ec_cec_remove(struct platform_device * pdev)335*45848b28SUwe Kleine-König static void cros_ec_cec_remove(struct platform_device *pdev)
3364be5e864SMauro Carvalho Chehab {
3374be5e864SMauro Carvalho Chehab struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
3384be5e864SMauro Carvalho Chehab struct device *dev = &pdev->dev;
3394be5e864SMauro Carvalho Chehab int ret;
3404be5e864SMauro Carvalho Chehab
3410ff7aee2SUwe Kleine-König /*
3420ff7aee2SUwe Kleine-König * blocking_notifier_chain_unregister() only fails if the notifier isn't
3430ff7aee2SUwe Kleine-König * in the list. We know it was added to it by .probe(), so there should
3440ff7aee2SUwe Kleine-König * be no need for error checking. Be cautious and still check.
3450ff7aee2SUwe Kleine-König */
3464be5e864SMauro Carvalho Chehab ret = blocking_notifier_chain_unregister(
3474be5e864SMauro Carvalho Chehab &cros_ec_cec->cros_ec->event_notifier,
3484be5e864SMauro Carvalho Chehab &cros_ec_cec->notifier);
3490ff7aee2SUwe Kleine-König if (ret)
3504be5e864SMauro Carvalho Chehab dev_err(dev, "failed to unregister notifier\n");
3514be5e864SMauro Carvalho Chehab
3524be5e864SMauro Carvalho Chehab cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
3534be5e864SMauro Carvalho Chehab cros_ec_cec->adap);
3544be5e864SMauro Carvalho Chehab cec_unregister_adapter(cros_ec_cec->adap);
3554be5e864SMauro Carvalho Chehab }
3564be5e864SMauro Carvalho Chehab
3574be5e864SMauro Carvalho Chehab static struct platform_driver cros_ec_cec_driver = {
3584be5e864SMauro Carvalho Chehab .probe = cros_ec_cec_probe,
359*45848b28SUwe Kleine-König .remove_new = cros_ec_cec_remove,
3604be5e864SMauro Carvalho Chehab .driver = {
3614be5e864SMauro Carvalho Chehab .name = DRV_NAME,
3624be5e864SMauro Carvalho Chehab .pm = &cros_ec_cec_pm_ops,
3634be5e864SMauro Carvalho Chehab },
3644be5e864SMauro Carvalho Chehab };
3654be5e864SMauro Carvalho Chehab
3664be5e864SMauro Carvalho Chehab module_platform_driver(cros_ec_cec_driver);
3674be5e864SMauro Carvalho Chehab
3684be5e864SMauro Carvalho Chehab MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
3694be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
3704be5e864SMauro Carvalho Chehab MODULE_LICENSE("GPL");
3714be5e864SMauro Carvalho Chehab MODULE_ALIAS("platform:" DRV_NAME);
372