mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
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 <xiay@nvidia.com>
Reviewed-on: http://git-master/r/304664
(cherry picked from commit 6ab7a446c4a3e8e4970ceec5a3c715453a24e4a5)
Signed-off-by: Xia Yang <xiay@nvidia.com>
Reviewed-on: http://git-master/r/347779
Reviewed-on: http://git-master/r/1164143
(cherry picked from commit 67a6c2d3cfbeeca5cb5ed57aae53f34d07decd4f)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* drivers/misc/tegra-cec/tegra_cec.c
|
* 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
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* 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 miscdevice *miscdev = file->private_data;
|
||||||
struct tegra_cec *cec = container_of(miscdev,
|
struct tegra_cec *cec = container_of(miscdev,
|
||||||
struct tegra_cec, misc_dev);
|
struct tegra_cec, misc_dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
dev_dbg(cec->dev, "%s\n", __func__);
|
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);
|
atomic_read(&cec->init_done) == 1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
file->private_data = cec;
|
file->private_data = cec;
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tegra_cec_release(struct inode *inode, struct file *file)
|
int tegra_cec_release(struct inode *inode, struct file *file)
|
||||||
@@ -71,42 +75,79 @@ int tegra_cec_release(struct inode *inode, struct file *file)
|
|||||||
return 0;
|
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)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
|
u8 tx_buf[TEGRA_CEC_FRAME_MAX_LENGTH];
|
||||||
struct tegra_cec *cec = file->private_data;
|
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);
|
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;
|
return -EFAULT;
|
||||||
|
|
||||||
writel((TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY |
|
mutex_lock(&cec->tx_lock);
|
||||||
TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
|
ret = tegra_cec_native_write_l(cec, tx_buf, count);
|
||||||
TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
|
mutex_unlock(&cec->tx_lock);
|
||||||
TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
|
if (ret)
|
||||||
TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
|
return ret;
|
||||||
TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |
|
else
|
||||||
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;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,16 +155,22 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer,
|
|||||||
size_t count, loff_t *ppos)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct tegra_cec *cec = file->private_data;
|
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);
|
atomic_read(&cec->init_done) == 1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (cec->rx_wake == 0)
|
if (cec->rx_wake == 0)
|
||||||
if (file->f_flags & O_NONBLOCK)
|
if (file->f_flags & O_NONBLOCK)
|
||||||
return -EAGAIN;
|
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))
|
if (copy_to_user(buffer, &(cec->rx_buffer), count))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -133,52 +180,100 @@ ssize_t tegra_cec_read(struct file *file, char __user *buffer,
|
|||||||
return count;
|
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)
|
static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct device *dev = data;
|
struct device *dev = data;
|
||||||
struct tegra_cec *cec = dev_get_drvdata(dev);
|
struct tegra_cec *cec = dev_get_drvdata(dev);
|
||||||
unsigned long status;
|
u32 status, mask;
|
||||||
|
|
||||||
status = readl(cec->cec_base + TEGRA_CEC_INT_STAT);
|
status = readl(cec->cec_base + TEGRA_CEC_INT_STAT);
|
||||||
|
mask = readl(cec->cec_base + TEGRA_CEC_INT_MASK);
|
||||||
|
|
||||||
|
status &= mask;
|
||||||
|
|
||||||
if (!status)
|
if (!status)
|
||||||
return IRQ_HANDLED;
|
goto out;
|
||||||
|
|
||||||
if ((status & TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN) ||
|
if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {
|
||||||
(status & TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED) ||
|
dev_err(dev, "tegra_cec: TX underrun, interrupt timing issue!\n");
|
||||||
(status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) ||
|
|
||||||
(status & TEGRA_CEC_INT_STAT_RX_BUS_ERROR_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 = -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 |
|
writel((TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN |
|
||||||
TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
|
TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
|
||||||
TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
|
TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
|
||||||
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) {
|
||||||
writel((TEGRA_CEC_INT_STAT_RX_REGISTER_FULL),
|
writel(TEGRA_CEC_INT_STAT_RX_REGISTER_FULL,
|
||||||
cec->cec_base + TEGRA_CEC_INT_STAT);
|
cec->cec_base + TEGRA_CEC_INT_STAT);
|
||||||
cec->rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER);
|
cec->rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER);
|
||||||
cec->rx_wake = 1;
|
cec->rx_wake = 1;
|
||||||
wake_up_interruptible(&cec->rx_waitq);
|
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;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +287,11 @@ static const struct file_operations tegra_cec_fops = {
|
|||||||
|
|
||||||
static void tegra_cec_init(struct tegra_cec *cec)
|
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__);
|
dev_notice(cec->dev, "%s started\n", __func__);
|
||||||
|
|
||||||
@@ -202,13 +302,10 @@ static void tegra_cec_init(struct tegra_cec *cec)
|
|||||||
|
|
||||||
writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL);
|
writel(0x00, cec->cec_base + TEGRA_CEC_SW_CONTROL);
|
||||||
|
|
||||||
writel((
|
cec->logical_addr = TEGRA_CEC_HWCTRL_RX_LADDR_UNREG;
|
||||||
(cec->logical_addr << TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK) &
|
writel(TEGRA_CEC_HWCTRL_RX_LADDR(cec->logical_addr) |
|
||||||
(~TEGRA_CEC_HW_CONTROL_RX_SNOOP) &
|
TEGRA_CEC_HWCTRL_TX_NAK_MODE |
|
||||||
(~TEGRA_CEC_HW_CONTROL_RX_NAK_MODE) &
|
TEGRA_CEC_HWCTRL_TX_RX_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->cec_base + TEGRA_CEC_HW_CONTROL);
|
||||||
|
|
||||||
writel(0x00, cec->cec_base + TEGRA_CEC_INPUT_FILTER);
|
writel(0x00, cec->cec_base + TEGRA_CEC_INPUT_FILTER);
|
||||||
@@ -241,16 +338,17 @@ static void tegra_cec_init(struct tegra_cec *cec)
|
|||||||
cec->cec_base + TEGRA_CEC_TX_TIMING_1);
|
cec->cec_base + TEGRA_CEC_TX_TIMING_1);
|
||||||
|
|
||||||
writel((0x07 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK) |
|
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) |
|
(0x05 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_NEW_FRAME_MASK) |
|
||||||
(0x03 << TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_RETRY_FRAME_MASK),
|
(0x03 << TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_RETRY_FRAME_MASK),
|
||||||
cec->cec_base + TEGRA_CEC_TX_TIMING_2);
|
cec->cec_base + TEGRA_CEC_TX_TIMING_2);
|
||||||
|
|
||||||
writel((TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
|
writel(TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
|
||||||
TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
|
TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
|
||||||
TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
|
TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
|
||||||
TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
|
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_FULL |
|
||||||
TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN),
|
TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN,
|
||||||
cec->cec_base + TEGRA_CEC_INT_MASK);
|
cec->cec_base + TEGRA_CEC_INT_MASK);
|
||||||
|
|
||||||
atomic_set(&cec->init_done, 1);
|
atomic_set(&cec->init_done, 1);
|
||||||
@@ -271,6 +369,9 @@ static ssize_t cec_logical_addr_show(struct device *dev,
|
|||||||
{
|
{
|
||||||
struct tegra_cec *cec = dev_get_drvdata(dev);
|
struct tegra_cec *cec = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (!atomic_read(&cec->init_done))
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
if (buf)
|
if (buf)
|
||||||
return sprintf(buf, "0x%x\n", (u32)cec->logical_addr);
|
return sprintf(buf, "0x%x\n", (u32)cec->logical_addr);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -351,7 +452,7 @@ static int tegra_cec_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
atomic_set(&cec->init_done, 0);
|
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");
|
cec->clk = clk_get(&pdev->dev, "cec");
|
||||||
|
|
||||||
@@ -365,8 +466,6 @@ static int tegra_cec_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* set context info. */
|
/* set context info. */
|
||||||
cec->dev = &pdev->dev;
|
cec->dev = &pdev->dev;
|
||||||
cec->rx_wake = 0;
|
|
||||||
cec->tx_wake = 0;
|
|
||||||
init_waitqueue_head(&cec->rx_waitq);
|
init_waitqueue_head(&cec->rx_waitq);
|
||||||
init_waitqueue_head(&cec->tx_waitq);
|
init_waitqueue_head(&cec->tx_waitq);
|
||||||
init_waitqueue_head(&cec->init_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,
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* drivers/misc/tegra-cec/tegra_cec.h
|
* 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
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -16,28 +16,43 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef TEGRA_CEC_H
|
||||||
|
#define TEGRA_CEC_H
|
||||||
|
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
|
#define TEGRA_CEC_FRAME_MAX_LENGTH 16
|
||||||
|
|
||||||
struct tegra_cec {
|
struct tegra_cec {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct miscdevice misc_dev;
|
struct miscdevice misc_dev;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
struct mutex tx_lock;
|
||||||
void __iomem *cec_base;
|
void __iomem *cec_base;
|
||||||
int tegra_cec_irq;
|
int tegra_cec_irq;
|
||||||
wait_queue_head_t rx_waitq;
|
wait_queue_head_t rx_waitq;
|
||||||
wait_queue_head_t tx_waitq;
|
wait_queue_head_t tx_waitq;
|
||||||
wait_queue_head_t init_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;
|
u16 logical_addr;
|
||||||
struct work_struct work;
|
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);
|
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*/
|
/*CEC Timing registers*/
|
||||||
#define TEGRA_CEC_SW_CONTROL 0X000
|
#define TEGRA_CEC_SW_CONTROL 0X000
|
||||||
#define TEGRA_CEC_HW_CONTROL 0X004
|
#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_RX 0X038
|
||||||
#define TEGRA_CEC_HW_DEBUG_TX 0X03C
|
#define TEGRA_CEC_HW_DEBUG_TX 0X03C
|
||||||
|
|
||||||
#define TEGRA_CEC_LOGICAL_ADDR 0x10
|
#define TEGRA_CEC_MAX_LOGICAL_ADDR 15
|
||||||
#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0xFFFF
|
#define TEGRA_CEC_HWCTRL_RX_LADDR_UNREG 0x0
|
||||||
#define TEGRA_CEC_HWCTRL_RX_LADDR(x) (x<<0)
|
#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0x7FFF
|
||||||
|
#define TEGRA_CEC_HWCTRL_RX_LADDR(x) \
|
||||||
#define TEGRA_CEC_HW_CONTROL_RX_LOGICAL_ADDRS_MASK 0
|
((x<<0) & TEGRA_CEC_HWCTRL_RX_LADDR_MASK)
|
||||||
#define TEGRA_CEC_HW_CONTROL_RX_SNOOP (1<<15)
|
#define TEGRA_CEC_HWCTRL_RX_SNOOP (1<<15)
|
||||||
#define TEGRA_CEC_HW_CONTROL_RX_NAK_MODE (1<<16)
|
#define TEGRA_CEC_HWCTRL_RX_NAK_MODE (1<<16)
|
||||||
#define TEGRA_CEC_HW_CONTROL_TX_NAK_MODE (1<<24)
|
#define TEGRA_CEC_HWCTRL_TX_NAK_MODE (1<<24)
|
||||||
#define TEGRA_CEC_HW_CONTROL_FAST_SIM_MODE (1<<30)
|
#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE (1<<30)
|
||||||
#define TEGRA_CEC_HW_CONTROL_TX_RX_MODE (1<<31)
|
#define TEGRA_CEC_HWCTRL_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_INPUT_FILTER_FIFO_LENGTH_MASK 0
|
||||||
|
|
||||||
#define TEGRA_CEC_TX_REGISTER_DATA_MASK 0
|
#define TEGRA_CEC_TX_REG_DATA_SHIFT 0
|
||||||
#define TEGRA_CEC_TX_REGISTER_EOM_MASK 8
|
#define TEGRA_CEC_TX_REG_EOM_SHIFT 8
|
||||||
#define TEGRA_CEC_TX_REGISTER_ADDRESS_MODE_MASK 12
|
#define TEGRA_CEC_TX_REG_ADDR_MODE_SHIFT 12
|
||||||
#define TEGRA_CEC_TX_REGISTER_GENERATE_START_BIT_MASK 16
|
#define TEGRA_CEC_TX_REG_START_BIT_SHIFT 16
|
||||||
#define TEGRA_CEC_TX_REGISTER_RETRY_FRAME_MASK 17
|
#define TEGRA_CEC_TX_REG_RETRY_BIT_SHIFT 17
|
||||||
|
|
||||||
#define TEGRA_CEC_RX_REGISTER_MASK 0
|
#define TEGRA_CEC_RX_REGISTER_MASK 0
|
||||||
#define TEGRA_CEC_RX_REGISTER_EOM (1<<8)
|
#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_1_TX_ACK_NAK_BIT_SAMPLE_TIME_MASK 24
|
||||||
|
|
||||||
#define TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK 0
|
#define TEGRA_CEC_TX_TIMING_2_BUS_IDLE_TIME_ADDITIONAL_FRAME_MASK 0
|
||||||
#define TEGRA_CEC_TX_TIMING_2_TX_BUS_IDLE_TIME_NEW_FRAME_MASK 4
|
#define TEGRA_CEC_TX_TIMING_2_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_RETRY_FRAME_MASK 8
|
||||||
|
|
||||||
#define TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY (1<<0)
|
#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_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_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER (1<<26)
|
||||||
|
|
||||||
#define TEGRA_CEC_NAME "tegra_cec"
|
#define TEGRA_CEC_NAME "tegra_cec"
|
||||||
|
|
||||||
|
#endif /* TEGRA_CEC_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user