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