diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2016-11-02 13:34:53 +0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2016-11-16 20:47:12 +0300 |
commit | aee4c782df24557975c257058f69ac130f6f42bc (patch) | |
tree | 0b3fcc7094c9699504c96fb9adfde8cec3e8e16c /drivers/staging/media | |
parent | a161bef04286e1344cd469098213a972af8fa7d6 (diff) | |
download | linux-aee4c782df24557975c257058f69ac130f6f42bc.tar.xz |
[media] pulse8-cec: move out of staging
Now that the CEC framework has been moved out of staging and into the
mainline kernel we can do the same for the pulse8-cec driver.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/staging/media')
-rw-r--r-- | drivers/staging/media/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/media/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/media/pulse8-cec/Kconfig | 10 | ||||
-rw-r--r-- | drivers/staging/media/pulse8-cec/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/media/pulse8-cec/TODO | 52 | ||||
-rw-r--r-- | drivers/staging/media/pulse8-cec/pulse8-cec.c | 761 |
6 files changed, 0 insertions, 827 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 0abe5ffb4934..ffb8fa72c3da 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -27,8 +27,6 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" -source "drivers/staging/media/pulse8-cec/Kconfig" - source "drivers/staging/media/s5p-cec/Kconfig" # Keep LIRC at the end, as it has sub-menus diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 246299eff80d..a28e82cf6447 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,5 +4,4 @@ obj-$(CONFIG_DVB_CXD2099) += cxd2099/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ -obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec/ obj-$(CONFIG_VIDEO_STI_HDMI_CEC) += st-cec/ diff --git a/drivers/staging/media/pulse8-cec/Kconfig b/drivers/staging/media/pulse8-cec/Kconfig deleted file mode 100644 index 6ffc407de62f..000000000000 --- a/drivers/staging/media/pulse8-cec/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -config USB_PULSE8_CEC - tristate "Pulse Eight HDMI CEC" - depends on USB_ACM && MEDIA_CEC_SUPPORT - 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/staging/media/pulse8-cec/Makefile b/drivers/staging/media/pulse8-cec/Makefile deleted file mode 100644 index 9800690bc25a..000000000000 --- a/drivers/staging/media/pulse8-cec/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec.o diff --git a/drivers/staging/media/pulse8-cec/TODO b/drivers/staging/media/pulse8-cec/TODO deleted file mode 100644 index fa6660245e5f..000000000000 --- a/drivers/staging/media/pulse8-cec/TODO +++ /dev/null @@ -1,52 +0,0 @@ -This driver needs to mature a bit more and another round of -code cleanups. - -Otherwise it looks to be in good shape. And of course the fact -that the CEC framework is in staging at the moment also prevents -this driver from being mainlined. - -Some notes: - -1) Regarding the "autonomous" mode of the Pulse-Eight: currently this -is disabled, but the idea is that this allows basic functionality -when the PC is off, and it can wake-up the PC through USB. - -To prevent the device to go into autonomous mode the driver would -have to send MSGCODE_SET_CONTROLLED 1 and then send a ping every -30 seconds (in practice once every 15 seconds would be good). When -powering off or going to standby send MSGCODE_SET_CONTROLLED 0 to -turn the autonomous mode back on. - -This needs to be implemented in the driver. Autonomous mode was -added in firmware v2. - -2) Writing to the EEPROM can only be done once every 10 seconds. - -3) To use this driver you also need to patch the inputattach utility, -this patch will be submitted once this driver is moved out of staging. - -diff -urN linuxconsoletools-1.4.9/utils/inputattach.c linuxconsoletools-1.4.9.new/utils/inputattach.c ---- linuxconsoletools-1.4.9/utils/inputattach.c 2016-01-09 16:27:02.000000000 +0100 -+++ linuxconsoletools-1.4.9.new/utils/inputattach.c 2016-03-20 11:35:31.707788967 +0100 -@@ -861,6 +861,9 @@ - { "--wacom_iv", "-wacom_iv", "Wacom protocol IV tablet", - B9600, CS8 | CRTSCTS, - SERIO_WACOM_IV, 0x00, 0x00, 0, wacom_iv_init }, -+{ "--pulse8-cec", "-pulse8-cec", "Pulse Eight HDMI CEC dongle", -+ B9600, CS8, -+ SERIO_PULSE8_CEC, 0x00, 0x00, 0, NULL }, - { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL } - }; - -diff -urN linuxconsoletools-1.4.9/utils/serio-ids.h linuxconsoletools-1.4.9.new/utils/serio-ids.h ---- linuxconsoletools-1.4.9/utils/serio-ids.h 2015-04-26 18:29:42.000000000 +0200 -+++ linuxconsoletools-1.4.9.new/utils/serio-ids.h 2016-03-20 11:41:00.153558539 +0100 -@@ -131,5 +131,8 @@ - #ifndef SERIO_EASYPEN - # define SERIO_EASYPEN 0x3f - #endif -+#ifndef SERIO_PULSE8_CEC -+# define SERIO_PULSE8_CEC 0x40 -+#endif - - #endif diff --git a/drivers/staging/media/pulse8-cec/pulse8-cec.c b/drivers/staging/media/pulse8-cec/pulse8-cec.c deleted file mode 100644 index 9092494bb43c..000000000000 --- a/drivers/staging/media/pulse8-cec/pulse8-cec.c +++ /dev/null @@ -1,761 +0,0 @@ -/* - * Pulse Eight HDMI CEC driver - * - * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version of 2 of the License, or (at your - * option) any later version. See the file COPYING in the main directory of - * this archive for more details. - */ - -/* - * 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 = 1; -module_param(debug, int, 0644); -module_param(persistent_config, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-1)"); -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, -}; - -#define MSGSTART 0xff -#define MSGEND 0xfe -#define MSGESC 0xfd -#define MSGOFFSET 3 - -#define DATA_SIZE 256 - -#define PING_PERIOD (15 * HZ) - -struct pulse8 { - struct device *dev; - struct serio *serio; - struct cec_adapter *adap; - unsigned int vers; - struct completion cmd_done; - struct work_struct work; - struct delayed_work ping_eeprom_work; - struct cec_msg rx_msg; - u8 data[DATA_SIZE]; - unsigned int len; - u8 buf[DATA_SIZE]; - unsigned int idx; - bool escape; - bool started; - struct mutex config_lock; - struct mutex write_lock; - bool config_pending; - bool restoring_config; - bool autonomous; -}; - -static void pulse8_ping_eeprom_work_handler(struct work_struct *work); - -static void pulse8_irq_work_handler(struct work_struct *work) -{ - struct pulse8 *pulse8 = - container_of(work, struct pulse8, work); - - switch (pulse8->data[0] & 0x3f) { - case MSGCODE_FRAME_DATA: - cec_received_msg(pulse8->adap, &pulse8->rx_msg); - break; - case MSGCODE_TRANSMIT_SUCCEEDED: - cec_transmit_done(pulse8->adap, CEC_TX_STATUS_OK, - 0, 0, 0, 0); - break; - case MSGCODE_TRANSMIT_FAILED_ACK: - cec_transmit_done(pulse8->adap, CEC_TX_STATUS_NACK, - 0, 1, 0, 0); - break; - case MSGCODE_TRANSMIT_FAILED_LINE: - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: - cec_transmit_done(pulse8->adap, CEC_TX_STATUS_ERROR, - 0, 0, 0, 1); - break; - } -} - -static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data, - unsigned int flags) -{ - struct pulse8 *pulse8 = serio_get_drvdata(serio); - - 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) { - struct cec_msg *msg = &pulse8->rx_msg; - - if (debug) - dev_info(pulse8->dev, "received: %*ph\n", - pulse8->idx, pulse8->buf); - pulse8->data[0] = pulse8->buf[0]; - switch (pulse8->buf[0] & 0x3f) { - case MSGCODE_FRAME_START: - msg->len = 1; - msg->msg[0] = pulse8->buf[1]; - break; - case MSGCODE_FRAME_DATA: - if (msg->len == CEC_MAX_MSG_SIZE) - break; - msg->msg[msg->len++] = pulse8->buf[1]; - if (pulse8->buf[0] & MSGCODE_FRAME_EOM) - schedule_work(&pulse8->work); - break; - case MSGCODE_TRANSMIT_SUCCEEDED: - case MSGCODE_TRANSMIT_FAILED_LINE: - case MSGCODE_TRANSMIT_FAILED_ACK: - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: - case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: - schedule_work(&pulse8->work); - break; - case MSGCODE_HIGH_ERROR: - case MSGCODE_LOW_ERROR: - case MSGCODE_RECEIVE_FAILED: - case MSGCODE_TIMEOUT_ERROR: - 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 void pulse8_disconnect(struct serio *serio) -{ - struct pulse8 *pulse8 = serio_get_drvdata(serio); - - cec_unregister_adapter(pulse8->adap); - cancel_delayed_work_sync(&pulse8->ping_eeprom_work); - dev_info(&serio->dev, "disconnected\n"); - serio_close(serio); - serio_set_drvdata(serio, NULL); - kfree(pulse8); -} - -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; - - /*dev_info(pulse8->dev, "transmit: %*ph\n", 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: failed %02x\n", - pulse8->data[0] & 0x3f); - 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; - - mutex_lock(&pulse8->write_lock); - err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size); - - if (err == -ENOTTY) { - 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) - goto unlock; - err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, - response, size); - } - -unlock: - mutex_unlock(&pulse8->write_lock); - return err == -ENOTTY ? -EIO : err; -} - -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; - time_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]; - time_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; - strncpy(log_addrs->osd_name, data, 13); - 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 int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable) -{ - struct pulse8 *pulse8 = adap->priv; - u8 cmd[16]; - int err; - - cmd[0] = MSGCODE_SET_CONTROLLED; - cmd[1] = enable; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - return enable ? err : 0; -} - -static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) -{ - struct pulse8 *pulse8 = adap->priv; - u16 mask = 0; - u16 pa = adap->phys_addr; - u8 cmd[16]; - int err = 0; - - mutex_lock(&pulse8->config_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; - strncpy(cmd + 1, adap->log_addrs.osd_name, 13); - if (osd_len < 4) { - memset(osd_str + osd_len, ' ', 4 - osd_len); - osd_len = 4; - osd_str[osd_len] = '\0'; - strcpy(adap->log_addrs.osd_name, osd_str); - } - 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->config_lock); - return err; -} - -static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, - u32 signal_free_time, struct cec_msg *msg) -{ - struct pulse8 *pulse8 = adap->priv; - u8 cmd[2]; - unsigned int i; - int err; - - cmd[0] = MSGCODE_TRANSMIT_IDLETIME; - cmd[1] = 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); - 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) { - cmd[0] = msg->len == 2 ? MSGCODE_TRANSMIT_EOM : - MSGCODE_TRANSMIT; - cmd[1] = msg->msg[1]; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - for (i = 0; !err && i + 2 < msg->len; i++) { - cmd[0] = (i + 2 == msg->len - 1) ? - MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; - cmd[1] = msg->msg[i + 2]; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - } - } - - return err; -} - -static int pulse8_received(struct cec_adapter *adap, struct cec_msg *msg) -{ - return -ENOMSG; -} - -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, - .received = pulse8_received, -}; - -static int pulse8_connect(struct serio *serio, struct serio_driver *drv) -{ - u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | CEC_CAP_PHYS_ADDR | - CEC_CAP_PASSTHROUGH | CEC_CAP_RC | 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, - "HDMI CEC", caps, 1, &serio->dev); - 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->work, pulse8_irq_work_handler); - mutex_init(&pulse8->write_lock); - mutex_init(&pulse8->config_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); - 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; - } - - INIT_DELAYED_WORK(&pulse8->ping_eeprom_work, - pulse8_ping_eeprom_work_handler); - schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); - - return 0; - -close_serio: - serio_close(serio); -delete_adap: - cec_delete_adapter(pulse8->adap); - serio_set_drvdata(serio, NULL); -free_device: - kfree(pulse8); - return err; -} - -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; - - schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); - cmd = MSGCODE_PING; - pulse8_send_and_wait(pulse8, &cmd, 1, - MSGCODE_COMMAND_ACCEPTED, 0); - - if (pulse8->vers < 2) - return; - - mutex_lock(&pulse8->config_lock); - 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; - } - mutex_unlock(&pulse8->config_lock); -} - -static 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); |