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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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