1 // SPDX-License-Identifier: GPL-2.0+
3 * CEC driver for ChromeOS Embedded Controller
5 * Copyright (c) 2018 BayLibre, SAS
6 * Author: Neil Armstrong <narmstrong@baylibre.com>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/dmi.h>
13 #include <linux/pci.h>
14 #include <linux/cec.h>
15 #include <linux/slab.h>
16 #include <linux/interrupt.h>
17 #include <media/cec.h>
18 #include <media/cec-notifier.h>
19 #include <linux/mfd/cros_ec.h>
20 #include <linux/mfd/cros_ec_commands.h>
22 #define DRV_NAME "cros-ec-cec"
25 * struct cros_ec_cec - Driver data for EC CEC
27 * @cros_ec: Pointer to EC device
28 * @notifier: Notifier info for responding to EC events
30 * @notify: CEC notifier pointer
31 * @rx_msg: storage for a received message
34 struct cros_ec_device
*cros_ec
;
35 struct notifier_block notifier
;
36 struct cec_adapter
*adap
;
37 struct cec_notifier
*notify
;
38 struct cec_msg rx_msg
;
41 static void handle_cec_message(struct cros_ec_cec
*cros_ec_cec
)
43 struct cros_ec_device
*cros_ec
= cros_ec_cec
->cros_ec
;
44 uint8_t *cec_message
= cros_ec
->event_data
.data
.cec_message
;
45 unsigned int len
= cros_ec
->event_size
;
47 cros_ec_cec
->rx_msg
.len
= len
;
48 memcpy(cros_ec_cec
->rx_msg
.msg
, cec_message
, len
);
50 cec_received_msg(cros_ec_cec
->adap
, &cros_ec_cec
->rx_msg
);
53 static void handle_cec_event(struct cros_ec_cec
*cros_ec_cec
)
55 struct cros_ec_device
*cros_ec
= cros_ec_cec
->cros_ec
;
56 uint32_t events
= cros_ec
->event_data
.data
.cec_events
;
58 if (events
& EC_MKBP_CEC_SEND_OK
)
59 cec_transmit_attempt_done(cros_ec_cec
->adap
,
62 /* FW takes care of all retries, tell core to avoid more retries */
63 if (events
& EC_MKBP_CEC_SEND_FAILED
)
64 cec_transmit_attempt_done(cros_ec_cec
->adap
,
65 CEC_TX_STATUS_MAX_RETRIES
|
69 static int cros_ec_cec_event(struct notifier_block
*nb
,
70 unsigned long queued_during_suspend
,
73 struct cros_ec_cec
*cros_ec_cec
;
74 struct cros_ec_device
*cros_ec
;
76 cros_ec_cec
= container_of(nb
, struct cros_ec_cec
, notifier
);
77 cros_ec
= cros_ec_cec
->cros_ec
;
79 if (cros_ec
->event_data
.event_type
== EC_MKBP_EVENT_CEC_EVENT
) {
80 handle_cec_event(cros_ec_cec
);
84 if (cros_ec
->event_data
.event_type
== EC_MKBP_EVENT_CEC_MESSAGE
) {
85 handle_cec_message(cros_ec_cec
);
92 static int cros_ec_cec_set_log_addr(struct cec_adapter
*adap
, u8 logical_addr
)
94 struct cros_ec_cec
*cros_ec_cec
= adap
->priv
;
95 struct cros_ec_device
*cros_ec
= cros_ec_cec
->cros_ec
;
97 struct cros_ec_command msg
;
98 struct ec_params_cec_set data
;
102 msg
.msg
.command
= EC_CMD_CEC_SET
;
103 msg
.msg
.outsize
= sizeof(msg
.data
);
104 msg
.data
.cmd
= CEC_CMD_LOGICAL_ADDRESS
;
105 msg
.data
.val
= logical_addr
;
107 ret
= cros_ec_cmd_xfer_status(cros_ec
, &msg
.msg
);
109 dev_err(cros_ec
->dev
,
110 "error setting CEC logical address on EC: %d\n", ret
);
117 static int cros_ec_cec_transmit(struct cec_adapter
*adap
, u8 attempts
,
118 u32 signal_free_time
, struct cec_msg
*cec_msg
)
120 struct cros_ec_cec
*cros_ec_cec
= adap
->priv
;
121 struct cros_ec_device
*cros_ec
= cros_ec_cec
->cros_ec
;
123 struct cros_ec_command msg
;
124 struct ec_params_cec_write data
;
128 msg
.msg
.command
= EC_CMD_CEC_WRITE_MSG
;
129 msg
.msg
.outsize
= cec_msg
->len
;
130 memcpy(msg
.data
.msg
, cec_msg
->msg
, cec_msg
->len
);
132 ret
= cros_ec_cmd_xfer_status(cros_ec
, &msg
.msg
);
134 dev_err(cros_ec
->dev
,
135 "error writing CEC msg on EC: %d\n", ret
);
142 static int cros_ec_cec_adap_enable(struct cec_adapter
*adap
, bool enable
)
144 struct cros_ec_cec
*cros_ec_cec
= adap
->priv
;
145 struct cros_ec_device
*cros_ec
= cros_ec_cec
->cros_ec
;
147 struct cros_ec_command msg
;
148 struct ec_params_cec_set data
;
152 msg
.msg
.command
= EC_CMD_CEC_SET
;
153 msg
.msg
.outsize
= sizeof(msg
.data
);
154 msg
.data
.cmd
= CEC_CMD_ENABLE
;
155 msg
.data
.val
= enable
;
157 ret
= cros_ec_cmd_xfer_status(cros_ec
, &msg
.msg
);
159 dev_err(cros_ec
->dev
,
160 "error %sabling CEC on EC: %d\n",
161 (enable
? "en" : "dis"), ret
);
168 static const struct cec_adap_ops cros_ec_cec_ops
= {
169 .adap_enable
= cros_ec_cec_adap_enable
,
170 .adap_log_addr
= cros_ec_cec_set_log_addr
,
171 .adap_transmit
= cros_ec_cec_transmit
,
174 #ifdef CONFIG_PM_SLEEP
175 static int cros_ec_cec_suspend(struct device
*dev
)
177 struct platform_device
*pdev
= to_platform_device(dev
);
178 struct cros_ec_cec
*cros_ec_cec
= dev_get_drvdata(&pdev
->dev
);
180 if (device_may_wakeup(dev
))
181 enable_irq_wake(cros_ec_cec
->cros_ec
->irq
);
186 static int cros_ec_cec_resume(struct device
*dev
)
188 struct platform_device
*pdev
= to_platform_device(dev
);
189 struct cros_ec_cec
*cros_ec_cec
= dev_get_drvdata(&pdev
->dev
);
191 if (device_may_wakeup(dev
))
192 disable_irq_wake(cros_ec_cec
->cros_ec
->irq
);
198 static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops
,
199 cros_ec_cec_suspend
, cros_ec_cec_resume
);
201 #if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
204 * The Firmware only handles a single CEC interface tied to a single HDMI
205 * connector we specify along with the DRM device name handling the HDMI output
208 struct cec_dmi_match
{
215 static const struct cec_dmi_match cec_dmi_match_table
[] = {
217 { "Google", "Fizz", "0000:00:02.0", "Port B" },
220 static int cros_ec_cec_get_notifier(struct device
*dev
,
221 struct cec_notifier
**notify
)
225 for (i
= 0 ; i
< ARRAY_SIZE(cec_dmi_match_table
) ; ++i
) {
226 const struct cec_dmi_match
*m
= &cec_dmi_match_table
[i
];
228 if (dmi_match(DMI_SYS_VENDOR
, m
->sys_vendor
) &&
229 dmi_match(DMI_PRODUCT_NAME
, m
->product_name
)) {
232 /* Find the device, bail out if not yet registered */
233 d
= bus_find_device_by_name(&pci_bus_type
, NULL
,
236 return -EPROBE_DEFER
;
238 *notify
= cec_notifier_get_conn(d
, m
->conn
);
243 /* Hardware support must be added in the cec_dmi_match_table */
244 dev_warn(dev
, "CEC notifier not configured for this hardware\n");
251 static int cros_ec_cec_get_notifier(struct device
*dev
,
252 struct cec_notifier
**notify
)
259 static int cros_ec_cec_probe(struct platform_device
*pdev
)
261 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(pdev
->dev
.parent
);
262 struct cros_ec_device
*cros_ec
= ec_dev
->ec_dev
;
263 struct cros_ec_cec
*cros_ec_cec
;
266 cros_ec_cec
= devm_kzalloc(&pdev
->dev
, sizeof(*cros_ec_cec
),
271 platform_set_drvdata(pdev
, cros_ec_cec
);
272 cros_ec_cec
->cros_ec
= cros_ec
;
274 ret
= cros_ec_cec_get_notifier(&pdev
->dev
, &cros_ec_cec
->notify
);
278 ret
= device_init_wakeup(&pdev
->dev
, 1);
280 dev_err(&pdev
->dev
, "failed to initialize wakeup\n");
284 cros_ec_cec
->adap
= cec_allocate_adapter(&cros_ec_cec_ops
, cros_ec_cec
,
285 DRV_NAME
, CEC_CAP_DEFAULTS
, 1);
286 if (IS_ERR(cros_ec_cec
->adap
))
287 return PTR_ERR(cros_ec_cec
->adap
);
289 /* Get CEC events from the EC. */
290 cros_ec_cec
->notifier
.notifier_call
= cros_ec_cec_event
;
291 ret
= blocking_notifier_chain_register(&cros_ec
->event_notifier
,
292 &cros_ec_cec
->notifier
);
294 dev_err(&pdev
->dev
, "failed to register notifier\n");
295 cec_delete_adapter(cros_ec_cec
->adap
);
299 ret
= cec_register_adapter(cros_ec_cec
->adap
, &pdev
->dev
);
301 cec_delete_adapter(cros_ec_cec
->adap
);
305 cec_register_cec_notifier(cros_ec_cec
->adap
, cros_ec_cec
->notify
);
310 static int cros_ec_cec_remove(struct platform_device
*pdev
)
312 struct cros_ec_cec
*cros_ec_cec
= platform_get_drvdata(pdev
);
313 struct device
*dev
= &pdev
->dev
;
316 ret
= blocking_notifier_chain_unregister(
317 &cros_ec_cec
->cros_ec
->event_notifier
,
318 &cros_ec_cec
->notifier
);
321 dev_err(dev
, "failed to unregister notifier\n");
325 cec_unregister_adapter(cros_ec_cec
->adap
);
327 if (cros_ec_cec
->notify
)
328 cec_notifier_put(cros_ec_cec
->notify
);
333 static struct platform_driver cros_ec_cec_driver
= {
334 .probe
= cros_ec_cec_probe
,
335 .remove
= cros_ec_cec_remove
,
338 .pm
= &cros_ec_cec_pm_ops
,
342 module_platform_driver(cros_ec_cec_driver
);
344 MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
345 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
346 MODULE_LICENSE("GPL");
347 MODULE_ALIAS("platform:" DRV_NAME
);