brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · 8fbbb40 Raw
598 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * CEC driver for ChromeOS Embedded Controller4 *5 * Copyright (c) 2018 BayLibre, SAS6 * 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 port27 *28 * @port_num: port number29 * @adap: CEC adapter30 * @notify: CEC notifier pointer31 * @rx_msg: storage for a received message32 * @cros_ec_cec: pointer to the parent struct33 */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 CEC44 *45 * @cros_ec: Pointer to EC device46 * @notifier: Notifier info for responding to EC events47 * @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG.48 * @num_ports: Number of CEC ports49 * @ports: Array of ports50 */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 a80	 *    cec_message MKBP event.81	 * 2. New EC firmware which supports multiple ports uses82	 *    EC_MKBP_CEC_HAVE_DATA to notify that data is ready and83	 *    EC_CMD_CEC_READ_MSG to read it.84	 * Check that the EC only has one CEC port, and then we can assume the85	 * 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_SLEEP255static 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#endif277 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 connector285 * corresponding to each CEC port. The order of connectors must match the order286 * in the EC (first connector is EC port 0, ...), and the number of connectors287 * must match the number of ports in the EC (which can be queried using the288 * 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_d_conns[] = { "Port D", NULL };302 303static const struct cec_dmi_match cec_dmi_match_table[] = {304	/* Google Fizz */305	{ "Google", "Fizz", "0000:00:02.0", port_b_conns },306	/* Google Brask */307	{ "Google", "Brask", "0000:00:02.0", port_b_conns },308	/* Google Moli */309	{ "Google", "Moli", "0000:00:02.0", port_b_conns },310	/* Google Kinox */311	{ "Google", "Kinox", "0000:00:02.0", port_b_conns },312	/* Google Kuldax */313	{ "Google", "Kuldax", "0000:00:02.0", port_b_conns },314	/* Google Aurash */315	{ "Google", "Aurash", "0000:00:02.0", port_b_conns },316	/* Google Gladios */317	{ "Google", "Gladios", "0000:00:02.0", port_b_conns },318	/* Google Lisbon */319	{ "Google", "Lisbon", "0000:00:02.0", port_b_conns },320	/* Google Dibbi */321	{ "Google", "Dibbi", "0000:00:02.0", port_db_conns },322	/* Google Constitution */323	{ "Google", "Constitution", "0000:00:02.0", port_ba_conns },324	/* Google Boxy */325	{ "Google", "Boxy", "0000:00:02.0", port_d_conns },326	/* Google Taranza */327	{ "Google", "Taranza", "0000:00:02.0", port_db_conns },328	/* Google Dexi */329	{ "Google", "Dexi", "0000:00:02.0", port_db_conns },330	/* Google Dita */331	{ "Google", "Dita", "0000:00:02.0", port_db_conns },332};333 334static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,335						const char * const **conns)336{337	int i;338 339	for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {340		const struct cec_dmi_match *m = &cec_dmi_match_table[i];341 342		if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&343		    dmi_match(DMI_PRODUCT_NAME, m->product_name)) {344			struct device *d;345 346			/* Find the device, bail out if not yet registered */347			d = bus_find_device_by_name(&pci_bus_type, NULL,348						    m->devname);349			if (!d)350				return ERR_PTR(-EPROBE_DEFER);351			put_device(d);352			*conns = m->conns;353			return d;354		}355	}356 357	/* Hardware support must be added in the cec_dmi_match_table */358	dev_warn(dev, "CEC notifier not configured for this hardware\n");359 360	return ERR_PTR(-ENODEV);361}362 363#else364 365static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,366						const char * const **conns)367{368	return ERR_PTR(-ENODEV);369}370 371#endif372 373static int cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec)374{375	struct ec_response_cec_port_count response;376	int ret;377 378	ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL,379			  0, &response, sizeof(response));380	if (ret < 0) {381		/*382		 * Old EC firmware only supports one port and does not support383		 * the port count command, so fall back to assuming one port.384		 */385		cros_ec_cec->num_ports = 1;386		return 0;387	}388 389	if (response.port_count == 0) {390		dev_err(cros_ec_cec->cros_ec->dev,391			"EC reports 0 CEC ports\n");392		return -ENODEV;393	}394 395	if (response.port_count > EC_CEC_MAX_PORTS) {396		dev_err(cros_ec_cec->cros_ec->dev,397			"EC reports too many ports: %d\n", response.port_count);398		return -EINVAL;399	}400 401	cros_ec_cec->num_ports = response.port_count;402	return 0;403}404 405static int cros_ec_cec_get_write_cmd_version(struct cros_ec_cec *cros_ec_cec)406{407	struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;408	struct ec_params_get_cmd_versions_v1 params = {409		.cmd = EC_CMD_CEC_WRITE_MSG,410	};411	struct ec_response_get_cmd_versions response;412	int ret;413 414	ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GET_CMD_VERSIONS, &params,415			  sizeof(params), &response, sizeof(response));416	if (ret < 0) {417		dev_err(cros_ec->dev,418			"error getting CEC write command version: %d\n", ret);419		return ret;420	}421 422	if (response.version_mask & EC_VER_MASK(1)) {423		cros_ec_cec->write_cmd_version = 1;424	} else {425		if (cros_ec_cec->num_ports != 1) {426			dev_err(cros_ec->dev,427				"v0 write command only supports 1 port, %d reported\n",428				cros_ec_cec->num_ports);429			return -EINVAL;430		}431		cros_ec_cec->write_cmd_version = 0;432	}433 434	return 0;435}436 437static int cros_ec_cec_init_port(struct device *dev,438				 struct cros_ec_cec *cros_ec_cec,439				 int port_num, struct device *hdmi_dev,440				 const char * const *conns)441{442	struct cros_ec_cec_port *port;443	int ret;444 445	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);446	if (!port)447		return -ENOMEM;448 449	port->cros_ec_cec = cros_ec_cec;450	port->port_num = port_num;451 452	port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME,453					  CEC_CAP_DEFAULTS |454					  CEC_CAP_CONNECTOR_INFO, 1);455	if (IS_ERR(port->adap))456		return PTR_ERR(port->adap);457 458	if (!conns[port_num]) {459		dev_err(dev, "no conn for port %d\n", port_num);460		ret = -ENODEV;461		goto out_probe_adapter;462	}463 464	port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num],465						      port->adap);466	if (!port->notify) {467		ret = -ENOMEM;468		goto out_probe_adapter;469	}470 471	ret = cec_register_adapter(port->adap, dev);472	if (ret < 0)473		goto out_probe_notify;474 475	cros_ec_cec->ports[port_num] = port;476 477	return 0;478 479out_probe_notify:480	cec_notifier_cec_adap_unregister(port->notify, port->adap);481out_probe_adapter:482	cec_delete_adapter(port->adap);483	return ret;484}485 486static int cros_ec_cec_probe(struct platform_device *pdev)487{488	struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);489	struct cros_ec_device *cros_ec = ec_dev->ec_dev;490	struct cros_ec_cec *cros_ec_cec;491	struct cros_ec_cec_port *port;492	struct device *hdmi_dev;493	const char * const *conns = NULL;494	int ret;495 496	hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conns);497	if (IS_ERR(hdmi_dev))498		return PTR_ERR(hdmi_dev);499 500	cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),501				   GFP_KERNEL);502	if (!cros_ec_cec)503		return -ENOMEM;504 505	platform_set_drvdata(pdev, cros_ec_cec);506	cros_ec_cec->cros_ec = cros_ec;507 508	device_init_wakeup(&pdev->dev, 1);509 510	ret = cros_ec_cec_get_num_ports(cros_ec_cec);511	if (ret)512		return ret;513 514	ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec);515	if (ret)516		return ret;517 518	for (int i = 0; i < cros_ec_cec->num_ports; i++) {519		ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i,520					    hdmi_dev, conns);521		if (ret)522			goto unregister_ports;523	}524 525	/* Get CEC events from the EC. */526	cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;527	ret = blocking_notifier_chain_register(&cros_ec->event_notifier,528					       &cros_ec_cec->notifier);529	if (ret) {530		dev_err(&pdev->dev, "failed to register notifier\n");531		goto unregister_ports;532	}533 534	return 0;535 536unregister_ports:537	/*538	 * Unregister any adapters which have been registered. We don't add the539	 * port to the array until the adapter has been registered successfully,540	 * so any non-NULL ports must have been registered.541	 */542	for (int i = 0; i < cros_ec_cec->num_ports; i++) {543		port = cros_ec_cec->ports[i];544		if (!port)545			break;546		cec_notifier_cec_adap_unregister(port->notify, port->adap);547		cec_unregister_adapter(port->adap);548	}549	return ret;550}551 552static void cros_ec_cec_remove(struct platform_device *pdev)553{554	struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);555	struct device *dev = &pdev->dev;556	struct cros_ec_cec_port *port;557	int ret;558 559	/*560	 * blocking_notifier_chain_unregister() only fails if the notifier isn't561	 * in the list. We know it was added to it by .probe(), so there should562	 * be no need for error checking. Be cautious and still check.563	 */564	ret = blocking_notifier_chain_unregister(565			&cros_ec_cec->cros_ec->event_notifier,566			&cros_ec_cec->notifier);567	if (ret)568		dev_err(dev, "failed to unregister notifier\n");569 570	for (int i = 0; i < cros_ec_cec->num_ports; i++) {571		port = cros_ec_cec->ports[i];572		cec_notifier_cec_adap_unregister(port->notify, port->adap);573		cec_unregister_adapter(port->adap);574	}575}576 577static const struct platform_device_id cros_ec_cec_id[] = {578	{ DRV_NAME, 0 },579	{}580};581MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);582 583static struct platform_driver cros_ec_cec_driver = {584	.probe = cros_ec_cec_probe,585	.remove_new = cros_ec_cec_remove,586	.driver = {587		.name = DRV_NAME,588		.pm = &cros_ec_cec_pm_ops,589	},590	.id_table = cros_ec_cec_id,591};592 593module_platform_driver(cros_ec_cec_driver);594 595MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");596MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");597MODULE_LICENSE("GPL");598