diff --git a/drivers/misc/tegra-cec/Makefile b/drivers/misc/tegra-cec/Makefile new file mode 100644 index 00000000..f47543b1 --- /dev/null +++ b/drivers/misc/tegra-cec/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for tegra cec support. +# + +subdir-ccflags-y = -Werror + +ccflags-y += -I$(overlay)/drivers/video/tegra/dc + +obj-$(CONFIG_TEGRA_CEC_SUPPORT) += tegra_cec.o diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c new file mode 100644 index 00000000..bc027a8a --- /dev/null +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -0,0 +1,1064 @@ +/* + * drivers/misc/tegra-cec/tegra_cec.c + * + * Copyright (c) 2012-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_TEGRA_POWERGATE) +#include +#endif +#include "tegra_cec.h" + +#include "dc.h" +#include "dc_priv.h" + +#define LOGICAL_ADDRESS_RESERVED2 0xD +#define LOGICAL_ADDRESS_TV 0x0 +#define LOGICAL_ADDRESS_BROADCAST 0xF +#define TEXT_VIEW_ON 0x0D +#define ACTIVE_SOURCE 0x82 +/* + * 400 ms is the time it takes for one 16 byte message to be + * transferred and 5 is the maximum number of retries. Add + * another 100 ms as a margin. + */ +#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) + +static bool post_recovery, text_view_on_sent; +static u8 text_view_on_command[] = { + LOGICAL_ADDRESS_RESERVED2 << 4 | LOGICAL_ADDRESS_TV, + TEXT_VIEW_ON +}; +static u8 active_source_command[] = { + LOGICAL_ADDRESS_RESERVED2 << 4 | LOGICAL_ADDRESS_BROADCAST, + ACTIVE_SOURCE, + 0x00, + 0x00 +}; + +static ssize_t cec_logical_addr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t cec_logical_addr_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(cec_logical_addr_config, S_IWUSR | S_IRUGO, + cec_logical_addr_show, cec_logical_addr_store); + +/* keeping this for debug support to track register read/writes */ +struct tegra_cec *tegra_cec_global; + +/* Try again in case of reset control failure */ +static int reset_retry_count = 5; + +static void tegra_cec_writel(u32 value, void __iomem *addr) +{ + /* TODO, for T23x, find out why this delay required */ + if (tegra_cec_global->soc->cec_always_on) + mdelay(1); + writel(value, addr); +} + +static u32 tegra_cec_readl(const void __iomem *addr) +{ + return readl(addr); +} + +static int tegra_cec_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct tegra_cec *cec = container_of(miscdev, + struct tegra_cec, misc_dev); + int ret = 0; + + dev_dbg(cec->dev, "%s\n", __func__); + + ret = wait_event_interruptible(cec->init_waitq, + atomic_read(&cec->init_done) == 1); + if (ret) + return ret; + file->private_data = cec; + + return ret; +} + +static int tegra_cec_release(struct inode *inode, struct file *file) +{ + struct tegra_cec *cec = file->private_data; + + dev_dbg(cec->dev, "%s\n", __func__); + + return 0; +} + +static inline void tegra_cec_native_tx(const struct tegra_cec *cec, u32 block) +{ + tegra_cec_writel(block, cec->cec_base + TEGRA_CEC_TX_REGISTER); + tegra_cec_writel(TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_STAT); + +} + +static inline void tegra_cec_error_recovery(struct tegra_cec *cec) +{ + u32 hw_ctrl; + + hw_ctrl = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + tegra_cec_writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); + if (cec->soc->cec_always_on) + tegra_cec_writel(0xFFFFFFFE, cec->cec_base + TEGRA_CEC_INT_STAT); + else + tegra_cec_writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); + tegra_cec_writel(hw_ctrl, cec->cec_base + TEGRA_CEC_HW_CONTROL); +} + +static +int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) +{ + int ret; + size_t i; + u32 start, mode, eom; + u32 mask; + + /* + * In case previous transmission was interrupted by signal, + * driver will try to complete the frame anyway. However, + * this means we have to wait for it to finish before beginning + * subsequent transmission. + */ + ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, + msecs_to_jiffies(CEC_XFER_TIMEOUT_MS)); + if (ret == 0) + return -ETIME; + else if (ret < 0) + return ret; + + mode = TEGRA_CEC_LADDR_MODE(buf[0]) << TEGRA_CEC_TX_REG_ADDR_MODE_SHIFT; + + cec->tx_wake = 0; + cec->tx_error = 0; + cec->tx_buf_cur = 0; + cec->tx_buf_cnt = cnt; + + for (i = 0; i < cnt; i++) { + start = i == 0 ? (1 << TEGRA_CEC_TX_REG_START_BIT_SHIFT) : 0; + eom = i == cnt-1 ? (1 << TEGRA_CEC_TX_REG_EOM_SHIFT) : 0; + cec->tx_buf[i] = start | mode | eom | buf[i]; + } + + mask = tegra_cec_readl(cec->cec_base + TEGRA_CEC_INT_MASK); + tegra_cec_writel(mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_MASK); + + ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, + msecs_to_jiffies(CEC_XFER_TIMEOUT_MS)); + if (ret > 0) { + ret = cec->tx_error; + } else if (ret == 0) { + dev_err(cec->dev, "timeout in %s:%d.", __func__, __LINE__); + tegra_cec_error_recovery(cec); + cec->tx_wake = 1; + ret = -ETIME; + } + + return ret; +} + +static ssize_t tegra_cec_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + u8 tx_buf[TEGRA_CEC_FRAME_MAX_LENGTH]; + struct tegra_cec *cec = file->private_data; + ssize_t ret; + + if (count == 0 || count > TEGRA_CEC_FRAME_MAX_LENGTH) + return -EMSGSIZE; + + ret = wait_event_interruptible(cec->init_waitq, + atomic_read(&cec->init_done) == 1); + if (ret) + return ret; + + if (copy_from_user(tx_buf, buf, count)) + return -EFAULT; + + mutex_lock(&cec->tx_lock); + ret = tegra_cec_native_write_l(cec, tx_buf, count); + mutex_unlock(&cec->tx_lock); + if (ret) + return ret; + else { + dev_dbg(cec->dev, "%s: %*phC", __func__, (int)count, tx_buf); + return count; + } +} + +static ssize_t tegra_cec_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct tegra_cec *cec = file->private_data; + ssize_t ret; + + count = sizeof(cec->rx_buffer); + + ret = wait_event_interruptible(cec->init_waitq, + atomic_read(&cec->init_done) == 1); + if (ret) + return ret; + + if (cec->rx_wake == 0) + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(cec->rx_waitq, cec->rx_wake == 1); + if (ret) + return ret; + + if (copy_to_user(buffer, &(cec->rx_buffer), count)) + return -EFAULT; + + dev_dbg(cec->dev, "%s: %*phC", __func__, (int)count, + &(cec->rx_buffer)); + cec->rx_buffer = 0x0; + cec->rx_wake = 0; + return count; +} + +static irqreturn_t tegra_cec_irq_handler(int irq, void *data) +{ + struct device *dev = data; + struct tegra_cec *cec = dev_get_drvdata(dev); + u32 status, mask; + + status = tegra_cec_readl(cec->cec_base + TEGRA_CEC_INT_STAT); + mask = tegra_cec_readl(cec->cec_base + TEGRA_CEC_INT_MASK); + + status &= mask; + + if (!status) + goto out; + + if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) { + dev_err(dev, "TX underrun, interrupt timing issue!\n"); + + tegra_cec_error_recovery(cec); + tegra_cec_writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_MASK); + + cec->tx_error = -EIO; + cec->tx_wake = 1; + + wake_up_interruptible(&cec->tx_waitq); + + goto out; + } else if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || + (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { + tegra_cec_error_recovery(cec); + tegra_cec_writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_MASK); + + cec->tx_error = -ECOMM; + cec->tx_wake = 1; + + wake_up_interruptible(&cec->tx_waitq); + + goto out; + } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { + tegra_cec_writel((TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + + if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) { + tegra_cec_error_recovery(cec); + + cec->tx_error = TEGRA_CEC_LADDR_MODE(cec->tx_buf[0]) ? + -ECONNRESET : -EHOSTUNREACH; + } + cec->tx_wake = 1; + + wake_up_interruptible(&cec->tx_waitq); + + goto out; + } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) + dev_warn(dev, "TX NAKed on the fly!\n"); + + if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { + if (cec->tx_buf_cur == cec->tx_buf_cnt) + tegra_cec_writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_MASK); + else + tegra_cec_native_tx(cec, + cec->tx_buf[cec->tx_buf_cur++]); + } + + if (status & (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | + TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | + TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { + tegra_cec_writel((TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | + TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | + TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + } else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { + tegra_cec_writel(TEGRA_CEC_INT_STAT_RX_REGISTER_FULL, + cec->cec_base + TEGRA_CEC_INT_STAT); + cec->rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER); + cec->rx_wake = 1; + wake_up_interruptible(&cec->rx_waitq); + } + +out: + return IRQ_HANDLED; +} + +static int tegra_cec_dump_registers(struct tegra_cec *cec) +{ + int value, i; + + dev_info(cec->dev, "base address = %llx\n", (u64)cec->cec_base); + for (i = 0; i <= cec->soc->offset; i += 4) { + value = tegra_cec_readl(cec->cec_base + i); + dev_info(cec->dev, "offset %08x: %08x\n", i, value); + } + return i; + +} + +static int tegra_cec_unpowergate(struct tegra_cec *cec) +{ + int ret = 0; + + if (cec->soc->cec_always_on) + return 0; + + if (!tegra_dc_is_nvdisplay()) + return 0; + +#if defined(CONFIG_TEGRA_POWERGATE) + ret = tegra_unpowergate_partition(cec->soc->powergate_id); +#else + ret = pm_runtime_get(cec->dev); +#endif + if (IS_ERR(ERR_PTR(ret))) + dev_err(cec->dev, "Failed to unpowergate DISP,err = %d\n", ret); + + return ret; +} + +static void tegra_cec_powergate(struct tegra_cec *cec) +{ + if (cec->soc->cec_always_on) + return; + + if (!tegra_dc_is_nvdisplay()) + return; + +#if defined(CONFIG_TEGRA_POWERGATE) + tegra_powergate_partition(cec->soc->powergate_id); +#else + pm_runtime_put(cec->dev); +#endif +} + +static int tegra_cec_set_rx_snoop(struct tegra_cec *cec, u32 enable) +{ + u32 state; + + if (!atomic_read(&cec->init_done)) + return -EAGAIN; + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + if (((state & TEGRA_CEC_HWCTRL_RX_SNOOP) != 0) ^ (enable != 0)) { + state ^= TEGRA_CEC_HWCTRL_RX_SNOOP; + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + } + return 0; +} + +static int tegra_cec_get_rx_snoop(struct tegra_cec *cec, u32 *state) +{ + if (!atomic_read(&cec->init_done)) + return -EAGAIN; + *state = (readl(cec->cec_base + TEGRA_CEC_HW_CONTROL) & TEGRA_CEC_HWCTRL_RX_SNOOP) >> 15; + return 0; +} + +static int tegra_cec_access_ok(bool write, unsigned long arg, size_t size) +{ + int err = 0; + +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE + u8 __maybe_unused access_type = write ? VERIFY_WRITE : VERIFY_READ; + err = !access_ok(access_type, arg, size); +#else + err = !access_ok((void *)arg, size); +#endif + + return err; +} + +static long tegra_cec_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int err; + u32 state; + + struct tegra_cec *cec = file->private_data; + + if (_IOC_TYPE(cmd) != TEGRA_CEC_IOC_MAGIC) + return -EINVAL; + + switch (cmd) { + case TEGRA_CEC_IOCTL_ERROR_RECOVERY: + mutex_lock(&cec->recovery_lock); + tegra_cec_error_recovery(cec); + mutex_unlock(&cec->recovery_lock); + break; + case TEGRA_CEC_IOCTL_DUMP_REGISTERS: + tegra_cec_dump_registers(cec); + break; + case TEGRA_CEC_IOCTL_SET_RX_SNOOP: + err = tegra_cec_access_ok(false, arg, sizeof(u32)); + if (err) + return -EFAULT; + if (copy_from_user((u32 *) &state, (u32 *) arg, sizeof(u32))) + return -EFAULT; + tegra_cec_set_rx_snoop(cec, state); + break; + case TEGRA_CEC_IOCTL_GET_RX_SNOOP: + err = tegra_cec_access_ok(true, arg, sizeof(u32)); + if (err) + return -EFAULT; + err = tegra_cec_get_rx_snoop(cec, &state); + if (!err) { + if (copy_to_user((u32 *) arg, &state, sizeof(u32))) + return -EFAULT; + } + break; + case TEGRA_CEC_IOCTL_GET_POST_RECOVERY: + err = tegra_cec_access_ok(true, arg, sizeof(u32)); + if (err) + return -EFAULT; + if (copy_to_user((bool *) arg, &post_recovery, sizeof(bool))) + return -EFAULT; + break; + default: + dev_err(cec->dev, "unsupported ioctl\n"); + return -EINVAL; + } + + return 0; +} + +static const struct file_operations tegra_cec_fops = { + .owner = THIS_MODULE, + .open = tegra_cec_open, + .release = tegra_cec_release, + .read = tegra_cec_read, + .write = tegra_cec_write, + .unlocked_ioctl = tegra_cec_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tegra_cec_ioctl, +#endif +}; + +static int tegra_cec_send_one_touch_play(struct tegra_cec *cec) +{ + int res = 0; + u8 phy_address[2] = {0}; + u32 state = 0; + + text_view_on_sent = true; + + if (cec->soc->use_tegra_dc) { + res = tegra_dc_get_source_physical_address(phy_address); + if (res) { + dev_notice(cec->dev, "Can't find physical address.\n"); + return res; + } + } else { + /* + * When tegradc is absent, UEFI suppose to write physical address + * at register TEGRA_CEC_HW_SPARE. + */ + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_SPARE); + phy_address[0] = state & 0x000F; + phy_address[1] = state & 0x00F0; + } + + dev_info(cec->dev, "physical address: %02x:%02x.\n", + phy_address[0], phy_address[1]); + + if ((phy_address[0] == 0) && (phy_address[1] == 0)) { + dev_err(cec->dev, "Can't find physical address.\n"); + return 0; + } + + active_source_command[2] = phy_address[0]; + active_source_command[3] = phy_address[1]; + + mutex_lock(&cec->tx_lock); + res = tegra_cec_native_write_l(cec, text_view_on_command, + sizeof(text_view_on_command)); + dev_notice(cec->dev, "Sent res: %d.\n", res); + if (!res) { + res = tegra_cec_native_write_l(cec, active_source_command, + sizeof(active_source_command)); + dev_notice(cec->dev, + "Broadcast res: %d.\n", res); + } + mutex_unlock(&cec->tx_lock); + + return res; +} + +static void tegra_cec_init(struct tegra_cec *cec) +{ + u32 state; + cec->rx_wake = 0; + cec->tx_wake = 1; + cec->tx_buf_cnt = 0; + cec->tx_buf_cur = 0; + cec->tx_error = 0; + + tegra_cec_global = cec; + dev_notice(cec->dev, "%s started\n", __func__); + + tegra_cec_writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); + tegra_cec_writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); + +#ifdef CONFIG_PM + if (wait_event_interruptible_timeout(cec->suspend_waitq, + atomic_xchg(&cec->init_cancel, 0) == 1, + msecs_to_jiffies(1000)) > 0) + return; +#else + msleep(1000); +#endif + + tegra_cec_writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); + + cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG; + + /* CEC initialization settings till T194 */ + if (!cec->soc->cec_always_on) { + + tegra_cec_writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); + + tegra_cec_writel(TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr) | + TEGRA_CEC_HWCTRL_TX_NAK_MODE | + TEGRA_CEC_HWCTRL_TX_RX_MODE, + cec->cec_base + TEGRA_CEC_HW_CONTROL); + + tegra_cec_writel((1U << 31) | 0x20, cec->cec_base + TEGRA_CEC_INPUT_FILTER); + + tegra_cec_writel((0x7a << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_LO_TIME_MASK) | + (0x6d << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_LO_TIME_MASK) | + (0x93 << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_DURATION_MASK) | + (0x86 << TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_DURATION_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_0); + + tegra_cec_writel((0x35 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_LO_TIME_MASK) | + (0x21 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_SAMPLE_TIME_MASK) | + (0x56 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_DURATION_MASK) | + (0x40 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MIN_DURATION_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_1); + + tegra_cec_writel((0x50 << TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_2); + + tegra_cec_writel((0x74 << TEGRA_CEC_TX_TIMING_0_TX_START_BIT_LO_TIME_MASK) | + (0x8d << TEGRA_CEC_TX_TIMING_0_TX_START_BIT_DURATION_MASK) | + (0x08 << TEGRA_CEC_TX_TIMING_0_TX_BUS_XITION_TIME_MASK) | + (0x71 << TEGRA_CEC_TX_TIMING_0_TX_BUS_ERROR_LO_TIME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_0); + + tegra_cec_writel((0x2f << TEGRA_CEC_TX_TIMING_1_TX_LO_DATA_BIT_LO_TIME_MASK) | + (0x13 << TEGRA_CEC_TX_TIMING_1_TX_HI_DATA_BIT_LO_TIME_MASK) | + (0x4b << TEGRA_CEC_TX_TIMING_1_TX_DATA_BIT_DURATION_MASK) | + (0x21 << TEGRA_CEC_TX_TIMING_1_TX_ACK_NAK_BIT_SAMPLE_TIME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_1); + + tegra_cec_writel( + (0x07 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK) | + (0x05 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_NEW_FRAME_MASK) | + (0x03 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_RETRY_FRAME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_2); + + } else { + + tegra_cec_writel(TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr), + cec->cec_base + TEGRA_CEC_HW_CONTROL); + + tegra_cec_writel(0x1, cec->cec_base + TEGRA_CEC_MESSAGE_FILTER_CTRL); + + state = (0xff << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_LO_TIME_MASK) | + (0x22 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_SAMPLE_TIME_MASK) | + (0xe0 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_DURATION_MASK) | + (0x41 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MIN_DURATION_MASK); + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_RX_TIMING_1); + + state = (0x7 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK) | + (0x1 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_NEW_FRAME_MASK) | + (0x3 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_RETRY_FRAME_MASK); + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_TX_TIMING_2); + + /* + * By default, keep RX buffer depth to 2 bytes like previous chips. + * Value 1 = 2 bytes (1 fifo depth) + * Value 0x40 = 128 bytes (64 fifo depth) + */ + tegra_cec_writel(0x1, cec->cec_base + TEGRA_CEC_RX_BUFFER_AFULL_CFG_0); + + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + state |= TEGRA_CEC_HWCTRL_TX_RX_MODE; + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + + } + + tegra_cec_writel(TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED | + TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN, + cec->cec_base + TEGRA_CEC_INT_MASK); + + atomic_set(&cec->init_done, 1); + wake_up_interruptible(&cec->init_waitq); + + if (!text_view_on_sent && !post_recovery) + tegra_cec_send_one_touch_play(cec); + dev_notice(cec->dev, "%s Done.\n", __func__); +} + +static void tegra_cec_init_worker(struct work_struct *work) +{ + struct tegra_cec *cec = container_of(work, struct tegra_cec, work); + + tegra_cec_init(cec); +} + +static ssize_t cec_logical_addr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tegra_cec *cec = dev_get_drvdata(dev); + + if (!atomic_read(&cec->init_done)) + return -EAGAIN; + + if (buf) + return sprintf(buf, "0x%x\n", (u32)cec->logical_addr); + return 1; +} + +static ssize_t cec_logical_addr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret; + u32 state; + u16 addr; + struct tegra_cec *cec; + + if (!buf || !count) + return -EINVAL; + + cec = dev_get_drvdata(dev); + if (!atomic_read(&cec->init_done)) + return -EAGAIN; + + ret = kstrtou16(buf, 0, &addr); + if (ret) + return ret; + + dev_info(dev, "set logical address: 0x%x\n", (u32)addr); + cec->logical_addr = addr; + + if (cec->soc->cec_always_on) { + // clear TX_RX_MODE + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + state &= ~TEGRA_CEC_HWCTRL_TX_RX_MODE; + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + // write logical address + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; + state |= TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr); + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + // enable tx_rx mode + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + state |= TEGRA_CEC_HWCTRL_TX_RX_MODE; + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + } else { + state = tegra_cec_readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; + state |= TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr); + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + } + + return count; +} + +static int tegra_cec_probe(struct platform_device *pdev) +{ + struct tegra_cec *cec; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + int ret = 0; + struct reset_control *rst = NULL; + + cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL); + + if (!cec) + return -ENOMEM; + + cec->soc = of_device_get_match_data(&pdev->dev); + if (cec->soc == NULL) { + dev_err(&pdev->dev, "No cec dev table found\n"); + devm_kfree(&pdev->dev, cec); + return -ENODEV; + } + + if (cec->soc->cec_always_on && reset_retry_count != 0) { + rst = devm_reset_control_get(&pdev->dev, "cec"); + if (IS_ERR(rst)) { + /* BPMP reset mechanism not available, return and retry again */ + dev_info(&pdev->dev, "reset control is not found, deferring probe to retry again.\n"); + devm_kfree(&pdev->dev, cec); + reset_retry_count--; + return -EPROBE_DEFER; + } + + /* take CEC engine out of reset */ + if (reset_control_reset(rst) != 0) { + dev_info(&pdev->dev, "reset control reset failed, deferring probe to retry again.\n"); + devm_kfree(&pdev->dev, cec); + reset_retry_count--; + return -EPROBE_DEFER; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(&pdev->dev, + "Unable to allocate resources for device.\n"); + ret = -EBUSY; + goto cec_error; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, + "Unable to request mem region for device.\n"); + ret = -EBUSY; + goto cec_error; + } + + cec->tegra_cec_irq = platform_get_irq(pdev, 0); + + if (cec->tegra_cec_irq <= 0) { + ret = -EBUSY; + goto cec_error; + } + + cec->cec_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + if (!cec->cec_base) { + dev_err(&pdev->dev, "Unable to grab IOs for device.\n"); + ret = -EBUSY; + goto cec_error; + } + + dev_info(&pdev->dev, "dt=%d start=0x%08llX end=0x%08llX irq=%d\n", + (pdev->dev.of_node != NULL), + res->start, res->end, + cec->tegra_cec_irq); + + atomic_set(&cec->init_done, 0); + mutex_init(&cec->tx_lock); + mutex_init(&cec->recovery_lock); + cec->dev = &pdev->dev; + + if (!cec->soc->cec_always_on) { + +#if !defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) + pm_runtime_enable(&pdev->dev); +#endif + + ret = tegra_cec_unpowergate(cec); + if (IS_ERR(ERR_PTR(ret))) + goto clk_error; + dev_info(&pdev->dev, "Unpowergated DISP\n"); + + if (tegra_dc_is_nvdisplay()) { + if (np) + cec->clk = of_clk_get_by_name(np, "cec"); + } else { + cec->clk = clk_get(&pdev->dev, "cec"); + } + + if (IS_ERR_OR_NULL(cec->clk)) { + dev_err(&pdev->dev, "can't get clock for CEC\n"); + ret = -ENOENT; + goto clk_error; + } + + ret = clk_prepare_enable(cec->clk); + dev_info(&pdev->dev, "Enable clock result: %d.\n", ret); + + } + + /* set context info. */ + init_waitqueue_head(&cec->rx_waitq); + init_waitqueue_head(&cec->tx_waitq); + init_waitqueue_head(&cec->init_waitq); + +#ifdef CONFIG_PM + init_waitqueue_head(&cec->suspend_waitq); + atomic_set(&cec->init_cancel, 0); +#endif + + platform_set_drvdata(pdev, cec); + /* clear out the hardware. */ + + INIT_WORK(&cec->work, tegra_cec_init_worker); + schedule_work(&cec->work); + + device_init_wakeup(&pdev->dev, 1); + + cec->misc_dev.minor = MISC_DYNAMIC_MINOR; + cec->misc_dev.name = TEGRA_CEC_NAME; + cec->misc_dev.fops = &tegra_cec_fops; + cec->misc_dev.parent = &pdev->dev; + + if (misc_register(&cec->misc_dev)) { + printk(KERN_WARNING "Couldn't register device , %s.\n", TEGRA_CEC_NAME); + goto cec_error; + } + + ret = devm_request_irq(&pdev->dev, cec->tegra_cec_irq, + tegra_cec_irq_handler, 0x0, "cec_irq", &pdev->dev); + + if (ret) { + dev_err(&pdev->dev, + "Unable to request interrupt for device (err=%d).\n", ret); + goto cec_error; + } + + /* + * Create a symlink for tegra_cec if it is not under platform bus or + * it has been created with different name. + */ + if ((pdev->dev.parent != &platform_bus) || + strcmp(dev_name(&pdev->dev), TEGRA_CEC_NAME)) { + ret = sysfs_create_link(&platform_bus.kobj, + &pdev->dev.kobj, TEGRA_CEC_NAME); + if (ret) + dev_warn(&pdev->dev, "Could not create sysfs link.\n"); + } + + ret = sysfs_create_file( + &pdev->dev.kobj, &dev_attr_cec_logical_addr_config.attr); + dev_info(&pdev->dev, "cec_add_sysfs ret=%d\n", ret); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to add sysfs: %d\n", ret); + goto cec_error; + } + + dev_notice(&pdev->dev, "probed\n"); + + return 0; + +cec_error: + cancel_work_sync(&cec->work); + if (!cec->soc->cec_always_on) { + clk_disable(cec->clk); + clk_put(cec->clk); + tegra_cec_powergate(cec); + } +clk_error: + return ret; +} + +static int tegra_cec_remove(struct platform_device *pdev) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + if (!cec->soc->cec_always_on) { + + clk_disable(cec->clk); + clk_put(cec->clk); + tegra_cec_powergate(cec); +#if !defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) + pm_runtime_disable(&pdev->dev); +#endif + } + + misc_deregister(&cec->misc_dev); + cancel_work_sync(&cec->work); + + return 0; +} + +#ifdef CONFIG_PM +static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + atomic_set(&cec->init_cancel, 1); + wmb(); + + wake_up_interruptible(&cec->suspend_waitq); + + /* cancel the work queue */ + cancel_work_sync(&cec->work); + + atomic_set(&cec->init_done, 0); + atomic_set(&cec->init_cancel, 0); + + if (!cec->soc->cec_always_on) { + clk_disable(cec->clk); + tegra_cec_powergate(cec); + } else { + /* + * TODO: + * 1. Program TEGRA_CEC_RX_BUFFER_AFULL_CFG_0 for 0x40 + * 2. Program TEGRA_CEC_MESSAGE_FILTER_CTRL, + * TEGRA_CEC_RX_PHYSICAL_ADDR_0, + * TEGRA_CEC_RX_OPCODE_0/1/2/3/4. + */ + } + + dev_notice(&pdev->dev, "suspended\n"); + return 0; +} + +static int tegra_cec_resume(struct platform_device *pdev) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + dev_notice(&pdev->dev, "Resuming\n"); + + if (!cec->soc->cec_always_on) { + tegra_cec_unpowergate(cec); + clk_enable(cec->clk); + } else { + /* TODO: + * 1. Read TEGRA_CEC_RX_BUFFER_STAT_0 value and read RX buffer data + * 2. Configure TEGRA_CEC_RX_BUFFER_AFULL_CFG_0 back to 0x1. + */ + } + + schedule_work(&cec->work); + + return 0; +} +#endif + +static int __init check_post_recovery(char *options) +{ + post_recovery = true; + + pr_info("tegra_cec: the post_recovery is %d .\n", post_recovery); + + return 0; +} + +early_param("post_recovery", check_post_recovery); + +static struct tegra_cec_soc tegra210_soc_data = { +#if defined(CONFIG_TEGRA_POWERGATE) + .powergate_id = TEGRA210_POWER_DOMAIN_DISA, +#endif + .offset = TEGRA_CEC_HW_SPARE, + .use_tegra_dc = true, + .cec_always_on = false, +}; + +static struct tegra_cec_soc tegra186_soc_data = { +#if defined(CONFIG_TEGRA_POWERGATE) + .powergate_id = TEGRA186_POWER_DOMAIN_DISP, +#endif + .offset = TEGRA_CEC_HW_SPARE, + .use_tegra_dc = true, + .cec_always_on = false, +}; + +static struct tegra_cec_soc tegra194_soc_data = { +#if defined(CONFIG_TEGRA_POWERGATE) + .powergate_id = TEGRA194_POWER_DOMAIN_DISP, +#endif + .offset = TEGRA_CEC_HW_SPARE, + .use_tegra_dc = true, + .cec_always_on = false, +}; + +static struct tegra_cec_soc tegra234_soc_data = { + .offset = TEGRA_CEC_RX_OPCODE_4, + .use_tegra_dc = false, + .cec_always_on = true, +}; + +static struct of_device_id tegra_cec_of_match[] = { + { .compatible = "nvidia,tegra210-cec", .data = &tegra210_soc_data }, + { .compatible = "nvidia,tegra186-cec", .data = &tegra186_soc_data }, + { .compatible = "nvidia,tegra194-cec", .data = &tegra194_soc_data }, + { .compatible = "nvidia,tegra234-cec", .data = &tegra234_soc_data }, + {}, +}; + +static struct platform_driver tegra_cec_driver = { + .driver = { + .name = TEGRA_CEC_NAME, + .owner = THIS_MODULE, + + .of_match_table = of_match_ptr(tegra_cec_of_match), + }, + .probe = tegra_cec_probe, + .remove = tegra_cec_remove, + +#ifdef CONFIG_PM + .suspend = tegra_cec_suspend, + .resume = tegra_cec_resume, +#endif +}; + +module_platform_driver(tegra_cec_driver); diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h new file mode 100644 index 00000000..0cfc694c --- /dev/null +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -0,0 +1,188 @@ +/* + * drivers/misc/tegra-cec/tegra_cec.h + * + * Copyright (c) 2012-2021, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TEGRA_CEC_H +#define TEGRA_CEC_H + +#include +#include +#include + +#define TEGRA_CEC_FRAME_MAX_LENGTH 16 + +struct tegra_cec_soc { + int powergate_id; + int offset; + bool use_tegra_dc; + bool cec_always_on; +}; + +struct tegra_cec { + struct device *dev; + struct miscdevice misc_dev; + struct clk *clk; + struct mutex tx_lock; + struct mutex recovery_lock; + void __iomem *cec_base; + int tegra_cec_irq; + wait_queue_head_t rx_waitq; + wait_queue_head_t tx_waitq; + wait_queue_head_t init_waitq; + atomic_t init_done; +#ifdef CONFIG_PM + wait_queue_head_t suspend_waitq; + atomic_t init_cancel; +#endif + u16 logical_addr; + struct work_struct work; + const struct tegra_cec_soc *soc; + unsigned int rx_wake; + unsigned int tx_wake; + u16 rx_buffer; + long tx_error; + u32 tx_buf[TEGRA_CEC_FRAME_MAX_LENGTH]; + u8 tx_buf_cur; + u8 tx_buf_cnt; + struct reset_control *reset; +}; +static int tegra_cec_remove(struct platform_device *pdev); + +#define TEGRA_CEC_LADDR_BROADCAST 0xF +#define TEGRA_CEC_LADDR_MASK 0xF +#define TEGRA_CEC_LADDR_WIDTH 4 +#define TEGRA_CEC_LADDR_MODE(blk) \ + ((blk & TEGRA_CEC_LADDR_MASK) == TEGRA_CEC_LADDR_BROADCAST) + +/*CEC Timing registers*/ +#define TEGRA_CEC_SW_CONTROL 0X000 +#define TEGRA_CEC_HW_CONTROL 0X004 +#define TEGRA_CEC_INPUT_FILTER 0X008 +#define TEGRA_CEC_TX_REGISTER 0X010 +#define TEGRA_CEC_RX_REGISTER 0X014 +#define TEGRA_CEC_RX_TIMING_0 0X018 +#define TEGRA_CEC_RX_TIMING_1 0X01C +#define TEGRA_CEC_RX_TIMING_2 0X020 +#define TEGRA_CEC_TX_TIMING_0 0X024 +#define TEGRA_CEC_TX_TIMING_1 0X028 +#define TEGRA_CEC_TX_TIMING_2 0X02C +#define TEGRA_CEC_INT_STAT 0X030 +#define TEGRA_CEC_INT_MASK 0X034 +#define TEGRA_CEC_HW_DEBUG_RX 0X038 +#define TEGRA_CEC_HW_DEBUG_TX 0X03C +#define TEGRA_CEC_HW_SPARE 0X040 + +#define TEGRA_CEC_WAKE_STAT_0 0x044 +#define TEGRA_CEC_WAKE_MASK_0 0x048 +#define TEGRA_CEC_RX_BUFFER_AFULL_CFG_0 0x04c +#define TEGRA_CEC_RX_BUFFER_STAT_0 0x050 +#define TEGRA_CEC_RX_TIMING_3_0 0x054 +#define TEGRA_CEC_MESSAGE_FILTER_CTRL 0X058 +#define TEGRA_CEC_RX_PHYSICAL_ADDR_0 0X05C +#define TEGRA_CEC_RX_OPCODE_0 0x060 +#define TEGRA_CEC_RX_OPCODE_1 0x064 +#define TEGRA_CEC_RX_OPCODE_2 0x068 +#define TEGRA_CEC_RX_OPCODE_3 0x06c +#define TEGRA_CEC_RX_OPCODE_4 0x070 + +#define TEGRA_CEC_MAX_LOGICAL_ADDR 15 +#define TEGRA_CEC_HWCTRL_RX_LADDR_UNREG 0x0 +#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0x7FFF +#define TEGRA_CEC_HWCTRL_RX_LADDR(x) \ + ((x<<0) & TEGRA_CEC_HWCTRL_RX_LADDR_MASK) +#define TEGRA_CEC_HWCTRL_RX_SNOOP (1<<15) +#define TEGRA_CEC_HWCTRL_RX_NAK_MODE (1<<16) +#define TEGRA_CEC_HWCTRL_TX_NAK_MODE (1<<24) +#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE (1<<30) +#define TEGRA_CEC_HWCTRL_TX_RX_MODE (1<<31) + +#define TEGRA_CEC_INPUT_FILTER_MODE (1<<31) +#define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_MASK 0 + +#define TEGRA_CEC_TX_REG_DATA_SHIFT 0 +#define TEGRA_CEC_TX_REG_EOM_SHIFT 8 +#define TEGRA_CEC_TX_REG_ADDR_MODE_SHIFT 12 +#define TEGRA_CEC_TX_REG_START_BIT_SHIFT 16 +#define TEGRA_CEC_TX_REG_RETRY_BIT_SHIFT 17 + +#define TEGRA_CEC_RX_REGISTER_MASK 0 +#define TEGRA_CEC_RX_REGISTER_EOM (1<<8) +#define TEGRA_CEC_RX_REGISTER_ACK (1<<9) + +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_LO_TIME_MASK 0 +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_LO_TIME_MASK 8 +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MAX_DURATION_MASK 16 +#define TEGRA_CEC_RX_TIMING_0_RX_START_BIT_MIN_DURATION_MASK 24 + +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_LO_TIME_MASK 0 +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_SAMPLE_TIME_MASK 8 +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_DURATION_MASK 16 +#define TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MIN_DURATION_MASK 24 + +#define TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK 0 + +#define TEGRA_CEC_TX_TIMING_0_TX_START_BIT_LO_TIME_MASK 0 +#define TEGRA_CEC_TX_TIMING_0_TX_START_BIT_DURATION_MASK 8 +#define TEGRA_CEC_TX_TIMING_0_TX_BUS_XITION_TIME_MASK 16 +#define TEGRA_CEC_TX_TIMING_0_TX_BUS_ERROR_LO_TIME_MASK 24 + +#define TEGRA_CEC_TX_TIMING_1_TX_LO_DATA_BIT_LO_TIME_MASK 0 +#define TEGRA_CEC_TX_TIMING_1_TX_HI_DATA_BIT_LO_TIME_MASK 8 +#define TEGRA_CEC_TX_TIMING_1_TX_DATA_BIT_DURATION_MASK 16 +#define TEGRA_CEC_TX_TIMING_1_TX_ACK_NAK_BIT_SAMPLE_TIME_MASK 24 + +#define TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK 0 +#define TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_NEW_FRAME_MASK 4 +#define TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_RETRY_FRAME_MASK 8 + +#define TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY (1<<0) +#define TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN (1<<1) +#define TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD (1<<2) +#define TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED (1<<3) +#define TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED (1<<4) +#define TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED (1<<5) +#define TEGRA_CEC_INT_STAT_RX_REGISTER_FULL (1<<8) +#define TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN (1<<9) +#define TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED (1<<10) +#define TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED (1<<11) +#define TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED (1<<12) +#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_H2L (1<<13) +#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_L2H (1<<14) + +#define TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY (1<<0) +#define TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN (1<<1) +#define TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD (1<<2) +#define TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED (1<<3) +#define TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED (1<<4) +#define TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED (1<<5) +#define TEGRA_CEC_INT_MASK_RX_REGISTER_FULL (1<<8) +#define TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN (1<<9) +#define TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED (1<<10) +#define TEGRA_CEC_INT_MASK_RX_BUS_ANOMALY_DETECTED (1<<11) +#define TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED (1<<12) +#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_H2L (1<<13) +#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_L2H (1<<14) + +#define TEGRA_CEC_HW_DEBUG_TX_DURATION_COUNT_MASK 0 +#define TEGRA_CEC_HW_DEBUG_TX_TXBIT_COUNT_MASK 17 +#define TEGRA_CEC_HW_DEBUG_TX_STATE_MASK 21 +#define TEGRA_CEC_HW_DEBUG_TX_FORCELOOUT (1<<25) +#define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER (1<<26) + +#define TEGRA_CEC_NAME "tegra_cec" + +#endif /* TEGRA_CEC_H */ diff --git a/include/uapi/misc/tegra_cec.h b/include/uapi/misc/tegra_cec.h new file mode 100644 index 00000000..1f4d0cfd --- /dev/null +++ b/include/uapi/misc/tegra_cec.h @@ -0,0 +1,31 @@ +/* + * include/uapi/misc/tegra_cec.h + * + * Copyright (c) 2012-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef __UAPI_TEGRA_CEC_H +#define __UAPI_TEGRA_CEC_H + +#define TEGRA_CEC_IOC_MAGIC 'C' + +#define TEGRA_CEC_IOCTL_ERROR_RECOVERY _IO(TEGRA_CEC_IOC_MAGIC, 1) +#define TEGRA_CEC_IOCTL_DUMP_REGISTERS _IO(TEGRA_CEC_IOC_MAGIC, 2) +#define TEGRA_CEC_IOCTL_SET_RX_SNOOP _IO(TEGRA_CEC_IOC_MAGIC, 3) +#define TEGRA_CEC_IOCTL_GET_RX_SNOOP _IO(TEGRA_CEC_IOC_MAGIC, 4) +#define TEGRA_CEC_IOCTL_GET_POST_RECOVERY _IO(TEGRA_CEC_IOC_MAGIC, 5) + +#endif /* __UAPI_TEGRA_CEC_H */