diff options
author | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2020-04-15 11:14:25 +0200 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2020-04-15 12:06:40 +0200 |
commit | a81068181aad78a5f6980408a4b6115d22bb8aed (patch) | |
tree | b5aa2c123bf45cd31419e8a0b1297c2155b30b5a /drivers/media/usb/pulse8-cec | |
parent | 4be5e8648b0c287aefc6ac3f3a0b12c696054f43 (diff) |
media: move CEC USB drivers to a separate directory
As CEC support doesn't depend on MEDIA_SUPPORT, let's
place the platform drivers outside the media menu.
As a side effect, instead of depends on USB, drivers
just select it.
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Diffstat (limited to 'drivers/media/usb/pulse8-cec')
-rw-r--r-- | drivers/media/usb/pulse8-cec/Kconfig | 12 | ||||
-rw-r--r-- | drivers/media/usb/pulse8-cec/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/usb/pulse8-cec/pulse8-cec.c | 909 |
3 files changed, 0 insertions, 923 deletions
diff --git a/drivers/media/usb/pulse8-cec/Kconfig b/drivers/media/usb/pulse8-cec/Kconfig deleted file mode 100644 index e802d30dbbee..000000000000 --- a/drivers/media/usb/pulse8-cec/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config USB_PULSE8_CEC - tristate "Pulse Eight HDMI CEC" - depends on USB_ACM - select CEC_CORE - select SERIO - select SERIO_SERPORT - help - This is a cec driver for the Pulse Eight HDMI CEC device. - - To compile this driver as a module, choose M here: the - module will be called pulse8-cec. diff --git a/drivers/media/usb/pulse8-cec/Makefile b/drivers/media/usb/pulse8-cec/Makefile deleted file mode 100644 index 7816c68bf928..000000000000 --- a/drivers/media/usb/pulse8-cec/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec.o diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/usb/pulse8-cec/pulse8-cec.c deleted file mode 100644 index 0655aa9ecf28..000000000000 --- a/drivers/media/usb/pulse8-cec/pulse8-cec.c +++ /dev/null @@ -1,909 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Pulse Eight HDMI CEC driver - * - * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl - */ - -/* - * Notes: - * - * - Devices with firmware version < 2 do not store their configuration in - * EEPROM. - * - * - In autonomous mode, only messages from a TV will be acknowledged, even - * polling messages. Upon receiving a message from a TV, the dongle will - * respond to messages from any logical address. - * - * - In autonomous mode, the dongle will by default reply Feature Abort - * [Unrecognized Opcode] when it receives Give Device Vendor ID. It will - * however observe vendor ID's reported by other devices and possibly - * alter this behavior. When TV's (and TV's only) report that their vendor ID - * is LG (0x00e091), the dongle will itself reply that it has the same vendor - * ID, and it will respond to at least one vendor specific command. - * - * - In autonomous mode, the dongle is known to attempt wakeup if it receives - * <User Control Pressed> ["Power On"], ["Power] or ["Power Toggle"], or if it - * receives <Set Stream Path> with its own physical address. It also does this - * if it receives <Vendor Specific Command> [0x03 0x00] from an LG TV. - */ - -#include <linux/completion.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/workqueue.h> -#include <linux/serio.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/delay.h> - -#include <media/cec.h> - -MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>"); -MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver"); -MODULE_LICENSE("GPL"); - -static int debug; -static int persistent_config; -module_param(debug, int, 0644); -module_param(persistent_config, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); -MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)"); - -enum pulse8_msgcodes { - MSGCODE_NOTHING = 0, - MSGCODE_PING, - MSGCODE_TIMEOUT_ERROR, - MSGCODE_HIGH_ERROR, - MSGCODE_LOW_ERROR, - MSGCODE_FRAME_START, - MSGCODE_FRAME_DATA, - MSGCODE_RECEIVE_FAILED, - MSGCODE_COMMAND_ACCEPTED, /* 0x08 */ - MSGCODE_COMMAND_REJECTED, - MSGCODE_SET_ACK_MASK, - MSGCODE_TRANSMIT, - MSGCODE_TRANSMIT_EOM, - MSGCODE_TRANSMIT_IDLETIME, - MSGCODE_TRANSMIT_ACK_POLARITY, - MSGCODE_TRANSMIT_LINE_TIMEOUT, - MSGCODE_TRANSMIT_SUCCEEDED, /* 0x10 */ - MSGCODE_TRANSMIT_FAILED_LINE, - MSGCODE_TRANSMIT_FAILED_ACK, - MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA, - MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE, - MSGCODE_FIRMWARE_VERSION, - MSGCODE_START_BOOTLOADER, - MSGCODE_GET_BUILDDATE, - MSGCODE_SET_CONTROLLED, /* 0x18 */ - MSGCODE_GET_AUTO_ENABLED, - MSGCODE_SET_AUTO_ENABLED, - MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS, - MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS, - MSGCODE_GET_LOGICAL_ADDRESS_MASK, - MSGCODE_SET_LOGICAL_ADDRESS_MASK, - MSGCODE_GET_PHYSICAL_ADDRESS, - MSGCODE_SET_PHYSICAL_ADDRESS, /* 0x20 */ - MSGCODE_GET_DEVICE_TYPE, - MSGCODE_SET_DEVICE_TYPE, - MSGCODE_GET_HDMI_VERSION, - MSGCODE_SET_HDMI_VERSION, - MSGCODE_GET_OSD_NAME, - MSGCODE_SET_OSD_NAME, - MSGCODE_WRITE_EEPROM, - MSGCODE_GET_ADAPTER_TYPE, /* 0x28 */ - MSGCODE_SET_ACTIVE_SOURCE, - - MSGCODE_FRAME_EOM = 0x80, - MSGCODE_FRAME_ACK = 0x40, -}; - -static const char * const pulse8_msgnames[] = { - "NOTHING", - "PING", - "TIMEOUT_ERROR", - "HIGH_ERROR", - "LOW_ERROR", - "FRAME_START", - "FRAME_DATA", - "RECEIVE_FAILED", - "COMMAND_ACCEPTED", - "COMMAND_REJECTED", - "SET_ACK_MASK", - "TRANSMIT", - "TRANSMIT_EOM", - "TRANSMIT_IDLETIME", - "TRANSMIT_ACK_POLARITY", - "TRANSMIT_LINE_TIMEOUT", - "TRANSMIT_SUCCEEDED", - "TRANSMIT_FAILED_LINE", - "TRANSMIT_FAILED_ACK", - "TRANSMIT_FAILED_TIMEOUT_DATA", - "TRANSMIT_FAILED_TIMEOUT_LINE", - "FIRMWARE_VERSION", - "START_BOOTLOADER", - "GET_BUILDDATE", - "SET_CONTROLLED", - "GET_AUTO_ENABLED", - "SET_AUTO_ENABLED", - "GET_DEFAULT_LOGICAL_ADDRESS", - "SET_DEFAULT_LOGICAL_ADDRESS", - "GET_LOGICAL_ADDRESS_MASK", - "SET_LOGICAL_ADDRESS_MASK", - "GET_PHYSICAL_ADDRESS", - "SET_PHYSICAL_ADDRESS", - "GET_DEVICE_TYPE", - "SET_DEVICE_TYPE", - "GET_HDMI_VERSION", - "SET_HDMI_VERSION", - "GET_OSD_NAME", - "SET_OSD_NAME", - "WRITE_EEPROM", - "GET_ADAPTER_TYPE", - "SET_ACTIVE_SOURCE", -}; - -static const char *pulse8_msgname(u8 cmd) -{ - static char unknown_msg[5]; - - if ((cmd & 0x3f) < ARRAY_SIZE(pulse8_msgnames)) - return pulse8_msgnames[cmd & 0x3f]; - snprintf(unknown_msg, sizeof(unknown_msg), "0x%02x", cmd); - return unknown_msg; -} - -#define MSGSTART 0xff -#define MSGEND 0xfe -#define MSGESC 0xfd -#define MSGOFFSET 3 - -#define DATA_SIZE 256 - -#define PING_PERIOD (15 * HZ) - -#define NUM_MSGS 8 - -struct pulse8 { - struct device *dev; - struct serio *serio; - struct cec_adapter *adap; - unsigned int vers; - - struct delayed_work ping_eeprom_work; - - struct work_struct irq_work; - struct cec_msg rx_msg[NUM_MSGS]; - unsigned int rx_msg_cur_idx, rx_msg_num; - /* protect rx_msg_cur_idx and rx_msg_num */ - spinlock_t msg_lock; - u8 new_rx_msg[CEC_MAX_MSG_SIZE]; - u8 new_rx_msg_len; - - struct work_struct tx_work; - u32 tx_done_status; - u32 tx_signal_free_time; - struct cec_msg tx_msg; - bool tx_msg_is_bcast; - - struct completion cmd_done; - u8 data[DATA_SIZE]; - unsigned int len; - u8 buf[DATA_SIZE]; - unsigned int idx; - bool escape; - bool started; - - /* locks access to the adapter */ - struct mutex lock; - bool config_pending; - bool restoring_config; - bool autonomous; -}; - -static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len) -{ - int err = 0; - - err = serio_write(serio, MSGSTART); - if (err) - return err; - for (; !err && cmd_len; command++, cmd_len--) { - if (*command >= MSGESC) { - err = serio_write(serio, MSGESC); - if (!err) - err = serio_write(serio, *command - MSGOFFSET); - } else { - err = serio_write(serio, *command); - } - } - if (!err) - err = serio_write(serio, MSGEND); - - return err; -} - -static int pulse8_send_and_wait_once(struct pulse8 *pulse8, - const u8 *cmd, u8 cmd_len, - u8 response, u8 size) -{ - int err; - - if (debug > 1) - dev_info(pulse8->dev, "transmit %s: %*ph\n", - pulse8_msgname(cmd[0]), cmd_len, cmd); - init_completion(&pulse8->cmd_done); - - err = pulse8_send(pulse8->serio, cmd, cmd_len); - if (err) - return err; - - if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ)) - return -ETIMEDOUT; - if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED && - cmd[0] != MSGCODE_SET_CONTROLLED && - cmd[0] != MSGCODE_SET_AUTO_ENABLED && - cmd[0] != MSGCODE_GET_BUILDDATE) - return -ENOTTY; - if (response && - ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) { - dev_info(pulse8->dev, "transmit %s failed with %s\n", - pulse8_msgname(cmd[0]), - pulse8_msgname(pulse8->data[0])); - return -EIO; - } - return 0; -} - -static int pulse8_send_and_wait(struct pulse8 *pulse8, - const u8 *cmd, u8 cmd_len, u8 response, u8 size) -{ - u8 cmd_sc[2]; - int err; - - err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size); - if (err != -ENOTTY) - return err; - - cmd_sc[0] = MSGCODE_SET_CONTROLLED; - cmd_sc[1] = 1; - err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (!err) - err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, - response, size); - return err == -ENOTTY ? -EIO : err; -} - -static void pulse8_tx_work_handler(struct work_struct *work) -{ - struct pulse8 *pulse8 = container_of(work, struct pulse8, tx_work); - struct cec_msg *msg = &pulse8->tx_msg; - unsigned int i; - u8 cmd[2]; - int err; - - if (msg->len == 0) - return; - - mutex_lock(&pulse8->lock); - cmd[0] = MSGCODE_TRANSMIT_IDLETIME; - cmd[1] = pulse8->tx_signal_free_time; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY; - cmd[1] = cec_msg_is_broadcast(msg); - pulse8->tx_msg_is_bcast = cec_msg_is_broadcast(msg); - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; - cmd[1] = msg->msg[0]; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (!err && msg->len > 1) { - for (i = 1; !err && i < msg->len; i++) { - cmd[0] = ((i == msg->len - 1)) ? - MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; - cmd[1] = msg->msg[i]; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - } - } - if (err && debug) - dev_info(pulse8->dev, "%s(0x%02x) failed with error %d for msg %*ph\n", - pulse8_msgname(cmd[0]), cmd[1], - err, msg->len, msg->msg); - msg->len = 0; - mutex_unlock(&pulse8->lock); - if (err) - cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR); -} - -static void pulse8_irq_work_handler(struct work_struct *work) -{ - struct pulse8 *pulse8 = - container_of(work, struct pulse8, irq_work); - unsigned long flags; - u32 status; - - spin_lock_irqsave(&pulse8->msg_lock, flags); - while (pulse8->rx_msg_num) { - spin_unlock_irqrestore(&pulse8->msg_lock, flags); - if (debug) - dev_info(pulse8->dev, "adap received %*ph\n", - pulse8->rx_msg[pulse8->rx_msg_cur_idx].len, - pulse8->rx_msg[pulse8->rx_msg_cur_idx].msg); - cec_received_msg(pulse8->adap, - &pulse8->rx_msg[pulse8->rx_msg_cur_idx]); - spin_lock_irqsave(&pulse8->msg_lock, flags); - if (pulse8->rx_msg_num) - pulse8->rx_msg_num--; - pulse8->rx_msg_cur_idx = - (pulse8->rx_msg_cur_idx + 1) % NUM_MSGS; - } - spin_unlock_irqrestore(&pulse8->msg_lock, flags); - - mutex_lock(&pulse8->lock); - status = pulse8->tx_done_status; - pulse8->tx_done_status = 0; - mutex_unlock(&pulse8->lock); - if (status) - cec_transmit_attempt_done(pulse8->adap, status); -} - -static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data, - unsigned int flags) -{ - struct pulse8 *pulse8 = serio_get_drvdata(serio); - unsigned long irq_flags; - unsigned int idx; - - if (!pulse8->started && data != MSGSTART) - return IRQ_HANDLED; - if (data == MSGESC) { - pulse8->escape = true; - return IRQ_HANDLED; - } - if (pulse8->escape) { - data += MSGOFFSET; - pulse8->escape = false; - } else if (data == MSGEND) { - u8 msgcode = pulse8->buf[0]; - - if (debug > 1) - dev_info(pulse8->dev, "received %s: %*ph\n", - pulse8_msgname(msgcode), - pulse8->idx, pulse8->buf); - switch (msgcode & 0x3f) { - case MSGCODE_FRAME_START: - /* - * Test if we are receiving a new msg when a previous - * message is still pending. - */ - if (!(msgcode & MSGCODE_FRAME_EOM)) { - pulse8->new_rx_msg_len = 1; - pulse8->new_rx_msg[0] = pulse8->buf[1]; - break; - } - /* fall through */ - case MSGCODE_FRAME_DATA: - if (pulse8->new_rx_msg_len < CEC_MAX_MSG_SIZE) - pulse8->new_rx_msg[pulse8->new_rx_msg_len++] = - pulse8->buf[1]; - if (!(msgcode & MSGCODE_FRAME_EOM)) - break; - - spin_lock_irqsave(&pulse8->msg_lock, irq_flags); - idx = (pulse8->rx_msg_cur_idx + pulse8->rx_msg_num) % - NUM_MSGS; - if (pulse8->rx_msg_num == NUM_MSGS) { - dev_warn(pulse8->dev, - "message queue is full, dropping %*ph\n", - pulse8->new_rx_msg_len, - pulse8->new_rx_msg); - spin_unlock_irqrestore(&pulse8->msg_lock, - irq_flags); - pulse8->new_rx_msg_len = 0; - break; - } - pulse8->rx_msg_num++; - memcpy(pulse8->rx_msg[idx].msg, pulse8->new_rx_msg, - pulse8->new_rx_msg_len); - pulse8->rx_msg[idx].len = pulse8->new_rx_msg_len; - spin_unlock_irqrestore(&pulse8->msg_lock, irq_flags); - schedule_work(&pulse8->irq_work); - pulse8->new_rx_msg_len = 0; - break; - case MSGCODE_TRANSMIT_SUCCEEDED: - WARN_ON(pulse8->tx_done_status); - pulse8->tx_done_status = CEC_TX_STATUS_OK; - schedule_work(&pulse8->irq_work); - break; - case MSGCODE_TRANSMIT_FAILED_ACK: - /* - * A NACK for a broadcast message makes no sense, these - * seem to be spurious messages and are skipped. - */ - if (pulse8->tx_msg_is_bcast) - break; - WARN_ON(pulse8->tx_done_status); - pulse8->tx_done_status = CEC_TX_STATUS_NACK; - schedule_work(&pulse8->irq_work); - break; - case MSGCODE_TRANSMIT_FAILED_LINE: - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: - WARN_ON(pulse8->tx_done_status); - pulse8->tx_done_status = CEC_TX_STATUS_ERROR; - schedule_work(&pulse8->irq_work); - break; - case MSGCODE_HIGH_ERROR: - case MSGCODE_LOW_ERROR: - case MSGCODE_RECEIVE_FAILED: - case MSGCODE_TIMEOUT_ERROR: - pulse8->new_rx_msg_len = 0; - break; - case MSGCODE_COMMAND_ACCEPTED: - case MSGCODE_COMMAND_REJECTED: - default: - if (pulse8->idx == 0) - break; - memcpy(pulse8->data, pulse8->buf, pulse8->idx); - pulse8->len = pulse8->idx; - complete(&pulse8->cmd_done); - break; - } - pulse8->idx = 0; - pulse8->started = false; - return IRQ_HANDLED; - } else if (data == MSGSTART) { - pulse8->idx = 0; - pulse8->started = true; - return IRQ_HANDLED; - } - - if (pulse8->idx >= DATA_SIZE) { - dev_dbg(pulse8->dev, - "throwing away %d bytes of garbage\n", pulse8->idx); - pulse8->idx = 0; - } - pulse8->buf[pulse8->idx++] = data; - return IRQ_HANDLED; -} - -static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable) -{ - struct pulse8 *pulse8 = cec_get_drvdata(adap); - u8 cmd[16]; - int err; - - mutex_lock(&pulse8->lock); - cmd[0] = MSGCODE_SET_CONTROLLED; - cmd[1] = enable; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (!enable) { - pulse8->rx_msg_num = 0; - pulse8->tx_done_status = 0; - } - mutex_unlock(&pulse8->lock); - return enable ? err : 0; -} - -static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) -{ - struct pulse8 *pulse8 = cec_get_drvdata(adap); - u16 mask = 0; - u16 pa = adap->phys_addr; - u8 cmd[16]; - int err = 0; - - mutex_lock(&pulse8->lock); - if (log_addr != CEC_LOG_ADDR_INVALID) - mask = 1 << log_addr; - cmd[0] = MSGCODE_SET_ACK_MASK; - cmd[1] = mask >> 8; - cmd[2] = mask & 0xff; - err = pulse8_send_and_wait(pulse8, cmd, 3, - MSGCODE_COMMAND_ACCEPTED, 0); - if ((err && mask != 0) || pulse8->restoring_config) - goto unlock; - - cmd[0] = MSGCODE_SET_AUTO_ENABLED; - cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - pulse8->autonomous = cmd[1]; - if (log_addr == CEC_LOG_ADDR_INVALID) - goto unlock; - - cmd[0] = MSGCODE_SET_DEVICE_TYPE; - cmd[1] = adap->log_addrs.primary_device_type[0]; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - - switch (adap->log_addrs.primary_device_type[0]) { - case CEC_OP_PRIM_DEVTYPE_TV: - mask = CEC_LOG_ADDR_MASK_TV; - break; - case CEC_OP_PRIM_DEVTYPE_RECORD: - mask = CEC_LOG_ADDR_MASK_RECORD; - break; - case CEC_OP_PRIM_DEVTYPE_TUNER: - mask = CEC_LOG_ADDR_MASK_TUNER; - break; - case CEC_OP_PRIM_DEVTYPE_PLAYBACK: - mask = CEC_LOG_ADDR_MASK_PLAYBACK; - break; - case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: - mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM; - break; - case CEC_OP_PRIM_DEVTYPE_SWITCH: - mask = CEC_LOG_ADDR_MASK_UNREGISTERED; - break; - case CEC_OP_PRIM_DEVTYPE_PROCESSOR: - mask = CEC_LOG_ADDR_MASK_SPECIFIC; - break; - default: - mask = 0; - break; - } - cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK; - cmd[1] = mask >> 8; - cmd[2] = mask & 0xff; - err = pulse8_send_and_wait(pulse8, cmd, 3, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - - cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS; - cmd[1] = log_addr; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - - cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS; - cmd[1] = pa >> 8; - cmd[2] = pa & 0xff; - err = pulse8_send_and_wait(pulse8, cmd, 3, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - - cmd[0] = MSGCODE_SET_HDMI_VERSION; - cmd[1] = adap->log_addrs.cec_version; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - - if (adap->log_addrs.osd_name[0]) { - size_t osd_len = strlen(adap->log_addrs.osd_name); - char *osd_str = cmd + 1; - - cmd[0] = MSGCODE_SET_OSD_NAME; - strscpy(cmd + 1, adap->log_addrs.osd_name, sizeof(cmd) - 1); - if (osd_len < 4) { - memset(osd_str + osd_len, ' ', 4 - osd_len); - osd_len = 4; - osd_str[osd_len] = '\0'; - strscpy(adap->log_addrs.osd_name, osd_str, - sizeof(adap->log_addrs.osd_name)); - } - err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len, - MSGCODE_COMMAND_ACCEPTED, 0); - if (err) - goto unlock; - } - -unlock: - if (pulse8->restoring_config) - pulse8->restoring_config = false; - else - pulse8->config_pending = true; - mutex_unlock(&pulse8->lock); - return log_addr == CEC_LOG_ADDR_INVALID ? 0 : err; -} - -static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, - u32 signal_free_time, struct cec_msg *msg) -{ - struct pulse8 *pulse8 = cec_get_drvdata(adap); - - pulse8->tx_msg = *msg; - if (debug) - dev_info(pulse8->dev, "adap transmit %*ph\n", - msg->len, msg->msg); - pulse8->tx_signal_free_time = signal_free_time; - schedule_work(&pulse8->tx_work); - return 0; -} - -static void pulse8_cec_adap_free(struct cec_adapter *adap) -{ - struct pulse8 *pulse8 = cec_get_drvdata(adap); - - cancel_delayed_work_sync(&pulse8->ping_eeprom_work); - cancel_work_sync(&pulse8->irq_work); - cancel_work_sync(&pulse8->tx_work); - kfree(pulse8); -} - -static const struct cec_adap_ops pulse8_cec_adap_ops = { - .adap_enable = pulse8_cec_adap_enable, - .adap_log_addr = pulse8_cec_adap_log_addr, - .adap_transmit = pulse8_cec_adap_transmit, - .adap_free = pulse8_cec_adap_free, -}; - -static void pulse8_disconnect(struct serio *serio) -{ - struct pulse8 *pulse8 = serio_get_drvdata(serio); - - cec_unregister_adapter(pulse8->adap); - pulse8->serio = NULL; - serio_set_drvdata(serio, NULL); - serio_close(serio); -} - -static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio, - struct cec_log_addrs *log_addrs, u16 *pa) -{ - u8 *data = pulse8->data + 1; - u8 cmd[2]; - int err; - struct tm tm; - time64_t date; - - pulse8->vers = 0; - - cmd[0] = MSGCODE_FIRMWARE_VERSION; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); - if (err) - return err; - pulse8->vers = (data[0] << 8) | data[1]; - dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers); - if (pulse8->vers < 2) { - *pa = CEC_PHYS_ADDR_INVALID; - return 0; - } - - cmd[0] = MSGCODE_GET_BUILDDATE; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4); - if (err) - return err; - date = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; - time64_to_tm(date, 0, &tm); - dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - - dev_dbg(pulse8->dev, "Persistent config:\n"); - cmd[0] = MSGCODE_GET_AUTO_ENABLED; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); - if (err) - return err; - pulse8->autonomous = data[0]; - dev_dbg(pulse8->dev, "Autonomous mode: %s", - data[0] ? "on" : "off"); - - cmd[0] = MSGCODE_GET_DEVICE_TYPE; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); - if (err) - return err; - log_addrs->primary_device_type[0] = data[0]; - dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]); - switch (log_addrs->primary_device_type[0]) { - case CEC_OP_PRIM_DEVTYPE_TV: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV; - break; - case CEC_OP_PRIM_DEVTYPE_RECORD: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD; - break; - case CEC_OP_PRIM_DEVTYPE_TUNER: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER; - break; - case CEC_OP_PRIM_DEVTYPE_PLAYBACK: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK; - break; - case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; - break; - case CEC_OP_PRIM_DEVTYPE_SWITCH: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; - break; - case CEC_OP_PRIM_DEVTYPE_PROCESSOR: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; - break; - default: - log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; - log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; - dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n", - log_addrs->primary_device_type[0]); - break; - } - - cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); - if (err) - return err; - log_addrs->log_addr_mask = (data[0] << 8) | data[1]; - dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n", - log_addrs->log_addr_mask); - if (log_addrs->log_addr_mask) - log_addrs->num_log_addrs = 1; - - cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); - if (err) - return err; - *pa = (data[0] << 8) | data[1]; - dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n", - cec_phys_addr_exp(*pa)); - - cmd[0] = MSGCODE_GET_HDMI_VERSION; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); - if (err) - return err; - log_addrs->cec_version = data[0]; - dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version); - - cmd[0] = MSGCODE_GET_OSD_NAME; - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0); - if (err) - return err; - strscpy(log_addrs->osd_name, data, sizeof(log_addrs->osd_name)); - dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name); - - return 0; -} - -static int pulse8_apply_persistent_config(struct pulse8 *pulse8, - struct cec_log_addrs *log_addrs, - u16 pa) -{ - int err; - - err = cec_s_log_addrs(pulse8->adap, log_addrs, false); - if (err) - return err; - - cec_s_phys_addr(pulse8->adap, pa, false); - - return 0; -} - -static void pulse8_ping_eeprom_work_handler(struct work_struct *work) -{ - struct pulse8 *pulse8 = - container_of(work, struct pulse8, ping_eeprom_work.work); - u8 cmd; - - mutex_lock(&pulse8->lock); - cmd = MSGCODE_PING; - pulse8_send_and_wait(pulse8, &cmd, 1, - MSGCODE_COMMAND_ACCEPTED, 0); - - if (pulse8->vers < 2) - goto unlock; - - if (pulse8->config_pending && persistent_config) { - dev_dbg(pulse8->dev, "writing pending config to EEPROM\n"); - cmd = MSGCODE_WRITE_EEPROM; - if (pulse8_send_and_wait(pulse8, &cmd, 1, - MSGCODE_COMMAND_ACCEPTED, 0)) - dev_info(pulse8->dev, "failed to write pending config to EEPROM\n"); - else - pulse8->config_pending = false; - } -unlock: - schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); - mutex_unlock(&pulse8->lock); -} - -static int pulse8_connect(struct serio *serio, struct serio_driver *drv) -{ - u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL; - struct pulse8 *pulse8; - int err = -ENOMEM; - struct cec_log_addrs log_addrs = {}; - u16 pa = CEC_PHYS_ADDR_INVALID; - - pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL); - - if (!pulse8) - return -ENOMEM; - - pulse8->serio = serio; - pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8, - dev_name(&serio->dev), caps, 1); - err = PTR_ERR_OR_ZERO(pulse8->adap); - if (err < 0) - goto free_device; - - pulse8->dev = &serio->dev; - serio_set_drvdata(serio, pulse8); - INIT_WORK(&pulse8->irq_work, pulse8_irq_work_handler); - INIT_WORK(&pulse8->tx_work, pulse8_tx_work_handler); - INIT_DELAYED_WORK(&pulse8->ping_eeprom_work, - pulse8_ping_eeprom_work_handler); - mutex_init(&pulse8->lock); - spin_lock_init(&pulse8->msg_lock); - pulse8->config_pending = false; - - err = serio_open(serio, drv); - if (err) - goto delete_adap; - - err = pulse8_setup(pulse8, serio, &log_addrs, &pa); - if (err) - goto close_serio; - - err = cec_register_adapter(pulse8->adap, &serio->dev); - if (err < 0) - goto close_serio; - - pulse8->dev = &pulse8->adap->devnode.dev; - - if (persistent_config && pulse8->autonomous) { - err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa); - if (err) - goto close_serio; - pulse8->restoring_config = true; - } - - schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); - - return 0; - -close_serio: - pulse8->serio = NULL; - serio_set_drvdata(serio, NULL); - serio_close(serio); -delete_adap: - cec_delete_adapter(pulse8->adap); -free_device: - kfree(pulse8); - return err; -} - -static const struct serio_device_id pulse8_serio_ids[] = { - { - .type = SERIO_RS232, - .proto = SERIO_PULSE8_CEC, - .id = SERIO_ANY, - .extra = SERIO_ANY, - }, - { 0 } -}; - -MODULE_DEVICE_TABLE(serio, pulse8_serio_ids); - -static struct serio_driver pulse8_drv = { - .driver = { - .name = "pulse8-cec", - }, - .description = "Pulse Eight HDMI CEC driver", - .id_table = pulse8_serio_ids, - .interrupt = pulse8_interrupt, - .connect = pulse8_connect, - .disconnect = pulse8_disconnect, -}; - -module_serio_driver(pulse8_drv); |