mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
tegra-cec: poll WR_LOCK for TX data sync
After programming the TX Register, it takes 3-4 clock cycles for TX data synchronization to the CEC core domain. The TX_EMPTY interrupt should not be cleared until this data synchronization is complete. Enable CEC HW feature to automatically clears the TX_EMPTY interrupt after TX data synchronization is complete. It takes time for HW to clear the interrupt and TX_EMPTY can still appear high, hence SW needs to poll WR_LOCK until it goes to 0. This will avoid SW to attempt the next TX block during the same TX_EMPTY interrupt. Also read RX_REGISTER based on the buffer occupancy which is indicated by the CEC_RX_BUFFER_STAT_0 register. Bug 4954851 Change-Id: I3ec3792c9ae3b8a00c800c921cf4e4d09369e6b9 Signed-off-by: Ken Chang <kenc@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3322519 (cherry picked from commit 94cf6106402dc833dbaa4305c3e1a8f85fd80d0e) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3336992 Reviewed-by: Bitan Biswas <bbiswas@nvidia.com> Reviewed-by: Prafull Suryawanshi <prafulls@nvidia.com> GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
7bca2b2466
commit
785d9aa288
@@ -110,10 +110,16 @@ 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)
|
||||
{
|
||||
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);
|
||||
u32 tx_reg, retry;
|
||||
|
||||
tegra_cec_writel(block, cec->cec_base + TEGRA_CEC_TX_REGISTER);
|
||||
|
||||
tx_reg = tegra_cec_readl(cec->cec_base + TEGRA_CEC_TX_REGISTER);
|
||||
retry = 10;
|
||||
while ((tx_reg & 0x80000000) && retry--) {
|
||||
udelay(31); // one clock cycle = 30.5us
|
||||
tx_reg = tegra_cec_readl(cec->cec_base + TEGRA_CEC_TX_REGISTER);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void tegra_cec_error_recovery(struct tegra_cec *cec)
|
||||
@@ -213,8 +219,6 @@ static ssize_t tegra_cec_read(struct file *file, char __user *buffer,
|
||||
struct tegra_cec *cec = file->private_data;
|
||||
ssize_t ret;
|
||||
|
||||
count = sizeof(cec->rx_buffer);
|
||||
|
||||
ret = wait_event_interruptible(cec->init_waitq,
|
||||
atomic_read(&cec->init_done) == 1);
|
||||
if (ret)
|
||||
@@ -228,13 +232,16 @@ static ssize_t tegra_cec_read(struct file *file, char __user *buffer,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (copy_to_user(buffer, &(cec->rx_buffer), count))
|
||||
count = sizeof(cec->rx_fifo[0]) * (cec->rx_fifo_data);
|
||||
if (copy_to_user(buffer, &(cec->rx_fifo[0]), count))
|
||||
return -EFAULT;
|
||||
|
||||
dev_dbg(cec->dev, "%s: %*phC", __func__, (int)count,
|
||||
&(cec->rx_buffer));
|
||||
cec->rx_buffer = 0x0;
|
||||
&(cec->rx_fifo[0]));
|
||||
|
||||
memset(&(cec->rx_fifo[0]), 0, count);
|
||||
cec->rx_wake = 0;
|
||||
cec->rx_fifo_data = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -242,7 +249,7 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
struct tegra_cec *cec = dev_get_drvdata(dev);
|
||||
u32 status, mask;
|
||||
u32 status, mask, i;
|
||||
|
||||
status = tegra_cec_readl(cec->cec_base + TEGRA_CEC_INT_STAT);
|
||||
mask = tegra_cec_readl(cec->cec_base + TEGRA_CEC_INT_MASK);
|
||||
@@ -314,9 +321,21 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
|
||||
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) {
|
||||
/*
|
||||
Read TEGRA_CEC_RX_BUFFER_STAT_0 which have total number of blocks,
|
||||
then read every block continuously from TEGRA_CEC_RX_REGISTER.
|
||||
TEGRA_CEC_INT_STAT_RX_REGISTER_FULL sets only once and not for
|
||||
every block if there are more than 1 block in FIFO.
|
||||
*/
|
||||
cec->rx_fifo_data =
|
||||
tegra_cec_readl(cec->cec_base + TEGRA_CEC_RX_BUFFER_STAT_0);
|
||||
for (i = 0; i < cec->rx_fifo_data; i++) {
|
||||
cec->rx_fifo[i] =
|
||||
readw(cec->cec_base + TEGRA_CEC_RX_REGISTER);
|
||||
}
|
||||
|
||||
tegra_cec_writel(TEGRA_CEC_INT_STAT_RX_REGISTER_FULL,
|
||||
cec->cec_base + TEGRA_CEC_INT_STAT);
|
||||
cec->rx_buffer = tegra_cec_readl(cec->cec_base + TEGRA_CEC_RX_REGISTER);
|
||||
cec->rx_wake = 1;
|
||||
wake_up_interruptible(&cec->rx_waitq);
|
||||
}
|
||||
@@ -508,8 +527,9 @@ static void tegra_cec_init(struct tegra_cec *cec)
|
||||
|
||||
cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG;
|
||||
|
||||
tegra_cec_writel(TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr),
|
||||
cec->cec_base + TEGRA_CEC_HW_CONTROL);
|
||||
state = TEGRA_CEC_HWCTRL_AUTO_CLR_TX_EMPTY_INTR |
|
||||
TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr);
|
||||
tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL);
|
||||
|
||||
tegra_cec_writel(0x1, cec->cec_base + TEGRA_CEC_MESSAGE_FILTER_CTRL);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
// Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#ifndef TEGRA_CEC_H
|
||||
#define TEGRA_CEC_H
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <uapi/misc/tegra_cec.h>
|
||||
|
||||
#define TEGRA_CEC_FRAME_MAX_LENGTH 16
|
||||
#define TEGRA_CEC_RX_FIFO_LENGTH 64
|
||||
|
||||
struct tegra_cec {
|
||||
struct device *dev;
|
||||
@@ -30,12 +31,13 @@ struct tegra_cec {
|
||||
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;
|
||||
struct reset_control *reset;
|
||||
u16 rx_fifo[TEGRA_CEC_RX_FIFO_LENGTH];
|
||||
u16 rx_fifo_data;
|
||||
};
|
||||
|
||||
#define TEGRA_CEC_LADDR_BROADCAST 0xF
|
||||
@@ -83,6 +85,7 @@ struct tegra_cec {
|
||||
#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_AUTO_CLR_TX_EMPTY_INTR (1<<29)
|
||||
#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE (1<<30)
|
||||
#define TEGRA_CEC_HWCTRL_TX_RX_MODE (1<<31)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user