brintos

brintos / linux-shallow public Read only

0
0
Text · 24.3 KiB · 171366f Raw
925 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Pulse Eight HDMI CEC driver4 *5 * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl6 */7 8/*9 * Notes:10 *11 * - Devices with firmware version < 2 do not store their configuration in12 *   EEPROM.13 *14 * - In autonomous mode, only messages from a TV will be acknowledged, even15 *   polling messages. Upon receiving a message from a TV, the dongle will16 *   respond to messages from any logical address.17 *18 * - In autonomous mode, the dongle will by default reply Feature Abort19 *   [Unrecognized Opcode] when it receives Give Device Vendor ID. It will20 *   however observe vendor ID's reported by other devices and possibly21 *   alter this behavior. When TV's (and TV's only) report that their vendor ID22 *   is LG (0x00e091), the dongle will itself reply that it has the same vendor23 *   ID, and it will respond to at least one vendor specific command.24 *25 * - In autonomous mode, the dongle is known to attempt wakeup if it receives26 *   <User Control Pressed> ["Power On"], ["Power] or ["Power Toggle"], or if it27 *   receives <Set Stream Path> with its own physical address. It also does this28 *   if it receives <Vendor Specific Command> [0x03 0x00] from an LG TV.29 */30 31#include <linux/completion.h>32#include <linux/init.h>33#include <linux/interrupt.h>34#include <linux/kernel.h>35#include <linux/module.h>36#include <linux/workqueue.h>37#include <linux/serio.h>38#include <linux/slab.h>39#include <linux/time.h>40#include <linux/delay.h>41 42#include <media/cec.h>43 44MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");45MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");46MODULE_LICENSE("GPL");47 48static int debug;49static int persistent_config;50module_param(debug, int, 0644);51module_param(persistent_config, int, 0644);52MODULE_PARM_DESC(debug, "debug level (0-2)");53MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)");54 55enum pulse8_msgcodes {56	MSGCODE_NOTHING = 0,57	MSGCODE_PING,58	MSGCODE_TIMEOUT_ERROR,59	MSGCODE_HIGH_ERROR,60	MSGCODE_LOW_ERROR,61	MSGCODE_FRAME_START,62	MSGCODE_FRAME_DATA,63	MSGCODE_RECEIVE_FAILED,64	MSGCODE_COMMAND_ACCEPTED,	/* 0x08 */65	MSGCODE_COMMAND_REJECTED,66	MSGCODE_SET_ACK_MASK,67	MSGCODE_TRANSMIT,68	MSGCODE_TRANSMIT_EOM,69	MSGCODE_TRANSMIT_IDLETIME,70	MSGCODE_TRANSMIT_ACK_POLARITY,71	MSGCODE_TRANSMIT_LINE_TIMEOUT,72	MSGCODE_TRANSMIT_SUCCEEDED,	/* 0x10 */73	MSGCODE_TRANSMIT_FAILED_LINE,74	MSGCODE_TRANSMIT_FAILED_ACK,75	MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,76	MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,77	MSGCODE_FIRMWARE_VERSION,78	MSGCODE_START_BOOTLOADER,79	MSGCODE_GET_BUILDDATE,80	MSGCODE_SET_CONTROLLED,		/* 0x18 */81	MSGCODE_GET_AUTO_ENABLED,82	MSGCODE_SET_AUTO_ENABLED,83	MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,84	MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,85	MSGCODE_GET_LOGICAL_ADDRESS_MASK,86	MSGCODE_SET_LOGICAL_ADDRESS_MASK,87	MSGCODE_GET_PHYSICAL_ADDRESS,88	MSGCODE_SET_PHYSICAL_ADDRESS,	/* 0x20 */89	MSGCODE_GET_DEVICE_TYPE,90	MSGCODE_SET_DEVICE_TYPE,91	MSGCODE_GET_HDMI_VERSION,	/* Removed in FW >= 10 */92	MSGCODE_SET_HDMI_VERSION,93	MSGCODE_GET_OSD_NAME,94	MSGCODE_SET_OSD_NAME,95	MSGCODE_WRITE_EEPROM,96	MSGCODE_GET_ADAPTER_TYPE,	/* 0x28 */97	MSGCODE_SET_ACTIVE_SOURCE,98	MSGCODE_GET_AUTO_POWER_ON,	/* New for FW >= 10 */99	MSGCODE_SET_AUTO_POWER_ON,100 101	MSGCODE_FRAME_EOM = 0x80,102	MSGCODE_FRAME_ACK = 0x40,103};104 105static const char * const pulse8_msgnames[] = {106	"NOTHING",107	"PING",108	"TIMEOUT_ERROR",109	"HIGH_ERROR",110	"LOW_ERROR",111	"FRAME_START",112	"FRAME_DATA",113	"RECEIVE_FAILED",114	"COMMAND_ACCEPTED",115	"COMMAND_REJECTED",116	"SET_ACK_MASK",117	"TRANSMIT",118	"TRANSMIT_EOM",119	"TRANSMIT_IDLETIME",120	"TRANSMIT_ACK_POLARITY",121	"TRANSMIT_LINE_TIMEOUT",122	"TRANSMIT_SUCCEEDED",123	"TRANSMIT_FAILED_LINE",124	"TRANSMIT_FAILED_ACK",125	"TRANSMIT_FAILED_TIMEOUT_DATA",126	"TRANSMIT_FAILED_TIMEOUT_LINE",127	"FIRMWARE_VERSION",128	"START_BOOTLOADER",129	"GET_BUILDDATE",130	"SET_CONTROLLED",131	"GET_AUTO_ENABLED",132	"SET_AUTO_ENABLED",133	"GET_DEFAULT_LOGICAL_ADDRESS",134	"SET_DEFAULT_LOGICAL_ADDRESS",135	"GET_LOGICAL_ADDRESS_MASK",136	"SET_LOGICAL_ADDRESS_MASK",137	"GET_PHYSICAL_ADDRESS",138	"SET_PHYSICAL_ADDRESS",139	"GET_DEVICE_TYPE",140	"SET_DEVICE_TYPE",141	"GET_HDMI_VERSION",142	"SET_HDMI_VERSION",143	"GET_OSD_NAME",144	"SET_OSD_NAME",145	"WRITE_EEPROM",146	"GET_ADAPTER_TYPE",147	"SET_ACTIVE_SOURCE",148	"GET_AUTO_POWER_ON",149	"SET_AUTO_POWER_ON",150};151 152static const char *pulse8_msgname(u8 cmd)153{154	static char unknown_msg[5];155 156	if ((cmd & 0x3f) < ARRAY_SIZE(pulse8_msgnames))157		return pulse8_msgnames[cmd & 0x3f];158	snprintf(unknown_msg, sizeof(unknown_msg), "0x%02x", cmd);159	return unknown_msg;160}161 162#define MSGSTART	0xff163#define MSGEND		0xfe164#define MSGESC		0xfd165#define MSGOFFSET	3166 167#define DATA_SIZE 256168 169#define PING_PERIOD	(15 * HZ)170 171#define NUM_MSGS 8172 173struct pulse8 {174	struct device *dev;175	struct serio *serio;176	struct cec_adapter *adap;177	unsigned int vers;178 179	struct delayed_work ping_eeprom_work;180 181	struct work_struct irq_work;182	struct cec_msg rx_msg[NUM_MSGS];183	unsigned int rx_msg_cur_idx, rx_msg_num;184	/* protect rx_msg_cur_idx and rx_msg_num */185	spinlock_t msg_lock;186	u8 new_rx_msg[CEC_MAX_MSG_SIZE];187	u8 new_rx_msg_len;188 189	struct work_struct tx_work;190	u32 tx_done_status;191	u32 tx_signal_free_time;192	struct cec_msg tx_msg;193	bool tx_msg_is_bcast;194 195	struct completion cmd_done;196	u8 data[DATA_SIZE];197	unsigned int len;198	u8 buf[DATA_SIZE];199	unsigned int idx;200	bool escape;201	bool started;202 203	/* locks access to the adapter */204	struct mutex lock;205	bool config_pending;206	bool restoring_config;207	bool autonomous;208};209 210static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len)211{212	int err = 0;213 214	err = serio_write(serio, MSGSTART);215	if (err)216		return err;217	for (; !err && cmd_len; command++, cmd_len--) {218		if (*command >= MSGESC) {219			err = serio_write(serio, MSGESC);220			if (!err)221				err = serio_write(serio, *command - MSGOFFSET);222		} else {223			err = serio_write(serio, *command);224		}225	}226	if (!err)227		err = serio_write(serio, MSGEND);228 229	return err;230}231 232static int pulse8_send_and_wait_once(struct pulse8 *pulse8,233				     const u8 *cmd, u8 cmd_len,234				     u8 response, u8 size)235{236	int err;237 238	if (debug > 1)239		dev_info(pulse8->dev, "transmit %s: %*ph\n",240			 pulse8_msgname(cmd[0]), cmd_len, cmd);241	init_completion(&pulse8->cmd_done);242 243	err = pulse8_send(pulse8->serio, cmd, cmd_len);244	if (err)245		return err;246 247	if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))248		return -ETIMEDOUT;249	if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED &&250	    cmd[0] != MSGCODE_SET_CONTROLLED &&251	    cmd[0] != MSGCODE_SET_AUTO_ENABLED &&252	    cmd[0] != MSGCODE_GET_BUILDDATE)253		return -ENOTTY;254	if (response &&255	    ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) {256		dev_info(pulse8->dev, "transmit %s failed with %s\n",257			 pulse8_msgname(cmd[0]),258			 pulse8_msgname(pulse8->data[0]));259		return -EIO;260	}261	return 0;262}263 264static int pulse8_send_and_wait(struct pulse8 *pulse8,265				const u8 *cmd, u8 cmd_len, u8 response, u8 size)266{267	u8 cmd_sc[2];268	int err;269 270	err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size);271	if (err != -ENOTTY)272		return err;273 274	cmd_sc[0] = MSGCODE_SET_CONTROLLED;275	cmd_sc[1] = 1;276	err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2,277					MSGCODE_COMMAND_ACCEPTED, 1);278	if (!err)279		err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len,280						response, size);281	return err == -ENOTTY ? -EIO : err;282}283 284static void pulse8_tx_work_handler(struct work_struct *work)285{286	struct pulse8 *pulse8 = container_of(work, struct pulse8, tx_work);287	struct cec_msg *msg = &pulse8->tx_msg;288	unsigned int i;289	u8 cmd[2];290	int err;291 292	if (msg->len == 0)293		return;294 295	mutex_lock(&pulse8->lock);296	cmd[0] = MSGCODE_TRANSMIT_IDLETIME;297	cmd[1] = pulse8->tx_signal_free_time;298	err = pulse8_send_and_wait(pulse8, cmd, 2,299				   MSGCODE_COMMAND_ACCEPTED, 1);300	cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY;301	cmd[1] = cec_msg_is_broadcast(msg);302	pulse8->tx_msg_is_bcast = cec_msg_is_broadcast(msg);303	if (!err)304		err = pulse8_send_and_wait(pulse8, cmd, 2,305					   MSGCODE_COMMAND_ACCEPTED, 1);306	cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;307	cmd[1] = msg->msg[0];308	if (!err)309		err = pulse8_send_and_wait(pulse8, cmd, 2,310					   MSGCODE_COMMAND_ACCEPTED, 1);311	if (!err && msg->len > 1) {312		for (i = 1; !err && i < msg->len; i++) {313			cmd[0] = ((i == msg->len - 1)) ?314				MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;315			cmd[1] = msg->msg[i];316			err = pulse8_send_and_wait(pulse8, cmd, 2,317						   MSGCODE_COMMAND_ACCEPTED, 1);318		}319	}320	if (err && debug)321		dev_info(pulse8->dev, "%s(0x%02x) failed with error %d for msg %*ph\n",322			 pulse8_msgname(cmd[0]), cmd[1],323			 err, msg->len, msg->msg);324	msg->len = 0;325	mutex_unlock(&pulse8->lock);326	if (err)327		cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR);328}329 330static void pulse8_irq_work_handler(struct work_struct *work)331{332	struct pulse8 *pulse8 =333		container_of(work, struct pulse8, irq_work);334	unsigned long flags;335	u32 status;336 337	spin_lock_irqsave(&pulse8->msg_lock, flags);338	while (pulse8->rx_msg_num) {339		spin_unlock_irqrestore(&pulse8->msg_lock, flags);340		if (debug)341			dev_info(pulse8->dev, "adap received %*ph\n",342				 pulse8->rx_msg[pulse8->rx_msg_cur_idx].len,343				 pulse8->rx_msg[pulse8->rx_msg_cur_idx].msg);344		cec_received_msg(pulse8->adap,345				 &pulse8->rx_msg[pulse8->rx_msg_cur_idx]);346		spin_lock_irqsave(&pulse8->msg_lock, flags);347		if (pulse8->rx_msg_num)348			pulse8->rx_msg_num--;349		pulse8->rx_msg_cur_idx =350			(pulse8->rx_msg_cur_idx + 1) % NUM_MSGS;351	}352	spin_unlock_irqrestore(&pulse8->msg_lock, flags);353 354	mutex_lock(&pulse8->lock);355	status = pulse8->tx_done_status;356	pulse8->tx_done_status = 0;357	mutex_unlock(&pulse8->lock);358	if (status)359		cec_transmit_attempt_done(pulse8->adap, status);360}361 362static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,363				    unsigned int flags)364{365	struct pulse8 *pulse8 = serio_get_drvdata(serio);366	unsigned long irq_flags;367	unsigned int idx;368 369	if (!pulse8->started && data != MSGSTART)370		return IRQ_HANDLED;371	if (data == MSGESC) {372		pulse8->escape = true;373		return IRQ_HANDLED;374	}375	if (pulse8->escape) {376		data += MSGOFFSET;377		pulse8->escape = false;378	} else if (data == MSGEND) {379		u8 msgcode = pulse8->buf[0];380 381		if (debug > 1)382			dev_info(pulse8->dev, "received %s: %*ph\n",383				 pulse8_msgname(msgcode),384				 pulse8->idx, pulse8->buf);385		switch (msgcode & 0x3f) {386		case MSGCODE_FRAME_START:387			/*388			 * Test if we are receiving a new msg when a previous389			 * message is still pending.390			 */391			if (!(msgcode & MSGCODE_FRAME_EOM)) {392				pulse8->new_rx_msg_len = 1;393				pulse8->new_rx_msg[0] = pulse8->buf[1];394				break;395			}396			fallthrough;397		case MSGCODE_FRAME_DATA:398			if (pulse8->new_rx_msg_len < CEC_MAX_MSG_SIZE)399				pulse8->new_rx_msg[pulse8->new_rx_msg_len++] =400					pulse8->buf[1];401			if (!(msgcode & MSGCODE_FRAME_EOM))402				break;403 404			spin_lock_irqsave(&pulse8->msg_lock, irq_flags);405			idx = (pulse8->rx_msg_cur_idx + pulse8->rx_msg_num) %406				NUM_MSGS;407			if (pulse8->rx_msg_num == NUM_MSGS) {408				dev_warn(pulse8->dev,409					 "message queue is full, dropping %*ph\n",410					 pulse8->new_rx_msg_len,411					 pulse8->new_rx_msg);412				spin_unlock_irqrestore(&pulse8->msg_lock,413						       irq_flags);414				pulse8->new_rx_msg_len = 0;415				break;416			}417			pulse8->rx_msg_num++;418			memcpy(pulse8->rx_msg[idx].msg, pulse8->new_rx_msg,419			       pulse8->new_rx_msg_len);420			pulse8->rx_msg[idx].len = pulse8->new_rx_msg_len;421			spin_unlock_irqrestore(&pulse8->msg_lock, irq_flags);422			schedule_work(&pulse8->irq_work);423			pulse8->new_rx_msg_len = 0;424			break;425		case MSGCODE_TRANSMIT_SUCCEEDED:426			WARN_ON(pulse8->tx_done_status);427			pulse8->tx_done_status = CEC_TX_STATUS_OK;428			schedule_work(&pulse8->irq_work);429			break;430		case MSGCODE_TRANSMIT_FAILED_ACK:431			/*432			 * A NACK for a broadcast message makes no sense, these433			 * seem to be spurious messages and are skipped.434			 */435			if (pulse8->tx_msg_is_bcast)436				break;437			WARN_ON(pulse8->tx_done_status);438			pulse8->tx_done_status = CEC_TX_STATUS_NACK;439			schedule_work(&pulse8->irq_work);440			break;441		case MSGCODE_TRANSMIT_FAILED_LINE:442		case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:443		case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:444			WARN_ON(pulse8->tx_done_status);445			pulse8->tx_done_status = CEC_TX_STATUS_ERROR;446			schedule_work(&pulse8->irq_work);447			break;448		case MSGCODE_HIGH_ERROR:449		case MSGCODE_LOW_ERROR:450		case MSGCODE_RECEIVE_FAILED:451		case MSGCODE_TIMEOUT_ERROR:452			pulse8->new_rx_msg_len = 0;453			break;454		case MSGCODE_COMMAND_ACCEPTED:455		case MSGCODE_COMMAND_REJECTED:456		default:457			if (pulse8->idx == 0)458				break;459			memcpy(pulse8->data, pulse8->buf, pulse8->idx);460			pulse8->len = pulse8->idx;461			complete(&pulse8->cmd_done);462			break;463		}464		pulse8->idx = 0;465		pulse8->started = false;466		return IRQ_HANDLED;467	} else if (data == MSGSTART) {468		pulse8->idx = 0;469		pulse8->started = true;470		return IRQ_HANDLED;471	}472 473	if (pulse8->idx >= DATA_SIZE) {474		dev_dbg(pulse8->dev,475			"throwing away %d bytes of garbage\n", pulse8->idx);476		pulse8->idx = 0;477	}478	pulse8->buf[pulse8->idx++] = data;479	return IRQ_HANDLED;480}481 482static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable)483{484	struct pulse8 *pulse8 = cec_get_drvdata(adap);485	u8 cmd[16];486	int err;487 488	mutex_lock(&pulse8->lock);489	cmd[0] = MSGCODE_SET_CONTROLLED;490	cmd[1] = enable;491	err = pulse8_send_and_wait(pulse8, cmd, 2,492				   MSGCODE_COMMAND_ACCEPTED, 1);493	if (!enable) {494		pulse8->rx_msg_num = 0;495		pulse8->tx_done_status = 0;496	}497	mutex_unlock(&pulse8->lock);498	return enable ? err : 0;499}500 501static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)502{503	struct pulse8 *pulse8 = cec_get_drvdata(adap);504	u16 mask = 0;505	u16 pa = adap->phys_addr;506	u8 cmd[16];507	int err = 0;508 509	mutex_lock(&pulse8->lock);510	if (log_addr != CEC_LOG_ADDR_INVALID)511		mask = 1 << log_addr;512	cmd[0] = MSGCODE_SET_ACK_MASK;513	cmd[1] = mask >> 8;514	cmd[2] = mask & 0xff;515	err = pulse8_send_and_wait(pulse8, cmd, 3,516				   MSGCODE_COMMAND_ACCEPTED, 0);517	if ((err && mask != 0) || pulse8->restoring_config)518		goto unlock;519 520	cmd[0] = MSGCODE_SET_AUTO_ENABLED;521	cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1;522	err = pulse8_send_and_wait(pulse8, cmd, 2,523				   MSGCODE_COMMAND_ACCEPTED, 0);524	if (err)525		goto unlock;526	pulse8->autonomous = cmd[1];527	if (log_addr == CEC_LOG_ADDR_INVALID)528		goto unlock;529 530	cmd[0] = MSGCODE_SET_DEVICE_TYPE;531	cmd[1] = adap->log_addrs.primary_device_type[0];532	err = pulse8_send_and_wait(pulse8, cmd, 2,533				   MSGCODE_COMMAND_ACCEPTED, 0);534	if (err)535		goto unlock;536 537	switch (adap->log_addrs.primary_device_type[0]) {538	case CEC_OP_PRIM_DEVTYPE_TV:539		mask = CEC_LOG_ADDR_MASK_TV;540		break;541	case CEC_OP_PRIM_DEVTYPE_RECORD:542		mask = CEC_LOG_ADDR_MASK_RECORD;543		break;544	case CEC_OP_PRIM_DEVTYPE_TUNER:545		mask = CEC_LOG_ADDR_MASK_TUNER;546		break;547	case CEC_OP_PRIM_DEVTYPE_PLAYBACK:548		mask = CEC_LOG_ADDR_MASK_PLAYBACK;549		break;550	case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:551		mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM;552		break;553	case CEC_OP_PRIM_DEVTYPE_SWITCH:554		mask = CEC_LOG_ADDR_MASK_UNREGISTERED;555		break;556	case CEC_OP_PRIM_DEVTYPE_PROCESSOR:557		mask = CEC_LOG_ADDR_MASK_SPECIFIC;558		break;559	default:560		mask = 0;561		break;562	}563	cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK;564	cmd[1] = mask >> 8;565	cmd[2] = mask & 0xff;566	err = pulse8_send_and_wait(pulse8, cmd, 3,567				   MSGCODE_COMMAND_ACCEPTED, 0);568	if (err)569		goto unlock;570 571	cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS;572	cmd[1] = log_addr;573	err = pulse8_send_and_wait(pulse8, cmd, 2,574				   MSGCODE_COMMAND_ACCEPTED, 0);575	if (err)576		goto unlock;577 578	cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS;579	cmd[1] = pa >> 8;580	cmd[2] = pa & 0xff;581	err = pulse8_send_and_wait(pulse8, cmd, 3,582				   MSGCODE_COMMAND_ACCEPTED, 0);583	if (err)584		goto unlock;585 586	if (pulse8->vers < 10) {587		cmd[0] = MSGCODE_SET_HDMI_VERSION;588		cmd[1] = adap->log_addrs.cec_version;589		err = pulse8_send_and_wait(pulse8, cmd, 2,590					   MSGCODE_COMMAND_ACCEPTED, 0);591		if (err)592			goto unlock;593	}594 595	if (adap->log_addrs.osd_name[0]) {596		size_t osd_len = strlen(adap->log_addrs.osd_name);597		char *osd_str = cmd + 1;598 599		cmd[0] = MSGCODE_SET_OSD_NAME;600		strscpy(cmd + 1, adap->log_addrs.osd_name, sizeof(cmd) - 1);601		if (osd_len < 4) {602			memset(osd_str + osd_len, ' ', 4 - osd_len);603			osd_len = 4;604			osd_str[osd_len] = '\0';605			strscpy(adap->log_addrs.osd_name, osd_str,606				sizeof(adap->log_addrs.osd_name));607		}608		err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len,609					   MSGCODE_COMMAND_ACCEPTED, 0);610		if (err)611			goto unlock;612	}613 614unlock:615	if (pulse8->restoring_config)616		pulse8->restoring_config = false;617	else618		pulse8->config_pending = true;619	mutex_unlock(&pulse8->lock);620	return log_addr == CEC_LOG_ADDR_INVALID ? 0 : err;621}622 623static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,624				    u32 signal_free_time, struct cec_msg *msg)625{626	struct pulse8 *pulse8 = cec_get_drvdata(adap);627 628	pulse8->tx_msg = *msg;629	if (debug)630		dev_info(pulse8->dev, "adap transmit %*ph\n",631			 msg->len, msg->msg);632	pulse8->tx_signal_free_time = signal_free_time;633	schedule_work(&pulse8->tx_work);634	return 0;635}636 637static void pulse8_cec_adap_free(struct cec_adapter *adap)638{639	struct pulse8 *pulse8 = cec_get_drvdata(adap);640 641	cancel_delayed_work_sync(&pulse8->ping_eeprom_work);642	cancel_work_sync(&pulse8->irq_work);643	cancel_work_sync(&pulse8->tx_work);644	kfree(pulse8);645}646 647static const struct cec_adap_ops pulse8_cec_adap_ops = {648	.adap_enable = pulse8_cec_adap_enable,649	.adap_log_addr = pulse8_cec_adap_log_addr,650	.adap_transmit = pulse8_cec_adap_transmit,651	.adap_free = pulse8_cec_adap_free,652};653 654static void pulse8_disconnect(struct serio *serio)655{656	struct pulse8 *pulse8 = serio_get_drvdata(serio);657 658	cec_unregister_adapter(pulse8->adap);659	serio_set_drvdata(serio, NULL);660	serio_close(serio);661}662 663static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio,664			struct cec_log_addrs *log_addrs, u16 *pa)665{666	u8 *data = pulse8->data + 1;667	u8 cmd[2];668	int err;669	time64_t date;670 671	pulse8->vers = 0;672 673	cmd[0] = MSGCODE_FIRMWARE_VERSION;674	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);675	if (err)676		return err;677	pulse8->vers = (data[0] << 8) | data[1];678	dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers);679	if (pulse8->vers < 2) {680		*pa = CEC_PHYS_ADDR_INVALID;681		return 0;682	}683 684	cmd[0] = MSGCODE_GET_BUILDDATE;685	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4);686	if (err)687		return err;688	date = ((unsigned)data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];689	dev_info(pulse8->dev, "Firmware build date %ptT\n", &date);690 691	dev_dbg(pulse8->dev, "Persistent config:\n");692	cmd[0] = MSGCODE_GET_AUTO_ENABLED;693	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);694	if (err)695		return err;696	pulse8->autonomous = data[0];697	dev_dbg(pulse8->dev, "Autonomous mode: %s",698		data[0] ? "on" : "off");699 700	if (pulse8->vers >= 10) {701		cmd[0] = MSGCODE_GET_AUTO_POWER_ON;702		err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);703		if (!err)704			dev_dbg(pulse8->dev, "Auto Power On: %s",705				data[0] ? "on" : "off");706	}707 708	cmd[0] = MSGCODE_GET_DEVICE_TYPE;709	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);710	if (err)711		return err;712	log_addrs->primary_device_type[0] = data[0];713	dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]);714	switch (log_addrs->primary_device_type[0]) {715	case CEC_OP_PRIM_DEVTYPE_TV:716		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV;717		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV;718		break;719	case CEC_OP_PRIM_DEVTYPE_RECORD:720		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD;721		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD;722		break;723	case CEC_OP_PRIM_DEVTYPE_TUNER:724		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER;725		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER;726		break;727	case CEC_OP_PRIM_DEVTYPE_PLAYBACK:728		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;729		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK;730		break;731	case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:732		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;733		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;734		break;735	case CEC_OP_PRIM_DEVTYPE_SWITCH:736		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;737		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;738		break;739	case CEC_OP_PRIM_DEVTYPE_PROCESSOR:740		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC;741		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;742		break;743	default:744		log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;745		log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;746		dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n",747			 log_addrs->primary_device_type[0]);748		break;749	}750 751	cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK;752	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);753	if (err)754		return err;755	log_addrs->log_addr_mask = (data[0] << 8) | data[1];756	dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n",757		log_addrs->log_addr_mask);758	if (log_addrs->log_addr_mask)759		log_addrs->num_log_addrs = 1;760 761	cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS;762	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);763	if (err)764		return err;765	*pa = (data[0] << 8) | data[1];766	dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n",767		cec_phys_addr_exp(*pa));768 769	log_addrs->cec_version = CEC_OP_CEC_VERSION_1_4;770	if (pulse8->vers < 10) {771		cmd[0] = MSGCODE_GET_HDMI_VERSION;772		err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);773		if (err)774			return err;775		log_addrs->cec_version = data[0];776		dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version);777	}778 779	cmd[0] = MSGCODE_GET_OSD_NAME;780	err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0);781	if (err)782		return err;783	strscpy(log_addrs->osd_name, data, sizeof(log_addrs->osd_name));784	dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name);785 786	return 0;787}788 789static int pulse8_apply_persistent_config(struct pulse8 *pulse8,790					  struct cec_log_addrs *log_addrs,791					  u16 pa)792{793	int err;794 795	err = cec_s_log_addrs(pulse8->adap, log_addrs, false);796	if (err)797		return err;798 799	cec_s_phys_addr(pulse8->adap, pa, false);800 801	return 0;802}803 804static void pulse8_ping_eeprom_work_handler(struct work_struct *work)805{806	struct pulse8 *pulse8 =807		container_of(work, struct pulse8, ping_eeprom_work.work);808	u8 cmd;809 810	mutex_lock(&pulse8->lock);811	cmd = MSGCODE_PING;812	if (pulse8_send_and_wait(pulse8, &cmd, 1,813				 MSGCODE_COMMAND_ACCEPTED, 0)) {814		dev_warn(pulse8->dev, "failed to ping EEPROM\n");815		goto unlock;816	}817 818	if (pulse8->vers < 2)819		goto unlock;820 821	if (pulse8->config_pending && persistent_config) {822		dev_dbg(pulse8->dev, "writing pending config to EEPROM\n");823		cmd = MSGCODE_WRITE_EEPROM;824		if (pulse8_send_and_wait(pulse8, &cmd, 1,825					 MSGCODE_COMMAND_ACCEPTED, 0))826			dev_info(pulse8->dev, "failed to write pending config to EEPROM\n");827		else828			pulse8->config_pending = false;829	}830unlock:831	schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD);832	mutex_unlock(&pulse8->lock);833}834 835static int pulse8_connect(struct serio *serio, struct serio_driver *drv)836{837	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;838	struct pulse8 *pulse8;839	int err = -ENOMEM;840	struct cec_log_addrs log_addrs = {};841	u16 pa = CEC_PHYS_ADDR_INVALID;842 843	pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL);844 845	if (!pulse8)846		return -ENOMEM;847 848	pulse8->serio = serio;849	pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8,850					    dev_name(&serio->dev), caps, 1);851	err = PTR_ERR_OR_ZERO(pulse8->adap);852	if (err < 0) {853		kfree(pulse8);854		return err;855	}856 857	pulse8->dev = &serio->dev;858	serio_set_drvdata(serio, pulse8);859	INIT_WORK(&pulse8->irq_work, pulse8_irq_work_handler);860	INIT_WORK(&pulse8->tx_work, pulse8_tx_work_handler);861	INIT_DELAYED_WORK(&pulse8->ping_eeprom_work,862			  pulse8_ping_eeprom_work_handler);863	mutex_init(&pulse8->lock);864	spin_lock_init(&pulse8->msg_lock);865	pulse8->config_pending = false;866 867	err = serio_open(serio, drv);868	if (err)869		goto delete_adap;870 871	err = pulse8_setup(pulse8, serio, &log_addrs, &pa);872	if (err)873		goto close_serio;874 875	err = cec_register_adapter(pulse8->adap, &serio->dev);876	if (err < 0)877		goto close_serio;878 879	pulse8->dev = &pulse8->adap->devnode.dev;880 881	if (persistent_config && pulse8->autonomous) {882		err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa);883		if (err)884			goto close_serio;885		pulse8->restoring_config = true;886	}887 888	schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD);889 890	return 0;891 892close_serio:893	pulse8->serio = NULL;894	serio_set_drvdata(serio, NULL);895	serio_close(serio);896delete_adap:897	cec_delete_adapter(pulse8->adap);898	return err;899}900 901static const struct serio_device_id pulse8_serio_ids[] = {902	{903		.type	= SERIO_RS232,904		.proto	= SERIO_PULSE8_CEC,905		.id	= SERIO_ANY,906		.extra	= SERIO_ANY,907	},908	{ 0 }909};910 911MODULE_DEVICE_TABLE(serio, pulse8_serio_ids);912 913static struct serio_driver pulse8_drv = {914	.driver		= {915		.name	= "pulse8-cec",916	},917	.description	= "Pulse Eight HDMI CEC driver",918	.id_table	= pulse8_serio_ids,919	.interrupt	= pulse8_interrupt,920	.connect	= pulse8_connect,921	.disconnect	= pulse8_disconnect,922};923 924module_serio_driver(pulse8_drv);925