xref: /linux/drivers/media/cec/platform/cros-ec/cros-ec-cec.c (revision 6f01dfb760c027d5dd6199d91ee9599f2676b5c6)
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 
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 
474be5e864SMauro Carvalho Chehab 	cros_ec_cec->rx_msg.len = len;
484be5e864SMauro Carvalho Chehab 	memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
494be5e864SMauro Carvalho Chehab 
504be5e864SMauro Carvalho Chehab 	cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg);
514be5e864SMauro Carvalho Chehab }
524be5e864SMauro Carvalho Chehab 
534be5e864SMauro Carvalho Chehab static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
544be5e864SMauro Carvalho Chehab {
554be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
564be5e864SMauro Carvalho Chehab 	uint32_t events = cros_ec->event_data.data.cec_events;
574be5e864SMauro Carvalho Chehab 
584be5e864SMauro Carvalho Chehab 	if (events & EC_MKBP_CEC_SEND_OK)
594be5e864SMauro Carvalho Chehab 		cec_transmit_attempt_done(cros_ec_cec->adap,
604be5e864SMauro Carvalho Chehab 					  CEC_TX_STATUS_OK);
614be5e864SMauro Carvalho Chehab 
624be5e864SMauro Carvalho Chehab 	/* FW takes care of all retries, tell core to avoid more retries */
634be5e864SMauro Carvalho Chehab 	if (events & EC_MKBP_CEC_SEND_FAILED)
644be5e864SMauro Carvalho Chehab 		cec_transmit_attempt_done(cros_ec_cec->adap,
654be5e864SMauro Carvalho Chehab 					  CEC_TX_STATUS_MAX_RETRIES |
664be5e864SMauro Carvalho Chehab 					  CEC_TX_STATUS_NACK);
674be5e864SMauro Carvalho Chehab }
684be5e864SMauro Carvalho Chehab 
694be5e864SMauro Carvalho Chehab static int cros_ec_cec_event(struct notifier_block *nb,
704be5e864SMauro Carvalho Chehab 			     unsigned long queued_during_suspend,
714be5e864SMauro Carvalho Chehab 			     void *_notify)
724be5e864SMauro Carvalho Chehab {
734be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec;
744be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec;
754be5e864SMauro Carvalho Chehab 
764be5e864SMauro Carvalho Chehab 	cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
774be5e864SMauro Carvalho Chehab 	cros_ec = cros_ec_cec->cros_ec;
784be5e864SMauro Carvalho Chehab 
794be5e864SMauro Carvalho Chehab 	if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
804be5e864SMauro Carvalho Chehab 		handle_cec_event(cros_ec_cec);
814be5e864SMauro Carvalho Chehab 		return NOTIFY_OK;
824be5e864SMauro Carvalho Chehab 	}
834be5e864SMauro Carvalho Chehab 
844be5e864SMauro Carvalho Chehab 	if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
854be5e864SMauro Carvalho Chehab 		handle_cec_message(cros_ec_cec);
864be5e864SMauro Carvalho Chehab 		return NOTIFY_OK;
874be5e864SMauro Carvalho Chehab 	}
884be5e864SMauro Carvalho Chehab 
894be5e864SMauro Carvalho Chehab 	return NOTIFY_DONE;
904be5e864SMauro Carvalho Chehab }
914be5e864SMauro Carvalho Chehab 
924be5e864SMauro Carvalho Chehab static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
934be5e864SMauro Carvalho Chehab {
944be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = adap->priv;
954be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
964be5e864SMauro Carvalho Chehab 	struct {
974be5e864SMauro Carvalho Chehab 		struct cros_ec_command msg;
984be5e864SMauro Carvalho Chehab 		struct ec_params_cec_set data;
994be5e864SMauro Carvalho Chehab 	} __packed msg = {};
1004be5e864SMauro Carvalho Chehab 	int ret;
1014be5e864SMauro Carvalho Chehab 
1024be5e864SMauro Carvalho Chehab 	msg.msg.command = EC_CMD_CEC_SET;
1034be5e864SMauro Carvalho Chehab 	msg.msg.outsize = sizeof(msg.data);
1044be5e864SMauro Carvalho Chehab 	msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS;
1054be5e864SMauro Carvalho Chehab 	msg.data.val = logical_addr;
1064be5e864SMauro Carvalho Chehab 
1074be5e864SMauro Carvalho Chehab 	ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
1084be5e864SMauro Carvalho Chehab 	if (ret < 0) {
1094be5e864SMauro Carvalho Chehab 		dev_err(cros_ec->dev,
1104be5e864SMauro Carvalho Chehab 			"error setting CEC logical address on EC: %d\n", ret);
1114be5e864SMauro Carvalho Chehab 		return ret;
1124be5e864SMauro Carvalho Chehab 	}
1134be5e864SMauro Carvalho Chehab 
1144be5e864SMauro Carvalho Chehab 	return 0;
1154be5e864SMauro Carvalho Chehab }
1164be5e864SMauro Carvalho Chehab 
1174be5e864SMauro Carvalho Chehab static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
1184be5e864SMauro Carvalho Chehab 				u32 signal_free_time, struct cec_msg *cec_msg)
1194be5e864SMauro Carvalho Chehab {
1204be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = adap->priv;
1214be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
1224be5e864SMauro Carvalho Chehab 	struct {
1234be5e864SMauro Carvalho Chehab 		struct cros_ec_command msg;
1244be5e864SMauro Carvalho Chehab 		struct ec_params_cec_write data;
1254be5e864SMauro Carvalho Chehab 	} __packed msg = {};
1264be5e864SMauro Carvalho Chehab 	int ret;
1274be5e864SMauro Carvalho Chehab 
1284be5e864SMauro Carvalho Chehab 	msg.msg.command = EC_CMD_CEC_WRITE_MSG;
1294be5e864SMauro Carvalho Chehab 	msg.msg.outsize = cec_msg->len;
1304be5e864SMauro Carvalho Chehab 	memcpy(msg.data.msg, cec_msg->msg, cec_msg->len);
1314be5e864SMauro Carvalho Chehab 
1324be5e864SMauro Carvalho Chehab 	ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
1334be5e864SMauro Carvalho Chehab 	if (ret < 0) {
1344be5e864SMauro Carvalho Chehab 		dev_err(cros_ec->dev,
1354be5e864SMauro Carvalho Chehab 			"error writing CEC msg on EC: %d\n", ret);
1364be5e864SMauro Carvalho Chehab 		return ret;
1374be5e864SMauro Carvalho Chehab 	}
1384be5e864SMauro Carvalho Chehab 
1394be5e864SMauro Carvalho Chehab 	return 0;
1404be5e864SMauro Carvalho Chehab }
1414be5e864SMauro Carvalho Chehab 
1424be5e864SMauro Carvalho Chehab static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
1434be5e864SMauro Carvalho Chehab {
1444be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = adap->priv;
1454be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
1464be5e864SMauro Carvalho Chehab 	struct {
1474be5e864SMauro Carvalho Chehab 		struct cros_ec_command msg;
1484be5e864SMauro Carvalho Chehab 		struct ec_params_cec_set data;
1494be5e864SMauro Carvalho Chehab 	} __packed msg = {};
1504be5e864SMauro Carvalho Chehab 	int ret;
1514be5e864SMauro Carvalho Chehab 
1524be5e864SMauro Carvalho Chehab 	msg.msg.command = EC_CMD_CEC_SET;
1534be5e864SMauro Carvalho Chehab 	msg.msg.outsize = sizeof(msg.data);
1544be5e864SMauro Carvalho Chehab 	msg.data.cmd = CEC_CMD_ENABLE;
1554be5e864SMauro Carvalho Chehab 	msg.data.val = enable;
1564be5e864SMauro Carvalho Chehab 
1574be5e864SMauro Carvalho Chehab 	ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
1584be5e864SMauro Carvalho Chehab 	if (ret < 0) {
1594be5e864SMauro Carvalho Chehab 		dev_err(cros_ec->dev,
1604be5e864SMauro Carvalho Chehab 			"error %sabling CEC on EC: %d\n",
1614be5e864SMauro Carvalho Chehab 			(enable ? "en" : "dis"), ret);
1624be5e864SMauro Carvalho Chehab 		return ret;
1634be5e864SMauro Carvalho Chehab 	}
1644be5e864SMauro Carvalho Chehab 
1654be5e864SMauro Carvalho Chehab 	return 0;
1664be5e864SMauro Carvalho Chehab }
1674be5e864SMauro Carvalho Chehab 
1684be5e864SMauro Carvalho Chehab static const struct cec_adap_ops cros_ec_cec_ops = {
1694be5e864SMauro Carvalho Chehab 	.adap_enable = cros_ec_cec_adap_enable,
1704be5e864SMauro Carvalho Chehab 	.adap_log_addr = cros_ec_cec_set_log_addr,
1714be5e864SMauro Carvalho Chehab 	.adap_transmit = cros_ec_cec_transmit,
1724be5e864SMauro Carvalho Chehab };
1734be5e864SMauro Carvalho Chehab 
1744be5e864SMauro Carvalho Chehab #ifdef CONFIG_PM_SLEEP
1754be5e864SMauro Carvalho Chehab static int cros_ec_cec_suspend(struct device *dev)
1764be5e864SMauro Carvalho Chehab {
1774be5e864SMauro Carvalho Chehab 	struct platform_device *pdev = to_platform_device(dev);
1784be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
1794be5e864SMauro Carvalho Chehab 
1804be5e864SMauro Carvalho Chehab 	if (device_may_wakeup(dev))
1814be5e864SMauro Carvalho Chehab 		enable_irq_wake(cros_ec_cec->cros_ec->irq);
1824be5e864SMauro Carvalho Chehab 
1834be5e864SMauro Carvalho Chehab 	return 0;
1844be5e864SMauro Carvalho Chehab }
1854be5e864SMauro Carvalho Chehab 
1864be5e864SMauro Carvalho Chehab static int cros_ec_cec_resume(struct device *dev)
1874be5e864SMauro Carvalho Chehab {
1884be5e864SMauro Carvalho Chehab 	struct platform_device *pdev = to_platform_device(dev);
1894be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
1904be5e864SMauro Carvalho Chehab 
1914be5e864SMauro Carvalho Chehab 	if (device_may_wakeup(dev))
1924be5e864SMauro Carvalho Chehab 		disable_irq_wake(cros_ec_cec->cros_ec->irq);
1934be5e864SMauro Carvalho Chehab 
1944be5e864SMauro Carvalho Chehab 	return 0;
1954be5e864SMauro Carvalho Chehab }
1964be5e864SMauro Carvalho Chehab #endif
1974be5e864SMauro Carvalho Chehab 
1984be5e864SMauro Carvalho Chehab static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
1994be5e864SMauro Carvalho Chehab 	cros_ec_cec_suspend, cros_ec_cec_resume);
2004be5e864SMauro Carvalho Chehab 
2014be5e864SMauro Carvalho Chehab #if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
2024be5e864SMauro Carvalho Chehab 
2034be5e864SMauro Carvalho Chehab /*
2044be5e864SMauro Carvalho Chehab  * The Firmware only handles a single CEC interface tied to a single HDMI
2054be5e864SMauro Carvalho Chehab  * connector we specify along with the DRM device name handling the HDMI output
2064be5e864SMauro Carvalho Chehab  */
2074be5e864SMauro Carvalho Chehab 
2084be5e864SMauro Carvalho Chehab struct cec_dmi_match {
2094be5e864SMauro Carvalho Chehab 	const char *sys_vendor;
2104be5e864SMauro Carvalho Chehab 	const char *product_name;
2114be5e864SMauro Carvalho Chehab 	const char *devname;
2124be5e864SMauro Carvalho Chehab 	const char *conn;
2134be5e864SMauro Carvalho Chehab };
2144be5e864SMauro Carvalho Chehab 
2154be5e864SMauro Carvalho Chehab static const struct cec_dmi_match cec_dmi_match_table[] = {
2164be5e864SMauro Carvalho Chehab 	/* Google Fizz */
2174be5e864SMauro Carvalho Chehab 	{ "Google", "Fizz", "0000:00:02.0", "Port B" },
2184be5e864SMauro Carvalho Chehab };
2194be5e864SMauro Carvalho Chehab 
2204be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
2214be5e864SMauro Carvalho Chehab 						const char **conn)
2224be5e864SMauro Carvalho Chehab {
2234be5e864SMauro Carvalho Chehab 	int i;
2244be5e864SMauro Carvalho Chehab 
2254be5e864SMauro Carvalho Chehab 	for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
2264be5e864SMauro Carvalho Chehab 		const struct cec_dmi_match *m = &cec_dmi_match_table[i];
2274be5e864SMauro Carvalho Chehab 
2284be5e864SMauro Carvalho Chehab 		if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
2294be5e864SMauro Carvalho Chehab 		    dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
2304be5e864SMauro Carvalho Chehab 			struct device *d;
2314be5e864SMauro Carvalho Chehab 
2324be5e864SMauro Carvalho Chehab 			/* Find the device, bail out if not yet registered */
2334be5e864SMauro Carvalho Chehab 			d = bus_find_device_by_name(&pci_bus_type, NULL,
2344be5e864SMauro Carvalho Chehab 						    m->devname);
2354be5e864SMauro Carvalho Chehab 			if (!d)
2364be5e864SMauro Carvalho Chehab 				return ERR_PTR(-EPROBE_DEFER);
2374be5e864SMauro Carvalho Chehab 			put_device(d);
2384be5e864SMauro Carvalho Chehab 			*conn = m->conn;
2394be5e864SMauro Carvalho Chehab 			return d;
2404be5e864SMauro Carvalho Chehab 		}
2414be5e864SMauro Carvalho Chehab 	}
2424be5e864SMauro Carvalho Chehab 
2434be5e864SMauro Carvalho Chehab 	/* Hardware support must be added in the cec_dmi_match_table */
2444be5e864SMauro Carvalho Chehab 	dev_warn(dev, "CEC notifier not configured for this hardware\n");
2454be5e864SMauro Carvalho Chehab 
2464be5e864SMauro Carvalho Chehab 	return ERR_PTR(-ENODEV);
2474be5e864SMauro Carvalho Chehab }
2484be5e864SMauro Carvalho Chehab 
2494be5e864SMauro Carvalho Chehab #else
2504be5e864SMauro Carvalho Chehab 
2514be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
2524be5e864SMauro Carvalho Chehab 						const char **conn)
2534be5e864SMauro Carvalho Chehab {
2544be5e864SMauro Carvalho Chehab 	return ERR_PTR(-ENODEV);
2554be5e864SMauro Carvalho Chehab }
2564be5e864SMauro Carvalho Chehab 
2574be5e864SMauro Carvalho Chehab #endif
2584be5e864SMauro Carvalho Chehab 
2594be5e864SMauro Carvalho Chehab static int cros_ec_cec_probe(struct platform_device *pdev)
2604be5e864SMauro Carvalho Chehab {
2614be5e864SMauro Carvalho Chehab 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
2624be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec = ec_dev->ec_dev;
2634be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec;
2644be5e864SMauro Carvalho Chehab 	struct device *hdmi_dev;
2654be5e864SMauro Carvalho Chehab 	const char *conn = NULL;
2664be5e864SMauro Carvalho Chehab 	int ret;
2674be5e864SMauro Carvalho Chehab 
2684be5e864SMauro Carvalho Chehab 	hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conn);
2694be5e864SMauro Carvalho Chehab 	if (IS_ERR(hdmi_dev))
2704be5e864SMauro Carvalho Chehab 		return PTR_ERR(hdmi_dev);
2714be5e864SMauro Carvalho Chehab 
2724be5e864SMauro Carvalho Chehab 	cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
2734be5e864SMauro Carvalho Chehab 				   GFP_KERNEL);
2744be5e864SMauro Carvalho Chehab 	if (!cros_ec_cec)
2754be5e864SMauro Carvalho Chehab 		return -ENOMEM;
2764be5e864SMauro Carvalho Chehab 
2774be5e864SMauro Carvalho Chehab 	platform_set_drvdata(pdev, cros_ec_cec);
2784be5e864SMauro Carvalho Chehab 	cros_ec_cec->cros_ec = cros_ec;
2794be5e864SMauro Carvalho Chehab 
280*6f01dfb7SDariusz Marcinkiewicz 	device_init_wakeup(&pdev->dev, 1);
2814be5e864SMauro Carvalho Chehab 
2824be5e864SMauro Carvalho Chehab 	cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
2834be5e864SMauro Carvalho Chehab 						 DRV_NAME,
2844be5e864SMauro Carvalho Chehab 						 CEC_CAP_DEFAULTS |
2854be5e864SMauro Carvalho Chehab 						 CEC_CAP_CONNECTOR_INFO, 1);
2864be5e864SMauro Carvalho Chehab 	if (IS_ERR(cros_ec_cec->adap))
2874be5e864SMauro Carvalho Chehab 		return PTR_ERR(cros_ec_cec->adap);
2884be5e864SMauro Carvalho Chehab 
2894be5e864SMauro Carvalho Chehab 	cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
2904be5e864SMauro Carvalho Chehab 							     cros_ec_cec->adap);
2914be5e864SMauro Carvalho Chehab 	if (!cros_ec_cec->notify) {
2924be5e864SMauro Carvalho Chehab 		ret = -ENOMEM;
2934be5e864SMauro Carvalho Chehab 		goto out_probe_adapter;
2944be5e864SMauro Carvalho Chehab 	}
2954be5e864SMauro Carvalho Chehab 
2964be5e864SMauro Carvalho Chehab 	/* Get CEC events from the EC. */
2974be5e864SMauro Carvalho Chehab 	cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
2984be5e864SMauro Carvalho Chehab 	ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
2994be5e864SMauro Carvalho Chehab 					       &cros_ec_cec->notifier);
3004be5e864SMauro Carvalho Chehab 	if (ret) {
3014be5e864SMauro Carvalho Chehab 		dev_err(&pdev->dev, "failed to register notifier\n");
3024be5e864SMauro Carvalho Chehab 		goto out_probe_notify;
3034be5e864SMauro Carvalho Chehab 	}
3044be5e864SMauro Carvalho Chehab 
3054be5e864SMauro Carvalho Chehab 	ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
3064be5e864SMauro Carvalho Chehab 	if (ret < 0)
3074be5e864SMauro Carvalho Chehab 		goto out_probe_notify;
3084be5e864SMauro Carvalho Chehab 
3094be5e864SMauro Carvalho Chehab 	return 0;
3104be5e864SMauro Carvalho Chehab 
3114be5e864SMauro Carvalho Chehab out_probe_notify:
3124be5e864SMauro Carvalho Chehab 	cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
3134be5e864SMauro Carvalho Chehab 					 cros_ec_cec->adap);
3144be5e864SMauro Carvalho Chehab out_probe_adapter:
3154be5e864SMauro Carvalho Chehab 	cec_delete_adapter(cros_ec_cec->adap);
3164be5e864SMauro Carvalho Chehab 	return ret;
3174be5e864SMauro Carvalho Chehab }
3184be5e864SMauro Carvalho Chehab 
3194be5e864SMauro Carvalho Chehab static int cros_ec_cec_remove(struct platform_device *pdev)
3204be5e864SMauro Carvalho Chehab {
3214be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
3224be5e864SMauro Carvalho Chehab 	struct device *dev = &pdev->dev;
3234be5e864SMauro Carvalho Chehab 	int ret;
3244be5e864SMauro Carvalho Chehab 
3254be5e864SMauro Carvalho Chehab 	ret = blocking_notifier_chain_unregister(
3264be5e864SMauro Carvalho Chehab 			&cros_ec_cec->cros_ec->event_notifier,
3274be5e864SMauro Carvalho Chehab 			&cros_ec_cec->notifier);
3284be5e864SMauro Carvalho Chehab 
3294be5e864SMauro Carvalho Chehab 	if (ret) {
3304be5e864SMauro Carvalho Chehab 		dev_err(dev, "failed to unregister notifier\n");
3314be5e864SMauro Carvalho Chehab 		return ret;
3324be5e864SMauro Carvalho Chehab 	}
3334be5e864SMauro Carvalho Chehab 
3344be5e864SMauro Carvalho Chehab 	cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
3354be5e864SMauro Carvalho Chehab 					 cros_ec_cec->adap);
3364be5e864SMauro Carvalho Chehab 	cec_unregister_adapter(cros_ec_cec->adap);
3374be5e864SMauro Carvalho Chehab 
3384be5e864SMauro Carvalho Chehab 	return 0;
3394be5e864SMauro Carvalho Chehab }
3404be5e864SMauro Carvalho Chehab 
3414be5e864SMauro Carvalho Chehab static struct platform_driver cros_ec_cec_driver = {
3424be5e864SMauro Carvalho Chehab 	.probe = cros_ec_cec_probe,
3434be5e864SMauro Carvalho Chehab 	.remove  = cros_ec_cec_remove,
3444be5e864SMauro Carvalho Chehab 	.driver = {
3454be5e864SMauro Carvalho Chehab 		.name = DRV_NAME,
3464be5e864SMauro Carvalho Chehab 		.pm = &cros_ec_cec_pm_ops,
3474be5e864SMauro Carvalho Chehab 	},
3484be5e864SMauro Carvalho Chehab };
3494be5e864SMauro Carvalho Chehab 
3504be5e864SMauro Carvalho Chehab module_platform_driver(cros_ec_cec_driver);
3514be5e864SMauro Carvalho Chehab 
3524be5e864SMauro Carvalho Chehab MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
3534be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
3544be5e864SMauro Carvalho Chehab MODULE_LICENSE("GPL");
3554be5e864SMauro Carvalho Chehab MODULE_ALIAS("platform:" DRV_NAME);
356