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
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Reviewed-by: Prafull Suryawanshi <prafulls@nvidia.com>
Tested-by: Ruopeng Huang (SW-TEGRA) <robhuang@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Ken Chang
2025-03-18 20:25:47 +08:00
committed by Jon Hunter
parent d460bf1928
commit 8e90031cd8
2 changed files with 39 additions and 41 deletions

View File

@@ -119,10 +119,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) 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); u32 tx_reg, retry;
tegra_cec_writel(TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY,
cec->cec_base + TEGRA_CEC_INT_STAT);
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) static inline void tegra_cec_error_recovery(struct tegra_cec *cec)
@@ -235,24 +241,16 @@ static ssize_t tegra_cec_read(struct file *file, char __user *buffer,
if (ret) if (ret)
return ret; return ret;
if (cec->rx_fifo_data != 0) { count = sizeof(cec->rx_fifo[0]) * (cec->rx_fifo_data);
count = sizeof(cec->rx_fifo[0]) * (cec->rx_fifo_data); if (copy_to_user(buffer, &(cec->rx_fifo[0]), count))
if (copy_to_user(buffer, &(cec->rx_fifo_data), count)) return -EFAULT;
return -EFAULT;
dev_dbg(cec->dev, "%s: %*phC", __func__, (int)count, dev_dbg(cec->dev, "%s: %*phC", __func__, (int)count,
&(cec->rx_fifo[0])); &(cec->rx_fifo[0]));
} else {
count = sizeof(cec->rx_buffer);
if (copy_to_user(buffer, &(cec->rx_buffer), count))
return -EFAULT;
dev_dbg(cec->dev, "%s: %*phC", __func__, (int)count, memset(&(cec->rx_fifo[0]), 0, count);
&(cec->rx_buffer));
}
cec->rx_buffer = 0x0;
cec->rx_wake = 0; cec->rx_wake = 0;
cec->rx_fifo_data = 0;
return count; return count;
} }
@@ -332,28 +330,27 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED), TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED),
cec->cec_base + TEGRA_CEC_INT_STAT); cec->cec_base + TEGRA_CEC_INT_STAT);
} else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { } 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, tegra_cec_writel(TEGRA_CEC_INT_STAT_RX_REGISTER_FULL,
cec->cec_base + TEGRA_CEC_INT_STAT); cec->cec_base + TEGRA_CEC_INT_STAT);
if (cec->is_tegra_cec_suspended) {
/* if (cec->is_tegra_cec_suspended)
Read TEGRA_CEC_RX_BUFFER_STAT_0 which have total number of blocks, pm_wakeup_event(dev, 0);
then read every block continuously from TEGRA_CEC_RX_REGISTER.
TEGRA_CEC_INT_STAT_RX_REGISTER_FULL sets only once and not for cec->rx_wake = 1;
every block if there are more than 1 block in FIFO. wake_up_interruptible(&cec->rx_waitq);
*/
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);
}
pm_wakeup_event(dev, 0);
} else {
cec->rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER);
cec->rx_fifo_data = 0;
}
cec->rx_wake = 1;
wake_up_interruptible(&cec->rx_waitq);
} }
out: out:
@@ -545,8 +542,9 @@ static void tegra_cec_init(struct tegra_cec *cec)
cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG; cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG;
tegra_cec_writel(TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr), state = TEGRA_CEC_HWCTRL_AUTO_CLR_TX_EMPTY_INTR |
cec->cec_base + TEGRA_CEC_HW_CONTROL); TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr);
tegra_cec_writel(state, cec->cec_base + TEGRA_CEC_HW_CONTROL);
state = (0xff << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_MAX_LO_TIME_MASK) | 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) | (0x22 << TEGRA_CEC_RX_TIMING_1_RX_DATA_BIT_SAMPLE_TIME_MASK) |

View File

@@ -34,7 +34,6 @@ struct tegra_cec {
struct work_struct work; struct work_struct work;
unsigned int rx_wake; unsigned int rx_wake;
unsigned int tx_wake; unsigned int tx_wake;
u16 rx_buffer;
long tx_error; long tx_error;
u32 tx_buf[TEGRA_CEC_FRAME_MAX_LENGTH]; u32 tx_buf[TEGRA_CEC_FRAME_MAX_LENGTH];
u8 tx_buf_cur; u8 tx_buf_cur;
@@ -90,6 +89,7 @@ struct tegra_cec {
#define TEGRA_CEC_HWCTRL_RX_SNOOP (1<<15) #define TEGRA_CEC_HWCTRL_RX_SNOOP (1<<15)
#define TEGRA_CEC_HWCTRL_RX_NAK_MODE (1<<16) #define TEGRA_CEC_HWCTRL_RX_NAK_MODE (1<<16)
#define TEGRA_CEC_HWCTRL_TX_NAK_MODE (1<<24) #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_FAST_SIM_MODE (1<<30)
#define TEGRA_CEC_HWCTRL_TX_RX_MODE (1<<31) #define TEGRA_CEC_HWCTRL_TX_RX_MODE (1<<31)