brintos

brintos / linux-shallow public Read only

0
0
Text · 51.8 KiB · cfbfc4c Raw
1837 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.4 */5 6/*7 * Currently this driver does not fully support the serial port of the8 * Extron, only the USB port is fully supported.9 *10 * Issues specific to using the serial port instead of the USB since the11 * serial port doesn't detect if the device is powered off:12 *13 * - Some periodic ping mechanism is needed to detect when the Extron is14 *   powered off and when it is powered on again.15 * - What to do when it is powered off and the driver is modprobed? Keep16 *   trying to contact the Extron indefinitely?17 */18 19#include <linux/completion.h>20#include <linux/ctype.h>21#include <linux/delay.h>22#include <linux/init.h>23#include <linux/interrupt.h>24#include <linux/kernel.h>25#include <linux/module.h>26#include <linux/slab.h>27#include <linux/time.h>28 29#include "extron-da-hd-4k-plus.h"30 31MODULE_AUTHOR("Hans Verkuil <hansverk@cisco.com>");32MODULE_DESCRIPTION("Extron DA HD 4K PLUS HDMI CEC driver");33MODULE_LICENSE("GPL");34 35static int debug;36module_param(debug, int, 0644);37MODULE_PARM_DESC(debug, "debug level (0-1)");38 39static unsigned int vendor_id;40module_param(vendor_id, uint, 0444);41MODULE_PARM_DESC(vendor_id, "CEC Vendor ID");42 43static char manufacturer_name[4];44module_param_string(manufacturer_name, manufacturer_name,45		    sizeof(manufacturer_name), 0644);46MODULE_PARM_DESC(manufacturer_name,47		 "EDID Vendor String (3 uppercase characters)");48 49static bool hpd_never_low;50module_param(hpd_never_low, bool, 0644);51MODULE_PARM_DESC(hpd_never_low, "Input HPD will never go low (1), or go low if all output HPDs are low (0, default)");52 53#define EXTRON_TIMEOUT_SECS 654 55static const u8 hdmi_edid[256] = {56	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,57	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,58	0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,59	0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,60	0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01,61	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,62	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,63	0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,64	0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,65	0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,66	0x87, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,67	0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,68	0x64, 0x6d, 0x69, 0x2d, 0x31, 0x30, 0x38, 0x30,69	0x70, 0x36, 0x30, 0x0a, 0x00, 0x00, 0x00, 0xfe,70	0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20,71	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x95,72 73	0x02, 0x03, 0x1b, 0xf1, 0x42, 0x10, 0x01, 0x23,74	0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x68,75	0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x21, 0x01,76	0xe2, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00,77	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,78	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,79	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,80	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,81	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,82	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,83	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,84	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,85	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,86	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,87	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,88	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89,89};90 91static const u8 hdmi_edid_4k_300[256] = {92	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,93	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,94	0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,95	0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,96	0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01,97	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,98	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,99	0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,100	0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,101	0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,102	0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,103	0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,104	0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36,105	0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0xfe,106	0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20,107	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x87,108 109	0x02, 0x03, 0x1f, 0xf1, 0x43, 0x10, 0x5f, 0x01,110	0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00,111	0x6b, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c,112	0x21, 0x00, 0x20, 0x01, 0xe2, 0x00, 0xca, 0x00,113	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,114	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,115	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,116	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,117	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,118	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,119	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,120	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,121	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,122	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,123	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,124	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6,125};126 127static const u8 hdmi_edid_4k_600[256] = {128	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,129	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,130	0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,131	0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,132	0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01,133	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,134	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xe8,135	0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58,136	0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,137	0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,138	0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,139	0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,140	0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36,141	0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0xfe,142	0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20,143	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x4c,144 145	0x02, 0x03, 0x28, 0xf1, 0x44, 0x61, 0x5f, 0x10,146	0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00,147	0x00, 0x6b, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00,148	0x3c, 0x21, 0x00, 0x20, 0x01, 0x67, 0xd8, 0x5d,149	0xc4, 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca,150	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,151	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,152	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,153	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,154	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,155	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,156	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,157	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,158	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,159	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,160	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82,161};162 163static int extron_send_byte(struct serio *serio, char byte)164{165	int err, i;166 167	for (i = 0; i < 100; i++) {168		err = serio_write(serio, byte);169		if (!err)170			break;171		usleep_range(80, 120);172	}173	if (err)174		dev_warn(&serio->dev, "unable to write byte after 100 attempts\n");175	return err ? -EIO : 0;176}177 178static int extron_send_len(struct serio *serio, const char *command,179			   const unsigned char *bin, unsigned int len)180{181	int err = 0;182 183	for (; !err && *command; command++)184		err = extron_send_byte(serio, *command);185	if (!err)186		err = extron_send_byte(serio, '\r');187	if (bin)188		for (; !err && len; len--)189			err = extron_send_byte(serio, *bin++);190	return err;191}192 193static int extron_send_and_wait_len(struct extron *extron, struct extron_port *port,194				    const char *cmd, const unsigned char *bin,195				    unsigned int len, const char *response)196{197	int timeout = EXTRON_TIMEOUT_SECS * HZ;198	int err;199 200	if (debug) {201		if (response)202			dev_info(extron->dev, "transmit %s (response: %s)\n",203				 cmd, response);204		else205			dev_info(extron->dev, "transmit %s\n", cmd);206	}207 208	mutex_lock(&extron->serio_lock);209	if (port) {210		init_completion(&port->cmd_done);211		port->cmd_error = 0;212		port->response = response;213	} else {214		init_completion(&extron->cmd_done);215		extron->cmd_error = 0;216		extron->response = response;217	}218	err = extron_send_len(extron->serio, cmd, bin, len);219 220	if (!err && response &&221	    !wait_for_completion_timeout(port ? &port->cmd_done : &extron->cmd_done, timeout)) {222		dev_info(extron->dev, "transmit %s failed with %s (expected: %s)\n",223			 cmd, extron->reply, response);224		err = -ETIMEDOUT;225	}226 227	if (!err && response && (port ? port->cmd_error : extron->cmd_error)) {228		dev_info(extron->dev, "transmit %s failed with E%02u (expected: %s)\n",229			 cmd, port ? port->cmd_error : extron->cmd_error, response);230		if (port)231			port->cmd_error = 0;232		else233			extron->cmd_error = 0;234		err = -EPROTO;235	}236	if (port)237		port->response = NULL;238	else239		extron->response = NULL;240	mutex_unlock(&extron->serio_lock);241	return err;242}243 244static int extron_send_and_wait(struct extron *extron, struct extron_port *port,245				const char *cmd, const char *response)246{247	return extron_send_and_wait_len(extron, port, cmd, NULL, 0, response);248}249 250static void extron_parse_edid(struct extron_port *port)251{252	const u8 *edid = port->edid;253	unsigned int i, end;254	u8 d;255 256	port->has_4kp30 = false;257	port->has_4kp60 = false;258	port->has_qy = false;259	port->has_qs = false;260	/* Store Established Timings 1 and 2 */261	port->est_i = edid[0x23];262	port->est_ii = edid[0x24];263 264	// Check DTDs in base block265	for (i = 0; i < 4; i++) {266		const u8 *dtd = edid + 0x36 + i * 18;267		unsigned int w, h;268		unsigned int mhz;269		u64 pclk;270 271		if (!dtd[0] && !dtd[1])272			continue;273		w = dtd[2] + ((dtd[4] & 0xf0) << 4);274		h = dtd[5] + ((dtd[7] & 0xf0) << 4);275		if (w != 3840 || h != 2160)276			continue;277 278		w += dtd[3] + ((dtd[4] & 0x0f) << 8);279		h += dtd[6] + ((dtd[7] & 0x0f) << 8);280		pclk = dtd[0] + (dtd[1] << 8);281		pclk *= 100000;282		mhz = div_u64(pclk, w * h);283		if (mhz >= 297)284			port->has_4kp30 = true;285		if (mhz >= 594)286			port->has_4kp60 = true;287	}288 289	if (port->edid_blocks == 1)290		return;291 292	edid += 128;293 294	/* Return if not a CTA-861 extension block */295	if (edid[0] != 0x02 || edid[1] != 0x03)296		return;297 298	/* search Video Data Block (tag 2) */299	d = edid[2] & 0x7f;300	/* Check if there are Data Blocks */301	if (d <= 4)302		return;303 304	i = 4;305	end = d;306 307	do {308		u8 tag = edid[i] >> 5;309		u8 len = edid[i] & 0x1f;310 311		/* Avoid buffer overrun in case the EDID is malformed */312		if (i + len + 1 > 0x7f)313			return;314 315		switch (tag) {316		case 2: /* Video Data Block */317			/* Search for VIC 97 */318			if (memchr(edid + i + 1, 97, len))319				port->has_4kp60 = true;320			/* Search for VIC 95 */321			if (memchr(edid + i + 1, 95, len))322				port->has_4kp30 = true;323			break;324 325		case 7: /* Use Extended Tag */326			switch (edid[i + 1]) {327			case 0: /* Video Capability Data Block */328				if (edid[i + 2] & 0x80)329					port->has_qy = true;330				if (edid[i + 2] & 0x40)331					port->has_qs = true;332				break;333			}334			break;335		}336		i += len + 1;337	} while (i < end);338}339 340static int get_edid_tag_location(const u8 *edid, unsigned int size,341				 u8 want_tag, u8 ext_tag)342{343	unsigned int offset = 128;344	int i, end;345	u8 d;346 347	edid += offset;348 349	/* Return if not a CTA-861 extension block */350	if (size < 256 || edid[0] != 0x02 || edid[1] != 0x03)351		return -ENOENT;352 353	/* search tag */354	d = edid[0x02] & 0x7f;355	if (d <= 4)356		return -ENOENT;357 358	i = 0x04;359	end = 0x00 + d;360 361	do {362		unsigned char tag = edid[i] >> 5;363		unsigned char len = edid[i] & 0x1f;364 365		if (tag != want_tag || i + len > end) {366			i += len + 1;367			continue;368		}369 370		if (tag < 7 || (len >= 1 && edid[i + 1] == ext_tag))371			return offset + i;372		i += len + 1;373	} while (i < end);374	return -ENOENT;375}376 377static void extron_edid_crc(u8 *edid)378{379	u8 sum = 0;380	int offset;381 382	/* Update CRC */383	for (offset = 0; offset < 127; offset++)384		sum += edid[offset];385	edid[127] = 256 - sum;386}387 388/*389 * Fill in EDID string. As per VESA EDID-1.3, strings are at most 13 chars390 * long. If shorter then add a 0x0a character after the string and pad the391 * remainder with spaces.392 */393static void extron_set_edid_string(u8 *start, const char *s)394{395	const unsigned int max_len = 13;396	int len = strlen(s);397 398	memset(start, ' ', max_len);399	if (len > max_len)400		len = max_len;401	memcpy(start, s, len);402	if (len < max_len)403		start[len] = 0x0a;404}405 406static void extron_update_edid(struct extron_port *port, unsigned int blocks)407{408	int offset;409	u8 c1, c2;410 411	c1 = ((manufacturer_name[0] - '@') << 2) |412		(((manufacturer_name[1] - '@') >> 3) & 0x03);413	c2 = (((manufacturer_name[1] - '@') & 0x07) << 5) |414		((manufacturer_name[2] - '@') & 0x1f);415 416	port->edid_tmp[8] = c1;417	port->edid_tmp[9] = c2;418 419	/* Set Established Timings, but always enable VGA */420	port->edid_tmp[0x23] = port->est_i | 0x20;421	port->edid_tmp[0x24] = port->est_ii;422 423	/* Set the Monitor Name to the unit name */424	extron_set_edid_string(port->edid_tmp + 0x5f, port->extron->unit_name);425	/* Set the ASCII String to the CEC adapter name */426	extron_set_edid_string(port->edid_tmp + 0x71, port->adap->name);427 428	extron_edid_crc(port->edid_tmp);429 430	/* Find Video Capability Data Block */431	offset = get_edid_tag_location(port->edid_tmp, blocks * 128, 7, 0);432	if (offset > 0) {433		port->edid_tmp[offset + 2] &= ~0xc0;434		if (port->has_qy)435			port->edid_tmp[offset + 2] |= 0x80;436		if (port->has_qs)437			port->edid_tmp[offset + 2] |= 0x40;438	}439 440	extron_edid_crc(port->edid_tmp + 128);441}442 443static int extron_write_edid(struct extron_port *port,444			     const u8 *edid, unsigned int blocks)445{446	struct extron *extron = port->extron;447	u16 phys_addr = CEC_PHYS_ADDR_INVALID;448	int ret;449 450	if (cec_get_edid_spa_location(edid, blocks * 128))451		phys_addr = 0;452 453	if (mutex_lock_interruptible(&extron->edid_lock))454		return -EINTR;455 456	memcpy(port->edid_tmp, edid, blocks * 128);457 458	if (manufacturer_name[0])459		extron_update_edid(port, blocks);460 461	ret = extron_send_and_wait_len(port->extron, port, "W+UF256,in.bin",462				       port->edid_tmp, sizeof(port->edid_tmp),463				       "Upl");464	if (ret)465		goto unlock;466	ret = extron_send_and_wait(port->extron, port, "WI1,in.binEDID",467				   "EdidI01");468	if (ret)469		goto unlock;470 471	port->edid_blocks = blocks;472	memcpy(port->edid, port->edid_tmp, blocks * 128);473	port->read_edid = true;474	mutex_unlock(&extron->edid_lock);475 476	cec_s_phys_addr(port->adap, phys_addr, false);477	return 0;478 479unlock:480	mutex_unlock(&extron->edid_lock);481	return ret;482}483 484static void update_edid_work(struct work_struct *w)485{486	struct extron *extron = container_of(w, struct extron,487					     work_update_edid.work);488	struct extron_port *in = extron->ports[extron->num_out_ports];489	struct extron_port *p;490	bool has_edid = false;491	bool has_4kp30 = true;492	bool has_4kp60 = true;493	bool has_qy = true;494	bool has_qs = true;495	u8 est_i = 0xff;496	u8 est_ii = 0xff;497	unsigned int out;498 499	for (out = 0; has_4kp60 && out < extron->num_out_ports; out++) {500		p = extron->ports[out];501		if (p->read_edid) {502			has_4kp60 = p->has_4kp60;503			est_i &= p->est_i;504			est_ii &= p->est_ii;505			has_edid = true;506		}507	}508	for (out = 0; has_4kp30 && out < extron->num_out_ports; out++)509		if (extron->ports[out]->read_edid)510			has_4kp30 = extron->ports[out]->has_4kp30;511 512	for (out = 0; has_qy && out < extron->num_out_ports; out++)513		if (extron->ports[out]->read_edid)514			has_qy = extron->ports[out]->has_qy;515 516	for (out = 0; has_qs && out < extron->num_out_ports; out++)517		if (extron->ports[out]->read_edid)518			has_qs = extron->ports[out]->has_qs;519 520	/* exit if no output port had an EDID */521	if (!has_edid)522		return;523 524	/* exit if the input EDID properties remained unchanged */525	if (has_4kp60 == in->has_4kp60 && has_4kp30 == in->has_4kp30 &&526	    has_qy == in->has_qy && has_qs == in->has_qs &&527	    est_i == in->est_i && est_ii == in->est_ii)528		return;529 530	in->has_4kp60 = has_4kp60;531	in->has_4kp30 = has_4kp30;532	in->has_qy = has_qy;533	in->has_qs = has_qs;534	in->est_i = est_i;535	in->est_ii = est_ii;536	extron_write_edid(extron->ports[extron->num_out_ports],537			  has_4kp60 ? hdmi_edid_4k_600 :538			  (has_4kp30 ? hdmi_edid_4k_300 : hdmi_edid), 2);539}540 541static void extron_read_edid(struct extron_port *port)542{543	struct extron *extron = port->extron;544	char cmd[10], reply[10];545	unsigned int idx;546 547	idx = port->port.port + (port->is_input ? 0 : extron->num_in_ports);548	snprintf(cmd, sizeof(cmd), "WR%uEDID", idx);549	snprintf(reply, sizeof(reply), "EdidR%u", idx);550	if (mutex_lock_interruptible(&extron->edid_lock))551		return;552	if (port->read_edid)553		goto unlock;554	extron->edid_bytes_read = 0;555	extron->edid_port = port;556	port->edid_blocks = 0;557	if (!port->has_edid)558		goto no_edid;559 560	extron->edid_reading = true;561 562	if (!extron_send_and_wait(extron, port, cmd, reply))563		wait_for_completion_killable_timeout(&extron->edid_completion,564						     msecs_to_jiffies(1000));565	if (port->edid_blocks) {566		extron_parse_edid(port);567		port->read_edid = true;568		if (!port->is_input)569			v4l2_ctrl_s_ctrl(port->ctrl_tx_edid_present, 1);570	}571no_edid:572	extron->edid_reading = false;573unlock:574	mutex_unlock(&extron->edid_lock);575	cancel_delayed_work_sync(&extron->work_update_edid);576	if (manufacturer_name[0])577		schedule_delayed_work(&extron->work_update_edid,578				      msecs_to_jiffies(1000));579}580 581static void extron_irq_work_handler(struct work_struct *work)582{583	struct extron_port *port =584		container_of(work, struct extron_port, irq_work);585	struct extron *extron = port->extron;586	unsigned long flags;587	bool update_pa;588	u16 pa;589	bool update_has_signal;590	bool has_signal;591	bool update_has_edid;592	bool has_edid;593	u32 status;594 595	spin_lock_irqsave(&port->msg_lock, flags);596	while (port->rx_msg_num) {597		spin_unlock_irqrestore(&port->msg_lock, flags);598		cec_received_msg(port->adap,599				 &port->rx_msg[port->rx_msg_cur_idx]);600		spin_lock_irqsave(&port->msg_lock, flags);601		if (port->rx_msg_num)602			port->rx_msg_num--;603		port->rx_msg_cur_idx =604			(port->rx_msg_cur_idx + 1) % NUM_MSGS;605	}606	update_pa = port->update_phys_addr;607	pa = port->phys_addr;608	port->update_phys_addr = false;609	update_has_signal = port->update_has_signal;610	has_signal = port->has_signal;611	port->update_has_signal = false;612	update_has_edid = port->update_has_edid;613	has_edid = port->has_edid;614	port->update_has_edid = false;615	status = port->tx_done_status;616	port->tx_done_status = 0;617	spin_unlock_irqrestore(&port->msg_lock, flags);618 619	if (status)620		cec_transmit_done(port->adap, status, 0, 0, 0, 0);621 622	if (update_has_signal && port->is_input)623		v4l2_ctrl_s_ctrl(port->ctrl_rx_power_present, has_signal);624 625	if (update_has_edid && !port->is_input) {626		v4l2_ctrl_s_ctrl(port->ctrl_tx_hotplug,627				 port->has_edid);628		if (port->has_edid) {629			port->port.found_sink = true;630			port->port.lost_sink_ts = ktime_set(0, 0);631		} else {632			port->port.lost_sink_ts = ktime_get();633		}634		if (!has_edid) {635			port->edid_blocks = 0;636			port->read_edid = false;637			if (extron->edid_reading && !has_edid &&638			    extron->edid_port == port)639				extron->edid_reading = false;640			v4l2_ctrl_s_ctrl(port->ctrl_tx_edid_present, 0);641		} else if (!extron->edid_reading || extron->edid_port != port) {642			extron_read_edid(port);643		}644	}645	if (update_pa)646		cec_s_phys_addr(port->adap, pa, false);647}648 649static void extron_process_received(struct extron_port *port, const char *data)650{651	struct cec_msg msg = {};652	unsigned int len = strlen(data);653	unsigned long irq_flags;654	unsigned int idx;655 656	if (!port || port->disconnected)657		return;658 659	if (len < 5 || (len - 2) % 3 || data[len - 2] != '*')660		goto malformed;661 662	while (*data != '*') {663		int v = hex2bin(&msg.msg[msg.len], data + 1, 1);664 665		if (*data != '%' || v)666			goto malformed;667		msg.len++;668		data += 3;669	}670 671	spin_lock_irqsave(&port->msg_lock, irq_flags);672	idx = (port->rx_msg_cur_idx + port->rx_msg_num) %673		NUM_MSGS;674	if (port->rx_msg_num == NUM_MSGS) {675		dev_warn(port->dev,676			 "message queue is full, dropping %*ph\n",677			 msg.len, msg.msg);678		spin_unlock_irqrestore(&port->msg_lock,679				       irq_flags);680		return;681	}682	port->rx_msg_num++;683	port->rx_msg[idx] = msg;684	spin_unlock_irqrestore(&port->msg_lock, irq_flags);685	if (!port->disconnected)686		schedule_work(&port->irq_work);687	return;688 689malformed:690	dev_info(port->extron->dev, "malformed msg received: '%s'\n", data);691}692 693static void extron_port_signal_change(struct extron_port *port, bool has_sig)694{695	unsigned long irq_flags;696	bool update = false;697 698	if (!port)699		return;700 701	spin_lock_irqsave(&port->msg_lock, irq_flags);702	if (!port->update_has_signal && port->has_signal != has_sig) {703		port->update_has_signal = true;704		update = true;705	}706	port->has_signal = has_sig;707	spin_unlock_irqrestore(&port->msg_lock, irq_flags);708	if (update && !port->disconnected)709		schedule_work(&port->irq_work);710}711 712static void extron_process_signal_change(struct extron *extron, const char *data)713{714	unsigned int i;715 716	extron_port_signal_change(extron->ports[extron->num_out_ports],717				  data[0] == '1');718	for (i = 0; i < extron->num_out_ports; i++)719		extron_port_signal_change(extron->ports[i],720					  data[2 + 2 * i] != '0');721}722 723static void extron_port_edid_change(struct extron_port *port, bool has_edid)724{725	unsigned long irq_flags;726	bool update = false;727 728	if (!port)729		return;730 731	spin_lock_irqsave(&port->msg_lock, irq_flags);732	if (!port->update_has_edid && port->has_edid != has_edid) {733		port->update_has_edid = true;734		update = true;735	}736	port->has_edid = has_edid;737	spin_unlock_irqrestore(&port->msg_lock, irq_flags);738	if (update && !port->disconnected)739		schedule_work(&port->irq_work);740}741 742static void extron_process_edid_change(struct extron *extron, const char *data)743{744	unsigned int i;745 746	/*747	 * Do nothing if the Extron isn't ready yet. Trying to do this748	 * while the Extron firmware is still settling will fail.749	 */750	if (!extron->is_ready)751		return;752 753	for (i = 0; i < extron->num_out_ports; i++)754		extron_port_edid_change(extron->ports[i],755					data[2 + 2 * i] != '0');756}757 758static void extron_phys_addr_change(struct extron_port *port, u16 pa)759{760	unsigned long irq_flags;761	bool update = false;762 763	if (!port)764		return;765 766	spin_lock_irqsave(&port->msg_lock, irq_flags);767	if (!port->update_phys_addr && port->phys_addr != pa) {768		update = true;769		port->update_phys_addr = true;770	}771	port->phys_addr = pa;772	spin_unlock_irqrestore(&port->msg_lock, irq_flags);773	if (update && !port->disconnected)774		schedule_work(&port->irq_work);775}776 777static void extron_process_tx_done(struct extron_port *port, char status)778{779	unsigned long irq_flags;780	unsigned int tx_status;781 782	if (!port)783		return;784 785	switch (status) {786	case '0':787		tx_status = CEC_TX_STATUS_NACK | CEC_TX_STATUS_MAX_RETRIES;788		break;789	case '1':790		tx_status = CEC_TX_STATUS_OK;791		break;792	default:793		tx_status = CEC_TX_STATUS_ERROR;794		break;795	}796	spin_lock_irqsave(&port->msg_lock, irq_flags);797	port->tx_done_status = tx_status;798	spin_unlock_irqrestore(&port->msg_lock, irq_flags);799	if (!port->disconnected)800		schedule_work(&port->irq_work);801}802 803static void extron_add_edid(struct extron_port *port, const char *hex)804{805	struct extron *extron = port ? port->extron : NULL;806 807	if (!port || port != extron->edid_port)808		return;809	while (extron->edid_bytes_read < sizeof(port->edid) && *hex) {810		int err = hex2bin(&port->edid[extron->edid_bytes_read], hex, 1);811 812		if (err) {813			extron->edid_reading = false;814			complete(&extron->edid_completion);815			break;816		}817		extron->edid_bytes_read++;818		hex += 2;819	}820	if (extron->edid_bytes_read == 128 &&821	    port->edid[126] == 0) {822		/* There are no extension blocks, we're done */823		port->edid_blocks = 1;824		extron->edid_reading = false;825		complete(&extron->edid_completion);826	}827	if (extron->edid_bytes_read < sizeof(port->edid))828		return;829	if (!*hex)830		port->edid_blocks = 2;831	extron->edid_reading = false;832	complete(&extron->edid_completion);833}834 835static irqreturn_t extron_interrupt(struct serio *serio, unsigned char data,836				    unsigned int flags)837{838	struct extron *extron = serio_get_drvdata(serio);839	struct extron_port *port = NULL;840	bool found_response;841	unsigned int p;842 843	if (data == '\r' || data == '\n') {844		if (extron->idx == 0)845			return IRQ_HANDLED;846		memcpy(extron->data, extron->buf, extron->idx);847		extron->len = extron->idx;848		extron->data[extron->len] = 0;849		if (debug)850			dev_info(extron->dev, "received %s\n", extron->data);851		extron->idx = 0;852		if (!memcmp(extron->data, "Sig", 3) &&853		    extron->data[4] == '*') {854			extron_process_signal_change(extron, extron->data + 3);855		} else if (!memcmp(extron->data, "Hdcp", 4) &&856			   extron->data[5] == '*') {857			extron_process_edid_change(extron, extron->data + 4);858		} else if (!memcmp(extron->data, "DcecI", 5) &&859			   extron->data[5] >= '1' &&860			   extron->data[5] < '1' + extron->num_in_ports) {861			unsigned int p = extron->data[5] - '1';862 863			p += extron->num_out_ports;864			extron_process_tx_done(extron->ports[p],865					       extron->data[extron->len - 1]);866		} else if (!memcmp(extron->data, "Ceci", 4) &&867			   extron->data[4] >= '1' &&868			   extron->data[4] < '1' + extron->num_in_ports &&869			   extron->data[5] == '*') {870			unsigned int p = extron->data[4] - '1';871 872			p += extron->num_out_ports;873			extron_process_received(extron->ports[p],874						extron->data + 6);875		} else if (!memcmp(extron->data, "DcecO", 5) &&876			   extron->data[5] >= '1' &&877			   extron->data[5] < '1' + extron->num_out_ports) {878			unsigned int p = extron->data[5] - '1';879 880			extron_process_tx_done(extron->ports[p],881					       extron->data[extron->len - 1]);882		} else if (!memcmp(extron->data, "Ceco", 4) &&883			   extron->data[4] >= '1' &&884			   extron->data[4] < '1' + extron->num_out_ports &&885			   extron->data[5] == '*') {886			unsigned int p = extron->data[4] - '1';887 888			extron_process_received(extron->ports[p],889						extron->data + 6);890		} else if (!memcmp(extron->data, "Pceco", 5) &&891			   extron->data[5] >= '1' &&892			   extron->data[5] < '1' + extron->num_out_ports) {893			unsigned int p = extron->data[5] - '1';894			unsigned int tmp_pa[2] = { 0xff, 0xff };895 896			if (sscanf(extron->data + 7, "%%%02x%%%02x",897				   &tmp_pa[0], &tmp_pa[1]) == 2)898				extron_phys_addr_change(extron->ports[p],899							tmp_pa[0] << 8 | tmp_pa[1]);900		} else if (!memcmp(extron->data, "Pceci", 5) &&901			   extron->data[5] >= '1' &&902			   extron->data[5] < '1' + extron->num_in_ports) {903			unsigned int p = extron->data[5] - '1';904			unsigned int tmp_pa[2] = { 0xff, 0xff };905 906			p += extron->num_out_ports;907			if (sscanf(extron->data + 7, "%%%02x%%%02x",908				   &tmp_pa[0], &tmp_pa[1]) == 2)909				extron_phys_addr_change(extron->ports[p],910							tmp_pa[0] << 8 | tmp_pa[1]);911		} else if (!memcmp(extron->data, "EdidR", 5) &&912			   extron->data[5] >= '1' &&913			   extron->data[5] < '1' + extron->num_ports &&914			   extron->data[6] == '*') {915			unsigned int p = extron->data[5] - '1';916 917			if (p)918				p--;919			else920				p = extron->num_out_ports;921			extron_add_edid(extron->ports[p], extron->data + 7);922		} else if (extron->edid_reading && extron->len == 32 &&923			   extron->edid_port) {924			extron_add_edid(extron->edid_port, extron->data);925		}926 927		found_response = false;928		if (extron->response &&929		    !strncmp(extron->response, extron->data,930			     strlen(extron->response)))931			found_response = true;932 933		for (p = 0; !found_response && p < extron->num_ports; p++) {934			port = extron->ports[p];935			if (port && port->response &&936			    !strncmp(port->response, extron->data,937				     strlen(port->response)))938				found_response = true;939		}940 941		if (!found_response && extron->response &&942		    extron->data[0] == 'E' &&943		    isdigit(extron->data[1]) &&944		    isdigit(extron->data[2]) &&945		    !extron->data[3]) {946			extron->cmd_error = (extron->data[1] - '0') * 10 +947				extron->data[2] - '0';948			extron->response = NULL;949			complete(&extron->cmd_done);950		}951 952		if (!found_response)953			return IRQ_HANDLED;954 955		memcpy(extron->reply, extron->data, extron->len);956		extron->reply[extron->len] = 0;957		if (!port) {958			extron->response = NULL;959			complete(&extron->cmd_done);960		} else {961			port->response = NULL;962			complete(&port->cmd_done);963		}964		return IRQ_HANDLED;965	}966 967	if (extron->idx >= DATA_SIZE - 1) {968		dev_info(extron->dev,969			 "throwing away %d bytes of garbage\n", extron->idx);970		extron->idx = 0;971	}972	extron->buf[extron->idx++] = (char)data;973	return IRQ_HANDLED;974}975 976static int extron_cec_adap_enable(struct cec_adapter *adap, bool enable)977{978	struct extron_port *port = cec_get_drvdata(adap);979 980	return (port->disconnected && enable) ? -ENODEV : 0;981}982 983static int extron_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)984{985	struct extron_port *port = cec_get_drvdata(adap);986	char cmd[26];987	char resp[25];988	u8 la = log_addr == CEC_LOG_ADDR_INVALID ? 15 : log_addr;989	int err;990 991	if (port->disconnected)992		return -ENODEV;993	snprintf(cmd, sizeof(cmd), "W%c%u*%uLCEC",994		 port->direction, port->port.port, la);995	snprintf(resp, sizeof(resp), "Lcec%c%u*%u",996		 port->direction, port->port.port, la);997	err = extron_send_and_wait(port->extron, port, cmd, resp);998	return log_addr != CEC_LOG_ADDR_INVALID && err ? err : 0;999}1000 1001static int extron_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,1002				    u32 signal_free_time, struct cec_msg *msg)1003{1004	struct extron_port *port = cec_get_drvdata(adap);1005	char buf[CEC_MAX_MSG_SIZE * 3 + 1];1006	char cmd[CEC_MAX_MSG_SIZE * 3 + 13];1007	unsigned int i;1008 1009	if (port->disconnected)1010		return -ENODEV;1011	buf[0] = 0;1012	for (i = 0; i < msg->len - 1; i++)1013		sprintf(buf + i * 3, "%%%02X", msg->msg[i + 1]);1014	snprintf(cmd, sizeof(cmd), "W%c%u*%u*%u*%sDCEC",1015		 port->direction, port->port.port,1016		 cec_msg_initiator(msg), cec_msg_destination(msg), buf);1017	return extron_send_and_wait(port->extron, port, cmd, NULL);1018}1019 1020static void extron_cec_adap_unconfigured(struct cec_adapter *adap)1021{1022	struct extron_port *port = cec_get_drvdata(adap);1023 1024	if (port->disconnected)1025		return;1026	if (debug)1027		dev_info(port->extron->dev, "unconfigured port %d (%s)\n",1028			 port->port.port,1029			 port->extron->splitter.is_standby ? "Off" : "On");1030	if (!port->is_input)1031		cec_splitter_unconfigured_output(&port->port);1032}1033 1034static void extron_cec_configured(struct cec_adapter *adap)1035{1036	struct extron_port *port = cec_get_drvdata(adap);1037 1038	if (port->disconnected)1039		return;1040	if (debug)1041		dev_info(port->extron->dev, "configured port %d (%s)\n",1042			 port->port.port,1043			 port->extron->splitter.is_standby ? "Off" : "On");1044	if (!port->is_input)1045		cec_splitter_configured_output(&port->port);1046}1047 1048static void extron_cec_adap_nb_transmit_canceled(struct cec_adapter *adap,1049						 const struct cec_msg *msg)1050{1051	struct extron_port *port = cec_get_drvdata(adap);1052	struct cec_adapter *input_adap;1053 1054	if (!vendor_id)1055		return;1056	if (port->disconnected || port->is_input)1057		return;1058	input_adap = port->extron->ports[port->extron->num_out_ports]->adap;1059	cec_splitter_nb_transmit_canceled_output(&port->port, msg, input_adap);1060}1061 1062static int extron_received(struct cec_adapter *adap, struct cec_msg *msg)1063{1064	struct extron_port *port = cec_get_drvdata(adap);1065 1066	if (!vendor_id)1067		return -ENOMSG;1068	if (port->disconnected)1069		return -ENOMSG;1070	if (port->is_input)1071		return cec_splitter_received_input(&port->port, msg);1072	return cec_splitter_received_output(&port->port, msg,1073					    port->extron->ports[port->extron->num_out_ports]->adap);1074}1075 1076#define log_printf(adap, file, fmt, arg...) \1077	do { \1078		if (file) \1079			seq_printf((file), fmt, ## arg); \1080		else \1081			pr_info("cec-%s: " fmt, (adap)->name, ## arg);	\1082	} while (0)1083 1084static const char * const pwr_state[] = {1085	"on",1086	"standby",1087	"to on",1088	"to standby",1089};1090 1091static void extron_adap_status_port(struct extron_port *port, struct seq_file *file)1092{1093	struct cec_adapter *adap = port->adap;1094 1095	if (port->disconnected) {1096		log_printf(adap, file,1097			   "\tport %u: disconnected\n", port->port.port);1098		return;1099	}1100	if (port->is_input)1101		log_printf(adap, file,1102			   "\tport %u: %s signal, %s edid, %s 4kp30, %s 4kp60, %sQS/%sQY, is %s\n",1103			   port->port.port,1104			   port->has_signal ? "has" : "no",1105			   port->has_edid ? "has" : "no",1106			   port->has_4kp30 ? "has" : "no",1107			   port->has_4kp60 ? "has" : "no",1108			   port->has_qs ? "" : "no ",1109			   port->has_qy ? "" : "no ",1110			   !port->port.adap->is_configured ? "not configured" :1111				pwr_state[port->extron->splitter.is_standby]);1112	else1113		log_printf(adap, file,1114			   "\tport %u: %s sink, %s signal, %s edid, %s 4kp30, %s 4kp60, %sQS/%sQY, is %sactive source, is %s\n",1115			   port->port.port,1116			   port->port.found_sink ? "found" : "no",1117			   port->has_signal ? "has" : "no",1118			   port->has_edid ? "has" : "no",1119			   port->has_4kp30 ? "has" : "no",1120			   port->has_4kp60 ? "has" : "no",1121			   port->has_qs ? "" : "no ",1122			   port->has_qy ? "" : "no ",1123			   port->port.is_active_source ? "" : "not ",1124			   !port->port.adap->is_configured ? "not configured" :1125			   pwr_state[port->port.power_status & 3]);1126	if (port->port.out_give_device_power_status_seq)1127		log_printf(adap, file,1128			   "\tport %u: querying power status (%u, %lldms)\n",1129			   port->port.port,1130			   port->port.out_give_device_power_status_seq & ~(1 << 31),1131			   ktime_ms_delta(ktime_get(),1132					  port->port.out_give_device_power_status_ts));1133	if (port->port.out_request_current_latency_seq)1134		log_printf(adap, file,1135			   "\tport %u: querying latency (%u, %lldms)\n",1136			   port->port.port,1137			   port->port.out_request_current_latency_seq & ~(1 << 31),1138			   ktime_ms_delta(ktime_get(),1139					  port->port.out_request_current_latency_ts));1140}1141 1142static void extron_adap_status(struct cec_adapter *adap, struct seq_file *file)1143{1144	struct extron_port *port = cec_get_drvdata(adap);1145	struct extron *extron = port->extron;1146	unsigned int i;1147 1148	log_printf(adap, file, "name: %s type: %s\n",1149		   extron->unit_name, extron->unit_type);1150	log_printf(adap, file, "model: 60-160%c-01 (1 input, %u outputs)\n",1151		   '6' + extron->num_out_ports / 2, extron->num_out_ports);1152	log_printf(adap, file, "firmware version: %s CEC engine version: %s\n",1153		   extron->unit_fw_version, extron->unit_cec_engine_version);1154	if (extron->hpd_never_low)1155		log_printf(adap, file, "always keep input HPD high\n");1156	else1157		log_printf(adap, file,1158			   "pull input HPD low if all output HPDs are low\n");1159	if (vendor_id)1160		log_printf(adap, file,1161			   "splitter vendor ID: 0x%06x\n", vendor_id);1162	if (manufacturer_name[0])1163		log_printf(adap, file, "splitter manufacturer name: %s\n",1164			   manufacturer_name);1165	log_printf(adap, file, "splitter power status: %s\n",1166		   pwr_state[extron->splitter.is_standby]);1167	log_printf(adap, file, "%s port: %d (%s)\n",1168		   port->is_input ? "input" : "output",1169		   port->port.port, port->name);1170	log_printf(adap, file, "splitter input port:\n");1171	extron_adap_status_port(extron->ports[extron->num_out_ports], file);1172 1173	log_printf(adap, file, "splitter output ports:\n");1174	for (i = 0; i < extron->num_out_ports; i++)1175		extron_adap_status_port(extron->ports[i], file);1176 1177	if (!port->has_edid || !port->read_edid)1178		return;1179 1180	for (i = 0; i < port->edid_blocks * 128; i += 16) {1181		if (i % 128 == 0)1182			log_printf(adap, file, "\n");1183		log_printf(adap, file, "EDID: %*ph\n", 16, port->edid + i);1184	}1185}1186 1187static const struct cec_adap_ops extron_cec_adap_ops = {1188	.adap_enable = extron_cec_adap_enable,1189	.adap_log_addr = extron_cec_adap_log_addr,1190	.adap_transmit = extron_cec_adap_transmit,1191	.adap_nb_transmit_canceled = extron_cec_adap_nb_transmit_canceled,1192	.adap_unconfigured = extron_cec_adap_unconfigured,1193	.adap_status = extron_adap_status,1194	.configured = extron_cec_configured,1195	.received = extron_received,1196};1197 1198static int extron_querycap(struct file *file, void *priv,1199			   struct v4l2_capability *cap)1200{1201	struct extron_port *port = video_drvdata(file);1202 1203	strscpy(cap->driver, "extron-da-hd-4k-plus-cec", sizeof(cap->driver));1204	strscpy(cap->card, cap->driver, sizeof(cap->card));1205	snprintf(cap->bus_info, sizeof(cap->bus_info), "serio:%s", port->name);1206	return 0;1207}1208 1209static int extron_enum_input(struct file *file, void *priv, struct v4l2_input *inp)1210{1211	struct extron_port *port = video_drvdata(file);1212 1213	if (inp->index)1214		return -EINVAL;1215	inp->type = V4L2_INPUT_TYPE_CAMERA;1216	snprintf(inp->name, sizeof(inp->name), "HDMI IN %u", port->port.port);1217	inp->status = v4l2_ctrl_g_ctrl(port->ctrl_rx_power_present) ?1218		0 : V4L2_IN_ST_NO_SIGNAL;1219	return 0;1220}1221 1222static int extron_g_input(struct file *file, void *priv, unsigned int *i)1223{1224	*i = 0;1225	return 0;1226}1227 1228static int extron_s_input(struct file *file, void *priv, unsigned int i)1229{1230	return i ? -EINVAL : 0;1231}1232 1233static int extron_enum_output(struct file *file, void *priv, struct v4l2_output *out)1234{1235	struct extron_port *port = video_drvdata(file);1236 1237	if (out->index)1238		return -EINVAL;1239	out->type = V4L2_OUTPUT_TYPE_ANALOG;1240	snprintf(out->name, sizeof(out->name), "HDMI OUT %u", port->port.port);1241	return 0;1242}1243 1244static int extron_g_output(struct file *file, void *priv, unsigned int *o)1245{1246	*o = 0;1247	return 0;1248}1249 1250static int extron_s_output(struct file *file, void *priv, unsigned int o)1251{1252	return o ? -EINVAL : 0;1253}1254 1255static int extron_g_edid(struct file *file, void *_fh,1256			 struct v4l2_edid *edid)1257{1258	struct extron_port *port = video_drvdata(file);1259 1260	memset(edid->reserved, 0, sizeof(edid->reserved));1261	if (port->disconnected)1262		return -ENODEV;1263	if (edid->pad)1264		return -EINVAL;1265	if (!port->has_edid)1266		return -ENODATA;1267	if (!port->read_edid)1268		extron_read_edid(port);1269	if (!port->read_edid)1270		return -ENODATA;1271	if (edid->start_block == 0 && edid->blocks == 0) {1272		edid->blocks = port->edid_blocks;1273		return 0;1274	}1275	if (edid->start_block >= port->edid_blocks)1276		return -EINVAL;1277	if (edid->blocks > port->edid_blocks - edid->start_block)1278		edid->blocks = port->edid_blocks - edid->start_block;1279	memcpy(edid->edid, port->edid + edid->start_block * 128, edid->blocks * 128);1280	return 0;1281}1282 1283static int extron_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid)1284{1285	struct extron_port *port = video_drvdata(file);1286 1287	memset(edid->reserved, 0, sizeof(edid->reserved));1288	if (port->disconnected)1289		return -ENODEV;1290	if (edid->pad)1291		return -EINVAL;1292 1293	/* Unfortunately it is not possible to clear the EDID */1294	if (edid->blocks == 0)1295		return -EINVAL;1296 1297	if (edid->blocks > MAX_EDID_BLOCKS) {1298		edid->blocks = MAX_EDID_BLOCKS;1299		return -E2BIG;1300	}1301 1302	if (cec_get_edid_spa_location(edid->edid, edid->blocks * 128))1303		v4l2_set_edid_phys_addr(edid->edid, edid->blocks * 128, 0);1304	extron_parse_edid(port);1305	return extron_write_edid(port, edid->edid, edid->blocks);1306}1307 1308static int extron_log_status(struct file *file, void *priv)1309{1310	struct extron_port *port = video_drvdata(file);1311 1312	extron_adap_status(port->adap, NULL);1313	return v4l2_ctrl_log_status(file, priv);1314}1315 1316static const struct v4l2_ioctl_ops extron_ioctl_ops = {1317	.vidioc_querycap		= extron_querycap,1318	.vidioc_enum_input		= extron_enum_input,1319	.vidioc_g_input			= extron_g_input,1320	.vidioc_s_input			= extron_s_input,1321	.vidioc_enum_output		= extron_enum_output,1322	.vidioc_g_output		= extron_g_output,1323	.vidioc_s_output		= extron_s_output,1324	.vidioc_g_edid			= extron_g_edid,1325	.vidioc_s_edid			= extron_s_edid,1326	.vidioc_log_status		= extron_log_status,1327	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,1328	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,1329};1330 1331static const struct v4l2_file_operations extron_fops = {1332	.owner		= THIS_MODULE,1333	.open		= v4l2_fh_open,1334	.release	= v4l2_fh_release,1335	.poll		= v4l2_ctrl_poll,1336	.unlocked_ioctl	= video_ioctl2,1337};1338 1339static const struct video_device extron_videodev = {1340	.name		= "extron-da-hd-4k-plus-cec",1341	.vfl_dir	= VFL_DIR_RX,1342	.fops		= &extron_fops,1343	.ioctl_ops	= &extron_ioctl_ops,1344	.minor		= -1,1345	.release	= video_device_release_empty,1346};1347 1348static void extron_disconnect(struct serio *serio)1349{1350	struct extron *extron = serio_get_drvdata(serio);1351	unsigned int p;1352 1353	kthread_stop(extron->kthread_setup);1354 1355	for (p = 0; p < extron->num_ports; p++) {1356		struct extron_port *port = extron->ports[p];1357 1358		if (!port)1359			continue;1360		port->disconnected = true;1361		cancel_work_sync(&port->irq_work);1362	}1363	cancel_delayed_work_sync(&extron->work_update_edid);1364	for (p = 0; p < extron->num_ports; p++) {1365		struct extron_port *port = extron->ports[p];1366 1367		if (!port)1368			continue;1369 1370		if (port->cec_was_registered) {1371			if (cec_is_registered(port->adap))1372				cec_unregister_adapter(port->adap);1373			/*1374			 * After registering the adapter, the1375			 * extron_setup_thread() function took an extra1376			 * reference to the device. We call the corresponding1377			 * put here.1378			 */1379			cec_put_device(port->adap);1380		} else {1381			cec_delete_adapter(port->adap);1382		}1383		video_unregister_device(&port->vdev);1384	}1385 1386	complete(&extron->edid_completion);1387 1388	for (p = 0; p < extron->num_ports; p++) {1389		struct extron_port *port = extron->ports[p];1390 1391		if (!port)1392			continue;1393		v4l2_ctrl_handler_free(&port->hdl);1394		mutex_destroy(&port->video_lock);1395		kfree(port);1396	}1397	mutex_destroy(&extron->edid_lock);1398	mutex_destroy(&extron->serio_lock);1399	extron->serio = NULL;1400	serio_set_drvdata(serio, NULL);1401	serio_close(serio);1402}1403 1404static int extron_setup(struct extron *extron)1405{1406	struct serio *serio = extron->serio;1407	struct extron_port *port;1408	u8 *reply = extron->reply;1409	unsigned int p;1410	unsigned int major, minor;1411	int err;1412 1413	/*1414	 * Attempt to disable CEC: avoid received CEC messages1415	 * from interfering with the other serial port traffic.1416	 */1417	extron_send_and_wait(extron, NULL, "WI1*0CCEC", NULL);1418	extron_send_and_wait(extron, NULL, "WO0*CCEC", NULL);1419 1420	/* Obtain unit part number */1421	err = extron_send_and_wait(extron, NULL, "N", "Pno");1422	if (err)1423		return err;1424	dev_info(extron->dev, "Unit part number: %s\n", reply + 3);1425	if (strcmp(reply + 3, "60-1607-01") &&1426	    strcmp(reply + 3, "60-1608-01") &&1427	    strcmp(reply + 3, "60-1609-01")) {1428		dev_err(extron->dev, "Unsupported model\n");1429		return -ENODEV;1430	}1431	/* Up to 6 output ports and one input port */1432	extron->num_out_ports = 2 * (reply[9] - '6');1433	extron->splitter.num_out_ports = extron->num_out_ports;1434	extron->splitter.ports = extron->splitter_ports;1435	extron->splitter.dev = extron->dev;1436	extron->num_in_ports = 1;1437	extron->num_ports = extron->num_out_ports + extron->num_in_ports;1438	dev_info(extron->dev, "Unit output ports: %d\n", extron->num_out_ports);1439	dev_info(extron->dev, "Unit input ports: %d\n", extron->num_in_ports);1440 1441	err = extron_send_and_wait(extron, NULL, "W CN", "Ipn ");1442	if (err)1443		return err;1444	dev_info(extron->dev, "Unit name: %s\n", reply + 4);1445	strscpy(extron->unit_name, reply + 4, sizeof(extron->unit_name));1446 1447	err = extron_send_and_wait(extron, NULL, "*Q", "Bld");1448	if (err)1449		return err;1450	dev_info(extron->dev, "Unit FW Version: %s\n", reply + 3);1451	strscpy(extron->unit_fw_version, reply + 3,1452		sizeof(extron->unit_fw_version));1453	if (sscanf(reply + 3, "%u.%u.", &major, &minor) < 2 ||1454	    major < 1 || minor < 2) {1455		dev_err(extron->dev,1456			"Unsupported FW version (only 1.02 or up is supported)\n");1457		return -ENODEV;1458	}1459 1460	err = extron_send_and_wait(extron, NULL, "2i", "Inf02*");1461	if (err)1462		return err;1463	dev_info(extron->dev, "Unit Type: %s\n", reply + 6);1464	strscpy(extron->unit_type, reply + 6, sizeof(extron->unit_type));1465 1466	err = extron_send_and_wait(extron, NULL, "39Q", "Ver39*");1467	if (err)1468		return err;1469	dev_info(extron->dev, "CEC Engine Version: %s\n", reply + 6);1470	strscpy(extron->unit_cec_engine_version, reply + 6,1471		sizeof(extron->unit_cec_engine_version));1472 1473	/* Disable CEC */1474	err = extron_send_and_wait(extron, NULL, "WI1*0CCEC", "CcecI1*");1475	if (err)1476		return err;1477	err = extron_send_and_wait(extron, NULL, "WO0*CCEC", "CcecO0");1478	if (err)1479		return err;1480 1481	extron->hpd_never_low = hpd_never_low;1482 1483	/* Pull input port HPD low if all output ports also have a low HPD */1484	if (hpd_never_low) {1485		dev_info(extron->dev, "Always keep input HPD high\n");1486	} else {1487		dev_info(extron->dev, "Pull input HPD low if all output HPDs are low\n");1488		extron_send_and_wait(extron, NULL, "W1ihpd", "Ihpd1");1489	}1490 1491	for (p = 0; p < extron->num_ports; p++) {1492		u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL;1493 1494		if (vendor_id)1495			caps &= ~CEC_CAP_LOG_ADDRS;1496		port = kzalloc(sizeof(*port), GFP_KERNEL);1497		if (!port)1498			return -ENOMEM;1499 1500		INIT_WORK(&port->irq_work, extron_irq_work_handler);1501		spin_lock_init(&port->msg_lock);1502		mutex_init(&port->video_lock);1503		port->extron = extron;1504		port->is_input = p >= extron->num_out_ports;1505		port->direction = port->is_input ? 'I' : 'O';1506		port->port.port = 1 + (port->is_input ? p - extron->num_out_ports : p);1507		port->port.splitter = &extron->splitter;1508		port->phys_addr = CEC_PHYS_ADDR_INVALID;1509		snprintf(port->name, sizeof(port->name), "%s-%s-%u",1510			 dev_name(&serio->dev), port->is_input ? "in" : "out",1511			 port->port.port);1512 1513		port->dev = extron->dev;1514		port->adap = cec_allocate_adapter(&extron_cec_adap_ops, port,1515						  port->name, caps, 1);1516		err = PTR_ERR_OR_ZERO(port->adap);1517		if (err < 0) {1518			kfree(port);1519			return err;1520		}1521 1522		port->adap->xfer_timeout_ms = EXTRON_TIMEOUT_SECS * 1000;1523		port->port.adap = port->adap;1524		port->vdev = extron_videodev;1525		port->vdev.lock = &port->video_lock;1526		port->vdev.v4l2_dev = &extron->v4l2_dev;1527		port->vdev.ctrl_handler = &port->hdl;1528		port->vdev.device_caps = V4L2_CAP_EDID;1529		video_set_drvdata(&port->vdev, port);1530 1531		v4l2_ctrl_handler_init(&port->hdl, 2);1532 1533		if (port->is_input) {1534			port->vdev.vfl_dir = VFL_DIR_RX;1535			port->ctrl_rx_power_present =1536				v4l2_ctrl_new_std(&port->hdl, NULL,1537						  V4L2_CID_DV_RX_POWER_PRESENT,1538						  0, 1, 0, 0);1539			port->has_edid = true;1540		} else {1541			port->vdev.vfl_dir = VFL_DIR_TX;1542			port->ctrl_tx_hotplug =1543				v4l2_ctrl_new_std(&port->hdl, NULL,1544						  V4L2_CID_DV_TX_HOTPLUG,1545						  0, 1, 0, 0);1546			port->ctrl_tx_edid_present =1547				v4l2_ctrl_new_std(&port->hdl, NULL,1548						  V4L2_CID_DV_TX_EDID_PRESENT,1549						  0, 1, 0, 0);1550		}1551 1552		err = port->hdl.error;1553		if (err < 0) {1554			cec_delete_adapter(port->adap);1555			kfree(port);1556			return err;1557		}1558		extron->ports[p] = port;1559		extron->splitter_ports[p] = &port->port;1560		if (port->is_input && manufacturer_name[0])1561			extron_write_edid(port, hdmi_edid, 2);1562	}1563 1564	/* Enable CEC (manual mode, i.e. controlled by the driver) */1565	err = extron_send_and_wait(extron, NULL, "WI1*20CCEC", "CcecI1*");1566	if (err)1567		return err;1568 1569	err = extron_send_and_wait(extron, NULL, "WO20*CCEC", "CcecO20");1570	if (err)1571		return err;1572 1573	/* Set logical addresses to 15 */1574	err = extron_send_and_wait(extron, NULL, "WI1*15LCEC", "LcecI1*15");1575	if (err)1576		return err;1577 1578	for (p = 0; p < extron->num_out_ports; p++) {1579		char cmd[20];1580		char resp[20];1581 1582		snprintf(cmd, sizeof(cmd), "WO%u*15LCEC", p + 1);1583		snprintf(resp, sizeof(resp), "LcecO%u*15", p + 1);1584		err = extron_send_and_wait(extron, extron->ports[p], cmd, resp);1585		if (err)1586			return err;1587	}1588 1589	/*1590	 * The Extron is now ready for operation. Specifically it is now1591	 * possible to retrieve EDIDs.1592	 */1593	extron->is_ready = true;1594 1595	/* Query HDCP and Signal states, used to update the initial state */1596	err = extron_send_and_wait(extron, NULL, "WHDCP", "Hdcp");1597	if (err)1598		return err;1599 1600	return extron_send_and_wait(extron, NULL, "WLS", "Sig");1601}1602 1603static int extron_setup_thread(void *_extron)1604{1605	struct extron *extron = _extron;1606	struct extron_port *port;1607	unsigned int p;1608	bool poll_splitter = false;1609	bool was_connected = true;1610	int err;1611 1612	while (1) {1613		if (kthread_should_stop())1614			return 0;1615		err = extron_send_and_wait(extron, NULL, "W3CV", "Vrb3");1616		// that should make it possible to detect a serio disconnect1617		// here by stopping the workqueue1618		if (err >= 0)1619			break;1620		was_connected = false;1621		ssleep(1);1622	}1623 1624	/*1625	 * If the Extron was not connected at probe() time, i.e. it just got1626	 * powered up and while the serial port is working, the firmware is1627	 * still booting up, then wait 10 seconds for the firmware to settle.1628	 *1629	 * Trying to continue too soon means that some commands will not1630	 * work yet.1631	 */1632	if (!was_connected)1633		ssleep(10);1634 1635	err = extron_setup(extron);1636	if (err)1637		goto disable_ports;1638 1639	for (p = 0; p < extron->num_ports; p++) {1640		struct cec_log_addrs log_addrs = {};1641 1642		port = extron->ports[p];1643		if (port->is_input && manufacturer_name[0])1644			v4l2_disable_ioctl(&port->vdev, VIDIOC_S_EDID);1645		err = video_register_device(&port->vdev, VFL_TYPE_VIDEO, -1);1646		if (err) {1647			v4l2_err(&extron->v4l2_dev, "Failed to register video device\n");1648			goto disable_ports;1649		}1650 1651		err = cec_register_adapter(port->adap, extron->dev);1652		if (err < 0)1653			goto disable_ports;1654		port->dev = &port->adap->devnode.dev;1655		port->cec_was_registered = true;1656		/*1657		 * This driver is unusual in that the whole setup takes place1658		 * in a thread since it can take such a long time before the1659		 * Extron Splitter boots up, and you do not want to block the1660		 * probe function on this driver. In addition, as soon as1661		 * CEC adapters come online, they can be used, and you cannot1662		 * just unregister them again if an error occurs, since that1663		 * can delete the underlying CEC adapter, which might already1664		 * be in use.1665		 *1666		 * So we take an additional reference to the adapter. This1667		 * allows us to unregister the device node if needed, without1668		 * deleting the actual adapter.1669		 *1670		 * In the disconnect function we will do the corresponding1671		 * put call to ensure the adapter is deleted.1672		 */1673		cec_get_device(port->adap);1674 1675		/*1676		 * If vendor_id wasn't set, then userspace configures the1677		 * CEC devices. Otherwise the driver configures the CEC1678		 * devices as TV (input) and Playback (outputs) devices1679		 * and the driver processes all CEC messages.1680		 */1681		if (!vendor_id)1682			continue;1683 1684		log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;1685		log_addrs.num_log_addrs = 1;1686		log_addrs.vendor_id = vendor_id;1687		if (port->is_input) {1688			strscpy(log_addrs.osd_name, "Splitter In",1689				sizeof(log_addrs.osd_name));1690			log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV;1691			log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_TV;1692			log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV;1693		} else {1694			snprintf(log_addrs.osd_name, sizeof(log_addrs.osd_name),1695				 "Splitter Out%u", port->port.port);1696			log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;1697			log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_PLAYBACK;1698			log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK;1699		}1700		err = cec_s_log_addrs(port->adap, &log_addrs, false);1701		if (err < 0)1702			goto disable_ports;1703	}1704	poll_splitter = true;1705 1706	port = extron->ports[extron->num_out_ports];1707	while (!kthread_should_stop()) {1708		ssleep(1);1709		if (hpd_never_low != extron->hpd_never_low) {1710			/*1711			 * Keep input port HPD high at all times, or pull it low1712			 * if all output ports also have a low HPD1713			 */1714			if (hpd_never_low) {1715				dev_info(extron->dev, "Always keep input HPD high\n");1716				extron_send_and_wait(extron, NULL, "W0ihpd", "Ihpd0");1717			} else {1718				dev_info(extron->dev, "Pull input HPD low if all output HPDs are low\n");1719				extron_send_and_wait(extron, NULL, "W1ihpd", "Ihpd1");1720			}1721			extron->hpd_never_low = hpd_never_low;1722		}1723		if (poll_splitter &&1724		    cec_splitter_poll(&extron->splitter, port->adap, debug) &&1725		    manufacturer_name[0]) {1726			/*1727			 * Sinks were lost, so see if the input edid needs to1728			 * be updated.1729			 */1730			cancel_delayed_work_sync(&extron->work_update_edid);1731			schedule_delayed_work(&extron->work_update_edid,1732					      msecs_to_jiffies(1000));1733		}1734	}1735	return 0;1736 1737disable_ports:1738	extron->is_ready = false;1739	for (p = 0; p < extron->num_ports; p++) {1740		struct extron_port *port = extron->ports[p];1741 1742		if (!port)1743			continue;1744		port->disconnected = true;1745		cancel_work_sync(&port->irq_work);1746		video_unregister_device(&port->vdev);1747		if (port->cec_was_registered)1748			cec_unregister_adapter(port->adap);1749	}1750	cancel_delayed_work_sync(&extron->work_update_edid);1751	complete(&extron->edid_completion);1752	dev_err(extron->dev, "Setup failed with error %d\n", err);1753	while (!kthread_should_stop())1754		ssleep(1);1755	return err;1756}1757 1758static int extron_connect(struct serio *serio, struct serio_driver *drv)1759{1760	struct extron *extron;1761	int err = -ENOMEM;1762 1763	if (manufacturer_name[0] &&1764	    (!isupper(manufacturer_name[0]) ||1765	     !isupper(manufacturer_name[1]) ||1766	     !isupper(manufacturer_name[2]))) {1767		dev_warn(&serio->dev, "ignoring invalid manufacturer name\n");1768		manufacturer_name[0] = 0;1769	}1770 1771	extron = kzalloc(sizeof(*extron), GFP_KERNEL);1772 1773	if (!extron)1774		return -ENOMEM;1775 1776	extron->serio = serio;1777	extron->dev = &serio->dev;1778	mutex_init(&extron->serio_lock);1779	mutex_init(&extron->edid_lock);1780	INIT_DELAYED_WORK(&extron->work_update_edid, update_edid_work);1781 1782	err = v4l2_device_register(extron->dev, &extron->v4l2_dev);1783	if (err)1784		goto free_device;1785 1786	err = serio_open(serio, drv);1787	if (err)1788		goto unreg_v4l2_dev;1789 1790	serio_set_drvdata(serio, extron);1791	init_completion(&extron->edid_completion);1792 1793	extron->kthread_setup = kthread_run(extron_setup_thread, extron,1794					    "extron-da-hd-4k-plus-cec-%s", dev_name(&serio->dev));1795	if (!IS_ERR(extron->kthread_setup))1796		return 0;1797 1798	dev_err(extron->dev, "kthread_run() failed\n");1799	err = PTR_ERR(extron->kthread_setup);1800 1801	extron->serio = NULL;1802	serio_set_drvdata(serio, NULL);1803	serio_close(serio);1804unreg_v4l2_dev:1805	v4l2_device_unregister(&extron->v4l2_dev);1806free_device:1807	mutex_destroy(&extron->edid_lock);1808	mutex_destroy(&extron->serio_lock);1809	kfree(extron);1810	return err;1811}1812 1813static const struct serio_device_id extron_serio_ids[] = {1814	{1815		.type	= SERIO_RS232,1816		.proto	= SERIO_EXTRON_DA_HD_4K_PLUS,1817		.id	= SERIO_ANY,1818		.extra	= SERIO_ANY,1819	},1820	{ 0 }1821};1822 1823MODULE_DEVICE_TABLE(serio, extron_serio_ids);1824 1825static struct serio_driver extron_drv = {1826	.driver		= {1827		.name	= "extron-da-hd-4k-plus-cec",1828	},1829	.description	= "Extron DA HD 4K PLUS HDMI CEC driver",1830	.id_table	= extron_serio_ids,1831	.interrupt	= extron_interrupt,1832	.connect	= extron_connect,1833	.disconnect	= extron_disconnect,1834};1835 1836module_serio_driver(extron_drv);1837