File Index
Files and directories
Sign in
FILE

cros-ec-cec.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/cec/platform/cros-ec/cros-ec-cec.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * CEC driver for ChromeOS Embedded Controller
  4 *
  5 * Copyright (c) 2018 BayLibre, SAS
  6 * Author: Neil Armstrong <narmstrong@baylibre.com>
  7 */
  8
  9#include <linux/kernel.h>
 10#include <linux/module.h>
 11#include <linux/mod_devicetable.h>
 12#include <linux/platform_device.h>
 13#include <linux/dmi.h>
 14#include <linux/pci.h>
 15#include <linux/cec.h>
 16#include <linux/slab.h>
 17#include <linux/interrupt.h>
 18#include <linux/platform_data/cros_ec_commands.h>
 19#include <linux/platform_data/cros_ec_proto.h>
 20#include <media/cec.h>
 21#include <media/cec-notifier.h>
 22
 23#define DRV_NAME	"cros-ec-cec"
 24
 25/**
 26 * struct cros_ec_cec_port - Driver data for a single EC CEC port
 27 *
 28 * @port_num: port number
 29 * @adap: CEC adapter
 30 * @notify: CEC notifier pointer
 31 * @rx_msg: storage for a received message
 32 * @cros_ec_cec: pointer to the parent struct
 33 */
 34struct cros_ec_cec_port {
 35	int port_num;
 36	struct cec_adapter *adap;
 37	struct cec_notifier *notify;
 38	struct cec_msg rx_msg;
 39	struct cros_ec_cec *cros_ec_cec;
 40};
 41
 42/**
 43 * struct cros_ec_cec - Driver data for EC CEC
 44 *
 45 * @cros_ec: Pointer to EC device
 46 * @notifier: Notifier info for responding to EC events
 47 * @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG.
 48 * @num_ports: Number of CEC ports
 49 * @ports: Array of ports
 50 */
 51struct cros_ec_cec {
 52	struct cros_ec_device *cros_ec;
 53	struct notifier_block notifier;
 54	int write_cmd_version;
 55	int num_ports;
 56	struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
 57};
 58
 59static void cros_ec_cec_received_message(struct cros_ec_cec_port *port,
 60					 uint8_t *msg, uint8_t len)
 61{
 62	if (len > CEC_MAX_MSG_SIZE)
 63		len = CEC_MAX_MSG_SIZE;
 64
 65	port->rx_msg.len = len;
 66	memcpy(port->rx_msg.msg, msg, len);
 67
 68	cec_received_msg(port->adap, &port->rx_msg);
 69}
 70
 71static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
 72{
 73	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
 74	uint8_t *cec_message = cros_ec->event_data.data.cec_message;
 75	unsigned int len = cros_ec->event_size;
 76	struct cros_ec_cec_port *port;
 77	/*
 78	 * There are two ways of receiving CEC messages:
 79	 * 1. Old EC firmware which only supports one port sends the data in a
 80	 *    cec_message MKBP event.
 81	 * 2. New EC firmware which supports multiple ports uses
 82	 *    EC_MKBP_CEC_HAVE_DATA to notify that data is ready and
 83	 *    EC_CMD_CEC_READ_MSG to read it.
 84	 * Check that the EC only has one CEC port, and then we can assume the
 85	 * message is from port 0.
 86	 */
 87	if (cros_ec_cec->num_ports != 1) {
 88		dev_err(cros_ec->dev,
 89			"received cec_message on device with %d ports\n",
 90			cros_ec_cec->num_ports);
 91		return;
 92	}
 93	port = cros_ec_cec->ports[0];
 94
 95	cros_ec_cec_received_message(port, cec_message, len);
 96}
 97
 98static void cros_ec_cec_read_message(struct cros_ec_cec_port *port)
 99{
100	struct cros_ec_device *cros_ec = port->cros_ec_cec->cros_ec;
101	struct ec_params_cec_read params = {
102		.port = port->port_num,
103	};
104	struct ec_response_cec_read response;
105	int ret;
106
107	ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_READ_MSG, &params,
108			  sizeof(params), &response, sizeof(response));
109	if (ret < 0) {
110		dev_err(cros_ec->dev,
111			"error reading CEC message on EC: %d\n", ret);
112		return;
113	}
114
115	cros_ec_cec_received_message(port, response.msg, response.msg_len);
116}
117
118static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
119{
120	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
121	uint32_t cec_events = cros_ec->event_data.data.cec_events;
122	uint32_t port_num = EC_MKBP_EVENT_CEC_GET_PORT(cec_events);
123	uint32_t events = EC_MKBP_EVENT_CEC_GET_EVENTS(cec_events);
124	struct cros_ec_cec_port *port;
125
126	if (port_num >= cros_ec_cec->num_ports) {
127		dev_err(cros_ec->dev,
128			"received CEC event for invalid port %d\n", port_num);
129		return;
130	}
131	port = cros_ec_cec->ports[port_num];
132
133	if (events & EC_MKBP_CEC_SEND_OK)
134		cec_transmit_attempt_done(port->adap, CEC_TX_STATUS_OK);
135
136	/* FW takes care of all retries, tell core to avoid more retries */
137	if (events & EC_MKBP_CEC_SEND_FAILED)
138		cec_transmit_attempt_done(port->adap,
139					  CEC_TX_STATUS_MAX_RETRIES |
140					  CEC_TX_STATUS_NACK);
141
142	if (events & EC_MKBP_CEC_HAVE_DATA)
143		cros_ec_cec_read_message(port);
144}
145
146static int cros_ec_cec_event(struct notifier_block *nb,
147			     unsigned long queued_during_suspend,
148			     void *_notify)
149{
150	struct cros_ec_cec *cros_ec_cec;
151	struct cros_ec_device *cros_ec;
152
153	cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
154	cros_ec = cros_ec_cec->cros_ec;
155
156	if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
157		handle_cec_event(cros_ec_cec);
158		return NOTIFY_OK;
159	}
160
161	if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
162		handle_cec_message(cros_ec_cec);
163		return NOTIFY_OK;
164	}
165
166	return NOTIFY_DONE;
167}
168
169static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
170{
171	struct cros_ec_cec_port *port = adap->priv;
172	struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
173	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
174	struct ec_params_cec_set params = {
175		.cmd = CEC_CMD_LOGICAL_ADDRESS,
176		.port = port->port_num,
177		.val = logical_addr,
178	};
179	int ret;
180
181	ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, &params, sizeof(params),
182			  NULL, 0);
183	if (ret < 0) {
184		dev_err(cros_ec->dev,
185			"error setting CEC logical address on EC: %d\n", ret);
186		return ret;
187	}
188
189	return 0;
190}
191
192static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
193				u32 signal_free_time, struct cec_msg *cec_msg)
194{
195	struct cros_ec_cec_port *port = adap->priv;
196	struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
197	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
198	struct ec_params_cec_write params;
199	struct ec_params_cec_write_v1 params_v1;
200	int ret;
201
202	if (cros_ec_cec->write_cmd_version == 0) {
203		memcpy(params.msg, cec_msg->msg, cec_msg->len);
204		ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_WRITE_MSG, &params,
205				  cec_msg->len, NULL, 0);
206	} else {
207		params_v1.port = port->port_num;
208		params_v1.msg_len = cec_msg->len;
209		memcpy(params_v1.msg, cec_msg->msg, cec_msg->len);
210		ret = cros_ec_cmd(cros_ec, cros_ec_cec->write_cmd_version,
211				  EC_CMD_CEC_WRITE_MSG, &params_v1,
212				  sizeof(params_v1), NULL, 0);
213	}
214
215	if (ret < 0) {
216		dev_err(cros_ec->dev,
217			"error writing CEC msg on EC: %d\n", ret);
218		return ret;
219	}
220
221	return 0;
222}
223
224static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
225{
226	struct cros_ec_cec_port *port = adap->priv;
227	struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
228	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
229	struct ec_params_cec_set params = {
230		.cmd = CEC_CMD_ENABLE,
231		.port = port->port_num,
232		.val = enable,
233	};
234	int ret;
235
236	ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, &params, sizeof(params),
237			  NULL, 0);
238	if (ret < 0) {
239		dev_err(cros_ec->dev,
240			"error %sabling CEC on EC: %d\n",
241			(enable ? "en" : "dis"), ret);
242		return ret;
243	}
244
245	return 0;
246}
247
248static const struct cec_adap_ops cros_ec_cec_ops = {
249	.adap_enable = cros_ec_cec_adap_enable,
250	.adap_log_addr = cros_ec_cec_set_log_addr,
251	.adap_transmit = cros_ec_cec_transmit,
252};
253
254#ifdef CONFIG_PM_SLEEP
255static int cros_ec_cec_suspend(struct device *dev)
256{
257	struct platform_device *pdev = to_platform_device(dev);
258	struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
259
260	if (device_may_wakeup(dev))
261		enable_irq_wake(cros_ec_cec->cros_ec->irq);
262
263	return 0;
264}
265
266static int cros_ec_cec_resume(struct device *dev)
267{
268	struct platform_device *pdev = to_platform_device(dev);
269	struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
270
271	if (device_may_wakeup(dev))
272		disable_irq_wake(cros_ec_cec->cros_ec->irq);
273
274	return 0;
275}
276#endif
277
278static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
279	cros_ec_cec_suspend, cros_ec_cec_resume);
280
281#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
282
283/*
284 * Specify the DRM device name handling the HDMI output and the HDMI connector
285 * corresponding to each CEC port. The order of connectors must match the order
286 * in the EC (first connector is EC port 0, ...), and the number of connectors
287 * must match the number of ports in the EC (which can be queried using the
288 * EC_CMD_CEC_PORT_COUNT host command).
289 */
290
291struct cec_dmi_match {
292	const char *sys_vendor;
293	const char *product_name;
294	const char *devname;
295	const char *const *conns;
296};
297
298static const char *const port_b_conns[] = { "Port B", NULL };
299static const char *const port_db_conns[] = { "Port D", "Port B", NULL };
300static const char *const port_ba_conns[] = { "Port B", "Port A", NULL };
301static const char *const port_ab_conns[] = { "Port A", "Port B", NULL };
302static const char *const port_d_conns[] = { "Port D", NULL };
303
304static const struct cec_dmi_match cec_dmi_match_table[] = {
305	/* Google Fizz */
306	{ "Google", "Fizz", "0000:00:02.0", port_b_conns },
307	/* Google Brask */
308	{ "Google", "Brask", "0000:00:02.0", port_b_conns },
309	/* Google Moli */
310	{ "Google", "Moli", "0000:00:02.0", port_b_conns },
311	/* Google Kinox */
312	{ "Google", "Kinox", "0000:00:02.0", port_b_conns },
313	/* Google Kuldax */
314	{ "Google", "Kuldax", "0000:00:02.0", port_b_conns },
315	/* Google Aurash */
316	{ "Google", "Aurash", "0000:00:02.0", port_b_conns },
317	/* Google Gladios */
318	{ "Google", "Gladios", "0000:00:02.0", port_b_conns },
319	/* Google Lisbon */
320	{ "Google", "Lisbon", "0000:00:02.0", port_b_conns },
321	/* Google Dibbi */
322	{ "Google", "Dibbi", "0000:00:02.0", port_db_conns },
323	/* Google Constitution */
324	{ "Google", "Constitution", "0000:00:02.0", port_ba_conns },
325	/* Google Boxy */
326	{ "Google", "Boxy", "0000:00:02.0", port_d_conns },
327	/* Google Taranza */
328	{ "Google", "Taranza", "0000:00:02.0", port_db_conns },
329	/* Google Dexi */
330	{ "Google", "Dexi", "0000:00:02.0", port_db_conns },
331	/* Google Dita */
332	{ "Google", "Dita", "0000:00:02.0", port_db_conns },
333	/* Google Dirks */
334	{ "Google", "Dirks", "0000:00:02.0", port_ab_conns },
335	/* Google Moxie */
336	{ "Google", "Moxie", "0000:00:02.0", port_b_conns },
337};
338
339static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
340						const char * const **conns)
341{
342	int i;
343
344	for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
345		const struct cec_dmi_match *m = &cec_dmi_match_table[i];
346
347		if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
348		    dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
349			struct device *d;
350
351			/* Find the device, bail out if not yet registered */
352			d = bus_find_device_by_name(&pci_bus_type, NULL,
353						    m->devname);
354			if (!d)
355				return ERR_PTR(-EPROBE_DEFER);
356			put_device(d);
357			*conns = m->conns;
358			return d;
359		}
360	}
361
362	/* Hardware support must be added in the cec_dmi_match_table */
363	dev_warn(dev, "CEC notifier not configured for this hardware\n");
364
365	return ERR_PTR(-ENODEV);
366}
367
368#else
369
370static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
371						const char * const **conns)
372{
373	return ERR_PTR(-ENODEV);
374}
375
376#endif
377
378static int cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec)
379{
380	struct ec_response_cec_port_count response;
381	int ret;
382
383	ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL,
384			  0, &response, sizeof(response));
385	if (ret < 0) {
386		/*
387		 * Old EC firmware only supports one port and does not support
388		 * the port count command, so fall back to assuming one port.
389		 */
390		cros_ec_cec->num_ports = 1;
391		return 0;
392	}
393
394	if (response.port_count == 0) {
395		dev_err(cros_ec_cec->cros_ec->dev,
396			"EC reports 0 CEC ports\n");
397		return -ENODEV;
398	}
399
400	if (response.port_count > EC_CEC_MAX_PORTS) {
401		dev_err(cros_ec_cec->cros_ec->dev,
402			"EC reports too many ports: %d\n", response.port_count);
403		return -EINVAL;
404	}
405
406	cros_ec_cec->num_ports = response.port_count;
407	return 0;
408}
409
410static int cros_ec_cec_get_write_cmd_version(struct cros_ec_cec *cros_ec_cec)
411{
412	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
413	struct ec_params_get_cmd_versions_v1 params = {
414		.cmd = EC_CMD_CEC_WRITE_MSG,
415	};
416	struct ec_response_get_cmd_versions response;
417	int ret;
418
419	ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GET_CMD_VERSIONS, &params,
420			  sizeof(params), &response, sizeof(response));
421	if (ret < 0) {
422		dev_err(cros_ec->dev,
423			"error getting CEC write command version: %d\n", ret);
424		return ret;
425	}
426
427	if (response.version_mask & EC_VER_MASK(1)) {
428		cros_ec_cec->write_cmd_version = 1;
429	} else {
430		if (cros_ec_cec->num_ports != 1) {
431			dev_err(cros_ec->dev,
432				"v0 write command only supports 1 port, %d reported\n",
433				cros_ec_cec->num_ports);
434			return -EINVAL;
435		}
436		cros_ec_cec->write_cmd_version = 0;
437	}
438
439	return 0;
440}
441
442static int cros_ec_cec_init_port(struct device *dev,
443				 struct cros_ec_cec *cros_ec_cec,
444				 int port_num, struct device *hdmi_dev,
445				 const char * const *conns)
446{
447	struct cros_ec_cec_port *port;
448	int ret;
449
450	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
451	if (!port)
452		return -ENOMEM;
453
454	port->cros_ec_cec = cros_ec_cec;
455	port->port_num = port_num;
456
457	port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME,
458					  CEC_CAP_DEFAULTS |
459					  CEC_CAP_CONNECTOR_INFO, 1);
460	if (IS_ERR(port->adap))
461		return PTR_ERR(port->adap);
462
463	if (!conns[port_num]) {
464		dev_err(dev, "no conn for port %d\n", port_num);
465		ret = -ENODEV;
466		goto out_probe_adapter;
467	}
468
469	port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num],
470						      port->adap);
471	if (!port->notify) {
472		ret = -ENOMEM;
473		goto out_probe_adapter;
474	}
475
476	ret = cec_register_adapter(port->adap, dev);
477	if (ret < 0)
478		goto out_probe_notify;
479
480	cros_ec_cec->ports[port_num] = port;
481
482	return 0;
483
484out_probe_notify:
485	cec_notifier_cec_adap_unregister(port->notify, port->adap);
486out_probe_adapter:
487	cec_delete_adapter(port->adap);
488	return ret;
489}
490
491static int cros_ec_cec_probe(struct platform_device *pdev)
492{
493	struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
494	struct cros_ec_device *cros_ec = ec_dev->ec_dev;
495	struct cros_ec_cec *cros_ec_cec;
496	struct cros_ec_cec_port *port;
497	struct device *hdmi_dev;
498	const char * const *conns = NULL;
499	int ret;
500
501	hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conns);
502	if (IS_ERR(hdmi_dev))
503		return PTR_ERR(hdmi_dev);
504
505	cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
506				   GFP_KERNEL);
507	if (!cros_ec_cec)
508		return -ENOMEM;
509
510	platform_set_drvdata(pdev, cros_ec_cec);
511	cros_ec_cec->cros_ec = cros_ec;
512
513	device_init_wakeup(&pdev->dev, 1);
514
515	ret = cros_ec_cec_get_num_ports(cros_ec_cec);
516	if (ret)
517		return ret;
518
519	ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec);
520	if (ret)
521		return ret;
522
523	for (int i = 0; i < cros_ec_cec->num_ports; i++) {
524		ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i,
525					    hdmi_dev, conns);
526		if (ret)
527			goto unregister_ports;
528	}
529
530	/* Get CEC events from the EC. */
531	cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
532	ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
533					       &cros_ec_cec->notifier);
534	if (ret) {
535		dev_err(&pdev->dev, "failed to register notifier\n");
536		goto unregister_ports;
537	}
538
539	return 0;
540
541unregister_ports:
542	/*
543	 * Unregister any adapters which have been registered. We don't add the
544	 * port to the array until the adapter has been registered successfully,
545	 * so any non-NULL ports must have been registered.
546	 */
547	for (int i = 0; i < cros_ec_cec->num_ports; i++) {
548		port = cros_ec_cec->ports[i];
549		if (!port)
550			break;
551		cec_notifier_cec_adap_unregister(port->notify, port->adap);
552		cec_unregister_adapter(port->adap);
553	}
554	return ret;
555}
556
557static void cros_ec_cec_remove(struct platform_device *pdev)
558{
559	struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
560	struct device *dev = &pdev->dev;
561	struct cros_ec_cec_port *port;
562	int ret;
563
564	/*
565	 * blocking_notifier_chain_unregister() only fails if the notifier isn't
566	 * in the list. We know it was added to it by .probe(), so there should
567	 * be no need for error checking. Be cautious and still check.
568	 */
569	ret = blocking_notifier_chain_unregister(
570			&cros_ec_cec->cros_ec->event_notifier,
571			&cros_ec_cec->notifier);
572	if (ret)
573		dev_err(dev, "failed to unregister notifier\n");
574
575	for (int i = 0; i < cros_ec_cec->num_ports; i++) {
576		port = cros_ec_cec->ports[i];
577		cec_notifier_cec_adap_unregister(port->notify, port->adap);
578		cec_unregister_adapter(port->adap);
579	}
580}
581
582static const struct platform_device_id cros_ec_cec_id[] = {
583	{ DRV_NAME, 0 },
584	{}
585};
586MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
587
588static struct platform_driver cros_ec_cec_driver = {
589	.probe = cros_ec_cec_probe,
590	.remove = cros_ec_cec_remove,
591	.driver = {
592		.name = DRV_NAME,
593		.pm = &cros_ec_cec_pm_ops,
594	},
595	.id_table = cros_ec_cec_id,
596};
597
598module_platform_driver(cros_ec_cec_driver);
599
600MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
601MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
602MODULE_LICENSE("GPL");
~/ Read-only file preview.