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