18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Pulse Eight HDMI CEC driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * Notes: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * - Devices with firmware version < 2 do not store their configuration in 128c2ecf20Sopenharmony_ci * EEPROM. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * - In autonomous mode, only messages from a TV will be acknowledged, even 158c2ecf20Sopenharmony_ci * polling messages. Upon receiving a message from a TV, the dongle will 168c2ecf20Sopenharmony_ci * respond to messages from any logical address. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * - In autonomous mode, the dongle will by default reply Feature Abort 198c2ecf20Sopenharmony_ci * [Unrecognized Opcode] when it receives Give Device Vendor ID. It will 208c2ecf20Sopenharmony_ci * however observe vendor ID's reported by other devices and possibly 218c2ecf20Sopenharmony_ci * alter this behavior. When TV's (and TV's only) report that their vendor ID 228c2ecf20Sopenharmony_ci * is LG (0x00e091), the dongle will itself reply that it has the same vendor 238c2ecf20Sopenharmony_ci * ID, and it will respond to at least one vendor specific command. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * - In autonomous mode, the dongle is known to attempt wakeup if it receives 268c2ecf20Sopenharmony_ci * <User Control Pressed> ["Power On"], ["Power] or ["Power Toggle"], or if it 278c2ecf20Sopenharmony_ci * receives <Set Stream Path> with its own physical address. It also does this 288c2ecf20Sopenharmony_ci * if it receives <Vendor Specific Command> [0x03 0x00] from an LG TV. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/completion.h> 328c2ecf20Sopenharmony_ci#include <linux/init.h> 338c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 378c2ecf20Sopenharmony_ci#include <linux/serio.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/time.h> 408c2ecf20Sopenharmony_ci#include <linux/delay.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <media/cec.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); 458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pulse Eight HDMI CEC driver"); 468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int debug; 498c2ecf20Sopenharmony_cistatic int persistent_config; 508c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 518c2ecf20Sopenharmony_cimodule_param(persistent_config, int, 0644); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-2)"); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)"); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cienum pulse8_msgcodes { 568c2ecf20Sopenharmony_ci MSGCODE_NOTHING = 0, 578c2ecf20Sopenharmony_ci MSGCODE_PING, 588c2ecf20Sopenharmony_ci MSGCODE_TIMEOUT_ERROR, 598c2ecf20Sopenharmony_ci MSGCODE_HIGH_ERROR, 608c2ecf20Sopenharmony_ci MSGCODE_LOW_ERROR, 618c2ecf20Sopenharmony_ci MSGCODE_FRAME_START, 628c2ecf20Sopenharmony_ci MSGCODE_FRAME_DATA, 638c2ecf20Sopenharmony_ci MSGCODE_RECEIVE_FAILED, 648c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, /* 0x08 */ 658c2ecf20Sopenharmony_ci MSGCODE_COMMAND_REJECTED, 668c2ecf20Sopenharmony_ci MSGCODE_SET_ACK_MASK, 678c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT, 688c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_EOM, 698c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_IDLETIME, 708c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_ACK_POLARITY, 718c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_LINE_TIMEOUT, 728c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_SUCCEEDED, /* 0x10 */ 738c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_FAILED_LINE, 748c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_FAILED_ACK, 758c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA, 768c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE, 778c2ecf20Sopenharmony_ci MSGCODE_FIRMWARE_VERSION, 788c2ecf20Sopenharmony_ci MSGCODE_START_BOOTLOADER, 798c2ecf20Sopenharmony_ci MSGCODE_GET_BUILDDATE, 808c2ecf20Sopenharmony_ci MSGCODE_SET_CONTROLLED, /* 0x18 */ 818c2ecf20Sopenharmony_ci MSGCODE_GET_AUTO_ENABLED, 828c2ecf20Sopenharmony_ci MSGCODE_SET_AUTO_ENABLED, 838c2ecf20Sopenharmony_ci MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS, 848c2ecf20Sopenharmony_ci MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS, 858c2ecf20Sopenharmony_ci MSGCODE_GET_LOGICAL_ADDRESS_MASK, 868c2ecf20Sopenharmony_ci MSGCODE_SET_LOGICAL_ADDRESS_MASK, 878c2ecf20Sopenharmony_ci MSGCODE_GET_PHYSICAL_ADDRESS, 888c2ecf20Sopenharmony_ci MSGCODE_SET_PHYSICAL_ADDRESS, /* 0x20 */ 898c2ecf20Sopenharmony_ci MSGCODE_GET_DEVICE_TYPE, 908c2ecf20Sopenharmony_ci MSGCODE_SET_DEVICE_TYPE, 918c2ecf20Sopenharmony_ci MSGCODE_GET_HDMI_VERSION, /* Removed in FW >= 10 */ 928c2ecf20Sopenharmony_ci MSGCODE_SET_HDMI_VERSION, 938c2ecf20Sopenharmony_ci MSGCODE_GET_OSD_NAME, 948c2ecf20Sopenharmony_ci MSGCODE_SET_OSD_NAME, 958c2ecf20Sopenharmony_ci MSGCODE_WRITE_EEPROM, 968c2ecf20Sopenharmony_ci MSGCODE_GET_ADAPTER_TYPE, /* 0x28 */ 978c2ecf20Sopenharmony_ci MSGCODE_SET_ACTIVE_SOURCE, 988c2ecf20Sopenharmony_ci MSGCODE_GET_AUTO_POWER_ON, /* New for FW >= 10 */ 998c2ecf20Sopenharmony_ci MSGCODE_SET_AUTO_POWER_ON, 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci MSGCODE_FRAME_EOM = 0x80, 1028c2ecf20Sopenharmony_ci MSGCODE_FRAME_ACK = 0x40, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const char * const pulse8_msgnames[] = { 1068c2ecf20Sopenharmony_ci "NOTHING", 1078c2ecf20Sopenharmony_ci "PING", 1088c2ecf20Sopenharmony_ci "TIMEOUT_ERROR", 1098c2ecf20Sopenharmony_ci "HIGH_ERROR", 1108c2ecf20Sopenharmony_ci "LOW_ERROR", 1118c2ecf20Sopenharmony_ci "FRAME_START", 1128c2ecf20Sopenharmony_ci "FRAME_DATA", 1138c2ecf20Sopenharmony_ci "RECEIVE_FAILED", 1148c2ecf20Sopenharmony_ci "COMMAND_ACCEPTED", 1158c2ecf20Sopenharmony_ci "COMMAND_REJECTED", 1168c2ecf20Sopenharmony_ci "SET_ACK_MASK", 1178c2ecf20Sopenharmony_ci "TRANSMIT", 1188c2ecf20Sopenharmony_ci "TRANSMIT_EOM", 1198c2ecf20Sopenharmony_ci "TRANSMIT_IDLETIME", 1208c2ecf20Sopenharmony_ci "TRANSMIT_ACK_POLARITY", 1218c2ecf20Sopenharmony_ci "TRANSMIT_LINE_TIMEOUT", 1228c2ecf20Sopenharmony_ci "TRANSMIT_SUCCEEDED", 1238c2ecf20Sopenharmony_ci "TRANSMIT_FAILED_LINE", 1248c2ecf20Sopenharmony_ci "TRANSMIT_FAILED_ACK", 1258c2ecf20Sopenharmony_ci "TRANSMIT_FAILED_TIMEOUT_DATA", 1268c2ecf20Sopenharmony_ci "TRANSMIT_FAILED_TIMEOUT_LINE", 1278c2ecf20Sopenharmony_ci "FIRMWARE_VERSION", 1288c2ecf20Sopenharmony_ci "START_BOOTLOADER", 1298c2ecf20Sopenharmony_ci "GET_BUILDDATE", 1308c2ecf20Sopenharmony_ci "SET_CONTROLLED", 1318c2ecf20Sopenharmony_ci "GET_AUTO_ENABLED", 1328c2ecf20Sopenharmony_ci "SET_AUTO_ENABLED", 1338c2ecf20Sopenharmony_ci "GET_DEFAULT_LOGICAL_ADDRESS", 1348c2ecf20Sopenharmony_ci "SET_DEFAULT_LOGICAL_ADDRESS", 1358c2ecf20Sopenharmony_ci "GET_LOGICAL_ADDRESS_MASK", 1368c2ecf20Sopenharmony_ci "SET_LOGICAL_ADDRESS_MASK", 1378c2ecf20Sopenharmony_ci "GET_PHYSICAL_ADDRESS", 1388c2ecf20Sopenharmony_ci "SET_PHYSICAL_ADDRESS", 1398c2ecf20Sopenharmony_ci "GET_DEVICE_TYPE", 1408c2ecf20Sopenharmony_ci "SET_DEVICE_TYPE", 1418c2ecf20Sopenharmony_ci "GET_HDMI_VERSION", 1428c2ecf20Sopenharmony_ci "SET_HDMI_VERSION", 1438c2ecf20Sopenharmony_ci "GET_OSD_NAME", 1448c2ecf20Sopenharmony_ci "SET_OSD_NAME", 1458c2ecf20Sopenharmony_ci "WRITE_EEPROM", 1468c2ecf20Sopenharmony_ci "GET_ADAPTER_TYPE", 1478c2ecf20Sopenharmony_ci "SET_ACTIVE_SOURCE", 1488c2ecf20Sopenharmony_ci "GET_AUTO_POWER_ON", 1498c2ecf20Sopenharmony_ci "SET_AUTO_POWER_ON", 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const char *pulse8_msgname(u8 cmd) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci static char unknown_msg[5]; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if ((cmd & 0x3f) < ARRAY_SIZE(pulse8_msgnames)) 1578c2ecf20Sopenharmony_ci return pulse8_msgnames[cmd & 0x3f]; 1588c2ecf20Sopenharmony_ci snprintf(unknown_msg, sizeof(unknown_msg), "0x%02x", cmd); 1598c2ecf20Sopenharmony_ci return unknown_msg; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define MSGSTART 0xff 1638c2ecf20Sopenharmony_ci#define MSGEND 0xfe 1648c2ecf20Sopenharmony_ci#define MSGESC 0xfd 1658c2ecf20Sopenharmony_ci#define MSGOFFSET 3 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define DATA_SIZE 256 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#define PING_PERIOD (15 * HZ) 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define NUM_MSGS 8 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct pulse8 { 1748c2ecf20Sopenharmony_ci struct device *dev; 1758c2ecf20Sopenharmony_ci struct serio *serio; 1768c2ecf20Sopenharmony_ci struct cec_adapter *adap; 1778c2ecf20Sopenharmony_ci unsigned int vers; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci struct delayed_work ping_eeprom_work; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci struct work_struct irq_work; 1828c2ecf20Sopenharmony_ci struct cec_msg rx_msg[NUM_MSGS]; 1838c2ecf20Sopenharmony_ci unsigned int rx_msg_cur_idx, rx_msg_num; 1848c2ecf20Sopenharmony_ci /* protect rx_msg_cur_idx and rx_msg_num */ 1858c2ecf20Sopenharmony_ci spinlock_t msg_lock; 1868c2ecf20Sopenharmony_ci u8 new_rx_msg[CEC_MAX_MSG_SIZE]; 1878c2ecf20Sopenharmony_ci u8 new_rx_msg_len; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci struct work_struct tx_work; 1908c2ecf20Sopenharmony_ci u32 tx_done_status; 1918c2ecf20Sopenharmony_ci u32 tx_signal_free_time; 1928c2ecf20Sopenharmony_ci struct cec_msg tx_msg; 1938c2ecf20Sopenharmony_ci bool tx_msg_is_bcast; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci struct completion cmd_done; 1968c2ecf20Sopenharmony_ci u8 data[DATA_SIZE]; 1978c2ecf20Sopenharmony_ci unsigned int len; 1988c2ecf20Sopenharmony_ci u8 buf[DATA_SIZE]; 1998c2ecf20Sopenharmony_ci unsigned int idx; 2008c2ecf20Sopenharmony_ci bool escape; 2018c2ecf20Sopenharmony_ci bool started; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* locks access to the adapter */ 2048c2ecf20Sopenharmony_ci struct mutex lock; 2058c2ecf20Sopenharmony_ci bool config_pending; 2068c2ecf20Sopenharmony_ci bool restoring_config; 2078c2ecf20Sopenharmony_ci bool autonomous; 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int err = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci err = serio_write(serio, MSGSTART); 2158c2ecf20Sopenharmony_ci if (err) 2168c2ecf20Sopenharmony_ci return err; 2178c2ecf20Sopenharmony_ci for (; !err && cmd_len; command++, cmd_len--) { 2188c2ecf20Sopenharmony_ci if (*command >= MSGESC) { 2198c2ecf20Sopenharmony_ci err = serio_write(serio, MSGESC); 2208c2ecf20Sopenharmony_ci if (!err) 2218c2ecf20Sopenharmony_ci err = serio_write(serio, *command - MSGOFFSET); 2228c2ecf20Sopenharmony_ci } else { 2238c2ecf20Sopenharmony_ci err = serio_write(serio, *command); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci if (!err) 2278c2ecf20Sopenharmony_ci err = serio_write(serio, MSGEND); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int pulse8_send_and_wait_once(struct pulse8 *pulse8, 2338c2ecf20Sopenharmony_ci const u8 *cmd, u8 cmd_len, 2348c2ecf20Sopenharmony_ci u8 response, u8 size) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int err; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (debug > 1) 2398c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "transmit %s: %*ph\n", 2408c2ecf20Sopenharmony_ci pulse8_msgname(cmd[0]), cmd_len, cmd); 2418c2ecf20Sopenharmony_ci init_completion(&pulse8->cmd_done); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci err = pulse8_send(pulse8->serio, cmd, cmd_len); 2448c2ecf20Sopenharmony_ci if (err) 2458c2ecf20Sopenharmony_ci return err; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ)) 2488c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2498c2ecf20Sopenharmony_ci if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED && 2508c2ecf20Sopenharmony_ci cmd[0] != MSGCODE_SET_CONTROLLED && 2518c2ecf20Sopenharmony_ci cmd[0] != MSGCODE_SET_AUTO_ENABLED && 2528c2ecf20Sopenharmony_ci cmd[0] != MSGCODE_GET_BUILDDATE) 2538c2ecf20Sopenharmony_ci return -ENOTTY; 2548c2ecf20Sopenharmony_ci if (response && 2558c2ecf20Sopenharmony_ci ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) { 2568c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "transmit %s failed with %s\n", 2578c2ecf20Sopenharmony_ci pulse8_msgname(cmd[0]), 2588c2ecf20Sopenharmony_ci pulse8_msgname(pulse8->data[0])); 2598c2ecf20Sopenharmony_ci return -EIO; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int pulse8_send_and_wait(struct pulse8 *pulse8, 2658c2ecf20Sopenharmony_ci const u8 *cmd, u8 cmd_len, u8 response, u8 size) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci u8 cmd_sc[2]; 2688c2ecf20Sopenharmony_ci int err; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size); 2718c2ecf20Sopenharmony_ci if (err != -ENOTTY) 2728c2ecf20Sopenharmony_ci return err; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci cmd_sc[0] = MSGCODE_SET_CONTROLLED; 2758c2ecf20Sopenharmony_ci cmd_sc[1] = 1; 2768c2ecf20Sopenharmony_ci err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2, 2778c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 1); 2788c2ecf20Sopenharmony_ci if (!err) 2798c2ecf20Sopenharmony_ci err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, 2808c2ecf20Sopenharmony_ci response, size); 2818c2ecf20Sopenharmony_ci return err == -ENOTTY ? -EIO : err; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void pulse8_tx_work_handler(struct work_struct *work) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = container_of(work, struct pulse8, tx_work); 2878c2ecf20Sopenharmony_ci struct cec_msg *msg = &pulse8->tx_msg; 2888c2ecf20Sopenharmony_ci unsigned int i; 2898c2ecf20Sopenharmony_ci u8 cmd[2]; 2908c2ecf20Sopenharmony_ci int err; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (msg->len == 0) 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci mutex_lock(&pulse8->lock); 2968c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_TRANSMIT_IDLETIME; 2978c2ecf20Sopenharmony_ci cmd[1] = pulse8->tx_signal_free_time; 2988c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 2998c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 1); 3008c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY; 3018c2ecf20Sopenharmony_ci cmd[1] = cec_msg_is_broadcast(msg); 3028c2ecf20Sopenharmony_ci pulse8->tx_msg_is_bcast = cec_msg_is_broadcast(msg); 3038c2ecf20Sopenharmony_ci if (!err) 3048c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 3058c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 1); 3068c2ecf20Sopenharmony_ci cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; 3078c2ecf20Sopenharmony_ci cmd[1] = msg->msg[0]; 3088c2ecf20Sopenharmony_ci if (!err) 3098c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 3108c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 1); 3118c2ecf20Sopenharmony_ci if (!err && msg->len > 1) { 3128c2ecf20Sopenharmony_ci for (i = 1; !err && i < msg->len; i++) { 3138c2ecf20Sopenharmony_ci cmd[0] = ((i == msg->len - 1)) ? 3148c2ecf20Sopenharmony_ci MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; 3158c2ecf20Sopenharmony_ci cmd[1] = msg->msg[i]; 3168c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 3178c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 1); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci if (err && debug) 3218c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "%s(0x%02x) failed with error %d for msg %*ph\n", 3228c2ecf20Sopenharmony_ci pulse8_msgname(cmd[0]), cmd[1], 3238c2ecf20Sopenharmony_ci err, msg->len, msg->msg); 3248c2ecf20Sopenharmony_ci msg->len = 0; 3258c2ecf20Sopenharmony_ci mutex_unlock(&pulse8->lock); 3268c2ecf20Sopenharmony_ci if (err) 3278c2ecf20Sopenharmony_ci cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void pulse8_irq_work_handler(struct work_struct *work) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = 3338c2ecf20Sopenharmony_ci container_of(work, struct pulse8, irq_work); 3348c2ecf20Sopenharmony_ci unsigned long flags; 3358c2ecf20Sopenharmony_ci u32 status; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci spin_lock_irqsave(&pulse8->msg_lock, flags); 3388c2ecf20Sopenharmony_ci while (pulse8->rx_msg_num) { 3398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pulse8->msg_lock, flags); 3408c2ecf20Sopenharmony_ci if (debug) 3418c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "adap received %*ph\n", 3428c2ecf20Sopenharmony_ci pulse8->rx_msg[pulse8->rx_msg_cur_idx].len, 3438c2ecf20Sopenharmony_ci pulse8->rx_msg[pulse8->rx_msg_cur_idx].msg); 3448c2ecf20Sopenharmony_ci cec_received_msg(pulse8->adap, 3458c2ecf20Sopenharmony_ci &pulse8->rx_msg[pulse8->rx_msg_cur_idx]); 3468c2ecf20Sopenharmony_ci spin_lock_irqsave(&pulse8->msg_lock, flags); 3478c2ecf20Sopenharmony_ci if (pulse8->rx_msg_num) 3488c2ecf20Sopenharmony_ci pulse8->rx_msg_num--; 3498c2ecf20Sopenharmony_ci pulse8->rx_msg_cur_idx = 3508c2ecf20Sopenharmony_ci (pulse8->rx_msg_cur_idx + 1) % NUM_MSGS; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pulse8->msg_lock, flags); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mutex_lock(&pulse8->lock); 3558c2ecf20Sopenharmony_ci status = pulse8->tx_done_status; 3568c2ecf20Sopenharmony_ci pulse8->tx_done_status = 0; 3578c2ecf20Sopenharmony_ci mutex_unlock(&pulse8->lock); 3588c2ecf20Sopenharmony_ci if (status) 3598c2ecf20Sopenharmony_ci cec_transmit_attempt_done(pulse8->adap, status); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data, 3638c2ecf20Sopenharmony_ci unsigned int flags) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = serio_get_drvdata(serio); 3668c2ecf20Sopenharmony_ci unsigned long irq_flags; 3678c2ecf20Sopenharmony_ci unsigned int idx; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!pulse8->started && data != MSGSTART) 3708c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3718c2ecf20Sopenharmony_ci if (data == MSGESC) { 3728c2ecf20Sopenharmony_ci pulse8->escape = true; 3738c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci if (pulse8->escape) { 3768c2ecf20Sopenharmony_ci data += MSGOFFSET; 3778c2ecf20Sopenharmony_ci pulse8->escape = false; 3788c2ecf20Sopenharmony_ci } else if (data == MSGEND) { 3798c2ecf20Sopenharmony_ci u8 msgcode = pulse8->buf[0]; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (debug > 1) 3828c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "received %s: %*ph\n", 3838c2ecf20Sopenharmony_ci pulse8_msgname(msgcode), 3848c2ecf20Sopenharmony_ci pulse8->idx, pulse8->buf); 3858c2ecf20Sopenharmony_ci switch (msgcode & 0x3f) { 3868c2ecf20Sopenharmony_ci case MSGCODE_FRAME_START: 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * Test if we are receiving a new msg when a previous 3898c2ecf20Sopenharmony_ci * message is still pending. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci if (!(msgcode & MSGCODE_FRAME_EOM)) { 3928c2ecf20Sopenharmony_ci pulse8->new_rx_msg_len = 1; 3938c2ecf20Sopenharmony_ci pulse8->new_rx_msg[0] = pulse8->buf[1]; 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci fallthrough; 3978c2ecf20Sopenharmony_ci case MSGCODE_FRAME_DATA: 3988c2ecf20Sopenharmony_ci if (pulse8->new_rx_msg_len < CEC_MAX_MSG_SIZE) 3998c2ecf20Sopenharmony_ci pulse8->new_rx_msg[pulse8->new_rx_msg_len++] = 4008c2ecf20Sopenharmony_ci pulse8->buf[1]; 4018c2ecf20Sopenharmony_ci if (!(msgcode & MSGCODE_FRAME_EOM)) 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci spin_lock_irqsave(&pulse8->msg_lock, irq_flags); 4058c2ecf20Sopenharmony_ci idx = (pulse8->rx_msg_cur_idx + pulse8->rx_msg_num) % 4068c2ecf20Sopenharmony_ci NUM_MSGS; 4078c2ecf20Sopenharmony_ci if (pulse8->rx_msg_num == NUM_MSGS) { 4088c2ecf20Sopenharmony_ci dev_warn(pulse8->dev, 4098c2ecf20Sopenharmony_ci "message queue is full, dropping %*ph\n", 4108c2ecf20Sopenharmony_ci pulse8->new_rx_msg_len, 4118c2ecf20Sopenharmony_ci pulse8->new_rx_msg); 4128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pulse8->msg_lock, 4138c2ecf20Sopenharmony_ci irq_flags); 4148c2ecf20Sopenharmony_ci pulse8->new_rx_msg_len = 0; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci pulse8->rx_msg_num++; 4188c2ecf20Sopenharmony_ci memcpy(pulse8->rx_msg[idx].msg, pulse8->new_rx_msg, 4198c2ecf20Sopenharmony_ci pulse8->new_rx_msg_len); 4208c2ecf20Sopenharmony_ci pulse8->rx_msg[idx].len = pulse8->new_rx_msg_len; 4218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pulse8->msg_lock, irq_flags); 4228c2ecf20Sopenharmony_ci schedule_work(&pulse8->irq_work); 4238c2ecf20Sopenharmony_ci pulse8->new_rx_msg_len = 0; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case MSGCODE_TRANSMIT_SUCCEEDED: 4268c2ecf20Sopenharmony_ci WARN_ON(pulse8->tx_done_status); 4278c2ecf20Sopenharmony_ci pulse8->tx_done_status = CEC_TX_STATUS_OK; 4288c2ecf20Sopenharmony_ci schedule_work(&pulse8->irq_work); 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci case MSGCODE_TRANSMIT_FAILED_ACK: 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * A NACK for a broadcast message makes no sense, these 4338c2ecf20Sopenharmony_ci * seem to be spurious messages and are skipped. 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci if (pulse8->tx_msg_is_bcast) 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci WARN_ON(pulse8->tx_done_status); 4388c2ecf20Sopenharmony_ci pulse8->tx_done_status = CEC_TX_STATUS_NACK; 4398c2ecf20Sopenharmony_ci schedule_work(&pulse8->irq_work); 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case MSGCODE_TRANSMIT_FAILED_LINE: 4428c2ecf20Sopenharmony_ci case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: 4438c2ecf20Sopenharmony_ci case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: 4448c2ecf20Sopenharmony_ci WARN_ON(pulse8->tx_done_status); 4458c2ecf20Sopenharmony_ci pulse8->tx_done_status = CEC_TX_STATUS_ERROR; 4468c2ecf20Sopenharmony_ci schedule_work(&pulse8->irq_work); 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci case MSGCODE_HIGH_ERROR: 4498c2ecf20Sopenharmony_ci case MSGCODE_LOW_ERROR: 4508c2ecf20Sopenharmony_ci case MSGCODE_RECEIVE_FAILED: 4518c2ecf20Sopenharmony_ci case MSGCODE_TIMEOUT_ERROR: 4528c2ecf20Sopenharmony_ci pulse8->new_rx_msg_len = 0; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci case MSGCODE_COMMAND_ACCEPTED: 4558c2ecf20Sopenharmony_ci case MSGCODE_COMMAND_REJECTED: 4568c2ecf20Sopenharmony_ci default: 4578c2ecf20Sopenharmony_ci if (pulse8->idx == 0) 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci memcpy(pulse8->data, pulse8->buf, pulse8->idx); 4608c2ecf20Sopenharmony_ci pulse8->len = pulse8->idx; 4618c2ecf20Sopenharmony_ci complete(&pulse8->cmd_done); 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci pulse8->idx = 0; 4658c2ecf20Sopenharmony_ci pulse8->started = false; 4668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4678c2ecf20Sopenharmony_ci } else if (data == MSGSTART) { 4688c2ecf20Sopenharmony_ci pulse8->idx = 0; 4698c2ecf20Sopenharmony_ci pulse8->started = true; 4708c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (pulse8->idx >= DATA_SIZE) { 4748c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, 4758c2ecf20Sopenharmony_ci "throwing away %d bytes of garbage\n", pulse8->idx); 4768c2ecf20Sopenharmony_ci pulse8->idx = 0; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci pulse8->buf[pulse8->idx++] = data; 4798c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = cec_get_drvdata(adap); 4858c2ecf20Sopenharmony_ci u8 cmd[16]; 4868c2ecf20Sopenharmony_ci int err; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mutex_lock(&pulse8->lock); 4898c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_CONTROLLED; 4908c2ecf20Sopenharmony_ci cmd[1] = enable; 4918c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 4928c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 1); 4938c2ecf20Sopenharmony_ci if (!enable) { 4948c2ecf20Sopenharmony_ci pulse8->rx_msg_num = 0; 4958c2ecf20Sopenharmony_ci pulse8->tx_done_status = 0; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci mutex_unlock(&pulse8->lock); 4988c2ecf20Sopenharmony_ci return enable ? err : 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = cec_get_drvdata(adap); 5048c2ecf20Sopenharmony_ci u16 mask = 0; 5058c2ecf20Sopenharmony_ci u16 pa = adap->phys_addr; 5068c2ecf20Sopenharmony_ci u8 cmd[16]; 5078c2ecf20Sopenharmony_ci int err = 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mutex_lock(&pulse8->lock); 5108c2ecf20Sopenharmony_ci if (log_addr != CEC_LOG_ADDR_INVALID) 5118c2ecf20Sopenharmony_ci mask = 1 << log_addr; 5128c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_ACK_MASK; 5138c2ecf20Sopenharmony_ci cmd[1] = mask >> 8; 5148c2ecf20Sopenharmony_ci cmd[2] = mask & 0xff; 5158c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 3, 5168c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5178c2ecf20Sopenharmony_ci if ((err && mask != 0) || pulse8->restoring_config) 5188c2ecf20Sopenharmony_ci goto unlock; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_AUTO_ENABLED; 5218c2ecf20Sopenharmony_ci cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1; 5228c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 5238c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5248c2ecf20Sopenharmony_ci if (err) 5258c2ecf20Sopenharmony_ci goto unlock; 5268c2ecf20Sopenharmony_ci pulse8->autonomous = cmd[1]; 5278c2ecf20Sopenharmony_ci if (log_addr == CEC_LOG_ADDR_INVALID) 5288c2ecf20Sopenharmony_ci goto unlock; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_DEVICE_TYPE; 5318c2ecf20Sopenharmony_ci cmd[1] = adap->log_addrs.primary_device_type[0]; 5328c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 5338c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5348c2ecf20Sopenharmony_ci if (err) 5358c2ecf20Sopenharmony_ci goto unlock; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci switch (adap->log_addrs.primary_device_type[0]) { 5388c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_TV: 5398c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_TV; 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_RECORD: 5428c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_RECORD; 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_TUNER: 5458c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_TUNER; 5468c2ecf20Sopenharmony_ci break; 5478c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_PLAYBACK: 5488c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_PLAYBACK; 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: 5518c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM; 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_SWITCH: 5548c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_UNREGISTERED; 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_PROCESSOR: 5578c2ecf20Sopenharmony_ci mask = CEC_LOG_ADDR_MASK_SPECIFIC; 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci default: 5608c2ecf20Sopenharmony_ci mask = 0; 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK; 5648c2ecf20Sopenharmony_ci cmd[1] = mask >> 8; 5658c2ecf20Sopenharmony_ci cmd[2] = mask & 0xff; 5668c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 3, 5678c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5688c2ecf20Sopenharmony_ci if (err) 5698c2ecf20Sopenharmony_ci goto unlock; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS; 5728c2ecf20Sopenharmony_ci cmd[1] = log_addr; 5738c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 5748c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5758c2ecf20Sopenharmony_ci if (err) 5768c2ecf20Sopenharmony_ci goto unlock; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS; 5798c2ecf20Sopenharmony_ci cmd[1] = pa >> 8; 5808c2ecf20Sopenharmony_ci cmd[2] = pa & 0xff; 5818c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 3, 5828c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5838c2ecf20Sopenharmony_ci if (err) 5848c2ecf20Sopenharmony_ci goto unlock; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (pulse8->vers < 10) { 5878c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_HDMI_VERSION; 5888c2ecf20Sopenharmony_ci cmd[1] = adap->log_addrs.cec_version; 5898c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 2, 5908c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 5918c2ecf20Sopenharmony_ci if (err) 5928c2ecf20Sopenharmony_ci goto unlock; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (adap->log_addrs.osd_name[0]) { 5968c2ecf20Sopenharmony_ci size_t osd_len = strlen(adap->log_addrs.osd_name); 5978c2ecf20Sopenharmony_ci char *osd_str = cmd + 1; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_SET_OSD_NAME; 6008c2ecf20Sopenharmony_ci strscpy(cmd + 1, adap->log_addrs.osd_name, sizeof(cmd) - 1); 6018c2ecf20Sopenharmony_ci if (osd_len < 4) { 6028c2ecf20Sopenharmony_ci memset(osd_str + osd_len, ' ', 4 - osd_len); 6038c2ecf20Sopenharmony_ci osd_len = 4; 6048c2ecf20Sopenharmony_ci osd_str[osd_len] = '\0'; 6058c2ecf20Sopenharmony_ci strscpy(adap->log_addrs.osd_name, osd_str, 6068c2ecf20Sopenharmony_ci sizeof(adap->log_addrs.osd_name)); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len, 6098c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0); 6108c2ecf20Sopenharmony_ci if (err) 6118c2ecf20Sopenharmony_ci goto unlock; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciunlock: 6158c2ecf20Sopenharmony_ci if (pulse8->restoring_config) 6168c2ecf20Sopenharmony_ci pulse8->restoring_config = false; 6178c2ecf20Sopenharmony_ci else 6188c2ecf20Sopenharmony_ci pulse8->config_pending = true; 6198c2ecf20Sopenharmony_ci mutex_unlock(&pulse8->lock); 6208c2ecf20Sopenharmony_ci return log_addr == CEC_LOG_ADDR_INVALID ? 0 : err; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, 6248c2ecf20Sopenharmony_ci u32 signal_free_time, struct cec_msg *msg) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = cec_get_drvdata(adap); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci pulse8->tx_msg = *msg; 6298c2ecf20Sopenharmony_ci if (debug) 6308c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "adap transmit %*ph\n", 6318c2ecf20Sopenharmony_ci msg->len, msg->msg); 6328c2ecf20Sopenharmony_ci pulse8->tx_signal_free_time = signal_free_time; 6338c2ecf20Sopenharmony_ci schedule_work(&pulse8->tx_work); 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic void pulse8_cec_adap_free(struct cec_adapter *adap) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = cec_get_drvdata(adap); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&pulse8->ping_eeprom_work); 6428c2ecf20Sopenharmony_ci cancel_work_sync(&pulse8->irq_work); 6438c2ecf20Sopenharmony_ci cancel_work_sync(&pulse8->tx_work); 6448c2ecf20Sopenharmony_ci kfree(pulse8); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic const struct cec_adap_ops pulse8_cec_adap_ops = { 6488c2ecf20Sopenharmony_ci .adap_enable = pulse8_cec_adap_enable, 6498c2ecf20Sopenharmony_ci .adap_log_addr = pulse8_cec_adap_log_addr, 6508c2ecf20Sopenharmony_ci .adap_transmit = pulse8_cec_adap_transmit, 6518c2ecf20Sopenharmony_ci .adap_free = pulse8_cec_adap_free, 6528c2ecf20Sopenharmony_ci}; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void pulse8_disconnect(struct serio *serio) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = serio_get_drvdata(serio); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci cec_unregister_adapter(pulse8->adap); 6598c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 6608c2ecf20Sopenharmony_ci serio_close(serio); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int pulse8_setup(struct pulse8 *pulse8, struct serio *serio, 6648c2ecf20Sopenharmony_ci struct cec_log_addrs *log_addrs, u16 *pa) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci u8 *data = pulse8->data + 1; 6678c2ecf20Sopenharmony_ci u8 cmd[2]; 6688c2ecf20Sopenharmony_ci int err; 6698c2ecf20Sopenharmony_ci time64_t date; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci pulse8->vers = 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_FIRMWARE_VERSION; 6748c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); 6758c2ecf20Sopenharmony_ci if (err) 6768c2ecf20Sopenharmony_ci return err; 6778c2ecf20Sopenharmony_ci pulse8->vers = (data[0] << 8) | data[1]; 6788c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers); 6798c2ecf20Sopenharmony_ci if (pulse8->vers < 2) { 6808c2ecf20Sopenharmony_ci *pa = CEC_PHYS_ADDR_INVALID; 6818c2ecf20Sopenharmony_ci return 0; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_BUILDDATE; 6858c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4); 6868c2ecf20Sopenharmony_ci if (err) 6878c2ecf20Sopenharmony_ci return err; 6888c2ecf20Sopenharmony_ci date = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; 6898c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "Firmware build date %ptT\n", &date); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "Persistent config:\n"); 6928c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_AUTO_ENABLED; 6938c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); 6948c2ecf20Sopenharmony_ci if (err) 6958c2ecf20Sopenharmony_ci return err; 6968c2ecf20Sopenharmony_ci pulse8->autonomous = data[0]; 6978c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "Autonomous mode: %s", 6988c2ecf20Sopenharmony_ci data[0] ? "on" : "off"); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (pulse8->vers >= 10) { 7018c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_AUTO_POWER_ON; 7028c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); 7038c2ecf20Sopenharmony_ci if (!err) 7048c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "Auto Power On: %s", 7058c2ecf20Sopenharmony_ci data[0] ? "on" : "off"); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_DEVICE_TYPE; 7098c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); 7108c2ecf20Sopenharmony_ci if (err) 7118c2ecf20Sopenharmony_ci return err; 7128c2ecf20Sopenharmony_ci log_addrs->primary_device_type[0] = data[0]; 7138c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]); 7148c2ecf20Sopenharmony_ci switch (log_addrs->primary_device_type[0]) { 7158c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_TV: 7168c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; 7178c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV; 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_RECORD: 7208c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD; 7218c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD; 7228c2ecf20Sopenharmony_ci break; 7238c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_TUNER: 7248c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER; 7258c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER; 7268c2ecf20Sopenharmony_ci break; 7278c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_PLAYBACK: 7288c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; 7298c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK; 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: 7328c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; 7338c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_SWITCH: 7368c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; 7378c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case CEC_OP_PRIM_DEVTYPE_PROCESSOR: 7408c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC; 7418c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci default: 7448c2ecf20Sopenharmony_ci log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; 7458c2ecf20Sopenharmony_ci log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; 7468c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n", 7478c2ecf20Sopenharmony_ci log_addrs->primary_device_type[0]); 7488c2ecf20Sopenharmony_ci break; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK; 7528c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); 7538c2ecf20Sopenharmony_ci if (err) 7548c2ecf20Sopenharmony_ci return err; 7558c2ecf20Sopenharmony_ci log_addrs->log_addr_mask = (data[0] << 8) | data[1]; 7568c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n", 7578c2ecf20Sopenharmony_ci log_addrs->log_addr_mask); 7588c2ecf20Sopenharmony_ci if (log_addrs->log_addr_mask) 7598c2ecf20Sopenharmony_ci log_addrs->num_log_addrs = 1; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS; 7628c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); 7638c2ecf20Sopenharmony_ci if (err) 7648c2ecf20Sopenharmony_ci return err; 7658c2ecf20Sopenharmony_ci *pa = (data[0] << 8) | data[1]; 7668c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n", 7678c2ecf20Sopenharmony_ci cec_phys_addr_exp(*pa)); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci log_addrs->cec_version = CEC_OP_CEC_VERSION_1_4; 7708c2ecf20Sopenharmony_ci if (pulse8->vers < 10) { 7718c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_HDMI_VERSION; 7728c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); 7738c2ecf20Sopenharmony_ci if (err) 7748c2ecf20Sopenharmony_ci return err; 7758c2ecf20Sopenharmony_ci log_addrs->cec_version = data[0]; 7768c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci cmd[0] = MSGCODE_GET_OSD_NAME; 7808c2ecf20Sopenharmony_ci err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0); 7818c2ecf20Sopenharmony_ci if (err) 7828c2ecf20Sopenharmony_ci return err; 7838c2ecf20Sopenharmony_ci strscpy(log_addrs->osd_name, data, sizeof(log_addrs->osd_name)); 7848c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int pulse8_apply_persistent_config(struct pulse8 *pulse8, 7908c2ecf20Sopenharmony_ci struct cec_log_addrs *log_addrs, 7918c2ecf20Sopenharmony_ci u16 pa) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci int err; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci err = cec_s_log_addrs(pulse8->adap, log_addrs, false); 7968c2ecf20Sopenharmony_ci if (err) 7978c2ecf20Sopenharmony_ci return err; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci cec_s_phys_addr(pulse8->adap, pa, false); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic void pulse8_ping_eeprom_work_handler(struct work_struct *work) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct pulse8 *pulse8 = 8078c2ecf20Sopenharmony_ci container_of(work, struct pulse8, ping_eeprom_work.work); 8088c2ecf20Sopenharmony_ci u8 cmd; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci mutex_lock(&pulse8->lock); 8118c2ecf20Sopenharmony_ci cmd = MSGCODE_PING; 8128c2ecf20Sopenharmony_ci if (pulse8_send_and_wait(pulse8, &cmd, 1, 8138c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0)) { 8148c2ecf20Sopenharmony_ci dev_warn(pulse8->dev, "failed to ping EEPROM\n"); 8158c2ecf20Sopenharmony_ci goto unlock; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (pulse8->vers < 2) 8198c2ecf20Sopenharmony_ci goto unlock; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (pulse8->config_pending && persistent_config) { 8228c2ecf20Sopenharmony_ci dev_dbg(pulse8->dev, "writing pending config to EEPROM\n"); 8238c2ecf20Sopenharmony_ci cmd = MSGCODE_WRITE_EEPROM; 8248c2ecf20Sopenharmony_ci if (pulse8_send_and_wait(pulse8, &cmd, 1, 8258c2ecf20Sopenharmony_ci MSGCODE_COMMAND_ACCEPTED, 0)) 8268c2ecf20Sopenharmony_ci dev_info(pulse8->dev, "failed to write pending config to EEPROM\n"); 8278c2ecf20Sopenharmony_ci else 8288c2ecf20Sopenharmony_ci pulse8->config_pending = false; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ciunlock: 8318c2ecf20Sopenharmony_ci schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); 8328c2ecf20Sopenharmony_ci mutex_unlock(&pulse8->lock); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int pulse8_connect(struct serio *serio, struct serio_driver *drv) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL; 8388c2ecf20Sopenharmony_ci struct pulse8 *pulse8; 8398c2ecf20Sopenharmony_ci int err = -ENOMEM; 8408c2ecf20Sopenharmony_ci struct cec_log_addrs log_addrs = {}; 8418c2ecf20Sopenharmony_ci u16 pa = CEC_PHYS_ADDR_INVALID; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (!pulse8) 8468c2ecf20Sopenharmony_ci return -ENOMEM; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci pulse8->serio = serio; 8498c2ecf20Sopenharmony_ci pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8, 8508c2ecf20Sopenharmony_ci dev_name(&serio->dev), caps, 1); 8518c2ecf20Sopenharmony_ci err = PTR_ERR_OR_ZERO(pulse8->adap); 8528c2ecf20Sopenharmony_ci if (err < 0) { 8538c2ecf20Sopenharmony_ci kfree(pulse8); 8548c2ecf20Sopenharmony_ci return err; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci pulse8->dev = &serio->dev; 8588c2ecf20Sopenharmony_ci serio_set_drvdata(serio, pulse8); 8598c2ecf20Sopenharmony_ci INIT_WORK(&pulse8->irq_work, pulse8_irq_work_handler); 8608c2ecf20Sopenharmony_ci INIT_WORK(&pulse8->tx_work, pulse8_tx_work_handler); 8618c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&pulse8->ping_eeprom_work, 8628c2ecf20Sopenharmony_ci pulse8_ping_eeprom_work_handler); 8638c2ecf20Sopenharmony_ci mutex_init(&pulse8->lock); 8648c2ecf20Sopenharmony_ci spin_lock_init(&pulse8->msg_lock); 8658c2ecf20Sopenharmony_ci pulse8->config_pending = false; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci err = serio_open(serio, drv); 8688c2ecf20Sopenharmony_ci if (err) 8698c2ecf20Sopenharmony_ci goto delete_adap; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci err = pulse8_setup(pulse8, serio, &log_addrs, &pa); 8728c2ecf20Sopenharmony_ci if (err) 8738c2ecf20Sopenharmony_ci goto close_serio; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci err = cec_register_adapter(pulse8->adap, &serio->dev); 8768c2ecf20Sopenharmony_ci if (err < 0) 8778c2ecf20Sopenharmony_ci goto close_serio; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci pulse8->dev = &pulse8->adap->devnode.dev; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (persistent_config && pulse8->autonomous) { 8828c2ecf20Sopenharmony_ci err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa); 8838c2ecf20Sopenharmony_ci if (err) 8848c2ecf20Sopenharmony_ci goto close_serio; 8858c2ecf20Sopenharmony_ci pulse8->restoring_config = true; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return 0; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ciclose_serio: 8938c2ecf20Sopenharmony_ci pulse8->serio = NULL; 8948c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 8958c2ecf20Sopenharmony_ci serio_close(serio); 8968c2ecf20Sopenharmony_cidelete_adap: 8978c2ecf20Sopenharmony_ci cec_delete_adapter(pulse8->adap); 8988c2ecf20Sopenharmony_ci return err; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic const struct serio_device_id pulse8_serio_ids[] = { 9028c2ecf20Sopenharmony_ci { 9038c2ecf20Sopenharmony_ci .type = SERIO_RS232, 9048c2ecf20Sopenharmony_ci .proto = SERIO_PULSE8_CEC, 9058c2ecf20Sopenharmony_ci .id = SERIO_ANY, 9068c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 9078c2ecf20Sopenharmony_ci }, 9088c2ecf20Sopenharmony_ci { 0 } 9098c2ecf20Sopenharmony_ci}; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(serio, pulse8_serio_ids); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic struct serio_driver pulse8_drv = { 9148c2ecf20Sopenharmony_ci .driver = { 9158c2ecf20Sopenharmony_ci .name = "pulse8-cec", 9168c2ecf20Sopenharmony_ci }, 9178c2ecf20Sopenharmony_ci .description = "Pulse Eight HDMI CEC driver", 9188c2ecf20Sopenharmony_ci .id_table = pulse8_serio_ids, 9198c2ecf20Sopenharmony_ci .interrupt = pulse8_interrupt, 9208c2ecf20Sopenharmony_ci .connect = pulse8_connect, 9218c2ecf20Sopenharmony_ci .disconnect = pulse8_disconnect, 9228c2ecf20Sopenharmony_ci}; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cimodule_serio_driver(pulse8_drv); 925