From 9f3804e6cdc2947615fc9da23539650f9bcad884 Mon Sep 17 00:00:00 2001 From: Ankit Pashiney Date: Tue, 19 Jun 2012 20:10:11 -0700 Subject: [PATCH 01/44] misc: tegra-cec: Initial CEC Driver for T3x platform READ API: read API ignores count and will always return 16 bit data. read API expects user to supply it with min of 16 bits data it returns CEC packet in following format bit 0-7: data bit 8: EOM bit 9: ACK WRITE API: write API ignores count and will always accept 32 bit data. write API expects user to supply it with min of 32 bits data it accepts CEC packet supllied in following format bit 0-7: data bit 8: EOM bit 12: Address mode, 0 = Direct, 1 = Broadcast bit 16: Generate Start bit, 0 = Disable, 1 = Enable bit 17: Retry frame, 0 = Disable, 1 = Enable Logical address is set to 4, as of now there is no mechanism to change this address from userspace. Driver is disabled by default in Kconfig Bug 200198493 Change-Id: Ia3835cec0bb717e63dabca5c5fcb1236847bf492 Reviewed-on: http://git-master/r/1164135 (cherry picked from commit 955ec819872e66c4732b38cd74c7ff3a302d95f2) Signed-off-by: Chun XU --- drivers/misc/tegra-cec/Makefile | 5 + drivers/misc/tegra-cec/tegra_cec.c | 383 +++++++++++++++++++++++++++++ drivers/misc/tegra-cec/tegra_cec.h | 134 ++++++++++ 3 files changed, 522 insertions(+) create mode 100644 drivers/misc/tegra-cec/Makefile create mode 100644 drivers/misc/tegra-cec/tegra_cec.c create mode 100644 drivers/misc/tegra-cec/tegra_cec.h diff --git a/drivers/misc/tegra-cec/Makefile b/drivers/misc/tegra-cec/Makefile new file mode 100644 index 00000000..ab380305 --- /dev/null +++ b/drivers/misc/tegra-cec/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for tegra cec support. +# + +obj-$(CONFIG_TEGRA_CEC_T30) += 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..a876d9d6 --- /dev/null +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -0,0 +1,383 @@ +/* + * drivers/misc/tegra-cec/tegra_cec.c + * + * Copyright (c) 2012, 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 "tegra_cec.h" + + +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); + dev_dbg(cec->dev, "%s\n", __func__); + file->private_data = cec; + + return 0; +} + +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; +} + +ssize_t tegra_cec_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct tegra_cec *cec = file->private_data; + unsigned long write_buff; + + count = 4; + + if (copy_from_user(&write_buff, buffer, count)) + return -EFAULT; + + writel((TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY | + 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_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), + cec->cec_base + TEGRA_CEC_INT_MASK); + + wait_event_interruptible(cec->tx_waitq, cec->tx_wake == 1); + writel(write_buff, cec->cec_base + TEGRA_CEC_TX_REGISTER); + cec->tx_wake = 0; + + 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_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), + cec->cec_base + TEGRA_CEC_INT_MASK); + + write_buff = 0x00; + return count; +} + +ssize_t tegra_cec_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct tegra_cec *cec = file->private_data; + unsigned short rx_buffer; + count = 2; + + if (cec->rx_wake == 0) + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + wait_event_interruptible(cec->rx_waitq, cec->rx_wake == 1); + + rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER); + + if (copy_to_user(buffer, &rx_buffer, count)) + return -EFAULT; + + 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); + unsigned long status; + + status = readl(cec->cec_base + TEGRA_CEC_INT_STAT); + + if (!status) + return IRQ_HANDLED; + + if ((status & TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN) || + (status & TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED) || + (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) || + (status & TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { + 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) { + writel((TEGRA_CEC_INT_STAT_RX_REGISTER_FULL), + cec->cec_base + TEGRA_CEC_INT_STAT); + cec->rx_wake = 1; + wake_up_interruptible(&cec->rx_waitq); + } else if ((status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) || + (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) || + (status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || + (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { + writel((TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY | + TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + } else if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { + cec->tx_wake = 1; + wake_up_interruptible(&cec->tx_waitq); + writel((TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY), + cec->cec_base + TEGRA_CEC_INT_STAT); + } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { + writel((TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED), + cec->cec_base + TEGRA_CEC_INT_STAT); + } + + return IRQ_HANDLED; +} + +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, +}; + +static void tegra_cec_init(struct tegra_cec *cec) +{ + + writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); + writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); + msleep(1000); + + writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); + + writel((TEGRA_CEC_LOGICAL_ADDR<cec_base + TEGRA_CEC_HW_CONTROL); + + writel(0x00, cec->cec_base + TEGRA_CEC_INPUT_FILTER); + + 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); + + 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); + + writel((0x50 << TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_2); + + 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); + + 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); + + writel((0x07 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK) | + (0x05 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_NEW_FRAME_MASK) | + (0x03 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_RETRY_FRAME_MASK), + cec->cec_base + TEGRA_CEC_TX_TIMING_2); + + 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_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), + cec->cec_base + TEGRA_CEC_INT_MASK); +} + +static int __devinit tegra_cec_probe(struct platform_device *pdev) +{ + struct tegra_cec *cec; + struct resource *res; + int ret = 0; + + cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL); + + if (!cec) + return -ENOMEM; + + 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_nocache(&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; + } + + 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; + } + + clk_enable(cec->clk); + + /* set context info. */ + cec->dev = &pdev->dev; + cec->rx_wake = 0; + cec->tx_wake = 0; + init_waitqueue_head(&cec->rx_waitq); + init_waitqueue_head(&cec->tx_waitq); + + platform_set_drvdata(pdev, cec); + /* clear out the hardware. */ + + tegra_cec_init(cec); + + 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, IRQF_DISABLED, "cec_irq", &pdev->dev); + + if (ret) { + dev_err(&pdev->dev, + "Unable to request interrupt for device (err=%d).\n", ret); + goto cec_error; + } + + dev_notice(&pdev->dev, "probed\n"); + + return 0; + +cec_error: + clk_disable(cec->clk) + clk_put(cec->clk); +clk_error: + return ret; +} + +static int tegra_cec_remove(struct platform_device *pdev) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + clk_disable(cec->clk) + clk_put(cec->clk); + + misc_deregister(&cec->misc_dev); + + 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); + + clk_disable(cec->clk); + + return 0; +} + +static int tegra_cec_resume(struct platform_device *pdev) +{ + + struct tegra_cec *cec = platform_get_drvdata(pdev); + clk_enable(cec->clk); + tegra_cec_init(cec); + return 0; +} +#endif + +static struct platform_driver tegra_cec_driver = { + .driver = { + .name = TEGRA_CEC_NAME, + .owner = THIS_MODULE, + }, + .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..acc94dc6 --- /dev/null +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -0,0 +1,134 @@ +/* + * drivers/misc/tegra-cec/tegra_cec.h + * + * Copyright (c) 2012, 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 + + +struct tegra_cec { + struct device *dev; + struct miscdevice misc_dev; + struct clk *clk; + void __iomem *cec_base; + int tegra_cec_irq; + wait_queue_head_t rx_waitq; + wait_queue_head_t tx_waitq; + unsigned int rx_wake; + unsigned int tx_wake; +}; +static int tegra_cec_remove(struct platform_device *pdev); + +/*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_LOGICAL_ADDR 0x10 + +#define TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK 0 +#define TEGRA_CEC_HW_CONTROL_RX_SNOOP (1<<15) +#define TEGRA_CEC_HW_CONTROL_RX_NAK_MODE (1<<16) +#define TEGRA_CEC_HW_CONTROL_TX_NAK_MODE (1<<24) +#define TEGRA_CEC_HW_CONTROL_FAST_SIM_MODE (1<<30) +#define TEGRA_CEC_HW_CONTROL_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_REGISTER_DATA_MASK 0 +#define TEGRA_CEC_TX_REGISTER_EOM_MASK 8 +#define TEGRA_CEC_TX_REGISTER_ADDRESS_MODE_MASK 12 +#define TEGRA_CEC_TX_REGISTER_GENERATE_START_BIT_MASK 16 +#define TEGRA_CEC_TX_REGISTER_RETRY_FRAME_MASK 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_TX_BUS_IDLE_TIME_NEW_FRAME_MASK 4 +#define TEGRA_CEC_TX_TIMING_2_TX_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" From 9723602a96b5538dc22cb7a6e67bc70aeda37571 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Tue, 2 Apr 2013 14:51:57 +0800 Subject: [PATCH 02/44] misc: cec: fix warnings tegra_cec.c: fix warnings as compilation fails after enabling warning as errors flag Bug 1258710 Change-Id: Iecd3051d482f5a7488c9f95f6124ad39371f0bd9 Signed-off-by: Benjamin Lu Reviewed-on: http://git-master/r/1164136 (cherry picked from commit 8112e3cd46d11eca51e95cc2fed66d2f7946553a) --- drivers/misc/tegra-cec/tegra_cec.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index a876d9d6..a368a229 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2013, 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, @@ -185,11 +185,11 @@ static void tegra_cec_init(struct tegra_cec *cec) writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); - writel((TEGRA_CEC_LOGICAL_ADDR<cec_base + TEGRA_CEC_HW_CONTROL); @@ -328,7 +328,7 @@ static int __devinit tegra_cec_probe(struct platform_device *pdev) return 0; cec_error: - clk_disable(cec->clk) + clk_disable(cec->clk); clk_put(cec->clk); clk_error: return ret; @@ -338,7 +338,7 @@ static int tegra_cec_remove(struct platform_device *pdev) { struct tegra_cec *cec = platform_get_drvdata(pdev); - clk_disable(cec->clk) + clk_disable(cec->clk); clk_put(cec->clk); misc_deregister(&cec->misc_dev); From b268d2ef296a0ae9f9eb367fd345c70011f2906c Mon Sep 17 00:00:00 2001 From: Ankita Garg Date: Tue, 17 Sep 2013 14:29:09 -0700 Subject: [PATCH 03/44] misc: tegra-cec: Updates for T124 In T124, the RX_REGISTER_FULL interrupt is cleared by reading the RX_REGISTER instead of writing to the INT_STAT register as before. Bug 1364229 Change-Id: Ib080a48910304553b0752a3ede55ab0d7653dd77 Signed-off-by: Ankita Garg Reviewed-on: http://git-master/r/275982 Reviewed-on: http://git-master/r/1164137 (cherry picked from commit b890e7f50d3d032fc3ede4c4935fdf2c4501f063) --- drivers/misc/tegra-cec/tegra_cec.c | 8 +++----- drivers/misc/tegra-cec/tegra_cec.h | 3 ++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index a368a229..5b7a778e 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -100,7 +100,6 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct tegra_cec *cec = file->private_data; - unsigned short rx_buffer; count = 2; if (cec->rx_wake == 0) @@ -109,12 +108,10 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer, wait_event_interruptible(cec->rx_waitq, cec->rx_wake == 1); - rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER); - - if (copy_to_user(buffer, &rx_buffer, count)) + if (copy_to_user(buffer, &(cec->rx_buffer), count)) return -EFAULT; - rx_buffer = 0x0; + cec->rx_buffer = 0x0; cec->rx_wake = 0; return count; } @@ -142,6 +139,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) } else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { 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); } else if ((status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) || diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index acc94dc6..4f908cc6 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.h * - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2013, 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, @@ -29,6 +29,7 @@ struct tegra_cec { wait_queue_head_t tx_waitq; unsigned int rx_wake; unsigned int tx_wake; + unsigned short rx_buffer; }; static int tegra_cec_remove(struct platform_device *pdev); From a8422afe1a392e2b4c8a4b5cc65756e7c45c655a Mon Sep 17 00:00:00 2001 From: Shridhar Rasal Date: Tue, 14 May 2013 15:00:33 +0530 Subject: [PATCH 04/44] misc: cec: add worker handler for resume HDMI CEC needs 1 sec delay for reset hardware in resume Adding work handler for CEC init Bug 1283088 Change-Id: I4498584ec93c1a5eefddfad9966cb5a1e7776e6e Signed-off-by: Shridhar Rasal Reviewed-on: http://git-master/r/228332 (cherry picked from commit 596245cee4b2c239843c2f920d83c21721af2ffb) Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/346038 Reviewed-on: http://git-master/r/1164138 (cherry picked from commit f62afdb0cb72a087442db6ed450b52925d20f9b6) --- drivers/misc/tegra-cec/tegra_cec.c | 18 ++++++++++++++---- drivers/misc/tegra-cec/tegra_cec.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 5b7a778e..c34f91f5 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -234,7 +234,14 @@ static void tegra_cec_init(struct tegra_cec *cec) cec->cec_base + TEGRA_CEC_INT_MASK); } -static int __devinit tegra_cec_probe(struct platform_device *pdev) +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 int tegra_cec_probe(struct platform_device *pdev) { struct tegra_cec *cec; struct resource *res; @@ -298,7 +305,8 @@ static int __devinit tegra_cec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cec); /* clear out the hardware. */ - tegra_cec_init(cec); + INIT_WORK(&cec->work, tegra_cec_init_worker); + schedule_work(&cec->work); device_init_wakeup(&pdev->dev, 1); @@ -340,6 +348,7 @@ static int tegra_cec_remove(struct platform_device *pdev) clk_put(cec->clk); misc_deregister(&cec->misc_dev); + cancel_work_sync(&cec->work); return 0; } @@ -356,10 +365,11 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) static int tegra_cec_resume(struct platform_device *pdev) { - struct tegra_cec *cec = platform_get_drvdata(pdev); + clk_enable(cec->clk); - tegra_cec_init(cec); + schedule_work(&cec->work); + return 0; } #endif diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 4f908cc6..c94f3996 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -30,6 +30,7 @@ struct tegra_cec { unsigned int rx_wake; unsigned int tx_wake; unsigned short rx_buffer; + struct work_struct work; }; static int tegra_cec_remove(struct platform_device *pdev); From 353c499996600d1f4e521d06fc3b566350b9369c Mon Sep 17 00:00:00 2001 From: Shridhar Rasal Date: Tue, 14 May 2013 15:00:33 +0530 Subject: [PATCH 05/44] misc: cec: add wait_event for CEC ops Add wait_event to make sure that CEC functions do not execute until CEC init completes Bug 1283088 Change-Id: I1d26360326338f549a14cbf109a24c2935ebe472 Signed-off-by: Shridhar Rasal Reviewed-on: http://git-master/r/232566 (cherry picked from commit 6e94e976c1e2121d81d19018b79a97d353b11d70) Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/346039 Reviewed-on: http://git-master/r/1164139 (cherry picked from commit 794145e9f8c16c7e36119f3652dd185cf12eb5dc) --- drivers/misc/tegra-cec/tegra_cec.c | 21 +++++++++++++++++++++ drivers/misc/tegra-cec/tegra_cec.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index c34f91f5..3fabac3f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -46,6 +46,8 @@ int tegra_cec_open(struct inode *inode, struct file *file) struct tegra_cec *cec = container_of(miscdev, struct tegra_cec, misc_dev); dev_dbg(cec->dev, "%s\n", __func__); + + wait_event_interruptible(cec->init_waitq, cec->init_done == 1); file->private_data = cec; return 0; @@ -68,6 +70,8 @@ ssize_t tegra_cec_write(struct file *file, const char __user *buffer, count = 4; + wait_event_interruptible(cec->init_waitq, cec->init_done == 1); + if (copy_from_user(&write_buff, buffer, count)) return -EFAULT; @@ -102,6 +106,8 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer, struct tegra_cec *cec = file->private_data; count = 2; + wait_event_interruptible(cec->init_waitq, cec->init_done == 1); + if (cec->rx_wake == 0) if (file->f_flags & O_NONBLOCK) return -EAGAIN; @@ -122,6 +128,8 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) struct tegra_cec *cec = dev_get_drvdata(dev); unsigned long status; + wait_event_interruptible(cec->init_waitq, cec->init_done == 1); + status = readl(cec->cec_base + TEGRA_CEC_INT_STAT); if (!status) @@ -176,6 +184,10 @@ static const struct file_operations tegra_cec_fops = { static void tegra_cec_init(struct tegra_cec *cec) { + dev_notice(cec->dev, "%s started\n", __func__); + + cec->init_done = 0; + writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); @@ -232,6 +244,11 @@ static void tegra_cec_init(struct tegra_cec *cec) TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), cec->cec_base + TEGRA_CEC_INT_MASK); + + cec->init_done = 1; + wake_up_interruptible(&cec->init_waitq); + + dev_notice(cec->dev, "%s Done.\n", __func__); } static void tegra_cec_init_worker(struct work_struct *work) @@ -301,6 +318,7 @@ static int tegra_cec_probe(struct platform_device *pdev) cec->tx_wake = 0; init_waitqueue_head(&cec->rx_waitq); init_waitqueue_head(&cec->tx_waitq); + init_waitqueue_head(&cec->init_waitq); platform_set_drvdata(pdev, cec); /* clear out the hardware. */ @@ -360,6 +378,7 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) clk_disable(cec->clk); + dev_notice(&pdev->dev, "suspended\n"); return 0; } @@ -367,6 +386,8 @@ static int tegra_cec_resume(struct platform_device *pdev) { struct tegra_cec *cec = platform_get_drvdata(pdev); + dev_notice(&pdev->dev, "Resuming\n"); + clk_enable(cec->clk); schedule_work(&cec->work); diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index c94f3996..814e7ba8 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -27,9 +27,11 @@ struct tegra_cec { int tegra_cec_irq; wait_queue_head_t rx_waitq; wait_queue_head_t tx_waitq; + wait_queue_head_t init_waitq; unsigned int rx_wake; unsigned int tx_wake; unsigned short rx_buffer; + unsigned int init_done; struct work_struct work; }; static int tegra_cec_remove(struct platform_device *pdev); From b38ef51b1aadfb477b84aec19673617057e487cb Mon Sep 17 00:00:00 2001 From: Deepak Nibade Date: Mon, 30 Sep 2013 11:43:59 +0530 Subject: [PATCH 06/44] misc: cec: cancel workqueue in suspend cancel the workqueue in suspend method before turning off the clock Bug 1360341 Change-Id: I126da686a6ba0c5eec55b67df1e9f962ce16fc48 Signed-off-by: Deepak Nibade Reviewed-on: http://git-master/r/280159 (cherry picked from commit 7cc6da6e41c7889f7ed8da7833d293b3b884bc0e) Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/346041 Reviewed-on: http://git-master/r/1164140 (cherry picked from commit e57647e7daacd5a3c22016cbe3f01ddb800d702c) --- drivers/misc/tegra-cec/tegra_cec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 3fabac3f..df7f08f4 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -376,6 +376,9 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) { struct tegra_cec *cec = platform_get_drvdata(pdev); + /* cancel the work queue */ + cancel_work_sync(&cec->work); + clk_disable(cec->clk); dev_notice(&pdev->dev, "suspended\n"); From ab709208e8d3cbbd3ba7df62e34b49d08cbfe18e Mon Sep 17 00:00:00 2001 From: Xia Yang Date: Mon, 28 Oct 2013 17:51:50 -0700 Subject: [PATCH 07/44] misc: cec: Fix bug potentially cause system hang Remove wait_event_interruptible() from interrupt context Change init_done to be atomic_t and reset as early as possible Bug 1395893 Change-Id: Ib0cf423a3405293000b0c0d9aa105da5bba22e53 Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/304631 (cherry picked from commit ef034436a3c8aaf7a9ce5dd9ebaf8dc90dbcce4b) Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/346042 Reviewed-on: http://git-master/r/1164141 (cherry picked from commit 76e681f957b27323227c9990679631636084b6ae) --- drivers/misc/tegra-cec/tegra_cec.c | 19 +++++++++++-------- drivers/misc/tegra-cec/tegra_cec.h | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index df7f08f4..0b6db11f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -47,7 +47,8 @@ int tegra_cec_open(struct inode *inode, struct file *file) struct tegra_cec, misc_dev); dev_dbg(cec->dev, "%s\n", __func__); - wait_event_interruptible(cec->init_waitq, cec->init_done == 1); + wait_event_interruptible(cec->init_waitq, + atomic_read(&cec->init_done) == 1); file->private_data = cec; return 0; @@ -70,7 +71,8 @@ ssize_t tegra_cec_write(struct file *file, const char __user *buffer, count = 4; - wait_event_interruptible(cec->init_waitq, cec->init_done == 1); + wait_event_interruptible(cec->init_waitq, + atomic_read(&cec->init_done) == 1); if (copy_from_user(&write_buff, buffer, count)) return -EFAULT; @@ -106,7 +108,8 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer, struct tegra_cec *cec = file->private_data; count = 2; - wait_event_interruptible(cec->init_waitq, cec->init_done == 1); + wait_event_interruptible(cec->init_waitq, + atomic_read(&cec->init_done) == 1); if (cec->rx_wake == 0) if (file->f_flags & O_NONBLOCK) @@ -128,8 +131,6 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) struct tegra_cec *cec = dev_get_drvdata(dev); unsigned long status; - wait_event_interruptible(cec->init_waitq, cec->init_done == 1); - status = readl(cec->cec_base + TEGRA_CEC_INT_STAT); if (!status) @@ -186,8 +187,6 @@ static void tegra_cec_init(struct tegra_cec *cec) dev_notice(cec->dev, "%s started\n", __func__); - cec->init_done = 0; - writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); @@ -245,7 +244,7 @@ static void tegra_cec_init(struct tegra_cec *cec) TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), cec->cec_base + TEGRA_CEC_INT_MASK); - cec->init_done = 1; + atomic_set(&cec->init_done, 1); wake_up_interruptible(&cec->init_waitq); dev_notice(cec->dev, "%s Done.\n", __func__); @@ -302,6 +301,8 @@ static int tegra_cec_probe(struct platform_device *pdev) goto cec_error; } + atomic_set(&cec->init_done, 0); + cec->clk = clk_get(&pdev->dev, "cec"); if (IS_ERR_OR_NULL(cec->clk)) { @@ -379,6 +380,8 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) /* cancel the work queue */ cancel_work_sync(&cec->work); + atomic_set(&cec->init_done, 0); + clk_disable(cec->clk); dev_notice(&pdev->dev, "suspended\n"); diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 814e7ba8..2ed99bbe 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -17,6 +17,7 @@ */ #include +#include struct tegra_cec { @@ -31,7 +32,7 @@ struct tegra_cec { unsigned int rx_wake; unsigned int tx_wake; unsigned short rx_buffer; - unsigned int init_done; + atomic_t init_done; struct work_struct work; }; static int tegra_cec_remove(struct platform_device *pdev); From 5025d29c00ebf9de189122c479fac99abf3a47d7 Mon Sep 17 00:00:00 2001 From: Ankita Garg Date: Thu, 17 Oct 2013 11:45:31 -0700 Subject: [PATCH 08/44] misc: cec: Add sysfs node to set logical addr Sysfs node takes string of decimal representation of logical address Logical address must be in bit-map form, e.g., "0x10" for address 4 Bug 1395893 Change-Id: I899fe80e1ebcc9957ac6efe3c4f6d07873b2a8db Signed-off-by: Ankita Garg Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/303968 (cherry picked from commit ff2b08c0dc1d6ff3c1d7cac012cdccfa17c6399c) Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/346043 Reviewed-on: http://git-master/r/1164142 (cherry picked from commit 706ba72c2cb9f05450a10110d70f2462b52046d2) --- drivers/misc/tegra-cec/tegra_cec.c | 60 +++++++++++++++++++++++++++++- drivers/misc/tegra-cec/tegra_cec.h | 5 ++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 0b6db11f..094e0081 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -39,6 +39,14 @@ #include "tegra_cec.h" +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); int tegra_cec_open(struct inode *inode, struct file *file) { @@ -194,7 +202,8 @@ static void tegra_cec_init(struct tegra_cec *cec) writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); - writel(((TEGRA_CEC_LOGICAL_ADDR<logical_addr << TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK) & (~TEGRA_CEC_HW_CONTROL_RX_SNOOP) & (~TEGRA_CEC_HW_CONTROL_RX_NAK_MODE) & (~TEGRA_CEC_HW_CONTROL_TX_NAK_MODE) & @@ -257,6 +266,46 @@ static void tegra_cec_init_worker(struct work_struct *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 (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, "tegra_cec: set logical address: %x\n", (u32)addr); + cec->logical_addr = addr; + state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; + state |= TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr); + writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + + return count; +} + static int tegra_cec_probe(struct platform_device *pdev) { struct tegra_cec *cec; @@ -302,6 +351,7 @@ static int tegra_cec_probe(struct platform_device *pdev) } atomic_set(&cec->init_done, 0); + cec->logical_addr = TEGRA_CEC_LOGICAL_ADDR; cec->clk = clk_get(&pdev->dev, "cec"); @@ -348,6 +398,14 @@ static int tegra_cec_probe(struct platform_device *pdev) goto cec_error; } + 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; diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 2ed99bbe..380fb076 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -32,7 +32,8 @@ struct tegra_cec { unsigned int rx_wake; unsigned int tx_wake; unsigned short rx_buffer; - atomic_t init_done; + atomic_t init_done; + u16 logical_addr; struct work_struct work; }; static int tegra_cec_remove(struct platform_device *pdev); @@ -55,6 +56,8 @@ static int tegra_cec_remove(struct platform_device *pdev); #define TEGRA_CEC_HW_DEBUG_TX 0X03C #define TEGRA_CEC_LOGICAL_ADDR 0x10 +#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0xFFFF +#define TEGRA_CEC_HWCTRL_RX_LADDR(x) (x<<0) #define TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK 0 #define TEGRA_CEC_HW_CONTROL_RX_SNOOP (1<<15) From 4026c9868c4c0b195fcbb920f3e5bd94ee225fa6 Mon Sep 17 00:00:00 2001 From: Xia Yang Date: Mon, 4 Nov 2013 14:31:55 -0800 Subject: [PATCH 09/44] tegra: cec: Driver rework Change write() to work per frame. Change write() to return -1 on error and set up errno write() API: -Userspace is responsible for re-transmission. -Read from user-space byte by byte, each byte representing a block, up to 16 bytes as specified by HDMI standard. -Return 0 on success transmission, -1 otherwise, with errno setup as follows: EIO - TX_REGISTER_UNDERRUN, should not happen, otherwise driver is have serious timing issue. ECOMM - BUS arbitration failure or anomaly BUS activity. Transmission is abandoned. ECONNRESET - For broadcast message only, someone on the BUS asserted NAK during transmission. EHOSTUNREACH - For direct message only, message was not ACK'd. (Required by logical address allocation) EMSGSIZE - Message size > 16 bit. EFAULT - Page fault accessing message buffer. EINTR - call interrupted by singal. read() API is unchanged, works per packet with no error report. Change-Id: Iabdd92b5658dd63c7b500a7ec88d79a64c8c0a43 Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/304664 (cherry picked from commit 6ab7a446c4a3e8e4970ceec5a3c715453a24e4a5) Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/347779 Reviewed-on: http://git-master/r/1164143 (cherry picked from commit 67a6c2d3cfbeeca5cb5ed57aae53f34d07decd4f) --- drivers/misc/tegra-cec/tegra_cec.c | 259 ++++++++++++++++++++--------- drivers/misc/tegra-cec/tegra_cec.h | 63 ++++--- 2 files changed, 219 insertions(+), 103 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 094e0081..7af9e367 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2014, 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, @@ -53,13 +53,17 @@ 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__); - wait_event_interruptible(cec->init_waitq, + ret = wait_event_interruptible(cec->init_waitq, atomic_read(&cec->init_done) == 1); + if (ret) + return ret; file->private_data = cec; - return 0; + return ret; } int tegra_cec_release(struct inode *inode, struct file *file) @@ -71,59 +75,102 @@ int tegra_cec_release(struct inode *inode, struct file *file) return 0; } -ssize_t tegra_cec_write(struct file *file, const char __user *buffer, +static inline void tegra_cec_native_tx(const struct tegra_cec *cec, u32 block) +{ + writel(block, cec->cec_base + TEGRA_CEC_TX_REGISTER); + writel(TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_STAT); +} + +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(cec->tx_waitq, cec->tx_wake == 1); + if (ret) + 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 = readl(cec->cec_base + TEGRA_CEC_INT_MASK); + writel(mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + cec->cec_base + TEGRA_CEC_INT_MASK); + + ret = wait_event_interruptible(cec->tx_waitq, cec->tx_wake == 1); + if (!ret) + ret = cec->tx_error; + + return ret; +} + +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; - unsigned long write_buff; + ssize_t ret; - count = 4; + if (count == 0 || count > TEGRA_CEC_FRAME_MAX_LENGTH) + return -EMSGSIZE; - wait_event_interruptible(cec->init_waitq, + ret = wait_event_interruptible(cec->init_waitq, atomic_read(&cec->init_done) == 1); + if (ret) + return ret; - if (copy_from_user(&write_buff, buffer, count)) + if (copy_from_user(tx_buf, buf, count)) return -EFAULT; - writel((TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY | - 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_RX_REGISTER_FULL | - TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), - cec->cec_base + TEGRA_CEC_INT_MASK); - - wait_event_interruptible(cec->tx_waitq, cec->tx_wake == 1); - writel(write_buff, cec->cec_base + TEGRA_CEC_TX_REGISTER); - cec->tx_wake = 0; - - 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_RX_REGISTER_FULL | - TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), - cec->cec_base + TEGRA_CEC_INT_MASK); - - write_buff = 0x00; - return count; + 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 + return count; } ssize_t tegra_cec_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct tegra_cec *cec = file->private_data; - count = 2; + ssize_t ret; - wait_event_interruptible(cec->init_waitq, + 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; - wait_event_interruptible(cec->rx_waitq, cec->rx_wake == 1); + 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; @@ -133,52 +180,100 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer, return count; } +static inline void tegra_cec_error_recovery(struct tegra_cec *cec) +{ + u32 hw_ctrl; + + hw_ctrl = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); + writel(hw_ctrl, cec->cec_base + TEGRA_CEC_HW_CONTROL); +} + static irqreturn_t tegra_cec_irq_handler(int irq, void *data) { struct device *dev = data; struct tegra_cec *cec = dev_get_drvdata(dev); - unsigned long status; + u32 status, mask; status = readl(cec->cec_base + TEGRA_CEC_INT_STAT); + mask = readl(cec->cec_base + TEGRA_CEC_INT_MASK); + + status &= mask; if (!status) - return IRQ_HANDLED; + goto out; - if ((status & TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN) || - (status & TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED) || - (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) || - (status & TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { + if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) { + dev_err(dev, "tegra_cec: TX underrun, interrupt timing issue!\n"); + + tegra_cec_error_recovery(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); + 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) { + 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, "tegra_cec: TX NAKed on the fly!\n"); + + if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { + if (cec->tx_buf_cur == cec->tx_buf_cnt) + 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)) { 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) { - writel((TEGRA_CEC_INT_STAT_RX_REGISTER_FULL), + 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); - } else if ((status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) || - (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) || - (status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || - (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { - writel((TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN | - TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD | - TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY | - TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED | - TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED), - cec->cec_base + TEGRA_CEC_INT_STAT); - } else if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { - cec->tx_wake = 1; - wake_up_interruptible(&cec->tx_waitq); - writel((TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY), - cec->cec_base + TEGRA_CEC_INT_STAT); - } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { - writel((TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED), - cec->cec_base + TEGRA_CEC_INT_STAT); } +out: return IRQ_HANDLED; } @@ -192,6 +287,11 @@ static const struct file_operations tegra_cec_fops = { static void tegra_cec_init(struct tegra_cec *cec) { + cec->rx_wake = 0; + cec->tx_wake = 1; + cec->tx_buf_cnt = 0; + cec->tx_buf_cur = 0; + cec->tx_error = 0; dev_notice(cec->dev, "%s started\n", __func__); @@ -202,14 +302,11 @@ static void tegra_cec_init(struct tegra_cec *cec) writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); - writel(( - (cec->logical_addr << TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK) & - (~TEGRA_CEC_HW_CONTROL_RX_SNOOP) & - (~TEGRA_CEC_HW_CONTROL_RX_NAK_MODE) & - (~TEGRA_CEC_HW_CONTROL_TX_NAK_MODE) & - (~TEGRA_CEC_HW_CONTROL_FAST_SIM_MODE)) | - (TEGRA_CEC_HW_CONTROL_TX_RX_MODE), - cec->cec_base + TEGRA_CEC_HW_CONTROL); + cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG; + 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); writel(0x00, cec->cec_base + TEGRA_CEC_INPUT_FILTER); @@ -241,17 +338,18 @@ static void tegra_cec_init(struct tegra_cec *cec) cec->cec_base + TEGRA_CEC_TX_TIMING_1); writel((0x07 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK) | - (0x05 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_NEW_FRAME_MASK) | - (0x03 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_RETRY_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); - 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_RX_REGISTER_FULL | - TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN), - cec->cec_base + TEGRA_CEC_INT_MASK); + 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); @@ -271,6 +369,9 @@ static ssize_t cec_logical_addr_show(struct device *dev, { 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; @@ -351,7 +452,7 @@ static int tegra_cec_probe(struct platform_device *pdev) } atomic_set(&cec->init_done, 0); - cec->logical_addr = TEGRA_CEC_LOGICAL_ADDR; + mutex_init(&cec->tx_lock); cec->clk = clk_get(&pdev->dev, "cec"); @@ -365,8 +466,6 @@ static int tegra_cec_probe(struct platform_device *pdev) /* set context info. */ cec->dev = &pdev->dev; - cec->rx_wake = 0; - cec->tx_wake = 0; init_waitqueue_head(&cec->rx_waitq); init_waitqueue_head(&cec->tx_waitq); init_waitqueue_head(&cec->init_waitq); @@ -390,7 +489,7 @@ static int tegra_cec_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, cec->tegra_cec_irq, - tegra_cec_irq_handler, IRQF_DISABLED, "cec_irq", &pdev->dev); + tegra_cec_irq_handler, 0x0, "cec_irq", &pdev->dev); if (ret) { dev_err(&pdev->dev, diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 380fb076..5e2e5981 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.h * - * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2014, 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, @@ -16,28 +16,43 @@ * along with this program. If not, see . */ +#ifndef TEGRA_CEC_H +#define TEGRA_CEC_H + #include #include +#define TEGRA_CEC_FRAME_MAX_LENGTH 16 struct tegra_cec { struct device *dev; struct miscdevice misc_dev; struct clk *clk; + struct mutex tx_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; - unsigned int rx_wake; - unsigned int tx_wake; - unsigned short rx_buffer; - atomic_t init_done; + atomic_t init_done; u16 logical_addr; struct work_struct work; + 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; }; 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 @@ -55,25 +70,25 @@ static int tegra_cec_remove(struct platform_device *pdev); #define TEGRA_CEC_HW_DEBUG_RX 0X038 #define TEGRA_CEC_HW_DEBUG_TX 0X03C -#define TEGRA_CEC_LOGICAL_ADDR 0x10 -#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0xFFFF -#define TEGRA_CEC_HWCTRL_RX_LADDR(x) (x<<0) +#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_HW_CONTROL_RX_LOGICAL_ADDRS_MASK 0 -#define TEGRA_CEC_HW_CONTROL_RX_SNOOP (1<<15) -#define TEGRA_CEC_HW_CONTROL_RX_NAK_MODE (1<<16) -#define TEGRA_CEC_HW_CONTROL_TX_NAK_MODE (1<<24) -#define TEGRA_CEC_HW_CONTROL_FAST_SIM_MODE (1<<30) -#define TEGRA_CEC_HW_CONTROL_TX_RX_MODE (1<<31) - -#define TEGRA_CEC_INPUT_FILTER_MODE (1<<31) +#define TEGRA_CEC_INPUT_FILTER_MODE (1<<31) #define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_MASK 0 -#define TEGRA_CEC_TX_REGISTER_DATA_MASK 0 -#define TEGRA_CEC_TX_REGISTER_EOM_MASK 8 -#define TEGRA_CEC_TX_REGISTER_ADDRESS_MODE_MASK 12 -#define TEGRA_CEC_TX_REGISTER_GENERATE_START_BIT_MASK 16 -#define TEGRA_CEC_TX_REGISTER_RETRY_FRAME_MASK 17 +#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) @@ -102,8 +117,8 @@ static int tegra_cec_remove(struct platform_device *pdev); #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_TX_BUS_IDLE_TIME_NEW_FRAME_MASK 4 -#define TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_RETRY_FRAME_MASK 8 +#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) @@ -140,3 +155,5 @@ static int tegra_cec_remove(struct platform_device *pdev); #define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER (1<<26) #define TEGRA_CEC_NAME "tegra_cec" + +#endif /* TEGRA_CEC_H */ From 6432b7db507818e6c53aa674c86c9ec786aea1c2 Mon Sep 17 00:00:00 2001 From: Xia Yang Date: Wed, 2 Jul 2014 15:39:38 -0700 Subject: [PATCH 10/44] misc: tegra_cec: add DT matching entry Auxdata still have to be provided in of_platform_populate() during early boot device detection to get the device correctly named as "tegra_cec". Failing to do so will result in device probe failure as of_device_make_bus_id() will name the device as "[phys_base].tegra_cec" and cause clock assignment failure. Bug 200013561 Change-Id: Ib0f898f89146cba7f02cffb768c6a2eb558400ce Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/433871 Reviewed-on: http://git-master/r/1164144 (cherry picked from commit 43402b18ee6def21d7d55420f5acd22403e86f1d) --- drivers/misc/tegra-cec/tegra_cec.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 7af9e367..3c8f23f9 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -34,8 +34,8 @@ #include #include - -#include +#include +#include #include "tegra_cec.h" @@ -451,6 +451,11 @@ static int tegra_cec_probe(struct platform_device *pdev) 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); @@ -558,10 +563,19 @@ static int tegra_cec_resume(struct platform_device *pdev) } #endif +static struct of_device_id tegra_cec_of_match[] = { + { .compatible = "nvidia,tegra114-cec", }, + { .compatible = "nvidia,tegra124-cec", }, + { .compatible = "nvidia,tegra210-cec", }, + {}, +}; + 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, From 43ca60174fe4f8b50afb14e9cc38f581ea45a8cf Mon Sep 17 00:00:00 2001 From: Spencer Sutterlin Date: Fri, 31 Oct 2014 18:05:00 -0700 Subject: [PATCH 11/44] misc: tegra_cec: fix sparse warning Fix sparse warning by adding static keyword to functions Bug 200032218 Change-Id: I0fe65173eb0295f5b981fa44f381683efe9a980b Signed-off-by: Spencer Sutterlin Reviewed-on: http://git-master/r/592290 Reviewed-on: http://git-master/r/1164145 (cherry picked from commit 74cfb080d07bc8e5758dfb83c4ce7507f16e4c9a) --- drivers/misc/tegra-cec/tegra_cec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 3c8f23f9..cda3a7c0 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -48,7 +48,7 @@ static ssize_t cec_logical_addr_show(struct device *dev, static DEVICE_ATTR(cec_logical_addr_config, S_IWUSR | S_IRUGO, cec_logical_addr_show, cec_logical_addr_store); -int tegra_cec_open(struct inode *inode, struct file *file) +static int tegra_cec_open(struct inode *inode, struct file *file) { struct miscdevice *miscdev = file->private_data; struct tegra_cec *cec = container_of(miscdev, @@ -66,7 +66,7 @@ int tegra_cec_open(struct inode *inode, struct file *file) return ret; } -int tegra_cec_release(struct inode *inode, struct file *file) +static int tegra_cec_release(struct inode *inode, struct file *file) { struct tegra_cec *cec = file->private_data; @@ -124,7 +124,7 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) return ret; } -ssize_t tegra_cec_write(struct file *file, const char __user *buf, +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]; @@ -151,7 +151,7 @@ ssize_t tegra_cec_write(struct file *file, const char __user *buf, return count; } -ssize_t tegra_cec_read(struct file *file, char __user *buffer, +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; From 2c5db18fa0bc2979cb51c38275c72fe0aa01f83e Mon Sep 17 00:00:00 2001 From: Toby Butzon Date: Sat, 7 Feb 2015 16:01:37 -0800 Subject: [PATCH 12/44] misc: tegra_cec: enable input filtering Bug 1605426 Change-Id: I7fdfd3eee37f91f016fcbfe573d761f551560dde Signed-off-by: Toby Butzon Reviewed-on: http://git-master/r/682207 (cherry picked from commit fb8396c2f5cc02e72dae45d9421d17565b607921) Reviewed-on: http://git-master/r/708747 (cherry picked from commit 1c18321a0e97b6fd2568b418ea353f6716b5a2e4) --- drivers/misc/tegra-cec/tegra_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index cda3a7c0..2758e8af 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -308,7 +308,7 @@ static void tegra_cec_init(struct tegra_cec *cec) TEGRA_CEC_HWCTRL_TX_RX_MODE, cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0x00, cec->cec_base + TEGRA_CEC_INPUT_FILTER); + writel((1U << 31) | 0x20, cec->cec_base + TEGRA_CEC_INPUT_FILTER); 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) | From 42e7733cc7eb9d73b68719db8941dbfae4c06f87 Mon Sep 17 00:00:00 2001 From: Xia Yang Date: Fri, 20 Feb 2015 15:45:52 -0800 Subject: [PATCH 13/44] misc: tegra-cec: Short-circuit init on suspend Bug 1591149 Initialization of cec engine is slow. Short-circuit the init in event of suspend. Change-Id: Ibfbd6f36883a7bf45fdb5137120b041a52f42086 Signed-off-by: Xia Yang Reviewed-on: http://git-master/r/714423 (cherry picked from commit d5f4a6602678c87e8caa5032e43f2415c314d800) Reviewed-on: http://git-master/r/716496 Reviewed-on: http://git-master/r/1164147 (cherry picked from commit 82a640a1ea11e850f769b78b5d0c531731b1ffd8) --- drivers/misc/tegra-cec/tegra_cec.c | 21 ++++++++++++++++++++- drivers/misc/tegra-cec/tegra_cec.h | 6 +++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 2758e8af..cfdb2d05 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2015, 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, @@ -298,7 +298,15 @@ static void tegra_cec_init(struct tegra_cec *cec) writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); + +#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 writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); @@ -475,6 +483,11 @@ static int tegra_cec_probe(struct platform_device *pdev) 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. */ @@ -539,10 +552,16 @@ 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); clk_disable(cec->clk); diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 5e2e5981..12913a25 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.h * - * Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2015, 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, @@ -35,6 +35,10 @@ struct tegra_cec { 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; unsigned int rx_wake; From ec2330c5772fe630773d1083d6ca6e1f9f795041 Mon Sep 17 00:00:00 2001 From: Mitch Luban Date: Wed, 2 Dec 2015 14:51:59 -0800 Subject: [PATCH 14/44] drivers: cec: add T186 support for cec Bug 200148417 Change-Id: I4a57303097c0687c903f284504df10930924f1ae Signed-off-by: Mitch Luban Reviewed-on: http://git-master/r/840657 Reviewed-on: http://git-master/r/1164148 (cherry picked from commit be0d9f8be8a19525c2e4794d08f0932bc53128ee) --- drivers/misc/tegra-cec/tegra_cec.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index cfdb2d05..4e854f6f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2016, 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, @@ -419,6 +419,9 @@ static int tegra_cec_probe(struct platform_device *pdev) { struct tegra_cec *cec; struct resource *res; +#if defined(CONFIG_TEGRA_DISPLAY) + struct device_node *np = pdev->dev->of_node; +#endif int ret = 0; cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL); @@ -467,7 +470,12 @@ static int tegra_cec_probe(struct platform_device *pdev) atomic_set(&cec->init_done, 0); mutex_init(&cec->tx_lock); +#if defined(CONFIG_TEGRA_DISPLAY) + if (np) + cec->clk = of_clk_get_by_name(np, "cec"); +#else cec->clk = clk_get(&pdev->dev, "cec"); +#endif if (IS_ERR_OR_NULL(cec->clk)) { dev_err(&pdev->dev, "can't get clock for CEC\n"); @@ -475,7 +483,7 @@ static int tegra_cec_probe(struct platform_device *pdev) goto clk_error; } - clk_enable(cec->clk); + clk_prepare_enable(cec->clk); /* set context info. */ cec->dev = &pdev->dev; @@ -586,6 +594,7 @@ static struct of_device_id tegra_cec_of_match[] = { { .compatible = "nvidia,tegra114-cec", }, { .compatible = "nvidia,tegra124-cec", }, { .compatible = "nvidia,tegra210-cec", }, + { .compatible = "nvidia,tegra186-cec", }, {}, }; From 290c0854e2613fa570c620edcd3f5f990de82e12 Mon Sep 17 00:00:00 2001 From: Chun XU Date: Fri, 6 May 2016 15:34:37 +0800 Subject: [PATCH 15/44] misc: tegra-cec: enable DISA temporarily in init Unpowergate DISA power domain if it is powergated during CEC driver initialization. Bug 200175747 Change-Id: I8b3d879fe0c33eaef74c1b4d3b549559c4977e4d Signed-off-by: Chun XU Reviewed-on: http://git-master/r/1142593 Reviewed-on: http://git-master/r/1164149 (cherry picked from commit c341176715597fe8268b20b291bcef07bb429500) --- drivers/misc/tegra-cec/tegra_cec.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 4e854f6f..88fd0c01 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -39,6 +39,8 @@ #include "tegra_cec.h" +#include + static ssize_t cec_logical_addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); @@ -287,6 +289,8 @@ static const struct file_operations tegra_cec_fops = { static void tegra_cec_init(struct tegra_cec *cec) { + int power_tmp = 0, is_tegra186 = 0, ret; + cec->rx_wake = 0; cec->tx_wake = 1; cec->tx_buf_cnt = 0; @@ -295,6 +299,17 @@ static void tegra_cec_init(struct tegra_cec *cec) dev_notice(cec->dev, "%s started\n", __func__); + is_tegra186 = of_device_is_compatible(cec->dev->of_node, "nvidia,tegra186-cec"); + if (is_tegra186 && !tegra_powergate_is_powered(TEGRA_POWERGATE_DISA)) { + ret = tegra_unpowergate_partition_with_clk_on(TEGRA_POWERGATE_DISA); + if (ret) { + dev_err(cec->dev, "Fail to unpowergate DISP.\n"); + return; + } + power_tmp = 1; + dev_warn(cec->dev, "Unpowergate DISP temporarily.\n"); + } + writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); @@ -362,6 +377,16 @@ static void tegra_cec_init(struct tegra_cec *cec) atomic_set(&cec->init_done, 1); wake_up_interruptible(&cec->init_waitq); + if (is_tegra186 && power_tmp == 1) { + ret = tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_DISA); + if (ret) { + dev_err(cec->dev, "Fail to powergate DISP.\n"); + return; + } + power_tmp = 0; + dev_warn(cec->dev, "Powergate DISP.\n"); + } + dev_notice(cec->dev, "%s Done.\n", __func__); } @@ -483,7 +508,8 @@ static int tegra_cec_probe(struct platform_device *pdev) goto clk_error; } - clk_prepare_enable(cec->clk); + ret = clk_prepare_enable(cec->clk); + dev_info(&pdev->dev, "Enable clock result: %d.\n", ret); /* set context info. */ cec->dev = &pdev->dev; From 912ff2686c3377371c01053695dbc629d690fba5 Mon Sep 17 00:00:00 2001 From: Ankit Pashiney Date: Mon, 1 Apr 2013 21:59:36 -0700 Subject: [PATCH 16/44] misc: Tegra CEC Support Bug 1258710 Change-Id: Ie449d4b8a28388087b601a0332eb065488a32009 Signed-off-by: Ankit Pashiney Reviewed-on: http://git-master/r/215363 (cherry picked from commit e156fdeb84a7f750b602c746489538f5b29837cd) Reviewed-on: http://git-master/r/1164171 (cherry picked from commit 64c30ff47c90f4dbf659570e9aaeea1270b9f756) --- drivers/misc/tegra-cec/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/Makefile b/drivers/misc/tegra-cec/Makefile index ab380305..db3705a8 100644 --- a/drivers/misc/tegra-cec/Makefile +++ b/drivers/misc/tegra-cec/Makefile @@ -2,4 +2,6 @@ # Makefile for tegra cec support. # -obj-$(CONFIG_TEGRA_CEC_T30) += tegra_cec.o +subdir-ccflags-y = -Werror + +obj-$(CONFIG_TEGRA_CEC_SUPPORT) += tegra_cec.o From 293e6c6256a3240a2b3ca9808080ecb38672ceec Mon Sep 17 00:00:00 2001 From: Chun XU Date: Fri, 6 May 2016 15:34:37 +0800 Subject: [PATCH 17/44] misc: tegra-cec: tegra_nvdisp_powergate_partition Use tegra_nvdisp_unpowergate_partition/tegra_nvdisp_powergate_partition in Tegra CEC driver to control DISA power domain. Bug 200214236 Bug 200214232 Bug 200185815 Change-Id: Ib860570f901278e9211b5262fddf8c63a700c5f9 Signed-off-by: Chun XU Reviewed-on: http://git-master/r/1176912 (cherry picked from commit 8e9f75ac1dadf04a8f89a74dac3e67cd1a06f886) --- drivers/misc/tegra-cec/tegra_cec.c | 45 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 88fd0c01..db2b2d28 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -40,6 +40,7 @@ #include "tegra_cec.h" #include +#include "../../../../display/drivers/video/tegra/dc/dc_priv.h" static ssize_t cec_logical_addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); @@ -289,8 +290,6 @@ static const struct file_operations tegra_cec_fops = { static void tegra_cec_init(struct tegra_cec *cec) { - int power_tmp = 0, is_tegra186 = 0, ret; - cec->rx_wake = 0; cec->tx_wake = 1; cec->tx_buf_cnt = 0; @@ -299,17 +298,6 @@ static void tegra_cec_init(struct tegra_cec *cec) dev_notice(cec->dev, "%s started\n", __func__); - is_tegra186 = of_device_is_compatible(cec->dev->of_node, "nvidia,tegra186-cec"); - if (is_tegra186 && !tegra_powergate_is_powered(TEGRA_POWERGATE_DISA)) { - ret = tegra_unpowergate_partition_with_clk_on(TEGRA_POWERGATE_DISA); - if (ret) { - dev_err(cec->dev, "Fail to unpowergate DISP.\n"); - return; - } - power_tmp = 1; - dev_warn(cec->dev, "Unpowergate DISP temporarily.\n"); - } - writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); @@ -377,16 +365,6 @@ static void tegra_cec_init(struct tegra_cec *cec) atomic_set(&cec->init_done, 1); wake_up_interruptible(&cec->init_waitq); - if (is_tegra186 && power_tmp == 1) { - ret = tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_DISA); - if (ret) { - dev_err(cec->dev, "Fail to powergate DISP.\n"); - return; - } - power_tmp = 0; - dev_warn(cec->dev, "Powergate DISP.\n"); - } - dev_notice(cec->dev, "%s Done.\n", __func__); } @@ -495,6 +473,15 @@ static int tegra_cec_probe(struct platform_device *pdev) atomic_set(&cec->init_done, 0); mutex_init(&cec->tx_lock); +#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) + ret = tegra_nvdisp_unpowergate_partition(TEGRA_POWERGATE_DISA); + if (ret) { + dev_err(&pdev->dev, "Fail to unpowergate DISP: %d.\n", ret); + goto clk_error; + } + dev_info(&pdev->dev, "Unpowergate DISP: %d.\n", ret); +#endif + #if defined(CONFIG_TEGRA_DISPLAY) if (np) cec->clk = of_clk_get_by_name(np, "cec"); @@ -564,6 +551,9 @@ static int tegra_cec_probe(struct platform_device *pdev) cec_error: clk_disable(cec->clk); clk_put(cec->clk); +#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) + tegra_nvdisp_powergate_partition(TEGRA_POWERGATE_DISA); +#endif clk_error: return ret; } @@ -574,6 +564,9 @@ static int tegra_cec_remove(struct platform_device *pdev) clk_disable(cec->clk); clk_put(cec->clk); +#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) + tegra_nvdisp_powergate_partition(TEGRA_POWERGATE_DISA); +#endif misc_deregister(&cec->misc_dev); cancel_work_sync(&cec->work); @@ -598,6 +591,9 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) atomic_set(&cec->init_cancel, 0); clk_disable(cec->clk); +#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) + tegra_nvdisp_powergate_partition(TEGRA_POWERGATE_DISA); +#endif dev_notice(&pdev->dev, "suspended\n"); return 0; @@ -609,6 +605,9 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); +#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) + tegra_nvdisp_unpowergate_partition(TEGRA_POWERGATE_DISA); +#endif clk_enable(cec->clk); schedule_work(&cec->work); From 5203c515307677becf2a617ee2ed064ce261fdeb Mon Sep 17 00:00:00 2001 From: Chun XU Date: Mon, 4 Jul 2016 17:16:44 +0800 Subject: [PATCH 18/44] misc: tegra-cec: add /sys/devices/platform symlink Create a symlink for tegra_cec if it is not under platform bus or it has been created with different name. Bug 200214224 Bug 200185815 Bug 1764073 Change-Id: Ib2f89db016982ca916894c37e47bbdf177fb617e Signed-off-by: Chun XU Reviewed-on: http://git-master/r/1175137 (cherry picked from commit 506e6370d234c4770afb22372ef1955faa7822cf) --- drivers/misc/tegra-cec/tegra_cec.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index db2b2d28..70b40879 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -536,6 +536,18 @@ static int tegra_cec_probe(struct platform_device *pdev) 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); From 9807746899a672221aa3144e1059ff78d5541905 Mon Sep 17 00:00:00 2001 From: Spencer Sutterlin Date: Thu, 29 Jan 2015 17:45:06 -0800 Subject: [PATCH 19/44] misc: tegra_cec: fix cec_logical_addr_store print Bug 1637415 Change-Id: Ib165165b67f5ea3723914270215de06df48f6591 Signed-off-by: Spencer Sutterlin Reviewed-on: http://git-master/r/679245 (cherry picked from commit e9eef85a417a85461d0e7c018fcec2f2838254c2) --- drivers/misc/tegra-cec/tegra_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 70b40879..a2e5cb7b 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -408,7 +408,7 @@ static ssize_t cec_logical_addr_store(struct device *dev, return ret; - dev_info(dev, "tegra_cec: set logical address: %x\n", (u32)addr); + dev_info(dev, "tegra_cec: set logical address: 0x%x\n", (u32)addr); cec->logical_addr = addr; state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; From 0a6fb5c3c956c14a2dee232154d7d02f5cca01a2 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 1 Feb 2017 10:48:55 +0200 Subject: [PATCH 20/44] misc: tegra-cec: Use per-chip powergate IDs Powergate identifiers are different on Tegra210 and Tegra186, so add per-SoC match data to use the correct value on each SoC. bug 200257351 Change-Id: I81b938386ab9cbaec735baac44ee74fccf39731c Signed-off-by: Mikko Perttunen Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/1297136 (cherry picked from commit 5438b348e036c31dcf9db0d1364fa6c6ac053428) --- drivers/misc/tegra-cec/tegra_cec.c | 33 +++++++++++++++++++++--------- drivers/misc/tegra-cec/tegra_cec.h | 3 +++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index a2e5cb7b..79f1268f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -31,17 +31,22 @@ #include #include #include +#include #include #include #include #include +#include #include "tegra_cec.h" -#include #include "../../../../display/drivers/video/tegra/dc/dc_priv.h" +struct tegra_cec_soc { + int powergate_id; +}; + static ssize_t cec_logical_addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); @@ -432,6 +437,8 @@ static int tegra_cec_probe(struct platform_device *pdev) if (!cec) return -ENOMEM; + cec->soc = of_device_get_match_data(&pdev->dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -474,7 +481,7 @@ static int tegra_cec_probe(struct platform_device *pdev) mutex_init(&cec->tx_lock); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - ret = tegra_nvdisp_unpowergate_partition(TEGRA_POWERGATE_DISA); + ret = tegra_nvdisp_unpowergate_partition(cec->soc->powergate_id); if (ret) { dev_err(&pdev->dev, "Fail to unpowergate DISP: %d.\n", ret); goto clk_error; @@ -564,7 +571,7 @@ cec_error: clk_disable(cec->clk); clk_put(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_powergate_partition(TEGRA_POWERGATE_DISA); + tegra_nvdisp_powergate_partition(cec->soc->powergate_id); #endif clk_error: return ret; @@ -577,7 +584,7 @@ static int tegra_cec_remove(struct platform_device *pdev) clk_disable(cec->clk); clk_put(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_powergate_partition(TEGRA_POWERGATE_DISA); + tegra_nvdisp_powergate_partition(cec->soc->powergate_id); #endif misc_deregister(&cec->misc_dev); @@ -604,7 +611,7 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) clk_disable(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_powergate_partition(TEGRA_POWERGATE_DISA); + tegra_nvdisp_powergate_partition(cec->soc->powergate_id); #endif dev_notice(&pdev->dev, "suspended\n"); @@ -618,7 +625,7 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_unpowergate_partition(TEGRA_POWERGATE_DISA); + tegra_nvdisp_unpowergate_partition(cec->soc->powergate_id); #endif clk_enable(cec->clk); schedule_work(&cec->work); @@ -627,11 +634,17 @@ static int tegra_cec_resume(struct platform_device *pdev) } #endif +static struct tegra_cec_soc tegra210_soc_data = { + .powergate_id = TEGRA210_POWER_DOMAIN_DISA, +}; + +static struct tegra_cec_soc tegra186_soc_data = { + .powergate_id = TEGRA186_POWER_DOMAIN_DISP, +}; + static struct of_device_id tegra_cec_of_match[] = { - { .compatible = "nvidia,tegra114-cec", }, - { .compatible = "nvidia,tegra124-cec", }, - { .compatible = "nvidia,tegra210-cec", }, - { .compatible = "nvidia,tegra186-cec", }, + { .compatible = "nvidia,tegra210-cec", .data = &tegra210_soc_data }, + { .compatible = "nvidia,tegra186-cec", .data = &tegra186_soc_data }, {}, }; diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 12913a25..3f06a81f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -24,6 +24,8 @@ #define TEGRA_CEC_FRAME_MAX_LENGTH 16 +struct tegra_cec_soc; + struct tegra_cec { struct device *dev; struct miscdevice misc_dev; @@ -41,6 +43,7 @@ struct tegra_cec { #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; From c061b057db0332682ffad62cab821ace1be8c2ae Mon Sep 17 00:00:00 2001 From: Chun XU Date: Tue, 27 Dec 2016 17:35:07 +0800 Subject: [PATCH 21/44] misc: tegra_cec: turn on TV ASAP Turn on the TV as soon as possible for better user experience by sending and CEC commands if the previous reboot is not recovery. If the previous reboot reason is recovery, don't send to avoid turning on TV when there is an auto OTA on-going. CEC driver uses RESERVED2 logical address as the source address. Later, the upper layer will send OneTouchPlay command by using its valid logical address. Bug 200253563 Change-Id: Iecd759bb50c1f21549f55906e68e3faab905f86b Signed-off-by: Chun XU Reviewed-on: http://git-master/r/1277092 (cherry picked from commit 2beab2dcfc4e40712004500df99633d59ae0e70c) --- drivers/misc/tegra-cec/tegra_cec.c | 69 +++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 79f1268f..2b8949b9 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2017, 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, @@ -42,6 +42,25 @@ #include "tegra_cec.h" #include "../../../../display/drivers/video/tegra/dc/dc_priv.h" +#include "../../../../display/drivers/video/tegra/dc/dc.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 + +static bool previous_reboot_reason_is_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 +}; struct tegra_cec_soc { int powergate_id; @@ -293,6 +312,40 @@ static const struct file_operations tegra_cec_fops = { .write = tegra_cec_write, }; +static int tegra_cec_send_one_touch_play(struct tegra_cec *cec) +{ + int res = 0; + u8 phy_address[2] = {0}; + + text_view_on_sent = true; + + res = tegra_dc_get_source_physical_address(phy_address); + if (res) { + dev_warn(cec->dev, "Can't find physical addresse.\n"); + return res; + } + + dev_info(cec->dev, "physical address: %02x:%02x.\n", + phy_address[0], phy_address[1]); + + 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) { cec->rx_wake = 0; @@ -370,6 +423,8 @@ static void tegra_cec_init(struct tegra_cec *cec) atomic_set(&cec->init_done, 1); wake_up_interruptible(&cec->init_waitq); + if (!text_view_on_sent && !previous_reboot_reason_is_recovery) + tegra_cec_send_one_touch_play(cec); dev_notice(cec->dev, "%s Done.\n", __func__); } @@ -634,6 +689,18 @@ static int tegra_cec_resume(struct platform_device *pdev) } #endif +static int __init check_previous_reboot_reason_is_recovery(char *options) +{ + previous_reboot_reason_is_recovery = true; + + pr_info("tegra_cec: the previous_reboot_reason is%s recovery.\n", + previous_reboot_reason_is_recovery ? "" : " not"); + + return 0; +} + +early_param("post_recovery", check_previous_reboot_reason_is_recovery); + static struct tegra_cec_soc tegra210_soc_data = { .powergate_id = TEGRA210_POWER_DOMAIN_DISA, }; From 4f6189525bff0eeacee39893cde5b09cdf63d217 Mon Sep 17 00:00:00 2001 From: Chun XU Date: Tue, 7 Feb 2017 19:19:12 +0800 Subject: [PATCH 22/44] misc: tegra_cec: add recover IOCTL Provide error recovery from IOCTL to user space. Bug 1866338 Change-Id: I705ada6c8d4cb13f1c882993468f467da2908fdf Signed-off-by: Chun XU Reviewed-on: http://git-master/r/1300499 (cherry picked from commit a646f4903c8794641432fa838a27ee5584944eb5) --- drivers/misc/tegra-cec/tegra_cec.c | 27 +++++++++++++++++++++++++++ drivers/misc/tegra-cec/tegra_cec.h | 7 ++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 2b8949b9..98eca5aa 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -304,12 +304,38 @@ out: return IRQ_HANDLED; } +static long tegra_cec_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + 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; + 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) @@ -534,6 +560,7 @@ static int tegra_cec_probe(struct platform_device *pdev) atomic_set(&cec->init_done, 0); mutex_init(&cec->tx_lock); + mutex_init(&cec->recovery_lock); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) ret = tegra_nvdisp_unpowergate_partition(cec->soc->powergate_id); diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 3f06a81f..34300d0f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.h * - * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2017, 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, @@ -31,6 +31,7 @@ struct tegra_cec { 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; @@ -163,4 +164,8 @@ static int tegra_cec_remove(struct platform_device *pdev); #define TEGRA_CEC_NAME "tegra_cec" +#define TEGRA_CEC_IOC_MAGIC 'C' + +#define TEGRA_CEC_IOCTL_ERROR_RECOVERY _IO(TEGRA_CEC_IOC_MAGIC, 1) + #endif /* TEGRA_CEC_H */ From 482bd11f323697663005c52b4f1845b9c18bb73a Mon Sep 17 00:00:00 2001 From: Santosh Reddy Galma Date: Wed, 8 Mar 2017 22:29:58 +0530 Subject: [PATCH 23/44] misc: tegra-cec: fix power domains API for cec use generic APIs tegra_unpowergate_partition() and tegra_powergate_partition() to unpowergate DISP power domain needed for CEC without enabling disp clocks so that display pll clocks are disabled in idle state even though DISP partition is awake. Bug 1810989 Change-Id: I37428b9012729a4458724314732f80a571057fd6 Signed-off-by: Santosh Reddy Galma Reviewed-on: http://git-master/r/1317241 (cherry picked from commit dffb3d3b5fb67cb6920fdbfc72cd3d6c37f0061c) --- drivers/misc/tegra-cec/tegra_cec.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 98eca5aa..b13100f3 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -41,7 +41,6 @@ #include #include "tegra_cec.h" -#include "../../../../display/drivers/video/tegra/dc/dc_priv.h" #include "../../../../display/drivers/video/tegra/dc/dc.h" #define LOGICAL_ADDRESS_RESERVED2 0xD @@ -563,7 +562,7 @@ static int tegra_cec_probe(struct platform_device *pdev) mutex_init(&cec->recovery_lock); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - ret = tegra_nvdisp_unpowergate_partition(cec->soc->powergate_id); + ret = tegra_unpowergate_partition(cec->soc->powergate_id); if (ret) { dev_err(&pdev->dev, "Fail to unpowergate DISP: %d.\n", ret); goto clk_error; @@ -653,7 +652,7 @@ cec_error: clk_disable(cec->clk); clk_put(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_powergate_partition(cec->soc->powergate_id); + tegra_powergate_partition(cec->soc->powergate_id); #endif clk_error: return ret; @@ -666,7 +665,7 @@ static int tegra_cec_remove(struct platform_device *pdev) clk_disable(cec->clk); clk_put(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_powergate_partition(cec->soc->powergate_id); + tegra_powergate_partition(cec->soc->powergate_id); #endif misc_deregister(&cec->misc_dev); @@ -693,7 +692,7 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) clk_disable(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_powergate_partition(cec->soc->powergate_id); + tegra_powergate_partition(cec->soc->powergate_id); #endif dev_notice(&pdev->dev, "suspended\n"); @@ -707,7 +706,7 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_nvdisp_unpowergate_partition(cec->soc->powergate_id); + tegra_unpowergate_partition(cec->soc->powergate_id); #endif clk_enable(cec->clk); schedule_work(&cec->work); From 5845f087dd1a8be917bb71cce80660904db61c35 Mon Sep 17 00:00:00 2001 From: Vince Hsu Date: Thu, 23 Mar 2017 15:26:02 +0800 Subject: [PATCH 24/44] misc: tegra_cec: turn some prints into debug level To prevent too many bufferred message from flushing out to serial when we turn on/off display and blocking other high priority task due to disabled IRQ, we change some prints to debug level to workaround that problem as a short-term solution. The long-term will be implemented later. bug 200205349 Change-Id: I6609844d5c2ee58b2df6a51e9537bb7dd1448995 Signed-off-by: Vince Hsu Reviewed-on: http://git-master/r/1326683 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Chun Xu GVS: Gerrit_Virtual_Submit Reviewed-by: Juha Tukkinen --- drivers/misc/tegra-cec/tegra_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index b13100f3..1b7a7e7c 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -493,7 +493,7 @@ static ssize_t cec_logical_addr_store(struct device *dev, return ret; - dev_info(dev, "tegra_cec: set logical address: 0x%x\n", (u32)addr); + dev_dbg(dev, "tegra_cec: set logical address: 0x%x\n", (u32)addr); cec->logical_addr = addr; state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; From f79ab92d84f3922f660745e499b6b0f1bc4b507f Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 14 Apr 2017 16:30:27 -0700 Subject: [PATCH 25/44] misc: tegra_cec: add missing cancel_work_sync() This patch just adds missing cancel_work_sync() in the exit path of the probe(). Bug 1887171 Bug 200289389 Change-Id: I7d42ae6d035f87b5ad1b726cbe5274fbd9755f4e Signed-off-by: Nicolin Chen Reviewed-on: http://git-master/r/1463243 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam --- drivers/misc/tegra-cec/tegra_cec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 1b7a7e7c..7c427d13 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -649,6 +649,7 @@ static int tegra_cec_probe(struct platform_device *pdev) return 0; cec_error: + cancel_work_sync(&cec->work); clk_disable(cec->clk); clk_put(cec->clk); #if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) From 74524fa09bd074b86e734ff2644208c2bd2293cd Mon Sep 17 00:00:00 2001 From: Chun Xu Date: Fri, 19 May 2017 11:09:52 +0800 Subject: [PATCH 26/44] misc: tegra-cec: dump read/write buffer for debug Dump read/write buffer with dev_dbg for debug. TDS-2199 Change-Id: I1fa3dec9e2ccd78023c464085fd2f6e3f49a87f2 Signed-off-by: Chun Xu Reviewed-on: http://git-master/r/1485394 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Prafull Suryawanshi GVS: Gerrit_Virtual_Submit Reviewed-by: Spencer Sutterlin Reviewed-by: Bharat Nihalani --- drivers/misc/tegra-cec/tegra_cec.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 7c427d13..6e1347d3 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -173,8 +173,10 @@ static ssize_t tegra_cec_write(struct file *file, const char __user *buf, mutex_unlock(&cec->tx_lock); if (ret) return ret; - else + 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, @@ -201,6 +203,8 @@ static ssize_t tegra_cec_read(struct file *file, char __user *buffer, 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; @@ -231,7 +235,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) goto out; if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) { - dev_err(dev, "tegra_cec: TX underrun, interrupt timing issue!\n"); + dev_err(dev, "TX underrun, interrupt timing issue!\n"); tegra_cec_error_recovery(cec); writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, @@ -271,7 +275,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) goto out; } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) - dev_warn(dev, "tegra_cec: TX NAKed on the fly!\n"); + 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) @@ -492,8 +496,7 @@ static ssize_t cec_logical_addr_store(struct device *dev, if (ret) return ret; - - dev_dbg(dev, "tegra_cec: set logical address: 0x%x\n", (u32)addr); + dev_info(dev, "set logical address: 0x%x\n", (u32)addr); cec->logical_addr = addr; state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; From a550d6f899392d7a9e8b417720631616765697ca Mon Sep 17 00:00:00 2001 From: Ishan Mittal Date: Mon, 19 Jun 2017 17:55:24 +0530 Subject: [PATCH 27/44] drivers: Fix references to nvidia/ drivers inside platform tegra and nvdisp point to display repo. changing that to nvidia/ Bug 200295104 Change-Id: I91e0356555c251df3eae98218a69f4bc7e3cd208 Signed-off-by: Ishan Mittal --- drivers/misc/tegra-cec/tegra_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 6e1347d3..bdad073f 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -41,7 +41,7 @@ #include #include "tegra_cec.h" -#include "../../../../display/drivers/video/tegra/dc/dc.h" +#include "../../../../nvidia/drivers/video/tegra/dc/dc.h" #define LOGICAL_ADDRESS_RESERVED2 0xD #define LOGICAL_ADDRESS_TV 0x0 From a89f31f800ef32674ee3f418fff626e37277b323 Mon Sep 17 00:00:00 2001 From: Chun Xu Date: Wed, 27 Dec 2017 03:20:05 -0800 Subject: [PATCH 28/44] misc: tegra-cec: add T194 CEC device Bug 2039509 Bug 200375853 Change-Id: Iebe1956a94cbf0209b11ad8c66d8fc5b7335549a Signed-off-by: Chun Xu Reviewed-on: https://git-master.nvidia.com/r/1641039 Reviewed-by: svc-mobile-coverity Reviewed-by: Naveen Kumar S GVS: Gerrit_Virtual_Submit Reviewed-by: Mitch Luban Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index bdad073f..442568f3 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012-2018, 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, @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -350,7 +351,7 @@ static int tegra_cec_send_one_touch_play(struct tegra_cec *cec) res = tegra_dc_get_source_physical_address(phy_address); if (res) { - dev_warn(cec->dev, "Can't find physical addresse.\n"); + dev_notice(cec->dev, "Can't find physical addresse.\n"); return res; } @@ -739,9 +740,19 @@ static struct tegra_cec_soc tegra186_soc_data = { .powergate_id = TEGRA186_POWER_DOMAIN_DISP, }; +static struct tegra_cec_soc tegra194_soc_data = { +// temporary WAR to get 4.4 builds working +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) + .powergate_id = TEGRA186_POWER_DOMAIN_DISP, +#else + .powergate_id = TEGRA194_POWER_DOMAIN_DISP, +#endif +}; + 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 }, {}, }; From 80169e7ded123bcffd236d3e6dc19252dfdd0c5d Mon Sep 17 00:00:00 2001 From: Ishwarya Balaji Gururajan Date: Wed, 7 Feb 2018 15:45:21 -0800 Subject: [PATCH 29/44] misc: tegra-cec: remove NVDISPLAY ifdefs remove NVDISPLAY ifdefs and replace it with runtime APIs Jira TDS-2761 Change-Id: I920fe5402a91d1780b07aadf542cc15361f73398 Signed-off-by: Ishwarya Balaji Gururajan Reviewed-on: https://git-master.nvidia.com/r/1653842 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 52 +++++++++++++++++------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 442568f3..7d7227e6 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -43,6 +43,7 @@ #include "tegra_cec.h" #include "../../../../nvidia/drivers/video/tegra/dc/dc.h" +#include "../../../../nvidia/drivers/video/tegra/dc/dc_priv.h" #define LOGICAL_ADDRESS_RESERVED2 0xD #define LOGICAL_ADDRESS_TV 0x0 @@ -511,9 +512,7 @@ static int tegra_cec_probe(struct platform_device *pdev) { struct tegra_cec *cec; struct resource *res; -#if defined(CONFIG_TEGRA_DISPLAY) - struct device_node *np = pdev->dev->of_node; -#endif + struct device_node *np = pdev->dev.of_node; int ret = 0; cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL); @@ -565,21 +564,24 @@ static int tegra_cec_probe(struct platform_device *pdev) mutex_init(&cec->tx_lock); mutex_init(&cec->recovery_lock); -#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - ret = tegra_unpowergate_partition(cec->soc->powergate_id); - if (ret) { - dev_err(&pdev->dev, "Fail to unpowergate DISP: %d.\n", ret); - goto clk_error; +#if defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) { + ret = tegra_unpowergate_partition(cec->soc->powergate_id); + if (ret) { + dev_err(&pdev->dev, "Fail to unpowergate DISP: %d.\n", + ret); + goto clk_error; + } + dev_info(&pdev->dev, "Unpowergate DISP: %d.\n", ret); } - dev_info(&pdev->dev, "Unpowergate DISP: %d.\n", ret); #endif -#if defined(CONFIG_TEGRA_DISPLAY) - if (np) - cec->clk = of_clk_get_by_name(np, "cec"); -#else - cec->clk = clk_get(&pdev->dev, "cec"); -#endif + 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"); @@ -656,8 +658,9 @@ cec_error: cancel_work_sync(&cec->work); clk_disable(cec->clk); clk_put(cec->clk); -#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_powergate_partition(cec->soc->powergate_id); +#if defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) + tegra_powergate_partition(cec->soc->powergate_id); #endif clk_error: return ret; @@ -669,8 +672,9 @@ static int tegra_cec_remove(struct platform_device *pdev) clk_disable(cec->clk); clk_put(cec->clk); -#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_powergate_partition(cec->soc->powergate_id); +#if defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) + tegra_powergate_partition(cec->soc->powergate_id); #endif misc_deregister(&cec->misc_dev); @@ -696,8 +700,9 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) atomic_set(&cec->init_cancel, 0); clk_disable(cec->clk); -#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_powergate_partition(cec->soc->powergate_id); +#if defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) + tegra_powergate_partition(cec->soc->powergate_id); #endif dev_notice(&pdev->dev, "suspended\n"); @@ -710,8 +715,9 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); -#if defined(CONFIG_TEGRA_NVDISPLAY) && defined(CONFIG_TEGRA_POWERGATE) - tegra_unpowergate_partition(cec->soc->powergate_id); +#if defined(CONFIG_TEGRA_POWERGATE) + if (tegra_dc_is_nvdisplay()) + tegra_unpowergate_partition(cec->soc->powergate_id); #endif clk_enable(cec->clk); schedule_work(&cec->work); From 0e3c13c2f1f76a98b606060b52df74a8b12d1b4f Mon Sep 17 00:00:00 2001 From: Aly Hirani Date: Wed, 6 Jun 2018 16:35:14 -0700 Subject: [PATCH 30/44] misc: tegra_cec: Add timeout for wait_event Bug 2151251 Change-Id: I75d16b33d1596242c28d693f824a51d72934a5c3 Signed-off-by: Aly Hirani Reviewed-on: https://git-master.nvidia.com/r/1742040 (cherry picked from commit c94751ec8838dd9edd5b2dafd5c308ecefe6a8b3) Reviewed-on: https://git-master.nvidia.com/r/1746695 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Vinayak Pane Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 7d7227e6..e1a0e974 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -124,7 +124,7 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) * this means we have to wait for it to finish before beginning * subsequent transmission. */ - ret = wait_event_interruptible(cec->tx_waitq, cec->tx_wake == 1); + ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, HZ); if (ret) return ret; @@ -145,7 +145,7 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) writel(mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, cec->cec_base + TEGRA_CEC_INT_MASK); - ret = wait_event_interruptible(cec->tx_waitq, cec->tx_wake == 1); + ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, HZ); if (!ret) ret = cec->tx_error; From 4ca144139ba4feedd30d00dd86fdd517a7dfd79c Mon Sep 17 00:00:00 2001 From: Aly Hirani Date: Thu, 7 Jun 2018 13:04:29 -0700 Subject: [PATCH 31/44] misc: tegra_cec: fixing return values for wait This change fixes the code that was checking the return value for the wait_event_interruptible_timeout. Change-Id: Ief6c180f1df9994dd6caa776e08cdb26e15bb32b Signed-off-by: Aly Hirani Reviewed-on: https://git-master.nvidia.com/r/1742938 (cherry picked from commit 8e4b55aa0571a10acf106d21f7c70b2d8b8108c6) Reviewed-on: https://git-master.nvidia.com/r/1746696 Reviewed-by: svc-mobile-coverity Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Vinayak Pane Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index e1a0e974..904f48be 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -124,8 +124,9 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) * 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, HZ); - if (ret) + ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, + msecs_to_jiffies(1000)); + if (ret <= 0) return ret; mode = TEGRA_CEC_LADDR_MODE(buf[0]) << TEGRA_CEC_TX_REG_ADDR_MODE_SHIFT; @@ -145,8 +146,9 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) 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, HZ); - if (!ret) + ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, + msecs_to_jiffies(1000)); + if (ret > 0) ret = cec->tx_error; return ret; From 23461a38516ce0dd88062903a98e4a46a36c58f4 Mon Sep 17 00:00:00 2001 From: Chun Xu Date: Wed, 6 Jun 2018 20:56:53 +0800 Subject: [PATCH 32/44] CEC: add timeout and recovery in CEC frame sending To avoid hang on CEC frame sending, introduce timeout and recovery. Bug 2151251 Change-Id: Ia7c87ee874734c3ed976f3a9142ecc7e2509ec43 Reviewed-on: https://git-master.nvidia.com/r/1741606 (cherry picked from commit e0010f8fb4443cc146a84d76b8666ee831010f56) Signed-off-by: Chun Xu Reviewed-on: https://git-master.nvidia.com/r/1764220 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 42 ++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 904f48be..eaf68014 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -50,6 +50,12 @@ #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 previous_reboot_reason_is_recovery, text_view_on_sent; static u8 text_view_on_command[] = { @@ -110,6 +116,16 @@ static inline void tegra_cec_native_tx(const struct tegra_cec *cec, u32 block) cec->cec_base + TEGRA_CEC_INT_STAT); } +static inline void tegra_cec_error_recovery(struct tegra_cec *cec) +{ + u32 hw_ctrl; + + hw_ctrl = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); + writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); + 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) { @@ -125,8 +141,10 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) * subsequent transmission. */ ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, - msecs_to_jiffies(1000)); - if (ret <= 0) + 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; @@ -147,9 +165,15 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) cec->cec_base + TEGRA_CEC_INT_MASK); ret = wait_event_interruptible_timeout(cec->tx_waitq, cec->tx_wake == 1, - msecs_to_jiffies(1000)); - if (ret > 0) + 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; } @@ -214,16 +238,6 @@ static ssize_t tegra_cec_read(struct file *file, char __user *buffer, return count; } -static inline void tegra_cec_error_recovery(struct tegra_cec *cec) -{ - u32 hw_ctrl; - - hw_ctrl = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); - writel(hw_ctrl, cec->cec_base + TEGRA_CEC_HW_CONTROL); -} - static irqreturn_t tegra_cec_irq_handler(int irq, void *data) { struct device *dev = data; From cf3a533906fea21f7fb2f6db5c2232f7c57a2a02 Mon Sep 17 00:00:00 2001 From: Chun Xu Date: Fri, 19 Jan 2018 11:08:51 +0800 Subject: [PATCH 33/44] tegra_cec: suppport dump registers and snoop mode Provide dump registers and changingRX snoop mode interface. Bug 200382184 Change-Id: Idb55d22112bc6be9de89b8ff8e060e73de469507 Signed-off-by: Chun Xu Reviewed-on: https://git-master.nvidia.com/r/1765068 (cherry picked from commit 3310df0f117d792d3d0bfab6326592851faaeed7) Reviewed-on: https://git-master.nvidia.com/r/1641846 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Prafull Suryawanshi Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 61 ++++++++++++++++++++++++++++++ drivers/misc/tegra-cec/tegra_cec.h | 4 ++ 2 files changed, 65 insertions(+) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index eaf68014..affe3cde 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -325,9 +325,49 @@ 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 <= TEGRA_CEC_HW_SPARE; i+=4) + { + value = readl(cec->cec_base + i); + dev_info(cec->dev, "offset %08x: %08x\n", i, value); + } + return i; + +} + +static int tegra_cec_set_rx_snoop(struct tegra_cec *cec, u32 enable) +{ + u32 state; + + if (!atomic_read(&cec->init_done)) + return -EAGAIN; + state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + if (((state & TEGRA_CEC_HWCTRL_RX_SNOOP) != 0) ^ (enable != 0)) { + state ^= TEGRA_CEC_HWCTRL_RX_SNOOP; + 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 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) @@ -339,6 +379,27 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, 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 = !access_ok(VERIFY_READ, 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 = !access_ok(VERIFY_WRITE, 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; default: dev_err(cec->dev, "unsupported ioctl\n"); return -EINVAL; diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 34300d0f..70881a3d 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -77,6 +77,7 @@ static int tegra_cec_remove(struct platform_device *pdev); #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 0X03C #define TEGRA_CEC_MAX_LOGICAL_ADDR 15 #define TEGRA_CEC_HWCTRL_RX_LADDR_UNREG 0x0 @@ -167,5 +168,8 @@ static int tegra_cec_remove(struct platform_device *pdev); #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) #endif /* TEGRA_CEC_H */ From 9ff4833af1d508c4d121a04f6d575753464c2303 Mon Sep 17 00:00:00 2001 From: Chun Xu Date: Wed, 18 Jul 2018 15:19:06 +0800 Subject: [PATCH 34/44] tegra-cec: add interface to get post_recovery Provide IOCTL interface for usersapce to get whether current boot is from booting from recovery. Bug 200394215 Change-Id: I45f492b766a75542ff470366001f92fa2fd65d23 Signed-off-by: Chun Xu Reviewed-on: https://git-master.nvidia.com/r/1780659 (cherry picked from commit 8a4bda6d7e0904ce9c0cafabac4aefb49e879b8b) Reviewed-on: https://git-master.nvidia.com/r/1792946 GVS: Gerrit_Virtual_Submit Reviewed-by: Prafull Suryawanshi Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 20 +++++++++++++------- drivers/misc/tegra-cec/tegra_cec.h | 11 ++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index affe3cde..81957418 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -57,7 +57,7 @@ */ #define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) -static bool previous_reboot_reason_is_recovery, text_view_on_sent; +static bool post_recovery, text_view_on_sent; static u8 text_view_on_command[] = { LOGICAL_ADDRESS_RESERVED2 << 4 | LOGICAL_ADDRESS_TV, TEXT_VIEW_ON @@ -400,6 +400,13 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, return -EFAULT; } break; + case TEGRA_CEC_IOCTL_GET_POST_RECOVERY: + err = !access_ok(VERIFY_WRITE, 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; @@ -531,7 +538,7 @@ static void tegra_cec_init(struct tegra_cec *cec) atomic_set(&cec->init_done, 1); wake_up_interruptible(&cec->init_waitq); - if (!text_view_on_sent && !previous_reboot_reason_is_recovery) + if (!text_view_on_sent && !post_recovery) tegra_cec_send_one_touch_play(cec); dev_notice(cec->dev, "%s Done.\n", __func__); } @@ -803,17 +810,16 @@ static int tegra_cec_resume(struct platform_device *pdev) } #endif -static int __init check_previous_reboot_reason_is_recovery(char *options) +static int __init check_post_recovery(char *options) { - previous_reboot_reason_is_recovery = true; + post_recovery = true; - pr_info("tegra_cec: the previous_reboot_reason is%s recovery.\n", - previous_reboot_reason_is_recovery ? "" : " not"); + pr_info("tegra_cec: the post_recovery is %d .\n", post_recovery); return 0; } -early_param("post_recovery", check_previous_reboot_reason_is_recovery); +early_param("post_recovery", check_post_recovery); static struct tegra_cec_soc tegra210_soc_data = { .powergate_id = TEGRA210_POWER_DOMAIN_DISA, diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index 70881a3d..cd3bdf3d 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -77,7 +77,7 @@ static int tegra_cec_remove(struct platform_device *pdev); #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 0X03C +#define TEGRA_CEC_HW_SPARE 0X040 #define TEGRA_CEC_MAX_LOGICAL_ADDR 15 #define TEGRA_CEC_HWCTRL_RX_LADDR_UNREG 0x0 @@ -167,9 +167,10 @@ static int tegra_cec_remove(struct platform_device *pdev); #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_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 /* TEGRA_CEC_H */ From 4625e851311ece4840b9d689a01b7de3d7a1c175 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 6 Aug 2018 20:48:47 -0700 Subject: [PATCH 35/44] media: tegra: Remove temp WAR for function calls There are two type of temp WARs in the nvidia repo: 1) Some functions are defined in driver files that depend on CONFIG_TEGRA_GRHOST_NVCSI. So these function call should have dependency protection in the header file. 2) The powergate header file is adding an unnecessary define check for tegra194-powergate.h and this broke two places that were referring one of the defines in it. So this change fix the WARs above by: a) Removing those temp WARs by adding dummy NOP functions for those platofrms that don't have CONFIG_TEGRA_GRHOST_NVCSI. b) Removing the define check in tegra194-powergate.h and the version check when referring TEGRA194_POWER_DOMAIN_DISP. Bug 2284925 Change-Id: Iad86bfe20f2981b3d31bccf5f51184799fe7e289 Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/1803439 GVS: Gerrit_Virtual_Submit Reviewed-by: David Bang Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 81957418..95c9ab9e 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -830,12 +830,7 @@ static struct tegra_cec_soc tegra186_soc_data = { }; static struct tegra_cec_soc tegra194_soc_data = { -// temporary WAR to get 4.4 builds working -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) - .powergate_id = TEGRA186_POWER_DOMAIN_DISP, -#else .powergate_id = TEGRA194_POWER_DOMAIN_DISP, -#endif }; static struct of_device_id tegra_cec_of_match[] = { From 3c80738a180130a44038857cf90cf040765b8605 Mon Sep 17 00:00:00 2001 From: Ankit Patel Date: Tue, 24 Mar 2020 13:23:37 +0530 Subject: [PATCH 36/44] headers : move/modified header for User-space ABI copied/moved/modified headers from the driver directory to the include/uapi directory, which have IOCTL components required to get access from user space as per Linux kernel user-space ABI. Bug 2062672 Signed-off-by: Ankit Patel Change-Id: Ie70b2839e0a1031a685ff3e4bb93c73741645f44 (cherry picked from commit 348c5ff55d7499222f154aea9e9893e6cfa677ce) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2318682 Reviewed-by: automaticguardword Reviewed-by: Bibek Basu Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.h | 11 ++--------- include/uapi/misc/tegra_cec.h | 31 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 include/uapi/misc/tegra_cec.h diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index cd3bdf3d..bbd54153 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.h * - * Copyright (c) 2012-2017, NVIDIA CORPORATION. All rights reserved. + * 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, @@ -21,6 +21,7 @@ #include #include +#include #define TEGRA_CEC_FRAME_MAX_LENGTH 16 @@ -165,12 +166,4 @@ static int tegra_cec_remove(struct platform_device *pdev); #define TEGRA_CEC_NAME "tegra_cec" -#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 /* 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 */ From b36167f31cdd6cf81dfbc60bb9d7e763efc4f912 Mon Sep 17 00:00:00 2001 From: Bitan Biswas Date: Tue, 19 May 2020 13:47:28 -0700 Subject: [PATCH 37/44] nvidia: drivers: fix linux-5.7-rc5 build Fix build linux-5.7-rc5 errors including following: - change timespec to timespec64. replace getnstimeofday with ktime_get_ts64 - replace usage of macro FIELD_SIZEOF with sizeof_field in ethtool.c nvethernet and eqos files. - support 2 arguments for of_get_phy_mode call bug 200617764 Change-Id: I46067d7d36d08ee9556b2722e9ccec707b8853d4 Signed-off-by: Bitan Biswas Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2347244 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 95c9ab9e..2111ebad 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -630,7 +630,7 @@ static int tegra_cec_probe(struct platform_device *pdev) goto cec_error; } - cec->cec_base = devm_ioremap_nocache(&pdev->dev, res->start, + cec->cec_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!cec->cec_base) { From 80d0f3b9fa85080027bbfb676c90b72fe7ea2076 Mon Sep 17 00:00:00 2001 From: Naveen Kumar S Date: Mon, 15 Jun 2020 17:01:48 +0530 Subject: [PATCH 38/44] misc: tegra-cec: update access_ok() parameters Starting K5.9, the access type parameter (read/write) of access_ok() API has been removed. And also, the address parameter has been updated to "void *" type from "unsigned long". Introduced API tegra_access_ok() to abstract the difference in parameters across different kernel versions. bug 200601926 Change-Id: I0d72e03f5e0c56eb935107eaf306bc2d68ed13e3 Signed-off-by: Naveen Kumar S Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2361042 Reviewed-by: Shu Zhong Reviewed-by: automaticguardword Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/misc/tegra-cec/tegra_cec.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 2111ebad..2ddd86cc 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2018, NVIDIA CORPORATION. All rights reserved. + * 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, @@ -361,6 +361,18 @@ static int tegra_cec_get_rx_snoop(struct tegra_cec *cec, u32 *state) return 0; } +static int tegra_cec_access_ok(u8 access_type, unsigned long arg, size_t size) +{ + int err = 0; + +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE + 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) @@ -383,7 +395,7 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, tegra_cec_dump_registers(cec); break; case TEGRA_CEC_IOCTL_SET_RX_SNOOP: - err = !access_ok(VERIFY_READ, arg, sizeof(u32)); + err = tegra_cec_access_ok(VERIFY_READ, arg, sizeof(u32)); if (err) return -EFAULT; if (copy_from_user((u32 *) &state, (u32 *) arg, sizeof(u32))) @@ -391,7 +403,7 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, tegra_cec_set_rx_snoop(cec, state); break; case TEGRA_CEC_IOCTL_GET_RX_SNOOP: - err = !access_ok(VERIFY_WRITE, arg, sizeof(u32)); + err = tegra_cec_access_ok(VERIFY_READ, arg, sizeof(u32)); if (err) return -EFAULT; err = tegra_cec_get_rx_snoop(cec, &state); @@ -401,7 +413,7 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, } break; case TEGRA_CEC_IOCTL_GET_POST_RECOVERY: - err = !access_ok(VERIFY_WRITE, arg, sizeof(u32)); + err = tegra_cec_access_ok(VERIFY_READ, arg, sizeof(u32)); if (err) return -EFAULT; if (copy_to_user((bool *) arg, &post_recovery, sizeof(bool))) From b55afecfc2758832ab932e69137d6f023cd356d7 Mon Sep 17 00:00:00 2001 From: Naveen Kumar S Date: Thu, 23 Jul 2020 18:44:45 +0530 Subject: [PATCH 39/44] misc: tegra-cec: fix access_ok() API's parameters This change fixes below follow-up issues with the earlier fix to access_ok() API done as part of https://git-master.nvidia.com/r/c/linux-nvidia/+/2361042: 1. Definition of VERIFY_READ and VERIFY_WRITE have been removed after K4.14. Hence, used them only for kernel versions < K5.4. 2. Changed access_type parameter of tegra_cec_access_ok() API to bool. WRITE or READ will be chosen based on the bool value. 3. In the last CL, VERIFY_WRITE was changed to VERIFY_READ in two access_ok() calls by mistake. Corrected them in this CL. 4. Defined access_type variable with attribute __maybe_unused since gcc doesn't seem to consider a variable as used if only passed as function parameter. bug 200601926 Change-Id: I9f3540cd028280ca679b4b69d5b4a72aa943b2ae Signed-off-by: Naveen Kumar S Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2394585 Tested-by: mobile promotions Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Shu Zhong Reviewed-by: Ujwal Patel Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/misc/tegra-cec/tegra_cec.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 2ddd86cc..b2c8b1a6 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -361,11 +361,12 @@ static int tegra_cec_get_rx_snoop(struct tegra_cec *cec, u32 *state) return 0; } -static int tegra_cec_access_ok(u8 access_type, unsigned long arg, size_t size) +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); @@ -395,7 +396,7 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, tegra_cec_dump_registers(cec); break; case TEGRA_CEC_IOCTL_SET_RX_SNOOP: - err = tegra_cec_access_ok(VERIFY_READ, arg, sizeof(u32)); + err = tegra_cec_access_ok(false, arg, sizeof(u32)); if (err) return -EFAULT; if (copy_from_user((u32 *) &state, (u32 *) arg, sizeof(u32))) @@ -403,7 +404,7 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, tegra_cec_set_rx_snoop(cec, state); break; case TEGRA_CEC_IOCTL_GET_RX_SNOOP: - err = tegra_cec_access_ok(VERIFY_READ, arg, sizeof(u32)); + err = tegra_cec_access_ok(true, arg, sizeof(u32)); if (err) return -EFAULT; err = tegra_cec_get_rx_snoop(cec, &state); @@ -413,7 +414,7 @@ static long tegra_cec_ioctl(struct file *file, unsigned int cmd, } break; case TEGRA_CEC_IOCTL_GET_POST_RECOVERY: - err = tegra_cec_access_ok(VERIFY_READ, arg, sizeof(u32)); + err = tegra_cec_access_ok(true, arg, sizeof(u32)); if (err) return -EFAULT; if (copy_to_user((bool *) arg, &post_recovery, sizeof(bool))) From 9152cfd9e9c39e5fa182913efc477e146a6cda62 Mon Sep 17 00:00:00 2001 From: Naveen Kumar S Date: Mon, 6 Jul 2020 21:31:49 +0530 Subject: [PATCH 40/44] video: tegra: dc: add pm_runtime support Add genpd/pm_runtime support to handle power domains in Tegra Display driver. Also updated CEC and HDA_DC drivers to add pm_runtime calls. Added new APIs in CEC and SOR drivers to abstract calls to pm_runtime and tegra_powergate APIs based on specific Kernel version. Guarded powergate_id with CONFIG_TEGRA_POWERGATE since its not needed when pm_runtime APIs are used, and the powergate IDs are defined in tegra_powergate.h which is not used in K5.9. Removed pm_runtime autosuspend setting since display driver doesn't assign PM suspend/resume hooks at present. Suspend/resume calls are linked to platform device callbacks. Removed pm_runtime_enable() call in dc_common probe since there's logically no need of power-domains in dc_common and also there is no power-domains node in display_hub node in device-tree. Same with DSI. All the power-domains handled by each DC are listed in "power-domains" and "power-domain-names" properties under respective dc nodes in device tree. bug 200601926 Change-Id: I0fc9d21ff56185a69de6e0663b5932a93cf40e2b Signed-off-by: Naveen Kumar S Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2358953 Tested-by: mobile promotions Reviewed-by: automaticguardword Reviewed-by: Shu Zhong Reviewed-by: Ujwal Patel Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/misc/tegra-cec/tegra_cec.c | 79 ++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index b2c8b1a6..9db5ffd8 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -39,7 +39,9 @@ #include #include +#if defined(CONFIG_TEGRA_POWERGATE) #include +#endif #include "tegra_cec.h" #include "../../../../nvidia/drivers/video/tegra/dc/dc.h" @@ -339,6 +341,36 @@ static int tegra_cec_dump_registers(struct tegra_cec *cec) } +static int tegra_cec_unpowergate(struct tegra_cec *cec) +{ + int ret = 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 (!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; @@ -660,19 +692,18 @@ static int tegra_cec_probe(struct platform_device *pdev) atomic_set(&cec->init_done, 0); mutex_init(&cec->tx_lock); mutex_init(&cec->recovery_lock); + cec->dev = &pdev->dev; -#if defined(CONFIG_TEGRA_POWERGATE) - if (tegra_dc_is_nvdisplay()) { - ret = tegra_unpowergate_partition(cec->soc->powergate_id); - if (ret) { - dev_err(&pdev->dev, "Fail to unpowergate DISP: %d.\n", - ret); - goto clk_error; - } - dev_info(&pdev->dev, "Unpowergate DISP: %d.\n", ret); - } +#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"); @@ -690,7 +721,6 @@ static int tegra_cec_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Enable clock result: %d.\n", ret); /* set context info. */ - cec->dev = &pdev->dev; init_waitqueue_head(&cec->rx_waitq); init_waitqueue_head(&cec->tx_waitq); init_waitqueue_head(&cec->init_waitq); @@ -755,10 +785,7 @@ cec_error: cancel_work_sync(&cec->work); clk_disable(cec->clk); clk_put(cec->clk); -#if defined(CONFIG_TEGRA_POWERGATE) - if (tegra_dc_is_nvdisplay()) - tegra_powergate_partition(cec->soc->powergate_id); -#endif + tegra_cec_powergate(cec); clk_error: return ret; } @@ -769,11 +796,11 @@ static int tegra_cec_remove(struct platform_device *pdev) clk_disable(cec->clk); clk_put(cec->clk); -#if defined(CONFIG_TEGRA_POWERGATE) + tegra_cec_powergate(cec); +#if !defined(CONFIG_TEGRA_POWERGATE) if (tegra_dc_is_nvdisplay()) - tegra_powergate_partition(cec->soc->powergate_id); + pm_runtime_disable(&pdev->dev); #endif - misc_deregister(&cec->misc_dev); cancel_work_sync(&cec->work); @@ -797,10 +824,7 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) atomic_set(&cec->init_cancel, 0); clk_disable(cec->clk); -#if defined(CONFIG_TEGRA_POWERGATE) - if (tegra_dc_is_nvdisplay()) - tegra_powergate_partition(cec->soc->powergate_id); -#endif + tegra_cec_powergate(cec); dev_notice(&pdev->dev, "suspended\n"); return 0; @@ -812,10 +836,7 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); -#if defined(CONFIG_TEGRA_POWERGATE) - if (tegra_dc_is_nvdisplay()) - tegra_unpowergate_partition(cec->soc->powergate_id); -#endif + tegra_cec_unpowergate(cec); clk_enable(cec->clk); schedule_work(&cec->work); @@ -835,15 +856,21 @@ static int __init check_post_recovery(char *options) 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 }; static struct tegra_cec_soc tegra186_soc_data = { +#if defined(CONFIG_TEGRA_POWERGATE) .powergate_id = TEGRA186_POWER_DOMAIN_DISP, +#endif }; static struct tegra_cec_soc tegra194_soc_data = { +#if defined(CONFIG_TEGRA_POWERGATE) .powergate_id = TEGRA194_POWER_DOMAIN_DISP, +#endif }; static struct of_device_id tegra_cec_of_match[] = { From 61e8cccc413462ddbf115856f85e3ea627c14514 Mon Sep 17 00:00:00 2001 From: Ankit Patel Date: Tue, 27 Apr 2021 21:13:17 +0530 Subject: [PATCH 41/44] drivers: use overlay instead of relative path Modified misc/tegra-cec and video/tegra/dc NVIDIA git repo Makefile, Now overlay macro will be used instead of relative path from kernel source tree, to find the included header file. Bug 200606454 Signed-off-by: Ankit Patel Change-Id: I684388e9c81be94915c05db3cdae8b6100c6db91 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2521240 Reviewed-by: Stephen Warren Reviewed-by: Bibek Basu Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/misc/tegra-cec/Makefile | 2 ++ drivers/misc/tegra-cec/tegra_cec.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/tegra-cec/Makefile b/drivers/misc/tegra-cec/Makefile index db3705a8..f47543b1 100644 --- a/drivers/misc/tegra-cec/Makefile +++ b/drivers/misc/tegra-cec/Makefile @@ -4,4 +4,6 @@ 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 index 9db5ffd8..37f7151d 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -44,8 +44,8 @@ #endif #include "tegra_cec.h" -#include "../../../../nvidia/drivers/video/tegra/dc/dc.h" -#include "../../../../nvidia/drivers/video/tegra/dc/dc_priv.h" +#include "dc.h" +#include "dc_priv.h" #define LOGICAL_ADDRESS_RESERVED2 0xD #define LOGICAL_ADDRESS_TV 0x0 From bcd8f08d8475d4cd4fdee9827b0a0a08550c23ee Mon Sep 17 00:00:00 2001 From: Prafull Suryawanshi Date: Mon, 3 May 2021 00:38:44 -0400 Subject: [PATCH 42/44] misc: tegra-cec: add T23x support. This change includes below - 1. For T23x, CEC engine is moved to AON partition. As AON partition does not powergate, skip all powergate/unpowergate calls for t23x. 2. The clock on which CEC engine operates, 32k/rtc_clk, it is always running clock in AON partition. So skip clock enable/disable APIs. 3. This change also skips calls to TEGRA DC engine which is not available from T23x. 4. RX Buffer size is increased to 64 depth from 1 depth. So new register is provided to program depth so that interrupt gets triggered when certain depth is filled. By default it is 64 depth so program it to 1 depth to match old behavior. 5. When tegradc is absent, read physical address from SPARE register which will be populated by UEFI. 6. This change also adds new register definitions in header file which needs to program when adding support for CEC SC7 wakeup. 7. Timing registers updated for T23x as per HW guidelines. 8. Some initialization code is changed for T23x. 9. Add devicetree binding documentation. bug 200727002 Change-Id: I71c2d323632c61e4c4b82bcdbca9e72179761224 Signed-off-by: Prafull Suryawanshi Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2523595 Tested-by: mobile promotions Reviewed-by: svc_kernel_abi Reviewed-by: Shu Zhong Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/misc/tegra-cec/tegra_cec.c | 363 +++++++++++++++++++++-------- drivers/misc/tegra-cec/tegra_cec.h | 23 +- 2 files changed, 282 insertions(+), 104 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 37f7151d..91f00fda 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2020, NVIDIA CORPORATION. All rights reserved. + * 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, @@ -71,10 +71,6 @@ static u8 active_source_command[] = { 0x00 }; -struct tegra_cec_soc { - int powergate_id; -}; - static ssize_t cec_logical_addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); @@ -84,6 +80,25 @@ static ssize_t cec_logical_addr_show(struct device *dev, 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; @@ -113,19 +128,23 @@ static int tegra_cec_release(struct inode *inode, struct file *file) static inline void tegra_cec_native_tx(const struct tegra_cec *cec, u32 block) { - writel(block, cec->cec_base + TEGRA_CEC_TX_REGISTER); - writel(TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY, + 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 = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0x0, cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0xFFFFFFFF, cec->cec_base + TEGRA_CEC_INT_STAT); - writel(hw_ctrl, cec->cec_base + TEGRA_CEC_HW_CONTROL); + 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 @@ -162,8 +181,8 @@ int tegra_cec_native_write_l(struct tegra_cec *cec, const u8 *buf, size_t cnt) cec->tx_buf[i] = start | mode | eom | buf[i]; } - mask = readl(cec->cec_base + TEGRA_CEC_INT_MASK); - writel(mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + 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, @@ -246,8 +265,8 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) struct tegra_cec *cec = dev_get_drvdata(dev); u32 status, mask; - status = readl(cec->cec_base + TEGRA_CEC_INT_STAT); - mask = readl(cec->cec_base + TEGRA_CEC_INT_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; @@ -258,7 +277,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) dev_err(dev, "TX underrun, interrupt timing issue!\n"); tegra_cec_error_recovery(cec); - writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + tegra_cec_writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, cec->cec_base + TEGRA_CEC_INT_MASK); cec->tx_error = -EIO; @@ -270,7 +289,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) } else if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { tegra_cec_error_recovery(cec); - writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + tegra_cec_writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, cec->cec_base + TEGRA_CEC_INT_MASK); cec->tx_error = -ECOMM; @@ -280,7 +299,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) goto out; } else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { - writel((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) { @@ -299,7 +318,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { if (cec->tx_buf_cur == cec->tx_buf_cnt) - writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, + tegra_cec_writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY, cec->cec_base + TEGRA_CEC_INT_MASK); else tegra_cec_native_tx(cec, @@ -310,13 +329,13 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { - writel((TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | + 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) { - writel(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; @@ -332,9 +351,8 @@ 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 <= TEGRA_CEC_HW_SPARE; i+=4) - { - value = readl(cec->cec_base + i); + 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; @@ -345,6 +363,9 @@ 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; @@ -361,6 +382,9 @@ static int tegra_cec_unpowergate(struct tegra_cec *cec) static void tegra_cec_powergate(struct tegra_cec *cec) { + if (cec->soc->cec_always_on) + return; + if (!tegra_dc_is_nvdisplay()) return; @@ -377,10 +401,10 @@ static int tegra_cec_set_rx_snoop(struct tegra_cec *cec, u32 enable) if (!atomic_read(&cec->init_done)) return -EAGAIN; - state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); + 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; - writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); } return 0; } @@ -476,18 +500,34 @@ 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; - res = tegra_dc_get_source_physical_address(phy_address); - if (res) { - dev_notice(cec->dev, "Can't find physical addresse.\n"); - return res; + 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]; @@ -508,17 +548,18 @@ static int tegra_cec_send_one_touch_play(struct tegra_cec *cec) 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__); - writel(0x00, cec->cec_base + TEGRA_CEC_HW_CONTROL); - writel(0x00, cec->cec_base + TEGRA_CEC_INT_MASK); - writel(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); + 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, @@ -529,49 +570,87 @@ static void tegra_cec_init(struct tegra_cec *cec) msleep(1000); #endif - writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); + tegra_cec_writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL); cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG; - 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); - writel((1U << 31) | 0x20, cec->cec_base + TEGRA_CEC_INPUT_FILTER); + /* CEC initialization settings till T194 */ + if (!cec->soc->cec_always_on) { - 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(0xffffffff, cec->cec_base + TEGRA_CEC_INT_STAT); - 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(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); - writel((0x50 << TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK), - cec->cec_base + TEGRA_CEC_RX_TIMING_2); + tegra_cec_writel((1U << 31) | 0x20, cec->cec_base + TEGRA_CEC_INPUT_FILTER); - 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((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); - 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((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); - 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); + tegra_cec_writel((0x50 << TEGRA_CEC_RX_TIMING_2_RX_END_OF_BLOCK_TIME_MASK), + cec->cec_base + TEGRA_CEC_RX_TIMING_2); - writel(TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN | + 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 | @@ -629,10 +708,27 @@ static ssize_t cec_logical_addr_store(struct device *dev, dev_info(dev, "set logical address: 0x%x\n", (u32)addr); cec->logical_addr = addr; - state = readl(cec->cec_base + TEGRA_CEC_HW_CONTROL); - state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; - state |= TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr); - writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL); + + 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; } @@ -643,6 +739,7 @@ static int tegra_cec_probe(struct platform_device *pdev) 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); @@ -650,6 +747,24 @@ static int tegra_cec_probe(struct platform_device *pdev) return -ENOMEM; cec->soc = of_device_get_match_data(&pdev->dev); + 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_err(&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_err(&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); @@ -694,32 +809,36 @@ static int tegra_cec_probe(struct platform_device *pdev) 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); + 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"); + 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); - 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); @@ -783,9 +902,11 @@ static int tegra_cec_probe(struct platform_device *pdev) cec_error: cancel_work_sync(&cec->work); - clk_disable(cec->clk); - clk_put(cec->clk); - tegra_cec_powergate(cec); + if (!cec->soc->cec_always_on) { + clk_disable(cec->clk); + clk_put(cec->clk); + tegra_cec_powergate(cec); + } clk_error: return ret; } @@ -794,13 +915,17 @@ static int tegra_cec_remove(struct platform_device *pdev) { struct tegra_cec *cec = platform_get_drvdata(pdev); - clk_disable(cec->clk); - clk_put(cec->clk); - tegra_cec_powergate(cec); + 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); + if (tegra_dc_is_nvdisplay()) + pm_runtime_disable(&pdev->dev); #endif + } + misc_deregister(&cec->misc_dev); cancel_work_sync(&cec->work); @@ -823,8 +948,18 @@ static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) atomic_set(&cec->init_done, 0); atomic_set(&cec->init_cancel, 0); - clk_disable(cec->clk); - tegra_cec_powergate(cec); + 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; @@ -836,8 +971,16 @@ static int tegra_cec_resume(struct platform_device *pdev) dev_notice(&pdev->dev, "Resuming\n"); - tegra_cec_unpowergate(cec); - clk_enable(cec->clk); + 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; @@ -859,24 +1002,40 @@ 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 }, {}, }; diff --git a/drivers/misc/tegra-cec/tegra_cec.h b/drivers/misc/tegra-cec/tegra_cec.h index bbd54153..0cfc694c 100644 --- a/drivers/misc/tegra-cec/tegra_cec.h +++ b/drivers/misc/tegra-cec/tegra_cec.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.h * - * Copyright (c) 2012-2020, NVIDIA CORPORATION. All rights reserved. + * 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, @@ -25,7 +25,12 @@ #define TEGRA_CEC_FRAME_MAX_LENGTH 16 -struct tegra_cec_soc; +struct tegra_cec_soc { + int powergate_id; + int offset; + bool use_tegra_dc; + bool cec_always_on; +}; struct tegra_cec { struct device *dev; @@ -53,6 +58,7 @@ struct tegra_cec { 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); @@ -80,6 +86,19 @@ static int tegra_cec_remove(struct platform_device *pdev); #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 From 3e3bd3b5f01cbd2aba75774233fdf92257baaf36 Mon Sep 17 00:00:00 2001 From: prafulls Date: Tue, 22 Mar 2022 07:26:52 +0000 Subject: [PATCH 43/44] misc: tegra-cec: replace dev_err with dev_info. The probe retry message shall not be error message. Replacing it with dev_info to fix kernel warning test. bug 3578262 Change-Id: I8fd016e11b6edc14a402214a44c1e916bcc0d16e Signed-off-by: prafulls Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2685084 Reviewed-by: svc-mobile-coverity Reviewed-by: svcacv Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Shu Zhong GVS: Gerrit_Virtual_Submit --- drivers/misc/tegra-cec/tegra_cec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 91f00fda..3f570448 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-cec/tegra_cec.c * - * Copyright (c) 2012-2021, NVIDIA CORPORATION. All rights reserved. + * 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, @@ -751,7 +751,7 @@ static int tegra_cec_probe(struct platform_device *pdev) rst = devm_reset_control_get(&pdev->dev, "cec"); if (IS_ERR(rst)) { /* BPMP reset mechanism not available, return and retry again */ - dev_err(&pdev->dev, "reset control is not found, deferring probe to retry again.\n"); + 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; @@ -759,7 +759,7 @@ static int tegra_cec_probe(struct platform_device *pdev) /* take CEC engine out of reset */ if (reset_control_reset(rst) != 0) { - dev_err(&pdev->dev, "reset control reset failed, deferring probe to retry again.\n"); + 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; From 6d4080d2bb4c2cfada3881c6d89ecb2d72b60a39 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Tue, 10 May 2022 04:40:15 +0000 Subject: [PATCH 44/44] drivers: cec: fix cert_c defect Add a sanity check on cec->soc, which could be NULL. CID 490328 Bug 3512545 Change-Id: Ibd334c5637a85342ed5307eecb697416f32e0360 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2710156 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/misc/tegra-cec/tegra_cec.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/misc/tegra-cec/tegra_cec.c b/drivers/misc/tegra-cec/tegra_cec.c index 3f570448..bc027a8a 100644 --- a/drivers/misc/tegra-cec/tegra_cec.c +++ b/drivers/misc/tegra-cec/tegra_cec.c @@ -747,6 +747,12 @@ static int tegra_cec_probe(struct platform_device *pdev) 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)) {