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