xref: /linux/drivers/media/cec/platform/cros-ec/cros-ec-cec.c (revision 594b6bdde2e7833a56413de5092b6e4188d33ff7)
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 
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 
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 
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 
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 
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 
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
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 
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" },
226*594b6bddSRory Liu 	/* Google Kuldax */
227*594b6bddSRory Liu 	{ "Google", "Kuldax", "0000:00:02.0", "Port B" },
2284be5e864SMauro Carvalho Chehab };
2294be5e864SMauro Carvalho Chehab 
2304be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
2314be5e864SMauro Carvalho Chehab 						const char **conn)
2324be5e864SMauro Carvalho Chehab {
2334be5e864SMauro Carvalho Chehab 	int i;
2344be5e864SMauro Carvalho Chehab 
2354be5e864SMauro Carvalho Chehab 	for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
2364be5e864SMauro Carvalho Chehab 		const struct cec_dmi_match *m = &cec_dmi_match_table[i];
2374be5e864SMauro Carvalho Chehab 
2384be5e864SMauro Carvalho Chehab 		if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
2394be5e864SMauro Carvalho Chehab 		    dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
2404be5e864SMauro Carvalho Chehab 			struct device *d;
2414be5e864SMauro Carvalho Chehab 
2424be5e864SMauro Carvalho Chehab 			/* Find the device, bail out if not yet registered */
2434be5e864SMauro Carvalho Chehab 			d = bus_find_device_by_name(&pci_bus_type, NULL,
2444be5e864SMauro Carvalho Chehab 						    m->devname);
2454be5e864SMauro Carvalho Chehab 			if (!d)
2464be5e864SMauro Carvalho Chehab 				return ERR_PTR(-EPROBE_DEFER);
2474be5e864SMauro Carvalho Chehab 			put_device(d);
2484be5e864SMauro Carvalho Chehab 			*conn = m->conn;
2494be5e864SMauro Carvalho Chehab 			return d;
2504be5e864SMauro Carvalho Chehab 		}
2514be5e864SMauro Carvalho Chehab 	}
2524be5e864SMauro Carvalho Chehab 
2534be5e864SMauro Carvalho Chehab 	/* Hardware support must be added in the cec_dmi_match_table */
2544be5e864SMauro Carvalho Chehab 	dev_warn(dev, "CEC notifier not configured for this hardware\n");
2554be5e864SMauro Carvalho Chehab 
2564be5e864SMauro Carvalho Chehab 	return ERR_PTR(-ENODEV);
2574be5e864SMauro Carvalho Chehab }
2584be5e864SMauro Carvalho Chehab 
2594be5e864SMauro Carvalho Chehab #else
2604be5e864SMauro Carvalho Chehab 
2614be5e864SMauro Carvalho Chehab static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
2624be5e864SMauro Carvalho Chehab 						const char **conn)
2634be5e864SMauro Carvalho Chehab {
2644be5e864SMauro Carvalho Chehab 	return ERR_PTR(-ENODEV);
2654be5e864SMauro Carvalho Chehab }
2664be5e864SMauro Carvalho Chehab 
2674be5e864SMauro Carvalho Chehab #endif
2684be5e864SMauro Carvalho Chehab 
2694be5e864SMauro Carvalho Chehab static int cros_ec_cec_probe(struct platform_device *pdev)
2704be5e864SMauro Carvalho Chehab {
2714be5e864SMauro Carvalho Chehab 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
2724be5e864SMauro Carvalho Chehab 	struct cros_ec_device *cros_ec = ec_dev->ec_dev;
2734be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec;
2744be5e864SMauro Carvalho Chehab 	struct device *hdmi_dev;
2754be5e864SMauro Carvalho Chehab 	const char *conn = NULL;
2764be5e864SMauro Carvalho Chehab 	int ret;
2774be5e864SMauro Carvalho Chehab 
2784be5e864SMauro Carvalho Chehab 	hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conn);
2794be5e864SMauro Carvalho Chehab 	if (IS_ERR(hdmi_dev))
2804be5e864SMauro Carvalho Chehab 		return PTR_ERR(hdmi_dev);
2814be5e864SMauro Carvalho Chehab 
2824be5e864SMauro Carvalho Chehab 	cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
2834be5e864SMauro Carvalho Chehab 				   GFP_KERNEL);
2844be5e864SMauro Carvalho Chehab 	if (!cros_ec_cec)
2854be5e864SMauro Carvalho Chehab 		return -ENOMEM;
2864be5e864SMauro Carvalho Chehab 
2874be5e864SMauro Carvalho Chehab 	platform_set_drvdata(pdev, cros_ec_cec);
2884be5e864SMauro Carvalho Chehab 	cros_ec_cec->cros_ec = cros_ec;
2894be5e864SMauro Carvalho Chehab 
2906f01dfb7SDariusz Marcinkiewicz 	device_init_wakeup(&pdev->dev, 1);
2914be5e864SMauro Carvalho Chehab 
2924be5e864SMauro Carvalho Chehab 	cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
2934be5e864SMauro Carvalho Chehab 						 DRV_NAME,
2944be5e864SMauro Carvalho Chehab 						 CEC_CAP_DEFAULTS |
2954be5e864SMauro Carvalho Chehab 						 CEC_CAP_CONNECTOR_INFO, 1);
2964be5e864SMauro Carvalho Chehab 	if (IS_ERR(cros_ec_cec->adap))
2974be5e864SMauro Carvalho Chehab 		return PTR_ERR(cros_ec_cec->adap);
2984be5e864SMauro Carvalho Chehab 
2994be5e864SMauro Carvalho Chehab 	cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
3004be5e864SMauro Carvalho Chehab 							     cros_ec_cec->adap);
3014be5e864SMauro Carvalho Chehab 	if (!cros_ec_cec->notify) {
3024be5e864SMauro Carvalho Chehab 		ret = -ENOMEM;
3034be5e864SMauro Carvalho Chehab 		goto out_probe_adapter;
3044be5e864SMauro Carvalho Chehab 	}
3054be5e864SMauro Carvalho Chehab 
3064be5e864SMauro Carvalho Chehab 	/* Get CEC events from the EC. */
3074be5e864SMauro Carvalho Chehab 	cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
3084be5e864SMauro Carvalho Chehab 	ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
3094be5e864SMauro Carvalho Chehab 					       &cros_ec_cec->notifier);
3104be5e864SMauro Carvalho Chehab 	if (ret) {
3114be5e864SMauro Carvalho Chehab 		dev_err(&pdev->dev, "failed to register notifier\n");
3124be5e864SMauro Carvalho Chehab 		goto out_probe_notify;
3134be5e864SMauro Carvalho Chehab 	}
3144be5e864SMauro Carvalho Chehab 
3154be5e864SMauro Carvalho Chehab 	ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
3164be5e864SMauro Carvalho Chehab 	if (ret < 0)
3174be5e864SMauro Carvalho Chehab 		goto out_probe_notify;
3184be5e864SMauro Carvalho Chehab 
3194be5e864SMauro Carvalho Chehab 	return 0;
3204be5e864SMauro Carvalho Chehab 
3214be5e864SMauro Carvalho Chehab out_probe_notify:
3224be5e864SMauro Carvalho Chehab 	cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
3234be5e864SMauro Carvalho Chehab 					 cros_ec_cec->adap);
3244be5e864SMauro Carvalho Chehab out_probe_adapter:
3254be5e864SMauro Carvalho Chehab 	cec_delete_adapter(cros_ec_cec->adap);
3264be5e864SMauro Carvalho Chehab 	return ret;
3274be5e864SMauro Carvalho Chehab }
3284be5e864SMauro Carvalho Chehab 
3294be5e864SMauro Carvalho Chehab static int cros_ec_cec_remove(struct platform_device *pdev)
3304be5e864SMauro Carvalho Chehab {
3314be5e864SMauro Carvalho Chehab 	struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
3324be5e864SMauro Carvalho Chehab 	struct device *dev = &pdev->dev;
3334be5e864SMauro Carvalho Chehab 	int ret;
3344be5e864SMauro Carvalho Chehab 
3354be5e864SMauro Carvalho Chehab 	ret = blocking_notifier_chain_unregister(
3364be5e864SMauro Carvalho Chehab 			&cros_ec_cec->cros_ec->event_notifier,
3374be5e864SMauro Carvalho Chehab 			&cros_ec_cec->notifier);
3384be5e864SMauro Carvalho Chehab 
3394be5e864SMauro Carvalho Chehab 	if (ret) {
3404be5e864SMauro Carvalho Chehab 		dev_err(dev, "failed to unregister notifier\n");
3414be5e864SMauro Carvalho Chehab 		return ret;
3424be5e864SMauro Carvalho Chehab 	}
3434be5e864SMauro Carvalho Chehab 
3444be5e864SMauro Carvalho Chehab 	cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
3454be5e864SMauro Carvalho Chehab 					 cros_ec_cec->adap);
3464be5e864SMauro Carvalho Chehab 	cec_unregister_adapter(cros_ec_cec->adap);
3474be5e864SMauro Carvalho Chehab 
3484be5e864SMauro Carvalho Chehab 	return 0;
3494be5e864SMauro Carvalho Chehab }
3504be5e864SMauro Carvalho Chehab 
3514be5e864SMauro Carvalho Chehab static struct platform_driver cros_ec_cec_driver = {
3524be5e864SMauro Carvalho Chehab 	.probe = cros_ec_cec_probe,
3534be5e864SMauro Carvalho Chehab 	.remove  = cros_ec_cec_remove,
3544be5e864SMauro Carvalho Chehab 	.driver = {
3554be5e864SMauro Carvalho Chehab 		.name = DRV_NAME,
3564be5e864SMauro Carvalho Chehab 		.pm = &cros_ec_cec_pm_ops,
3574be5e864SMauro Carvalho Chehab 	},
3584be5e864SMauro Carvalho Chehab };
3594be5e864SMauro Carvalho Chehab 
3604be5e864SMauro Carvalho Chehab module_platform_driver(cros_ec_cec_driver);
3614be5e864SMauro Carvalho Chehab 
3624be5e864SMauro Carvalho Chehab MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
3634be5e864SMauro Carvalho Chehab MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
3644be5e864SMauro Carvalho Chehab MODULE_LICENSE("GPL");
3654be5e864SMauro Carvalho Chehab MODULE_ALIAS("platform:" DRV_NAME);
366