summaryrefslogtreecommitdiff
path: root/drivers/media/usb/pulse8-cec
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab+huawei@kernel.org>2020-04-15 11:14:25 +0200
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>2020-04-15 12:06:40 +0200
commita81068181aad78a5f6980408a4b6115d22bb8aed (patch)
treeb5aa2c123bf45cd31419e8a0b1297c2155b30b5a /drivers/media/usb/pulse8-cec
parent4be5e8648b0c287aefc6ac3f3a0b12c696054f43 (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/Kconfig12
-rw-r--r--drivers/media/usb/pulse8-cec/Makefile2
-rw-r--r--drivers/media/usb/pulse8-cec/pulse8-cec.c909
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);