4535 lines
108 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Rockchip Electronics Co., Ltd.
*
* it66353 HDMI 3 in 1 out driver.
*
* Author: Kenneth.Hung@ite.com.tw
* Wangqiang Guo<kay.guo@rock-chips.com>
* Version: IT66353_SAMPLE_1.08
*
*/
#define _SHOW_PRAGMA_MSG
#include "config.h"
// #include "platform.h"
#include "debug.h"
#include "it66353_drv.h"
#include "it66353_EQ.h"
#include "it66353.h"
/*
* RK kernel follow
*/
#include <linux/miscdevice.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/printk.h>
#include <linux/kobject.h>
#include <linux/version.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
//#define DEBUG_EN
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00)
#define DRIVER_NAME "IT66353"
#define DEBUG(...)\
do {\
if (debug_on)\
printk(__VA_ARGS__);\
} while (0)
/*
* ********* compile options ***********
* CHECK_DEV_PRESENT:
* 1: FW will restart when device ID check failed.
*/
#define CHECK_DEV_PRESENT 0
#define PR_IO(x) { if (g_enable_io_log) dev_dbg(g_it66353->dev, x); }
// ********* compile options end *******
#if DEBUG_FSM_CHANGE
#define __init_str_SYS_FSM_STATE
#include "IT66353_drv_h_str.h"
#endif
/*
* for CEC
* #if EN_CEC
* #include "it66353_cec.h"
* #include "..\Customer\IT6635_CecSys.h"
* #endif
* for CEC
*/
struct it66353_dev {
struct device *dev;
struct miscdevice miscdev;
struct i2c_client *client;
struct timer_list timer;
struct delayed_work work_i2c_poll;
struct mutex poll_lock;
struct mutex port_lock;
struct task_struct *poll_task;
bool auto_switch_en;
bool is_chip_ready;
bool cec_switch_en;
bool nosignal;
u8 tx_current_5v;
u32 hdmi_rx_sel;
};
static struct it66353_dev *g_it66353;
static struct it66353_dev *it66353;
static u8 dev_state = DEV_FW_VAR_INIT;
IT6635_DEVICE_DATA it66353_gdev;
static int debug_on;
static void it66353_dev_loop(void);
static void _rx_set_hpd(u8 port, u8 hpd_value, u8 term_value);
/*
* RK kernel
*/
static void i2c_wr(struct it66353_dev *it66353, u16 reg, u8 *val, u32 n)
{
struct i2c_msg msg;
struct i2c_client *client = it66353->client;
int err;
u8 data[128];
data[0] = reg;
memcpy(&data[1], val, n);
msg.addr = client->addr;
msg.flags = 0;
msg.buf = data;
msg.len = n + 1;
err = i2c_transfer(client->adapter, &msg, 1);
if (err != 1) {
dev_err(it66353->dev, "writing register 0x%x from 0x%x failed\n",
reg, client->addr);
} else {
switch (n) {
case 1:
dev_dbg(it66353->dev, "I2C write 0x%02x = 0x%02x\n",
reg, data[1]);
break;
case 2:
dev_dbg(it66353->dev,
"I2C write 0x%02x = 0x%02x%02x\n",
reg, data[2], data[1]);
break;
case 4:
dev_dbg(it66353->dev,
"I2C write 0x%02x = 0x%02x%02x%02x%02x\n",
reg, data[4], data[3], data[2], data[1]);
break;
default:
dev_dbg(it66353->dev,
"I2C write %d bytes from address 0x%02x\n",
n, reg);
}
}
}
static void i2c_rd(struct it66353_dev *it66353, u16 reg, u8 *val, u32 n)
{
struct i2c_msg msg[2];
struct i2c_client *client = it66353->client;
int err;
u8 buf[1] = { reg };
// msg[0] addr to read
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = buf;
msg[0].len = 1;
// msg[1] read data
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = val;
msg[1].len = n;
err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (err != ARRAY_SIZE(msg)) {
dev_err(it66353->dev, "reading register 0x%x from 0x%x failed\n",
reg, client->addr);
}
}
static void i2c_rd8(u8 i2c_addr, u16 reg, u8 *val)
{
struct it66353_dev *it66353 = g_it66353;
it66353->client->addr = (i2c_addr >> 1);
i2c_rd(it66353, reg, val, 1);
}
static void it66353_i2c_read(u8 i2c_addr, u16 reg, u8 n, u8 *val)
{
struct it66353_dev *it66353 = g_it66353;
it66353->client->addr = (i2c_addr >> 1);
i2c_rd(it66353, reg, val, n);
}
static void i2c_wr8(u8 i2c_addr, u16 reg, u8 buf)
{
struct it66353_dev *it66353 = g_it66353;
it66353->client->addr = (i2c_addr >> 1);
i2c_wr(it66353, reg, &buf, 1);
}
static u8 rx_rd8(u8 offset)
{
u8 rd_data;
i2c_rd8(it66353_gdev.opts.dev_opt->RxAddr, offset, &rd_data);
return rd_data;
}
#if EN_CEC
static u8 cec_rd8(u8 offset)
{
u8 rd_data;
i2c_rd8(it66353_gdev.opts.dev_opt->CecAddr, offset, &rd_data);
return rd_data;
}
#endif
static u8 sw_rd8(u8 offset)
{
u8 rd_data;
i2c_rd8(it66353_gdev.opts.dev_opt->SwAddr, offset, &rd_data);
return rd_data;
}
static void swAddr_updata_bit(u16 reg, u32 mask, u32 val)
{
u8 val_p;
i2c_rd8(it66353_gdev.opts.dev_opt->SwAddr, reg, &val_p);
val_p = (val_p & ((~mask) & 0xFF)) | (mask & val);
i2c_wr8(it66353_gdev.opts.dev_opt->SwAddr, reg, val_p);
}
static void rxAddr_updata_bit(u16 reg, u32 mask, u32 val)
{
u8 val_p;
i2c_rd8(it66353_gdev.opts.dev_opt->RxAddr, reg, &val_p);
val_p = (val_p & ((~mask) & 0xFF)) | (mask & val);
i2c_wr8(it66353_gdev.opts.dev_opt->RxAddr, reg, val_p);
}
static void cecAddr_updata_bit(u16 reg, u32 mask, u32 val)
{
u8 val_p;
i2c_rd8(it66353_gdev.opts.dev_opt->CecAddr, reg, &val_p);
val_p = (val_p & ((~mask) & 0xFF)) | (mask & val);
i2c_wr8(it66353_gdev.opts.dev_opt->CecAddr, reg, val_p);
}
static long it66353_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
{
return 0;
}
static ssize_t it66353_write(struct file *file, const char __user *buf,
size_t size, loff_t *ppos)
{
return 1;
}
static ssize_t it66353_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
return 1;
}
static void it66353_work_i2c_poll(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct it66353_dev *it66353 =
container_of(dwork, struct it66353_dev, work_i2c_poll);
mutex_lock(&it66353->poll_lock);
it66353_dev_loop();
mutex_unlock(&it66353->poll_lock);
schedule_delayed_work(&it66353->work_i2c_poll, msecs_to_jiffies(50));
}
static ssize_t it66353_hdmirxsel_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct it66353_dev *it66353 = g_it66353;
dev_info(it66353->dev, "%s: hdmi rx select state: %d\n",
__func__, g_it66353->hdmi_rx_sel);
return sprintf(buf, "%d\n", g_it66353->hdmi_rx_sel);
}
static ssize_t it66353_hdmirxsel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct it66353_dev *it66353 = g_it66353;
u32 hdmirxstate = 0;
int ret;
mutex_lock(&it66353->port_lock);
ret = kstrtouint(buf, 10, &hdmirxstate);
if (!ret && hdmirxstate >= 0 && hdmirxstate <= RX_PORT_COUNT) {
it66353->hdmi_rx_sel = hdmirxstate;
dev_info(it66353->dev, "%s: state: %d\n", __func__, hdmirxstate);
/*
* _rx_set_hpd(hdmirxstate, 0, TERM_FOLLOW_HPD);
* msleep(200);
* _rx_set_hpd(hdmirxstate, 1, TERM_FOLLOW_HPD);
*/
it66353_set_active_port(hdmirxstate);
} else {
dev_info(it66353->dev, "%s: write hdmi_rx_sel failed!!!, hdmirxstate:%d \n",
__func__, hdmirxstate);
}
mutex_unlock(&it66353->port_lock);
return count;
}
static DEVICE_ATTR_RW(it66353_hdmirxsel);
static const struct file_operations it66353_fops = {
.owner = THIS_MODULE,
.read = it66353_read,
.write = it66353_write,
.unlocked_ioctl = it66353_ioctl,
};
static struct miscdevice it66353_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "it66353_dev",
.fops = &it66353_fops,
};
static int it66353_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct it66353_dev *it66353;
struct device *dev = &client->dev;
int ret;
dev_info(dev, "driver version: %02x.%02x.%02x",
DRIVER_VERSION >> 16,
(DRIVER_VERSION & 0xff00) >> 8,
DRIVER_VERSION & 0x00ff);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
dev_info(dev, "chip found @ 0x%x (%s)\n", client->addr << 1,
client->adapter->name);
it66353 = devm_kzalloc(dev, sizeof(struct it66353_dev), GFP_KERNEL);
if (!it66353)
return -ENOMEM;
it66353->client = client;
it66353->dev = dev;
client->flags |= I2C_CLIENT_SCCB;
mutex_init(&it66353->poll_lock);
mutex_init(&it66353->port_lock);
ret = misc_register(&it66353_miscdev);
if (ret) {
dev_err(it66353->dev,
"it66353 ERROR: could not register it66353 device\n");
return ret;
}
INIT_DELAYED_WORK(&it66353->work_i2c_poll, it66353_work_i2c_poll);
g_it66353 = it66353;
ret = device_create_file(it66353_miscdev.this_device,
&dev_attr_it66353_hdmirxsel);
if (ret) {
dev_err(it66353->dev, "failed to create attr hdmirxsel!\n");
goto err0;
}
it66353_options_init();
schedule_delayed_work(&it66353->work_i2c_poll, msecs_to_jiffies(10));
dev_info(it66353->dev, "%s found @ 0x%x (%s)\n",
client->name, client->addr << 1,
client->adapter->name);
return 0;
err0:
misc_deregister(&it66353_miscdev);
return ret;
}
static void it66353_remove(struct i2c_client *client)
{
cancel_delayed_work_sync(&it66353->work_i2c_poll);
device_remove_file(it66353_miscdev.this_device,
&dev_attr_it66353_hdmirxsel);
misc_deregister(&it66353_miscdev);
mutex_destroy(&it66353->poll_lock);
mutex_destroy(&it66353->port_lock);
}
static const struct of_device_id it66353_of_match[] = {
{ .compatible = "ite,it66353" },
{}
};
MODULE_DEVICE_TABLE(of, it66353_of_match);
static struct i2c_driver it66353_driver = {
.probe = it66353_probe,
.remove = it66353_remove,
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(it66353_of_match),
},
};
static int __init it66353_driver_init(void)
{
return i2c_add_driver(&it66353_driver);
}
static void __exit it66353_driver_exit(void)
{
i2c_del_driver(&it66353_driver);
}
device_initcall_sync(it66353_driver_init);
module_exit(it66353_driver_exit);
MODULE_DESCRIPTION("ITE IT66353 3 HDMI in switch driver");
MODULE_AUTHOR("Wangqiang Guo <kay.guo@rock-chips.com>");
MODULE_LICENSE("GPL v2");
/*
* end rk kernel
*/
static void _pr_buf(void *buffer, int length)
{
u8 *buf = (u8 *)buffer;
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf,
length, false);
}
u8 it66353_h2swwr(u8 offset, u8 wdata)
{
swAddr_updata_bit(offset, 0xFF, wdata);
return 0;
}
u8 it66353_h2swrd(u8 offset)
{
u8 rddata;
rddata = sw_rd8(offset);
return rddata;
}
u8 it66353_h2swset(u8 offset, u8 mask, u8 wdata)
{
swAddr_updata_bit(offset, mask, wdata);
return 0;
}
void it66353_h2swbrd(u8 offset, u8 length, u8 *rddata)
{
if (length > 0) {
g_it66353->client->addr = (SWAddr >> 1);
i2c_rd(g_it66353, offset, rddata, length);
}
}
void it66353_h2swbwr(u8 offset, u8 length, u8 *rddata)
{
if (length > 0) {
g_it66353->client->addr = (SWAddr >> 1);
i2c_wr(g_it66353, offset, rddata, length);
}
}
u8 it66353_h2rxedidwr(u8 offset, u8 *wrdata, u8 length)
{
#if 0
u8 i;
for (i = 0; i < length; i++) {
PR_IO("w %02x %02x %02x\r\n", offset+i, wrdata[i], RXEDIDAddr);
}
#endif
g_it66353->client->addr = (RXEDIDAddr >> 1);
i2c_wr(g_it66353, offset, wrdata, length);
return 0;
}
static u8 it66353_h2rxedidrd(u8 offset, u8 *wrdata, u8 length)
{
g_it66353->client->addr = (RXEDIDAddr >> 1);
i2c_wr(g_it66353, offset, wrdata, length);
return 0;
}
u8 it66353_h2rxwr(u8 offset, u8 rdata)
{
rxAddr_updata_bit(offset, 0xFF, rdata);
return 0;
}
u8 it66353_h2rxrd(u8 offset)
{
u8 rddata;
rddata = rx_rd8(offset);
return rddata;
}
u8 it66353_h2rxset(u8 offset, u8 mask, u8 wdata)
{
rxAddr_updata_bit(offset, mask, wdata);
return 0;
}
void it66353_h2rxbrd(u8 offset, u8 length, u8 *rddata)
{
if (length > 0) {
g_it66353->client->addr = (RXAddr >> 1);
i2c_rd(g_it66353, offset, rddata, length);
}
}
void it66353_h2rxbwr(u8 offset, u8 length, u8 *rddata)
{
if (length > 0) {
g_it66353->client->addr = (RXAddr >> 1);
i2c_wr(g_it66353, offset, rddata, length);
}
}
#if EN_CEC
u8 it66353_cecwr(u8 offset, u8 wdata)
{
return cecAddr_updata_bit(g_it66353, offset, 0xFF, wdata);
}
u8 cecrd(u8 offset)
{
u8 rddata;
rddata = cec_rd8(offset);
return rddata;
}
u8 it66353_cecset(u8 offset, u8 mask, u8 rdata)
{
return cecAddr_updata_bit(offset, mask, rddata);
}
void it66353_cecbrd(u8 offset, u8 length, u8 *rddata)
{
if (length > 0) {
g_it66353->client->addr = (CecAddr >> 1);
i2c_rd(g_it66353, offset, rddata, length);
}
}
void it66353_cecbwr(u8 offset, u8 length, u8 *rddata)
{
if (length > 0) {
g_it66353->client->addr = (CecAddr >> 1);
i2c_wr(g_it66353, offset, rddata, length);
}
}
#endif
void it66353_chgrxbank(u8 bankno)
{
it66353_h2rxset(0x0f, 0x07, bankno & 0x07);
}
static bool _tx_is_sink_hpd_high(void)
{
if (it66353_h2swrd(0x11) & 0x20)
return TRUE;
else
return FALSE;
}
static bool _tx_ddcwait(void)
{
u8 ddcwaitcnt, ddc_status;
ddcwaitcnt = 0;
do {
ddcwaitcnt++;
msleep(DDCWAITTIME);
} while ((it66353_h2swrd(0x1B) & 0x80) == 0x00 && ddcwaitcnt < DDCWAITNUM);
if (ddcwaitcnt == DDCWAITNUM) {
ddc_status = it66353_h2swrd(0x1B) & 0xFE;
dev_err(g_it66353->dev, "** TX DDC Bus Sta=%02x\r\n", ddc_status);
dev_err(g_it66353->dev, "** TX DDC Bus Wait TimeOut => ");
if (it66353_h2swrd(0x27) & 0x80) {
dev_err(g_it66353->dev, "** DDC Bus Hang\r\n");
// Do not handle the DDC Bus Hang here
// h2txwr(port, 0x2E, 0x0F); // Abort DDC Command
// h2txwr(port, 0x16, 0x08); // Clear Interrupt
} else if (ddc_status & 0x20) {
dev_err(g_it66353->dev, "** DDC NoACK\r\n");
} else if (ddc_status & 0x10) {
dev_err(g_it66353->dev, "** DDC WaitBus\r\n");
} else if (ddc_status & 0x08) {
dev_err(g_it66353->dev, "** DDC ArbiLose\r\n");
} else {
dev_err(g_it66353->dev, "** UnKnown Issue\r\n");
}
return FALSE;
} else {
return TRUE;
}
}
static u8 _tx_scdc_write(u8 offset, u8 wdata)
{
int ddcwaitsts;
u8 reg3C;
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass) {
dev_err(g_it66353->dev, "** EnRxDDCBypass:Abort SCDC write\r\n");
return FALSE;
}
if ((it66353_h2swrd(0x11) & 0x20) == 0x00) {
dev_err(g_it66353->dev, "** HPD-Low:Abort SCDC write\r\n");
return FALSE;
}
reg3C = it66353_h2swrd(0x3C);
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0xA8); // EDID Address
it66353_h2swwr(0x3F, offset); // EDID Offset
it66353_h2swwr(0x40, 0x01); // ByteNum[7:0]
it66353_h2swwr(0x42, wdata); // WrData
it66353_h2swwr(0x3D, 0x01); // Sequential Burst Write
ddcwaitsts = _tx_ddcwait();
it66353_h2swwr(0x3C, reg3C); // Disable PC DDC Mode
if (ddcwaitsts == 0) {
DEBUG("SCDC wr %02x %02x, ddcwaitsts = %d\r\n",
offset, wdata, ddcwaitsts);
}
return ddcwaitsts;
}
static u8 __maybe_unused _tx_scdc_read(u8 offset, u8 *data_buf)
{
int ddcwaitsts;
u8 reg3C;
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass) {
dev_err(g_it66353->dev, "EnRxDDCBypass:Abort SCDC read\r\n");
return FALSE;
}
if ((it66353_h2swrd(0x11) & 0x20) == 0x00) {
dev_err(g_it66353->dev, "HPD-Low:Abort SCDC read\r\n");
return FALSE;
}
reg3C = it66353_h2swrd(0x3C);
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0xA8); // EDID Address
it66353_h2swwr(0x3F, offset); // EDID Offset
it66353_h2swwr(0x40, 0x01); // ByteNum[7:0]
//it66353_h2swwr(0x42, data); // WrData
it66353_h2swwr(0x3D, 0x00); // Sequential Burst Write
ddcwaitsts = _tx_ddcwait();
it66353_h2swwr(0x3C, reg3C); // Disable PC DDC Mode
if (ddcwaitsts == 0) {
dev_err(g_it66353->dev, "SCDC rd %02x ddcwaitsts = %d\r\n",
offset, ddcwaitsts);
} else {
*data_buf = it66353_h2swrd(0x42);
}
return ddcwaitsts;
}
static u8 __maybe_unused _tx_hdcp_write(u8 offset, u8 data)
{
int ddcwaitsts;
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass) {
dev_err(g_it66353->dev, "EnRxDDCBypass:Abort HDCP write\r\n");
return FALSE;
}
if ((it66353_h2swrd(0x11) & 0x20) == 0x00) {
dev_err(g_it66353->dev, "HPD-Low:Abort HDCP write\r\n");
return FALSE;
}
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0x74); // EDID Address
it66353_h2swwr(0x3F, offset); // EDID Offset
it66353_h2swwr(0x40, 0x01); // ByteNum[7:0]
it66353_h2swwr(0x42, data); // WrData
it66353_h2swwr(0x3D, 0x01); // Sequential Burst Write
ddcwaitsts = _tx_ddcwait();
it66353_h2swset(0x3C, 0x01, 0x00); // Disable PC DDC Mode
if (ddcwaitsts == 0) {
DEBUG("SCDC wr %02x %02x, ddcwaitsts = %d\r\n", offset, data, ddcwaitsts);
}
return ddcwaitsts;
}
static u8 __maybe_unused _tx_hdcp_read(u8 offset, u8 *data_buf, u8 len)
{
int ddcwaitsts;
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass) {
dev_err(g_it66353->dev, "EnRxDDCBypass:Abort HDCP read\r\n");
return FALSE;
}
if ((it66353_h2swrd(0x11) & 0x20) == 0x00) {
dev_err(g_it66353->dev, "HPD-Low:Abort HDCP read\r\n");
return FALSE;
}
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0x74); // EDID Address
it66353_h2swwr(0x3F, offset); // EDID Offset
it66353_h2swwr(0x40, len); // ByteNum[7:0]
// it66353_h2swwr(0x42, data); // WrData
it66353_h2swwr(0x3D, 0x00); // Sequential Burst Write
ddcwaitsts = _tx_ddcwait();
it66353_h2swset(0x3C, 0x01, 0x00); // Disable PC DDC Mode
if (ddcwaitsts == 0) {
dev_err(g_it66353->dev, "SCDC rd %02x ddcwaitsts = %d\r\n",
offset, ddcwaitsts);
} else {
u8 i;
DEBUG("HDCP read - %02X : ", offset);
for (i = 0; i < len; i++) {
data_buf[i] = it66353_h2swrd(0x42);
DEBUG("%02X ", data_buf[i]);
}
DEBUG("\r\n");
}
return ddcwaitsts;
}
static u8 __maybe_unused _tx_scdc_read_ced(u8 *data_buf)
{
int ddcwaitsts;
u8 i;
if ((it66353_h2swrd(0x11) & 0x20) == 0x00) {
dev_err(g_it66353->dev, "HPD-Low:Abort SCDC read\r\n");
return FALSE;
}
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0xA8); // EDID Address
it66353_h2swwr(0x3F, 0x50); // EDID Offset
it66353_h2swwr(0x40, 0x06); // ByteNum[7:0]
// it66353_h2swwr(0x42, data); // WrData
it66353_h2swwr(0x3D, 0x00); // Sequential Burst Write
ddcwaitsts = _tx_ddcwait();
it66353_h2swset(0x3C, 0x01, 0x00); // Disable PC DDC Mode
if (ddcwaitsts == 0) {
dev_err(g_it66353->dev, "SCDC rd ced ddcwaitsts = %d\r\n", ddcwaitsts);
} else {
for (i = 0; i < 6; i++) {
data_buf[i] = it66353_h2swrd(0x42);
}
}
return ddcwaitsts;
}
static void _tx_power_down(void)
{
if (it66353_gdev.opts.dev_opt->DoTxPowerDown) {
it66353_h2swset(0xD3, 0x80, 0x00);
it66353_h2swset(0xD1, 0x60, 0x60);
}
}
static void _tx_power_on(void)
{
it66353_h2swset(0xD3, 0x80, 0x80); // Reg_XP_ALPWDB=1
it66353_h2swset(0xD1, 0x60, 0x00); // Reg_XP_PWDi = 0, Reg_XP_PWDPLL=0
}
static void __maybe_unused _tx_show_sink_ced(void)
{
u8 ced_valid;
u8 i;
u8 pr_ced = 0;
u8 ced_value[6];
// static u8 read_from_scdc;
ced_valid = it66353_h2swrd(0xB0);
if (ced_valid) {
DEBUG("Begin READ CED:\r\n");
pr_ced = 1;
for (i = 0; i < 6; i++) {
if (ced_valid & (0x01 << i)) { // 0x5? error status is valid
it66353_h2swset(0xAC, 0xE0, (i<<5)); // offset select
ced_value[i] = it66353_h2swrd(0xB1);
}
}
it66353_h2swwr(0xAD, 0xFF); // clear CED valid on 0xB0
} else {
#if 0
if (read_from_scdc > 10) {
#if 0
for (i = 0; i < 6; i++) {
_tx_scdc_read(0x50 + i, &ced_value[i]);
}
#else
_tx_scdc_read_ced(&ced_value[0]);
#endif
dev_info("SCDC: ");
pr_ced = 1;
read_from_scdc = 0;
} else {
read_from_scdc++;
}
#else
// read_from_scdc = read_from_scdc; // suppress warning
#endif
}
if (pr_ced) {
for (i = 0; i < 3; i++) {
DEBUG("ced_valid = %02X, ch%d V=%d err=%04X\r\n",
ced_valid, i, (ced_value[2*i+1]>>7)&0x01,
((ced_value[2*i+1]&0xEF)<<8) + ced_value[2*i]);
}
DEBUG("\r\n");
}
}
static void __maybe_unused _tx_ovwr_hdmi_clk(u8 ratio)
{
switch (ratio) {
case HDMI_MODE_AUTO:
it66353_h2swset(0xB2, 0x03, 0x00);
break;
case HDMI_MODE_14:
it66353_h2swset(0xB2, 0x03, 0x01);
break;
case HDMI_MODE_20:
it66353_h2swset(0xB2, 0x03, 0x03);
break;
default:
break;
}
}
static void __maybe_unused _tx_ovwr_h20_scrb(u8 scrb)
{
switch (scrb) {
case HDMI_MODE_AUTO:
it66353_h2swset(0xB2, 0x0C, 0x00);
break;
case HDMI_MODE_14:
it66353_h2swset(0xB2, 0x0C, 0x08);
break;
case HDMI_MODE_20:
it66353_h2swset(0xB2, 0x0C, 0x0c);
break;
default:
break;
}
}
u8 it66353_rx_is_ch_symlock(u8 ch)
{
if ((it66353_h2rxrd(0x14) & (0x08 << ch))) {
return 1;
}
return 0;
}
static void __maybe_unused it66353it66353_rx_ovwr_hdmi_clk(u8 port, u8 ratio)
{
switch (ratio) {
case HDMI_MODE_AUTO:
it66353_h2swset(0x51 + port, 0x28, 0x00);
break;
case HDMI_MODE_14:
it66353_h2swset(0x51 + port, 0x28, 0x20);
break;
case HDMI_MODE_20:
it66353_h2swset(0x51 + port, 0x28, 0x28);
break;
default:
break;
}
}
static void __maybe_unused it66353it66353_rx_ovwr_h20_scrb(u8 port, u8 scrb)
{
switch (scrb) {
case HDMI_MODE_AUTO:
it66353_h2swset(0x51 + port, 0x30, 0x00);
break;
case HDMI_MODE_14:
it66353_h2swset(0x51 + port, 0x30, 0x20);
break;
case HDMI_MODE_20:
it66353_h2swset(0x51 + port, 0x30, 0x30);
break;
default:
break;
}
}
static void it66353_sw_config_timer0(u8 count)
{
// init timer = count[6:0] * 10 ms
it66353_h2swwr(0x1C, count);
}
static void it66353it66353_sw_enable_timer0(void)
{
it66353_h2swset(0x38, 0x02, 0x02);
}
static void _sw_clear_timer0_interrupt(void)
{
it66353_h2swset(0x28, 0x02, 0x02);
}
static void __maybe_unused _sw_enable_txoe_timer_check(void)
{
it66353_sw_disable_timer0();
it66353_sw_config_timer0(45); // 450ms time out
_sw_clear_timer0_interrupt();
it66353it66353_sw_enable_timer0();
}
static void __maybe_unused _sw_disable_txoe_timer_check(void)
{
it66353_sw_disable_timer0();
}
static void __maybe_unused _sw_show_hdcp_status(void)
{
u8 hdcp_sts;
if (it66353_gdev.vars.Rev >= 0xC0) {
hdcp_sts = it66353_h2swrd(0xB3);
if (hdcp_sts & BIT(5)) {
DEBUG("HDCP 2 done\r\n");
it66353_sw_clear_hdcp_status();
}
if (hdcp_sts & BIT(6)) {
DEBUG("HDCP 1 done\r\n");
it66353_sw_clear_hdcp_status();
}
}
}
u8 it66353_get_port_info1(u8 port, u8 info)
{
u8 tmp;
tmp = it66353_h2swrd(0x61 + port * 3);
if ((tmp & info) == info) {
return 1;
} else {
return 0;
}
}
static void _tx_ovwr_hdmi_mode(u8 mode)
{
switch (mode) {
case HDMI_MODE_AUTO:
it66353_h2swset(0xB2, 0x0F, 0x00);
break;
case HDMI_MODE_14:
it66353_h2swset(0xB2, 0x0F, 0x05);
break;
case HDMI_MODE_20:
it66353_h2swset(0xB2, 0x0F, 0x0F);
break;
default:
break;
}
}
static void _tx_setup_afe(u32 vclk)
{
u8 H2ON_PLL, DRV_TERMON, DRV_RTERM, DRV_ISW, DRV_ISWC, DRV_TPRE, DRV_NOPE, H2ClkRatio;
u8 DRV_PISW, DRV_PISWC, DRV_HS;
// vclk = 340000UL;
DEBUG("_tx_setup_afe %u\r\n", vclk);
// it66353_h2rxset(0x23, 0x04, 0x04);
if (vclk > 100000UL) { // IP_VCLK05 > 50MHz
it66353_h2swset(0xD1, 0x07, 0x04);
} else {
it66353_h2swset(0xD1, 0x07, 0x03);
}
if (vclk > 162000UL) { // IP_VCLK05 > 81MHz
// it66353_h2swset(0xD4, 0x04, 0x04);
DRV_HS = 1;
} else {
DRV_HS = 0;
}
it66353_h2swset(0xd8, 0xf0, 0x00);
if (vclk > 300000UL) { // single-end swing = 520mV
DRV_TERMON = 1;
DRV_RTERM = 0x5;
DRV_ISW = 0x0E;
DRV_ISWC = 0x0B;
DRV_TPRE = 0x0;
DRV_NOPE = 0;
H2ON_PLL = 1;
DRV_PISW = 1;
DRV_PISWC = 1;
it66353_h2swset(0xd8, 0xf0, 0x30);
} else if (vclk > 100000UL) { // single-end swing = 450mV
DRV_TERMON = 1;
DRV_RTERM = 0x1;
DRV_ISW = 0x9;
DRV_ISWC = 0x9;
DRV_TPRE = 0;
DRV_NOPE = 1;
H2ON_PLL = 0;
DRV_PISW = 1;
DRV_PISWC = 1;
} else { // single-end swing = 500mV
DRV_TERMON = 0;
DRV_RTERM = 0x0;
DRV_ISW = 0x3;
DRV_ISWC = 0x3;
DRV_TPRE = 0;
DRV_NOPE = 1;
H2ON_PLL = 0;
DRV_PISW = 1;
DRV_PISWC = 1;
}
it66353_h2swset(0xD0, 0x08, (H2ON_PLL << 3));
it66353_h2swset(0xD3, 0x1F, DRV_ISW);
it66353_h2swset(0xD4, 0xF4, (DRV_PISWC << 6)+(DRV_PISW << 4)+(DRV_HS << 2));
it66353_h2swset(0xD5, 0xBF, (DRV_NOPE << 7) + (DRV_TERMON << 5) + DRV_RTERM);
it66353_h2swset(0xD7, 0x1F, DRV_ISWC);
it66353_h2swset(0xD6, 0x0F, DRV_TPRE);
H2ClkRatio = (it66353_h2swrd(0xb2) & 0x02) >> 1;// RegH2ClkRatio from TX
DEBUG("TX Output H2ClkRatio=%d ...\r\n", H2ClkRatio);
// msleep(10);
// it66353_h2rxset(0x23, 0x04, 0x00);
}
static u8 _rx_calc_edid_sum(u8 *edid)
{
u8 i;
u16 sum = 0x100;
for (i = 0; i < 127; i++) {
sum = sum - edid[i];
}
return (sum & 0xFF);
}
void it66353_rx_caof_init(u8 port)
{
u8 reg08;
u8 failcnt;
it66353_h2swset(0x05, 0x01, 0x01);
it66353_h2swset(0x59 + port, 0x20, 0x20); // new at IT6635B0
it66353_h2swset(0x05 + port, 0x01, 0x01); // IPLL RST, it6635
it66353_rx_auto_power_down_enable(port, 0);
it66353_rx_term_power_down(port, 0x00); // disable PWD CHx termination
msleep(1);
it66353_chgrxbank(3);
it66353_h2rxset(0x3A, 0x80, 0x00); // Reg_CAOFTrg low
it66353_h2rxset(0xA0, 0x80, 0x80);
it66353_h2rxset(0xA1, 0x80, 0x80);
it66353_h2rxset(0xA2, 0x80, 0x80);
it66353_chgrxbank(0);
it66353_h2rxset(0x2A, 0x41, 0x41); // CAOF RST and CAOFCLK inversion
msleep(1);
it66353_h2rxset(0x2A, 0x40, 0x00); // deassert CAOF RST
it66353_h2rxwr(0x25, 0x00); // Disable AFE PWD
it66353_h2rxset(0x3C, 0x10, 0x00); // disable PLLBufRst
it66353_chgrxbank(3);
it66353_h2rxset(0x3B, 0xC0, 0x00); // Reg_ENSOF, Reg_ENCAOF
it66353_h2rxset(0x48, 0x80, 0x80); // for read back sof value registers
msleep(10);
it66353_h2rxset(0x3A, 0x80, 0x80); // Reg_CAOFTrg high
// wait for INT Done
it66353_chgrxbank(0);
reg08 = 0;
failcnt = 0;
while (reg08 == 0x00) {
reg08 = it66353_h2rxrd(0x08) & 0x10;
if (reg08 == 0) {
failcnt++;
if (failcnt >= 10) {
dev_err(g_it66353->dev, "ERROR: CAOF fail !!!\r\n");
it66353_chgrxbank(3);
it66353_h2rxset(0x3A, 0x80, 0x00);// disable CAOF_Trig
it66353_chgrxbank(0);
it66353_h2rxset(0x2A, 0x40, 0x40);// reset CAOF when caof fail
it66353_h2rxset(0x2A, 0x40, 0x00);
break;
}
}
msleep(2);
}
it66353_chgrxbank(3);
it66353_h2rxset(0x48, 0x80, 0x80);
DEBUG("CAOF_Int=%02x, Status=%02x\r\n\r\n",
(it66353_h2rxrd(0x59) & 0xC0),
((it66353_h2rxrd(0x5A) << 4) + (it66353_h2rxrd(0x59) & 0x0F)));
it66353_chgrxbank(0);
it66353_h2swset(0x59+port, 0x20, 0x00);
it66353_h2swset(0x05+port, 0x01, 0x00);
it66353_h2swset(0x05, 0x01, 0x00);
it66353_h2rxset(0x08, 0x30, 0x30);
it66353_h2rxset(0x3C, 0x10, 0x10);
it66353_chgrxbank(3);
it66353_h2rxset(0x3A, 0x80, 0x00); // Reg_CAOFTrg low
it66353_h2rxset(0xA0, 0x80, 0x00);
it66353_h2rxset(0xA1, 0x80, 0x00);
it66353_h2rxset(0xA2, 0x80, 0x00);
it66353_chgrxbank(0);
it66353_rx_auto_power_down_enable(port, it66353_gdev.opts.dev_opt->RxAutoPowerDown);
}
static void _rx_show_ced_info(void)
{
u8 symlock = (it66353_h2rxrd(0x14) & 0x38) >> 3;
u8 ch;
if (0x38 != symlock) {
DEBUG("symlock = %02x\r\n", symlock);
} else {
for (ch = 0; ch < 3; ch++) {
if (it66353_gdev.vars.RxCEDErrValid & (0x01 << ch)) {
DEBUG("ch_%d CED=0x%04x\r\n", ch, it66353_gdev.vars.RxCEDErr[ch]);
} else {
DEBUG("ch_%d CED=invalid\r\n", ch);
}
}
}
}
static void _rx_setup_afe(u32 vclk)
{
it66353_chgrxbank(3);
if (vclk >= (1024UL * 102UL)) {
it66353_h2rxset(0xA7, 0x40, 0x40);
} else {
it66353_h2rxset(0xA7, 0x40, 0x00);
}
it66353_chgrxbank(0);
}
static u8 _rx_is_any_ch_symlock(void)
{
if ((it66353_h2rxrd(0x14) & 0x38)) {
return 1;
}
return 0;
}
u8 it66353_rx_is_all_ch_symlock(void)
{
if ((it66353_h2rxrd(0x14) & 0x38) == 0x38) {
DBG_SYMLOCK_1();
// it66353_txoe(1);
return 1;
}
DBG_SYMLOCK_0();
return 0;
}
static bool _rx_is_5v_active(void)
{
return (it66353_h2rxrd(0x13) & 0x01);
}
u8 it66353_rx_is_clock_stable(void)
{
if (it66353_get_port_info0(it66353_gdev.vars.Rx_active_port,
(PI_CLK_STABLE | PI_CLK_VALID | PI_5V))) {
DBG_CLKSTABLE_1();
return 1;
} else {
DBG_CLKSTABLE_0();
return 0;
}
}
#if EN_AUTO_RS
static u8 _rx_need_hpd_toggle(void)
{
u8 hdcp_sts;
if (it66353_gdev.vars.Rev >= 0xC0) {
hdcp_sts = it66353_h2swrd(0xB3);
if (hdcp_sts & BIT(5)) {
dev_info(g_it66353->dev, "HDCP 2 done\r\n");
return 1;
}
if (hdcp_sts & BIT(6)) {
dev_info(g_it66353->dev, "HDCP 1 done\r\n");
return 1;
}
if (hdcp_sts & BIT(7)) {
dev_info(g_it66353->dev, "HDCP acc\r\n");
// return 0;
}
} else {
if (it66353_sw_get_timer0_interrupt()) {
dev_info(g_it66353->dev, "TXOE timeout 2\r\n");
return 1;
}
}
return 0;
#if 0
// todo: need more information
return 1;
#endif
}
#endif
static void _rx_int_enable(void)
{
/*
* Set RX Interrupt Enable
*/
it66353_h2rxwr(0x53, 0xFF); // Enable RxIntEn[7:0]
it66353_h2rxwr(0x54, 0xFF); // Enable RxIntEn[15:8]
it66353_h2rxwr(0x55, 0xFF); // Enable RxIntEn[23:16]
it66353_h2rxwr(0x56, 0xFF); // Enable RxIntEn[31:24]
it66353_h2rxwr(0x57, 0xFF); // Enable RxIntEn[39:32]
it66353_h2rxwr(0x5D, 0xF7); // Enable BKIntEn[7:0], but timer int
it66353_h2rxwr(0x5E, 0xFF); // Enable BKIntEn[15:8]
it66353_h2rxwr(0x5F, 0xFF); // Enable BKIntEn[23:16]
it66353_h2rxset(0x60, 0x20, 0x20); // RegEnIntOut=1
}
static void _rx_wdog_rst(u8 port)
{
#if 0
u8 mask;
mask = (0x10 << port) | (1 << port);
it66353_h2swset(0x16, mask, mask);
msleep(1);
it66353_h2swset(0x16, mask, 0x00);
#else
it66353_h2swset(0x2b, 0x01, 0x00);
msleep(2);
// it66353_h2swwr(0x20 + port * 2, 0x7C);// clear clock related interrupt
it66353_h2swset(0x2b, 0x01, 0x01);
// it66353_h2swwr(0x20 + port * 2, 0x04);
#endif
}
static void _rx_ovwr_hdmi_mode(u8 port, u8 mode)
{
switch (mode) {
case HDMI_MODE_AUTO:
it66353_h2swset(0x51 + port, 0x38, 0x00);
it66353_h2swset(0x98 + port, 0xC0, 0x00);
break;
case HDMI_MODE_14:
it66353_h2swset(0x51 + port, 0x38, 0x20);
it66353_h2swset(0x98 + port, 0xC0, 0x00);
break;
case HDMI_MODE_20:
it66353_h2swset(0x51 + port, 0x38, 0x38);
it66353_h2swset(0x98 + port, 0xC0, 0xC0);
break;
}
}
static void _rx_set_hpd(u8 port, u8 hpd_value, u8 term_value)
{
if (port < RX_PORT_COUNT) {
switch (term_value) {
case TERM_LOW:
term_value = 0xFF;
break;
case TERM_HIGH:
term_value = 0x00;
break;
case TERM_FOLLOW_TX:
if (it66353_h2swrd(0x11) & 0x40)
term_value = 0x00;
else
term_value = 0xFF;
break;
case TERM_FOLLOW_HPD:
default:
if (hpd_value) {
term_value = 0x00;
} else {
term_value = 0xFF;
}
break;
}
// if (it66353_gdev.vars.RxHPDFlag[port] != value)
// {
it66353_gdev.vars.RxHPDFlag[port] = hpd_value;
if (hpd_value) {
if (it66353_gdev.vars.Rx_active_port == port) {
DBG_TM(RX_HPD_HIGH);
if (it66353_gdev.opts.rx_opt[port]->EnRxDDCBypass == 0) {
it66353_h2swset(0x3C, 0x01, 0x01);
msleep(1);
it66353_h2swset(0x3C, 0x01, 0x00);
}
}
if (it66353_gdev.opts.rx_opt[port]->DisableEdidRam == 0) {
_rx_edid_ram_enable(port);
}
if (it66353_gdev.opts.rx_opt[port]->HPDOutputInverse) {
it66353_h2swset(0x4C + port, 0xC0, 0x40);// RXHPD=0
} else {
it66353_h2swset(0x4C + port, 0xC0, 0xC0);// RXHPD=1
}
#if 0
if (it66353_gdev.vars.Rx_active_port == port) {
// term power down = 0
it66353_rx_term_power_down(port, 0x7e);
} else {
#if NON_ACTIVE_PORT_DETECT_CLOCK
// term power down = 0
it66353_rx_term_power_down(port, 0x7e);
#else
// term power down = 0
it66353_rx_term_power_down(port, 0xFF);
#endif
}
#else
it66353_rx_term_power_down(port, term_value);
#endif
} else {
if (it66353_gdev.vars.Rx_active_port == port) {
DBG_TM(RX_HPD_LOW);
}
_rx_edid_ram_disable(port);
it66353_rx_term_power_down(port, term_value);
if (it66353_gdev.opts.rx_opt[port]->HPDOutputInverse) {
it66353_h2swset(0x4C + port, 0xC0, 0xC0);// RXHPD=1
} else {
it66353_h2swset(0x4C + port, 0xC0, 0x40);// RXHPD=0
}
if (port == it66353_gdev.vars.Rx_active_port) {
it66353_h2swset(0xB2, 0x0A, 0x0A); // clear H2Mode
}
}
dev_info(g_it66353->dev, "Set RxP%d HPD = %d %02x\r\n",
(int)port, (int)hpd_value, (int)term_value);
// }
} else {
dev_err(g_it66353->dev, "Invaild port %d\r\n", port);
}
}
static void _rx_set_hpd_all(u8 value)
{
u8 i;
for (i = 0; i < RX_PORT_COUNT; i++) {
_rx_set_hpd(i, value, TERM_FOLLOW_HPD);
}
}
static void _rx_set_hpd_with_5v_all(u8 non_active_port_only)
{
u8 i;
for (i = 0; i < RX_PORT_COUNT; i++) {
if (non_active_port_only) {
if (it66353_gdev.vars.Rx_active_port == i) {
continue;
}
}
if (it66353_gdev.opts.rx_opt[i]->NonActivePortReplyHPD) {
if (it66353_get_port_info0(i, PI_5V)) {
_rx_set_hpd(i, 1, TERM_FOLLOW_HPD);
} else {
_rx_set_hpd(i, 0, TERM_FOLLOW_HPD);
}
}
}
}
static u8 _rx_get_all_port_5v(void)
{
u8 i;
u8 ret = 0;
for (i = 0; i < RX_PORT_COUNT; i++) {
if (it66353_get_port_info0(i, PI_5V)) {
ret |= (1 << i);
}
}
return ret;
}
static void __maybe_unused it66353it66353_rx_handle_output_err(void)
{
#if EN_AUTO_RS
if (it66353_gdev.opts.active_rx_opt->EnableAutoEQ) {
if (it66353_gdev.vars.try_fixed_EQ) {
dev_info(g_it66353->dev, "*** fixed EQ fail\r\n");
it66353_gdev.vars.try_fixed_EQ = 0;
it66353_eq_reset_txoe_ready();
it66353_eq_reset_state();
it66353_fsm_chg(RX_CHECK_EQ);
} else {
it66353_auto_eq_adjust();
}
}
#endif
}
void it66353_rx_auto_power_down_enable(u8 port, u8 enable)
{
if (enable) {
/*
* //will auto power down D0~D2 3.3V
* it66353_h2swset(0x90 + port, 0x3D, 0x3D);
* // will not power down D0~D2 3.3V
* it66353_h2swset(0x90 + port, 0x3D, 0x1D);
*/
it66353_h2swset(0x90 + port, 0x3D, 0x1D);
} else {
it66353_h2swset(0x90 + port, 0x3D, 0x00);
}
}
static void it66353_rx_auto_power_down_enable_all(u8 enable)
{
u8 i;
for (i = 0; i < RX_PORT_COUNT; i++) {
it66353_rx_auto_power_down_enable(i, enable);
}
}
void it66353_rx_term_power_down(u8 port, u8 channel)
{
// to detect clock,
// 0x88[7][0] must be '0','0';
it66353_h2swset(0x88 + port, 0xFF, channel);
}
static void _sw_int_enable(u8 port, u8 enable)
{
if (enable) {
// Enable Switch RX Port Interrupt
it66353_h2swwr(0x30 + port * 2, 0xff);
it66353_h2swset(0x31 + port * 2, 0x01, 0x01);
} else {
// Disable Switch RX Port Interrupt
it66353_h2swwr(0x30 + port * 2, 0x00);
it66353_h2swset(0x31 + port * 2, 0x01, 0x00);
it66353_h2swwr(0x20 + port * 2, 0xff);
it66353_h2swwr(0x21 + port * 2, 0xff);
}
}
static void _sw_int_enable_all(u8 enable)
{
u8 i;
for (i = 0; i < RX_PORT_COUNT; i++) {
_sw_int_enable(i, enable);
}
}
void it66353_sw_disable_timer0(void)
{
// disable timer will also clear timer interrupt flag
it66353_h2swset(0x38, 0x02, 0x00);
}
#if EN_AUTO_RS
u8 it66353_sw_get_timer0_interrupt(void)
{
return ((it66353_h2swrd(0x28)&0x02));
}
#endif
static void _sw_config_timer1(u8 count)
{
// init timer = count[6:0] * 10 ms
// init timer = BIT7|count[6:0] * 100 ms
it66353_h2swwr(0x1D, count);
}
static void _sw_enable_timer1(void)
{
it66353_h2swset(0x38, 0x04, 0x04);
}
static void _sw_disable_timer1(void)
{
it66353_h2swset(0x38, 0x04, 0x00);
}
static u8 _sw_get_timer1_interrupt(void)
{
return ((it66353_h2swrd(0x28)&0x04));
}
static void _sw_clear_timer1_interrupt(void)
{
it66353_h2swset(0x28, 0x04, 0x04);
}
static void _sw_enable_hpd_toggle_timer(u8 timeout)
{
// init timer = count[6:0] * 10 ms
// init timer = BIT7|count[6:0] * 100 ms
_sw_config_timer1(timeout); // HPT toggle time out
_sw_clear_timer1_interrupt();
_sw_enable_timer1();
}
static void _sw_disable_hpd_toggle_timer(void)
{
_sw_disable_timer1();
}
static u8 _sw_check_hpd_toggle_timer(void)
{
return _sw_get_timer1_interrupt();
}
static void _sw_reset_scdc_monitor(void)
{
it66353_h2swwr(0xAD, 0xFF);
}
static void _sw_monitor_and_fix_scdc_write(void)
{
u8 reg;
reg = it66353_h2swrd(0xAD);
if (reg & 0x10) { // P0SCDCWrReg20hVld
dev_info(g_it66353->dev, "## src SCDC wr %02x\r\n", reg);
if (it66353_gdev.vars.current_hdmi_mode == HDMI_MODE_20) {
if ((reg&0x03) != 0x03) {
_tx_scdc_write(0x20, 0x03);
}
} else if (it66353_gdev.vars.current_hdmi_mode == HDMI_MODE_14) {
if ((reg&0x03) != 0x00) {
_tx_scdc_write(0x20, 0x00);
}
}
_sw_reset_scdc_monitor();
}
}
void it66353_sw_clear_hdcp_status(void)
{
it66353_h2swwr(0xB0, 0xC0);
}
static void _sw_sdi_check(void)
{
u8 port;
u8 reg6C, reg70;
port = it66353_gdev.vars.Rx_active_port;
if (it66353_gdev.vars.sdi_stable_count < 8) {
if (it66353_get_port_info0(port, PI_CLK_STABLE)) {
it66353_gdev.vars.sdi_stable_count++;
} else {
it66353_gdev.vars.sdi_stable_count = 0;
}
} else {
// perform check
it66353_gdev.vars.sdi_stable_count = 0;
reg6C = it66353_h2swrd(0x6c + port);
reg70 = it66353_h2swrd(0x70 + port);
if (reg70 & BIT(3)) {
reg6C = reg6C/8;
} else if (reg70 & BIT(2)) {
reg6C = reg6C/4;
} else if (reg70 & BIT(1)) {
reg6C = reg6C/2;
} else {
// reg6C = reg6C/1;
}
if (reg6C < 22) {
reg70 = it66353_h2swrd(0x61 + port * 3);
if (0 == (reg70 & BIT(1))) {
// need re-calculate RDetIPLL_HS1P48G
reg70 = 1 << port;
it66353_h2swset(0x2A, reg70, reg70);
DEBUG("check_for_sdi recheck ...\r\n");
} else {
it66353_gdev.vars.check_for_sdi = 0;
DEBUG("check_for_sdi disabled ...%02x\r\n",
it66353_h2rxrd(0x13));
}
} else {
it66353_gdev.vars.check_for_sdi = 0;
}
}
}
static void _sw_hdcp_access_enable(u8 enable)
{
if (it66353_gdev.vars.spmon == 2) {
DEBUG(" >> skip HDCP acc %d\r\n", enable);
return;
}
DEBUG(" >> HDCP acc %d\r\n", enable);
if (enable) {
it66353_h2swwr(0xAB, 0x60);
// it66353_h2swset(0x3C, 0x01, 0x00);
} else {
it66353_h2swwr(0xAB, 0x74);
// it66353_h2swset(0x3C, 0x01, 0x01);
}
}
static void _tx_init(void)
{
if (it66353_gdev.opts.dev_opt->ForceRxOn) {
// for ATC electrical test
it66353_h2swwr(0xFF, 0xC3);
it66353_h2swwr(0xFF, 0xA5);
it66353_h2swset(0xF4, 0x80, it66353_gdev.opts.dev_opt->ForceRxOn << 7);
it66353_h2swwr(0xFF, 0xFF);
}
it66353_h2swset(0x50, 0x0B, 0x08);
it66353_h2swset(0x3A, 0xC0, (1 << 7) + (0 << 6));
it66353_h2swset(0x3B, 0x03, 0); // DDC 75K
it66353_h2swset(0x43, 0xFC, (0 << 7) + (0 << 5) + (0 << 4) + (2 << 2));
it66353_h2swset(0xA9, 0xC0, (it66353_gdev.opts.tx_opt->EnTxChSwap << 7) +
(it66353_gdev.opts.tx_opt->EnTxPNSwap << 6));
// Enable HPD and RxSen Interrupt
it66353_h2swwr(0x27, 0xff);
it66353_h2swset(0x37, 0x78, 0x78);
_tx_power_down();
it66353_h2swset(0xBD, 0x01, it66353_gdev.opts.tx_opt->EnTxVCLKInv);
it66353_h2swset(0xA9, 0x20, it66353_gdev.opts.tx_opt->EnTxOutD1t << 5);
it66353_h2swset(0x50, 0x03, it66353_gdev.vars.Rx_active_port);
it66353_enable_tx_port(1);
}
static void _tx_reset(void)
{
DEBUG("TX Reset\r\n");
it66353_h2swset(0x09, 0x01, 0x01); // RegSoftTxVRst=1
it66353_h2swset(0x09, 0x01, 0x00); // RegSoftTxVRst=0
// Enable TX DDC Master Reset
it66353_h2swset(0x3B, 0x10, 0x10); // DDC Master Reset
it66353_h2swset(0x3B, 0x10, 0x00);
_tx_init();
}
static void _rx_init(void)
{
// Add RX initial option setting here
it66353_h2rxset(0x34, 0x01, 0x01); // Reg_AutoRCLK=1 (default)
it66353_h2rxset(0x21, 0x40, 0x40); // Reg_AutoEDIDRst=1
it66353_h2rxwr(0x3A, 0xCB); // to reduce RxDeskew Err and Chx LagErr
it66353_h2rxset(0x3B, 0x20, 0x20); // CED_Opt
it66353_h2swset(0x44, 0x08, 0x00);
it66353_h2rxset(0x29, 0x40, 0x00);
it66353_h2rxset(0x3C, 0x01, 0x00);
// it66353_h2rxset(0x3d, 0x02, 0x02); // Reg_deskewdown = 1
}
void it66353_rx_reset(void)
{
DEBUG("RX Reset\r\n");
it66353_h2rxset(0x29, 0x40, 0x00);
it66353_h2swset(0x44, 0x08, 0x08);
it66353_h2rxwr(0x23, 0x01); // SWRst=1
// it66353_h2rxwr(0x22, 0x08); // RegRst=1
it66353_h2rxwr(0x23, 0xAF);
msleep(1);
it66353_h2rxwr(0x23, 0xA0);
_rx_init();
}
static void _sw_init(void)
{
u8 port;
// H2SW Initial Setting
it66353_h2swset(0x44, 0x03, RCLKFreqSel);
msleep(1);
it66353_init_rclk();
// Enable Slave Address
it66353_h2swwr(0xEF, it66353_gdev.opts.dev_opt->RxAddr | 0x01);
#if EN_CEC
if (it66353_gdev.opts.EnCEC) {
// if CEC is enabled, we should have a accurate RCLK.
u16 cec_timer_unit;
it66353_h2swwr(0xEE, (it66353_gdev.opts.dev_opt->CecAddr | 0x01));
it66353_cecset(0x08, 0x01, 0x01);
cec_timer_unit = it66353_gdev.vars.RCLK / (16*10);
Cec_Init(0xff & cec_timer_unit);
} else
#endif
{
u8 tmp;
it66353_h2swwr(0xEE, (it66353_gdev.opts.dev_opt->CecAddr | 0x01));
// it66353_cecset(0x0d, 0x10, 0x00); // Disable CEC_IOPU
tmp = 0x40;
cecAddr_updata_bit(0x10, 0xff, tmp); // Disable CEC_IOPU
it66353_h2swwr(0xEE, (it66353_gdev.opts.dev_opt->CecAddr & 0xFE));
}
it66353_h2swset(0x44, 0x40, 0x00); // EnRxPort2Pwd=0
msleep(10);
it66353_rx_caof_init(it66353_gdev.vars.Rx_active_port);
// Setup INT Pin: Active Low & Open-Drain
it66353_h2swset(0x11, 0x07, 0x03);
// Enable SW Interrupt
it66353_h2swset(0x37, 0xE0, 0xE0);
it66353_h2swset(0x38, 0xF9, 0xF9);
// enable non main port to power down
it66353_h2swset(0x15, 0x08, 0 << 3);
it66353_h2swset(0x2B, 0x02, 0x00);
it66353_h2swset(0x2C, 0xC0, 0xC0);
it66353_h2swset(0x50, 0xf0, 0x00);
it66353_h2swset(0xC4, 0x08, 0x08);
it66353_h2swset(0xC5, 0x08, 0x08);
it66353_h2swset(0xC6, 0x08, 0x08);
// P0~P3 auto power downs
#if 0
it66353_rx_auto_power_down_enable_all(1);
#else
it66353_rx_auto_power_down_enable_all(it66353_gdev.opts.dev_opt->RxAutoPowerDown);
it66353_rx_term_power_down(RX_PORT_0, 0);
it66353_rx_term_power_down(RX_PORT_1, 0);
it66353_rx_term_power_down(RX_PORT_2, 0);
it66353_rx_term_power_down(RX_PORT_3, 0);
#endif
it66353_h2swset(0xF5, 0xE0,
(it66353_gdev.opts.active_rx_opt->EnRxDDCBypass << 7)+
(it66353_gdev.opts.active_rx_opt->EnRxPWR5VBypass << 6)+
(it66353_gdev.opts.active_rx_opt->EnRxHPDBypass << 5));
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == 1) {
it66353_h2swset(0x3C, 0x01, 0x01);// disable DDCRegen by set RegTxMastersel=1
it66353_h2swset(0xb3, 0x20, 0x20);
_rx_edid_ram_disable(RX_PORT_0);
_rx_edid_ram_disable(RX_PORT_1);
_rx_edid_ram_disable(RX_PORT_2);
_rx_edid_ram_disable(RX_PORT_3);
} else {
// config EDID RAM
for (port = 0; port < RX_PORT_COUNT; port++) {
if (it66353_gdev.opts.rx_opt[port]->DisableEdidRam) {
_rx_edid_ram_disable(port);
_rx_edid_address_disable(port);
} else {
_rx_edid_ram_enable(port);
_rx_edid_address_enable(port);
}
}
}
if (it66353_gdev.opts.active_rx_opt->EnRxHPDBypass) {
it66353_h2swset(0x4c, 0x40, 0x00);
it66353_h2swset(0x4d, 0x40, 0x00);
it66353_h2swset(0x4e, 0x40, 0x00);
}
// disable EDID read/write to clear P0AutoH2Mode and AutoScrbEn
it66353_h2swset(0xB2, 0x60, 0x00);
// it66353_h2swset(0xB2, 0x40, 0x00);
// enable TX port latch ERROR count
it66353_h2swset(0xAC, 0x11, 0x11);
// ddc monitor
it66353_h2swwr(0xB0, 0x80);
}
static void _sw_reset(void)
{
DEBUG("Switch Reset\r\n");
it66353_h2swwr(0xEF, it66353_gdev.opts.dev_opt->RxAddr | 0x01);
it66353_h2swset(0x0A, 0x01, 0x01); // SoftRstAll=1
if (it66353_h2swrd(0xEF) == (it66353_gdev.opts.dev_opt->RxAddr | 0x01)) {
it66353_h2swset(0x44, 0xA0, 0x80);// ForceWrUpd = 1 and SWGateRCLK = 0
}
// it66353_h2swset(0x0A, 0x02, 0x02); // SoftSWRRst=1
_sw_init();
}
// To have accurate RCLK,
// we should use "it66353_cal_rclk" instead of "it66353_init_rclk"
#if EN_CEC
static void it66353_cal_rclk(void)
{
u8 i;
u8 timer_int, timer_flt, wclk_high_ext;
u32 wclk_valid_num, wclk_high_num, wclk_high_num_b, wclk_high_num_c;
u32 sum, rclk_tmp, rclk, rddata;
sum = 0;
for (i = 0; i < 5; i++) {
it66353_h2swset(0x11, 0x80, 0x80);
msleep(99);
it66353_h2swset(0x11, 0x80, 0x00);
rddata = it66353_h2swrd(0x12);
rddata += (it66353_h2swrd(0x13) << 8);
rddata += (it66353_h2swrd(0x14) << 16);
sum += rddata;
}
sum /= 5;
rclk = sum / 100;
DEBUG("RCLK=%d kHz\r\n\r\n", rclk);
timer_int = rclk / 1000;
timer_flt = (rclk - timer_int * 1000) * 256 / 1000;
it66353_h2swwr(0x1E, timer_int);
it66353_h2swwr(0x1F, timer_flt);
rclk_tmp = (rclk) * (1 << RCLKFreqSel);
wclk_valid_num = (8UL * rclk_tmp + 625) / 1250UL;
wclk_high_num = (8 * rclk_tmp + 3125) / 6250UL;
it66353_h2swset(0x2C, 0x3F, (u8)wclk_high_num & 0xFF);
it66353_h2swwr(0x2D, (u8)wclk_valid_num & 0xFF);
wclk_high_num_b = 32UL * rclk_tmp / (37125UL);
wclk_high_num = 32UL * rclk_tmp - (wclk_high_num_b * 37125UL);
wclk_high_ext = wclk_high_num * 2 / 37125UL;
it66353_h2swwr(0x2E, (wclk_high_ext << 6) + ((u8)wclk_high_num_b));
wclk_high_num_c = 4UL * rclk_tmp / 10625UL;
wclk_high_num = 4UL * rclk_tmp - (wclk_high_num_c * 10625UL);
wclk_high_ext = wclk_high_num * 4 / 10625UL;
it66353_h2swwr(0x2F, (wclk_high_ext << 6) + ((u8)wclk_high_num_c));
it66353_gdev.vars.RCLK = rclk;
}
#endif
void it66353_init_rclk(void)
{
#if EN_CEC
if (it66353_gdev.opts.EnCEC) {
it66353_cal_rclk();
} else
#endif
{
#if 0
// RCLK=20000 kHz
it66353_h2swwr(0x1e, 0x14);
it66353_h2swwr(0x1f, 0x00);
it66353_h2swset(0x2c, 0x3f, 0x1a);
it66353_h2swwr(0x2d, 0x80);
it66353_h2swwr(0x2e, 0x11);
it66353_h2swwr(0x2f, 0x87);
it66353_gdev.vars.RCLK = 20000;
#else
#if 0
// RCLK=19569 kHz
it66353_h2swwr(0x1e, 0x13);
it66353_h2swwr(0x1f, 0x91);
it66353_h2swset(0x2c, 0x3f, 0x19);
it66353_h2swwr(0x2d, 0x7d);
it66353_h2swwr(0x2e, 0x50);
it66353_h2swwr(0x2f, 0x47);
it66353_gdev.vars.RCLK = 19569;
#endif
// RCLK=18562 kHz
it66353_h2swwr(0x1e, 0x12);
it66353_h2swwr(0x1f, 0x90);
it66353_h2swset(0x2c, 0x3f, 0x18);
it66353_h2swwr(0x2d, 0x77);
it66353_h2swwr(0x2e, 0x10);
it66353_h2swwr(0x2f, 0xc6);
it66353_gdev.vars.RCLK = 18562;
#endif
}
}
u8 it66353_get_port_info0(u8 port, u8 info)
{
u8 tmp;
tmp = it66353_h2swrd(0x60 + port * 3);
if ((tmp & info) == info)
return 1;
else
return 0;
}
void it66353_enable_tx_port(u8 enable)
{
it66353_h2swset(0x50, 0x08, (enable << 3));
}
void it66353_txoe(u8 enable)
{
if (it66353_gdev.vars.current_txoe == enable) {
DEBUG(" >> it66353_txoe return %d \r\n", enable);
return;
}
DEBUG("TXOE=%d align=%d\r\n", enable, it66353_gdev.opts.active_rx_opt->TxOEAlignment);
if (enable) {
if (it66353_gdev.vars.current_hdmi_mode == HDMI_MODE_20) {
_tx_ovwr_hdmi_mode(HDMI_MODE_20);
_tx_scdc_write(0x20, 0x03);
} else if (it66353_gdev.vars.current_hdmi_mode == HDMI_MODE_14) {
_tx_ovwr_hdmi_mode(HDMI_MODE_14);
_tx_scdc_write(0x20, 0x00); // todo: ? check if safe to send this?
}
it66353_h2swset(0xD4, 0x03, 0x01); // Set DRV_RST='0'
it66353_h2swset(0xD4, 0x03, 0x00); // Set DRV_RST='0'
// REPORT_TXOE_1();
} else {
// REPORT_TXOE_0();
it66353_h2swset(0xD4, 0x07, 0x03); // Set DRV_RST='1'
}
it66353_gdev.vars.current_txoe = enable;
}
static void it66353_auto_txoe(u8 enable)
{
DEBUG("A_TXOE=%d align=%d\r\n", enable, it66353_gdev.opts.active_rx_opt->TxOEAlignment);
if (enable) {
it66353_h2swset(0xEB, 0x07, 0x02); // output when data ready
// it66353_h2swset(0xEB, 0x07, 0x07); // output when clock ready
//[7]Reg_GateTxOut, [5]Disoutdeskew, [1]Reg_EnTxDly
it66353_h2swset(0xEA, 0xA2, 0x00);
it66353_h2swset(0xEB, 0x10, 0x00); //[4]RegEnTxDODeskew_doneDly
} else {
it66353_h2swset(0xEB, 0x03, 0x01);
}
}
void it66353_set_tx_5v(u8 output_value)
{
if (it66353_gdev.vars.Tx_current_5v != output_value) {
it66353_gdev.vars.Tx_current_5v = output_value;
DEBUG("TX 5V output=%d\r\n", output_value);
}
if (output_value) {
it66353_h2swset(0xF4, 0x0C, 0x0C); // TXPWR5V=1
} else {
it66353_h2swset(0xF4, 0x0C, 0x08); // TXPWR5V=0
}
}
static u32 it66353_get_rx_vclk(u8 port)
{
u32 tmds_clk;
#if USING_WDOG
u16 tmds_clk_speed;
u8 wdog_clk_div;
u8 sw_reg20;
if (port >= RX_PORT_COUNT) {
dev_err(g_it66353->dev, "it66353_get_rx_vclk p=%u\r\n", port);
return 0;
}
_rx_wdog_rst(it66353_gdev.vars.Rx_active_port);
__RETRY_VCLK:
wdog_clk_div = it66353_h2swrd(0x70 + port) & 0x07;
if (wdog_clk_div & 0x04)
wdog_clk_div = 8;
else if (wdog_clk_div & 0x02)
wdog_clk_div = 4;
else if (wdog_clk_div & 0x01)
wdog_clk_div = 2;
else
wdog_clk_div = 1;
tmds_clk_speed = it66353_h2swrd(0x6C + port);
sw_reg20 = it66353_h2swrd(0x20 + port * 2);
if (sw_reg20 & 0x7C) {
dev_err(g_it66353->dev, "it66353_get_rx_vclk sw_reg20=%02x\r\n",
sw_reg20);
tmds_clk_speed = ((tmds_clk_speed * 2) >> (RCLKFreqSel));
tmds_clk = it66353_gdev.vars.RCLK * 256 * wdog_clk_div / tmds_clk_speed;
dev_err(g_it66353->dev,
"RXP%d WatchDog detect TMDSCLK = %lu kHz (div=%d, 6C=%02x)\r\n",
port, tmds_clk, wdog_clk_div, tmds_clk_speed);
tmds_clk_speed = 0;
it66353_h2swwr(0x20 + port * 2, sw_reg20);
goto __RETRY_VCLK;
}
if (tmds_clk_speed) {
tmds_clk_speed = ((tmds_clk_speed * 2) >> (RCLKFreqSel));
tmds_clk = it66353_gdev.vars.RCLK * 256 * wdog_clk_div / tmds_clk_speed;
DEBUG("RXP%d WatchDog detect TMDSCLK = %lu kHz (div=%d, 6C=%02x)\r\n",
port, tmds_clk, wdog_clk_div, tmds_clk_speed);
} else {
dev_err(g_it66353->dev, "TMDSCLKSpeed == 0 p=%u\r\n", port);
tmds_clk = 0;
}
#else
u8 clk;
if (port >= RX_PORT_COUNT) {
dev_err(g_it66353->dev, "it66353_get_rx_vclk p=%u\r\n", port);
return 0;
}
clk = it66353_h2swrd(0x61 + port*3);
// the assigned tmds_clk value should refer to _tx_setup_afe()
if (clk & 0x04) {
DEBUG("RXP%d clock > 340M\r\n", port);
tmds_clk = 340000UL;
} else if (clk & 0x02) {
DEBUG("RXP%d clock > 148M\r\n", port);
tmds_clk = 163000UL;
} else if (clk & 0x01) {
DEBUG("RXP%d clock > 100M\r\n", port);
tmds_clk = 148500UL;
} else {
DEBUG("RXP%d clock < 100M\r\n", port);
tmds_clk = 74000UL;
}
#endif
return tmds_clk;
}
static void it66353_detect_port(u8 port)
{
u8 sw_reg20;
u8 sw_reg21;
u8 rddata;
u8 sts_off0;
sw_reg20 = it66353_h2swrd(0x20 + port * 2);
sw_reg21 = it66353_h2swrd(0x21 + port * 2) & 0x01;
if (sw_reg20) {
sts_off0 = 0x60 + port * 3;
rddata = it66353_h2swrd(sts_off0);
if (sw_reg20 & 0x01) {
DEBUG("--RXP-%d 5V Chg => 5V = %d\r\n", port, (rddata & 0x01));
if (it66353_gdev.vars.Rx_active_port != port) {
if ((rddata & 0x01)) {
// 5V presents
if (it66353_gdev.opts.rx_opt[port]->NonActivePortReplyHPD) {
_rx_set_hpd(port, 1, TERM_FOLLOW_HPD);
sw_reg20 &= 0x01;
} else {
_rx_set_hpd(port, 0, TERM_FOLLOW_HPD);
}
} else {
_rx_set_hpd(port, 0, TERM_FOLLOW_HPD);
}
}
}
if (sw_reg20 & 0x02) {
DEBUG("--RXP-%d RX Clock Valid Chg => RxCLK_Valid = %d\r\n",
port, (rddata & 0x08) >> 3);
}
if (sw_reg20 & 0x04) {
DEBUG("--RXP-%d RX Clock Stable Chg => RxCLK_Stb = %d\r\n\r\n",
port, (rddata & 0x10) >> 4);
}
if (sw_reg20 & 0x08) {
DEBUG("--RXP-%d RX Clock Frequency Change ...\r\n", port);
}
sts_off0 = 0x61 + port * 3;
rddata = it66353_h2swrd(sts_off0);
if (sw_reg20 & 0x10) {
DEBUG("--RXP-%d RX Clock Ratio Chg => Clk_Ratio = %d \r\n",
port, (rddata & 0x40) >> 6);
}
if (sw_reg20 & 0x20) {
DEBUG("--RXP%d RX Scrambling Enable Chg => Scr_En = %d \r\n",
port, (rddata & 0x80) >> 7);
}
sts_off0 = 0x62 + port * 3;
rddata = it66353_h2swrd(sts_off0);
if (sw_reg20 & 0x40) {
DEBUG("--RXP%d RX Scrambling Status Chg => ScrbSts = %d \r\n",
port, (rddata & 0x02) >> 1);
}
if (sw_reg20 & 0x80) {
DEBUG("--RXP%d RX HDMI2 Detected Interrupt => HDMI2DetSts = %d \r\n",
port, (rddata & 0x3C) >> 2);
}
it66353_h2swwr(0x20 + port * 2, sw_reg20);
}
if (sw_reg21) {
it66353_h2swwr(0x21 + port * 2, sw_reg21);
#if 1
if (sw_reg21 & 0x01) {
DEBUG("--RXP%d EDID Bus Hang\r\n", port);
}
#endif
}
}
static void it66353_detect_ports(void)
{
u8 i;
for (i = 0; i < 4; i++) {
if (it66353_gdev.vars.Rx_active_port != i) {
it66353_detect_port(i);
}
}
}
static void it66353_rx_irq(void)
{
u8 rddata, hdmi_int;
u8 rx_reg05, rx_reg06, rx_reg10;
rddata = it66353_h2rxrd(0x96);
hdmi_int = (rddata & 0x40) >> 6;
if (hdmi_int) {
rx_reg05 = it66353_h2rxrd(0x05);
rx_reg06 = it66353_h2rxrd(0x06);
rx_reg10 = it66353_h2rxrd(0x10);
it66353_h2rxwr(0x05, rx_reg05);
it66353_h2rxwr(0x06, rx_reg06);
if (rx_reg05 & 0x01) {
DEBUG("..RX5V change\r\n");
it66353_eq_reset_txoe_ready();
it66353_eq_reset_state();
it66353_auto_detect_hdmi_encoding();
if (it66353_gdev.opts.active_rx_opt->TryFixedEQFirst) {
it66353_gdev.vars.try_fixed_EQ = 1;
}
if (0 == _rx_is_5v_active()) {
it66353_fsm_chg_delayed(RX_UNPLUG);
}
}
if (rx_reg05 & 0x10) {
DEBUG("..RX HDMIMode chg => HDMIMode = %d\r\n",
(it66353_h2rxrd(0x13) & 0x02) >> 1);
}
if (rx_reg05 & 0x40) {
DEBUG("..RX DeSkew Err\r\n");
it66353_gdev.vars.rx_deskew_err++;
if (it66353_gdev.vars.rx_deskew_err > 50) {
it66353_gdev.vars.rx_deskew_err = 0;
it66353_toggle_hpd(1000);
}
}
if (rx_reg05 & 0x80) {
DEBUG("..RXP H2V FIFO Skew Fail\r\n");
}
if (rx_reg06 & 0x01) {
u8 symlock = ((it66353_h2rxrd(0x13) & 0x80) >> 7);
DEBUG("..RX CHx SymLock Chg => RxSymLock = %d\r\n", symlock);
if (symlock) {
// it66353_gdev.vars.count_fsm_err = 0;
}
}
if (rx_reg06 & 0x02) {
DEBUG("..RX CH0 SymFIFORst\r\n");
}
if (rx_reg06 & 0x04) {
DEBUG("..RX CH1 SymFIFORst\r\n");
}
if (rx_reg06 & 0x08) {
DEBUG("..RX CH2 SymFIFORst\r\n");
}
if (rx_reg06 & 0x10) {
DEBUG("..RX CH0 SymLockRst\r\n");
}
if (rx_reg06 & 0x20) {
DEBUG("..RX CH1 SymLockRst\r\n");
}
if (rx_reg06 & 0x40) {
DEBUG("..RX CH2 SymLockRst\r\n");
}
if (rx_reg06 & 0x80) {
DEBUG("..RX FSM Fail\r\n");
it66353_gdev.vars.count_fsm_err++;
if (it66353_gdev.vars.count_fsm_err > 20) {
if (it66353_gdev.opts.active_rx_opt->FixIncorrectHdmiEnc) {
it66353_fix_incorrect_hdmi_encoding();
}
it66353_eq_reset_txoe_ready();
it66353_eq_reset_state();
it66353_fsm_chg(RX_WAIT_CLOCK);
it66353_gdev.vars.count_fsm_err = 0;
}
} else {
if (it66353_gdev.vars.count_fsm_err > 0) {
it66353_gdev.vars.count_fsm_err--;
}
}
#if EN_H14_SKEW
{
u8 rx_reg07;
rx_reg07 = it66353_h2rxrd(0x07);
it66353_h2rxwr(0x07, rx_reg07);
if (rx_reg07 & 0x01) {
DEBUG("..RX CH0 Lag Err\r\n");
it66353_rx_skew_adj(0);
}
if (rx_reg07 & 0x02) {
DEBUG("..RX CH1 Lag Err\r\n");
it66353_rx_skew_adj(1);
}
if (rx_reg07 & 0x04) {
DEBUG("..RX CH2 Lag Err\r\n");
it66353_rx_skew_adj(2);
}
}
#endif
if (rx_reg10 & 0x08) {
it66353_h2rxwr(0x10, 0x08);
DEBUG("..RX FW Timer Interrupt ...\r\n");
}
}
}
static void it66353_sw_irq(u8 port)
{
u8 sw_reg20;
u8 sw_reg21;
u8 rddata;
u8 sts_off0;
sw_reg20 = it66353_h2swrd(0x20 + port * 2);
sw_reg21 = it66353_h2swrd(0x21 + port * 2) & 0x01;
if (sw_reg20 || sw_reg21) {
it66353_h2swwr(0x20 + port * 2, sw_reg20);
it66353_h2swwr(0x21 + port * 2, sw_reg21);
sts_off0 = 0x60 + port * 3;
if (sw_reg20 & 0x01) {
// not here
rddata = it66353_h2swrd(sts_off0);
DEBUG("..RX-P%d PWR5V Chg => PWR5V = %d\r\n", port, (rddata & 0x01));
// _rx_wdog_rst(port);
if (0 == (rddata & 0x01)) {
it66353_fsm_chg_delayed(RX_UNPLUG);
}
}
if (sw_reg20 & 0x02) {
rddata = it66353_h2swrd(sts_off0);
DEBUG("..RXP%d RX Clock Valid Chg => RxCLK_Valid = %d\r\n",
port, (rddata & 0x08) >> 3);
if (port == it66353_gdev.vars.Rx_active_port) {
if (0 == (rddata & 0x08)) { // clock not valid
DBG_TM(CLK_UNSTABLE);
if (it66353_gdev.vars.RxHPDFlag[it66353_gdev.vars.Rx_active_port] > 0) {
it66353_fsm_chg_delayed(RX_WAIT_CLOCK);
}
} else {
DBG_TM(CLK_STABLE);
}
}
}
if (sw_reg20 & 0x04) {
msleep(10);
rddata = it66353_h2swrd(sts_off0);
DEBUG("..RXP%d RX Clock Stable Chg => RxCLK_Stb = %d\r\n\r\n",
port, (rddata & 0x10) >> 4);
if (0 == (rddata & 0x10)) {
DBG_CLKSTABLE_0();
DBG_SYMLOCK_0();
if (it66353_gdev.vars.RxHPDFlag[port]) {
it66353_fsm_chg_delayed(RX_WAIT_CLOCK);
}
} else {
it66353_gdev.vars.vclk = it66353_get_rx_vclk(it66353_gdev.vars.Rx_active_port);
if ((it66353_gdev.vars.vclk != it66353_gdev.vars.vclk_prev)) {
it66353_gdev.vars.vclk_prev = it66353_gdev.vars.vclk;
if (it66353_gdev.vars.RxHPDFlag[port]) {
it66353_fsm_chg_delayed(RX_WAIT_CLOCK);
}
}
}
}
if (sw_reg20 & 0x08) {
DEBUG("..RXP%d RX Clock Frequency Chg ...\r\n", port);
}
if (sw_reg20 & 0x10) {
u8 new_ratio = (it66353_h2swrd(0x61 + port * 3) & 0x40) >> 6;
DEBUG("..RXP%d RX Clock Ratio Chg => Clk_Ratio = %d \r\n",
port, new_ratio);
if (it66353_gdev.vars.Rx_active_port == port) {
if (new_ratio > 0) {
it66353_auto_txoe(it66353_gdev.opts.active_rx_opt->TxOEAlignment);
} else {
it66353_auto_txoe(0);
}
it66353_txoe(1);
if (new_ratio != it66353_gdev.vars.clock_ratio) {
// it66353_auto_detect_hdmi_encoding();
// it66353_fsm_chg_delayed(RX_WAIT_CLOCK);
}
}
}
if (sw_reg20 & 0x20) {
DEBUG("..RXP%d RX Scrambling Enable Chg => Scr_En = %d \r\n",
port, (it66353_h2swrd(0x61 + port * 3) & 0x80) >> 7);
}
if (sw_reg20 & 0x40) {
u8 new_scramble = (it66353_h2swrd(0x62 + port * 3) & 0x02) >> 1;
DEBUG("..RXP%d RX Scrambling Status Chg => ScrbSts = %d \r\n",
port, new_scramble);
if (it66353_gdev.vars.Rx_active_port == port) {
if (new_scramble != it66353_gdev.vars.h2_scramble) {
// it66353_fsm_chg_delayed(RX_WAIT_CLOCK);
}
}
}
if (sw_reg20 & 0x80) {
DEBUG("..RXP%d RX HDMI2 Detected Interrupt => HDMI2DetSts = %d \r\n",
port, (it66353_h2swrd(0x62 + port * 3) & 0x3C) >> 2);
}
if (sw_reg21 & 0x01) {
DEBUG("..RXP%d EDID Bus Hang\r\n", port);
}
}
}
static void it66353_tx_irq(void)
{
u8 sw_reg27;
u8 sw_reg28;
u8 rddata;
u8 reg3C;
sw_reg27 = it66353_h2swrd(0x27);
sw_reg28 = it66353_h2swrd(0x28) & ~(0x02|0x04);
it66353_h2swwr(0x27, sw_reg27);
it66353_h2swwr(0x28, sw_reg28);
if (sw_reg27 & 0x08) {
// dev_info(g_it66353->dev, " => HDCP 0x74 is detected\r\n");
}
if (sw_reg27 & 0x10) {
DEBUG(" => HDCP 0x74 NOACK\r\n");
}
if (sw_reg27 & 0x20) {
rddata = it66353_h2swrd(0x11);
if ((rddata & 0x20)) {
DEBUG(" => HPD High\r\n");
} else {
DEBUG(" => HPD Low\r\n");
if (it66353_gdev.vars.state_sys_fsm != RX_TOGGLE_HPD &&
it66353_gdev.vars.state_sys_fsm != RX_UNPLUG) {
it66353_fsm_chg_delayed(TX_UNPLUG);
}
}
}
if (sw_reg27 & 0x40) {
DEBUG(" TX RxSen chg\r\n");
if (it66353_h2swrd(0x11) & 0x40) {
// rxsen = 1
} else {
// rxsen = 0
// _rx_int_enable_all(0);
// _rx_set_hpd_all(0);
// it66353_fsm_chg(TX_WAIT_HPD);
}
}
if (sw_reg27 & 0x80) {
// DEBUG(" TX DDC Bus Hang\r\n");
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
reg3C = it66353_h2swrd(0x3C);
it66353_h2swset(0x3C, 0x01, 0x01);
it66353_h2swwr(0x3D, 0x0A); // Generate SCL Clock
it66353_h2swwr(0x3C, reg3C);
}
}
if (sw_reg28 & 0x02) {
// dev_info(g_it66353->dev, "SW User Timer 0 Interrupt ...\r\n");
}
if (sw_reg28 & 0x04) {
// dev_info(g_it66353->dev, "SW User Timer 1 Interrupt ...\r\n");
}
if (sw_reg28 & 0x08) {
// DEBUG(" TX DDC Command Fail\r\n");
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
reg3C = it66353_h2swrd(0x3C);
it66353_h2swset(0x3C, 0x01, 0x01);
it66353_h2swwr(0x3D, 0x0F);
it66353_h2swwr(0x3C, reg3C);
}
}
if (sw_reg28 & 0x80) {
DEBUG(" TX DDC FIFO Error\r\n");
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
reg3C = it66353_h2swrd(0x3C);
it66353_h2swset(0x3C, 0x01, 0x01);
it66353_h2swwr(0x3D, 0x09);
it66353_h2swwr(0x3C, reg3C);
}
}
}
static void it66353_wait_for_ddc_idle(void)
{
u8 ddc_sts;
u8 idle_cnt = 0;
u8 busy_cnt = 0;
u8 chk_dly = 3;
while (1) {
ddc_sts = it66353_h2swrd(0xB3);
if ((ddc_sts & 0x10)) {
busy_cnt = 0;
idle_cnt++;
chk_dly++;
if (idle_cnt >= 5) {
break;
}
} else {
busy_cnt++;
idle_cnt = 0;
chk_dly = 3;
msleep(100);
if (busy_cnt > 10) {
dev_err(g_it66353->dev, "**Wait DDC idle timeout\n");
break;
}
}
msleep(chk_dly);
}
}
#if DEBUG_FSM_CHANGE
void __it66353_fsm_chg(u8 new_state, int caller)
#else
void it66353_fsm_chg(u8 new_state)
#endif
{
#if DEBUG_FSM_CHANGE
if (new_state <= IDLE && it66353_gdev.vars.state_sys_fsm <= IDLE) {
DEBUG("state_fsm %s -> %s (%d)\r\n",
s__SYS_FSM_STATE[it66353_gdev.vars.state_sys_fsm],
s__SYS_FSM_STATE[new_state], caller);
} else {
dev_err(g_it66353->dev, "state_fsm %d, new %d -> %d\r\n",
it66353_gdev.vars.state_sys_fsm, new_state, caller);
}
#else
DEBUG("state_fsm %d -> %d\r\n", it66353_gdev.vars.state_sys_fsm, new_state);
#endif
if (RX_PORT_CHANGE != new_state) {
if (it66353_gdev.vars.state_sys_fsm == new_state) {
DEBUG("skip fsm chg 1\r\n");
return;
}
}
if (new_state == RX_WAIT_CLOCK) {
if (it66353_gdev.vars.RxHPDFlag[it66353_gdev.vars.Rx_active_port] == 0) {
// don't change before HPD High
DEBUG("skip fsm chg 2\r\n");
return;
}
}
it66353_gdev.vars.state_sys_fsm = new_state;
it66353_gdev.vars.fsm_return = 0;
switch (it66353_gdev.vars.state_sys_fsm) {
case RX_TOGGLE_HPD:
_sw_enable_hpd_toggle_timer(it66353_gdev.vars.hpd_toggle_timeout);
break;
case RX_PORT_CHANGE:
it66353_txoe(0);
DBG_TM(RX_SWITCH_PORT);
DEBUG("Active port change from P%d to P%d\r\n",
it66353_gdev.vars.Rx_active_port, it66353_gdev.vars.Rx_new_port);
if (it66353_gdev.vars.clock_ratio > 0 &&
it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
_tx_scdc_write(0x20, 0x00);
}
if (it66353_gdev.opts.tx_opt->TurnOffTx5VWhenSwitchPort) {
it66353_set_tx_5v(0);
}
// _rx_int_enable(it66353_gdev.vars.Rx_active_port, 1);
// _rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0);
// _rx_wdog_rst(it66353_gdev.vars.Rx_prev_port);
// make HPD low to stop DDC traffic
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_HPD);
// wait 200ms for DDC traffic stopped
msleep(200);
// set it66353_gdev.vars.force_hpd_state to SW_HPD_AUTO
// this to reset the force hpd low in previous active port
// remove this line if you want to keep HPD low after port changing
it66353_gdev.vars.force_hpd_state = SW_HPD_AUTO;
it66353_gdev.vars.Rx_active_port = it66353_gdev.vars.Rx_new_port;
it66353_wait_for_ddc_idle();
it66353_h2swset(0x50, 0x03, it66353_gdev.vars.Rx_active_port);
it66353_set_RS(it66353_gdev.opts.active_rx_opt->DefaultEQ[0],
it66353_gdev.opts.active_rx_opt->DefaultEQ[1],
it66353_gdev.opts.active_rx_opt->DefaultEQ[2]);
it66353_gdev.EQ.sys_aEQ = SysAEQ_RUN;
it66353_auto_detect_hdmi_encoding();
it66353_eq_reset_state();
it66353_eq_reset_txoe_ready();
break;
case TX_OUTPUT:
it66353_gdev.vars.count_symlock_lost = 0;
it66353_gdev.vars.count_symlock_unstable = 0;
_sw_disable_hpd_toggle_timer();
if ((it66353_gdev.opts.active_rx_opt->FixIncorrectHdmiEnc) &&
(it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == FALSE)) {
if (it66353_gdev.vars.current_hdmi_mode == HDMI_MODE_20) {
_tx_scdc_write(0x20, 0x03);
} else if (it66353_gdev.vars.current_hdmi_mode == HDMI_MODE_14) {
// todo: to check sink support SCDC ?
_tx_scdc_write(0x20, 0x00);
}
it66353_auto_detect_hdmi_encoding();
}
_sw_hdcp_access_enable(1);
it66353_txoe(1);
if (it66353_gdev.vars.spmon == 1) {
if ((it66353_gdev.opts.active_rx_opt->DisableEdidRam &
(1 << it66353_gdev.vars.Rx_active_port)) == 0) {
_rx_edid_ram_enable(it66353_gdev.vars.Rx_active_port);
}
}
break;
case TX_OUTPUT_PREPARE:
it66353_gdev.vars.count_try_force_hdmi_mode = 0;
// it66353_auto_txoe(1);
it66353_h2rxwr(0x05, 0xFF);
it66353_h2rxwr(0x06, 0xFF);
it66353_h2rxwr(0x07, 0xFF);
break;
#if EN_AUTO_RS
case RX_CHECK_EQ:
it66353_gdev.vars.count_symlock_fail = 0;
// _sw_hdcp_access_enable(0);
break;
#endif
case SETUP_AFE:
// it66353_gdev.vars.en_count_hdcp = 1;
// it66353_gdev.vars.tick_set_afe = get_tick_count();
it66353_rx_term_power_down(it66353_gdev.vars.Rx_active_port, 0x00);
it66353_gdev.vars.vclk = it66353_get_rx_vclk(it66353_gdev.vars.Rx_active_port);
if (it66353_gdev.vars.vclk) {
it66353_gdev.vars.clock_ratio =
((it66353_h2swrd(0x61 + it66353_gdev.vars.Rx_active_port * 3) >> 6) & 1);
dev_dbg(g_it66353->dev, "Clk Ratio = %d\r\n",
it66353_gdev.vars.clock_ratio);
if (it66353_gdev.vars.clock_ratio > 0) {
if (it66353_gdev.vars.vclk < 300000UL) {
it66353_gdev.vars.vclk = 300001UL;
}
// CED opt for HDBaseT disabled
it66353_h2rxset(0x3B, 0x10, 0x00);
} else {
if (it66353_gdev.vars.vclk >= 300000UL) {
it66353_gdev.vars.vclk = 297000UL;
}
// CED opt for HDBaseT enabled
it66353_h2rxset(0x3B, 0x10, 0x10);
}
#if 0 // for 8-7 480p
if (it66353_gdev.vars.vclk < 35000UL) {
dev_dbg("## ATC 480P\r\n");
// it66353_h2rxset(0x3c, 0x01, 0x00);
it66353_h2swset(0x2b, 0x02, 0x00);
} else {
// it66353_h2rxset(0x3c, 0x01, 0x01);
it66353_h2swset(0x2b, 0x02, 0x02);
}
#endif
_tx_power_on();
_rx_setup_afe(it66353_gdev.vars.vclk);
_tx_setup_afe(it66353_gdev.vars.vclk);
if (it66353_gdev.vars.clock_ratio == 0) {
it66353_auto_txoe(0);
dev_dbg(g_it66353->dev, "Clk Ratio==0, align=0\n");
} else {
it66353_auto_txoe(it66353_gdev.opts.active_rx_opt->TxOEAlignment);
dev_dbg(g_it66353->dev, "Clk Ratio==1, align=%d\n",
it66353_gdev.opts.active_rx_opt->TxOEAlignment);
}
it66353_txoe(1);
}
break;
case RX_WAIT_CLOCK:
it66353_txoe(0);
if (it66353_gdev.opts.dev_opt->TxPowerDownWhileWaitingClock) {
_tx_power_down();
}
it66353_sw_disable_timer0();
it66353_sw_clear_hdcp_status();
// _rx_wdog_rst(it66353_gdev.vars.Rx_active_port);
#if EN_AUTO_RS
it66353_gdev.vars.RxCEDErrRec[1][0] = 0xffff;
it66353_gdev.vars.RxCEDErrRec[1][1] = 0xffff;
it66353_gdev.vars.RxCEDErrRec[1][2] = 0xffff;
it66353_gdev.EQ.manu_eq_fine_tune_count[0] = 0;
it66353_gdev.EQ.manu_eq_fine_tune_count[1] = 0;
it66353_gdev.EQ.manu_eq_fine_tune_count[2] = 0;
it66353_gdev.EQ.ced_err_avg_prev[0] = 0x8888;
it66353_gdev.EQ.ced_err_avg_prev[1] = 0x8888;
it66353_gdev.EQ.ced_err_avg_prev[2] = 0x8888;
it66353_gdev.EQ.ced_acc_count = 0;
#endif
it66353_gdev.vars.count_symlock = 0;
it66353_gdev.vars.count_unlock = 0;
it66353_gdev.vars.check_for_hpd_toggle = 0;
it66353_gdev.vars.sdi_stable_count = 0;
it66353_gdev.vars.check_for_sdi = 1;
it66353_gdev.vars.rx_deskew_err = 0;
break;
case RX_HPD:
_rx_int_enable();
#if 1
// it66353it66353_rx_ovwr_hdmi_clk(it66353_gdev.vars.Rx_active_port, HDMI_MODE_14);
// it66353it66353_rx_ovwr_h20_scrb(it66353_gdev.vars.Rx_active_port, 0);
#else
it66353it66353_rx_ovwr_hdmi_clk(it66353_gdev.vars.Rx_active_port, RX_CLK_H20);
it66353it66353_rx_ovwr_h20_scrb(it66353_gdev.vars.Rx_active_port, 1);
#endif
// it66353_gdev.vars.Rx_prev_port = it66353_gdev.vars.Rx_active_port;
// _rx_int_enable_all(0);
// _rx_set_hpd_all(0);
_sw_hdcp_access_enable(1);
_sw_int_enable(it66353_gdev.vars.Rx_active_port, 1);
// _rx_set_hpd(it66353_gdev.vars.Rx_active_port, 1);
_rx_wdog_rst(it66353_gdev.vars.Rx_active_port);
if (it66353_gdev.vars.spmon == 1) {
if ((it66353_gdev.opts.active_rx_opt->DisableEdidRam &
(1<<it66353_gdev.vars.Rx_active_port)) == 0) {
_rx_edid_ram_disable(it66353_gdev.vars.Rx_active_port);
}
}
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 1, TERM_FOLLOW_HPD);
if (it66353_gdev.vars.is_hdmi20_sink == 0) {
it66353_auto_txoe(0);
} else {
it66353_auto_txoe(it66353_gdev.opts.active_rx_opt->TxOEAlignment);
}
it66353_txoe(1);
break;
case TX_GOT_HPD:
it66353_txoe(0);
// _tx_power_on();
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
it66353_setup_edid_ram(0);
it66353_gdev.vars.edid_ready = 1;
}
#if EN_CEC
if (it66353_gdev.opts.EnCEC) {
u8 u8phyAB = (it66353_gdev.vars.PhyAdr[0] << 4) | (it66353_gdev.vars.PhyAdr[1] & 0xF);
u8 u8phyCD = (it66353_gdev.vars.PhyAdr[2] << 4) | (it66353_gdev.vars.PhyAdr[3] & 0xF);
CecSys_Init(u8phyAB, u8phyCD, it66353_gdev.vars.Rx_active_port);
}
#endif
if (it66353_gdev.opts.active_rx_opt->NonActivePortReplyHPD) {
_rx_set_hpd_with_5v_all(true);
}
break;
case TX_WAIT_HPD:
it66353_txoe(0);
it66353_auto_txoe(0);
if (_rx_is_5v_active()) {
it66353_set_tx_5v(1);
} else {
it66353_set_tx_5v(0);
}
// it66353_set_RS(10);
break;
case TX_UNPLUG:
it66353_txoe(0);
it66353_auto_txoe(0);
it66353_gdev.vars.edid_ready = 0;
// _rx_int_enable(it66353_gdev.vars.Rx_active_port, 0);
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_TX);
// _rx_set_hpd_all(0);
it66353_h2swset(0xB2, 0x0A, 0x0A); // W1C AutoH2Mode and AutoScrbEn
_tx_power_down();
break;
case RX_UNPLUG:
it66353_txoe(0);
it66353_auto_txoe(0);
// _rx_int_enable(it66353_gdev.vars.Rx_active_port, 0);
if (it66353_gdev.vars.force_hpd_state == SW_HPD_LOW) {
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_HPD);
} else {
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_TX);
}
it66353_set_tx_5v(_rx_is_5v_active());
// it66353_gdev.EQ.sys_aEQ = SysAEQ_RUN;
it66353_h2swset(0xB2, 0x0A, 0x0A); // W1C AutoH2Mode and AutoScrbEn
_tx_power_down();
break;
default:
break;
}
}
#if DEBUG_FSM_CHANGE
void __it66353_fsm_chg2(u8 new_state, int caller)
#else
void it66353_fsm_chg_delayed(u8 new_state)
#endif
{
#if DEBUG_FSM_CHANGE
if (new_state <= IDLE && it66353_gdev.vars.state_sys_fsm <= IDLE) {
dev_dbg(g_it66353->dev, "#state_fsm %s -> %s (%d)\r\n",
s__SYS_FSM_STATE[it66353_gdev.vars.state_sys_fsm],
s__SYS_FSM_STATE[new_state], caller);
} else {
dev_err(g_it66353->dev, "#state_fsm %d, new %d -> %d\r\n",
it66353_gdev.vars.state_sys_fsm, new_state, caller);
}
#else
dev_dbg(g_it66353->dev, "#state_fsm %d -> %d\r\n",
it66353_gdev.vars.state_sys_fsm, new_state);
#endif
it66353_fsm_chg(new_state);
it66353_gdev.vars.fsm_return = 1;
}
static void _rx_pll_reset(void)
{
it66353_h2swset(0x06+it66353_gdev.vars.Rx_active_port, 0x01, 0x01);
msleep(2);
it66353_h2swset(0x06+it66353_gdev.vars.Rx_active_port, 0x01, 0x00);
}
void it66353_auto_detect_hdmi_encoding(void)
{
_rx_ovwr_hdmi_mode(it66353_gdev.vars.Rx_active_port, HDMI_MODE_AUTO);
_tx_ovwr_hdmi_mode(HDMI_MODE_AUTO);
it66353_gdev.vars.current_hdmi_mode = HDMI_MODE_AUTO;
// _rx_pll_reset();
dev_info(g_it66353->dev, "HDMI_MODE=AUTO \r\n");
}
void it66353_force_hdmi20(void)
{
_rx_ovwr_hdmi_mode(it66353_gdev.vars.Rx_active_port, HDMI_MODE_20);
_tx_ovwr_hdmi_mode(HDMI_MODE_20);
it66353_gdev.vars.current_hdmi_mode = HDMI_MODE_20;
// _rx_pll_reset();
dev_info(g_it66353->dev, "HDMI_MODE=F20\r\n");
}
static void it66353_force_hdmi14(void)
{
_rx_ovwr_hdmi_mode(it66353_gdev.vars.Rx_active_port, HDMI_MODE_14);
_tx_ovwr_hdmi_mode(HDMI_MODE_14);
it66353_gdev.vars.current_hdmi_mode = HDMI_MODE_14;
// _rx_pll_reset();
dev_info(g_it66353->dev, "HDMI_MODE=F14\r\n");
}
void it66353_fix_incorrect_hdmi_encoding(void)
{
switch (it66353_gdev.vars.current_hdmi_mode) {
case HDMI_MODE_AUTO:
// try HDMI 2.0
it66353_force_hdmi20();
break;
case HDMI_MODE_20:
// try HDMI 1.4
it66353_force_hdmi14();
break;
case HDMI_MODE_14:
// try HDMI 2.0
it66353_auto_detect_hdmi_encoding();
break;
default:
// try HDMI 2.0
it66353_auto_detect_hdmi_encoding();
break;
}
_rx_pll_reset();
}
#if EN_AUTO_RS
static void it66353_fsm_EQ_check(void)
{
static u8 aeq_retry;
u8 eq_state;
if (it66353_rx_is_clock_stable()) {
_rx_show_ced_info();
if (it66353_eq_get_txoe_ready() == 1) {
it66353_eq_load_previous();
dev_info(g_it66353->dev, "EQ restore2 !\r\n");
// it66353_fsm_chg(TX_OUTPUT);
it66353_fsm_chg(TX_OUTPUT_PREPARE);
} else {
eq_state = it66353_eq_get_state();
dev_info(g_it66353->dev, "[%d] eq_state=%d\r\n",
__LINE__, (int)eq_state);
if (eq_state == SysAEQ_RUN) {
it66353_eq_set_txoe_ready(0);
if (it66353_auto_eq_adjust()) {
it66353_gdev.vars.check_for_hpd_toggle = 1;
it66353_eq_set_state(SysAEQ_DONE);
it66353_fsm_chg(TX_OUTPUT_PREPARE);
dev_info(g_it66353->dev, "EQ done !\r\n");
} else {
aeq_retry++;
if (aeq_retry > 5) {
aeq_retry = 0;
it66353_gdev.vars.check_for_hpd_toggle = 1;
it66353_eq_set_state(SysAEQ_DONE);
it66353_fsm_chg(TX_OUTPUT_PREPARE);
dev_info(g_it66353->dev, "EQ give up !\r\n");
}
}
} else {
if (eq_state == SysAEQ_DONE) {
it66353_eq_load_previous();
it66353_fsm_chg(TX_OUTPUT_PREPARE);
dev_info(g_it66353->dev, "EQ restore !\r\n");
} else if (eq_state == SysAEQ_OFF) {
it66353_eq_load_default();
it66353_fsm_chg(TX_OUTPUT_PREPARE);
dev_info(g_it66353->dev, "EQ default !\r\n");
} else {
dev_err(g_it66353->dev, "??eq_state=%d\r\n", eq_state);
}
}
}
} else {
it66353_fsm_chg(RX_WAIT_CLOCK);
}
}
#endif
static void it66353_fsm(void)
{
static __maybe_unused u8 prep_count;
static __maybe_unused u8 prep_fail_count;
// LOOP_FSM:
switch (it66353_gdev.vars.state_sys_fsm) {
case RX_TOGGLE_HPD:
if ((it66353_gdev.opts.active_rx_opt->NonActivePortReplyHPD == 0) &&
(it66353_gdev.opts.tx_opt->TurnOffTx5VWhenSwitchPort == 0)) {
_sw_disable_hpd_toggle_timer();
it66353_fsm_chg(RX_UNPLUG);
} else {
if (_sw_check_hpd_toggle_timer()) {
_sw_disable_hpd_toggle_timer();
it66353_fsm_chg(RX_UNPLUG);
} else {
// keep waiting hpd toggle
}
}
break;
case RX_PORT_CHANGE:
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_HPD);
it66353_rx_reset();
it66353_rx_caof_init(it66353_gdev.vars.Rx_active_port);
_rx_pll_reset();
it66353_gdev.vars.hpd_toggle_timeout = it66353_gdev.opts.active_rx_opt->HPDTogglePeriod;
it66353_fsm_chg(RX_TOGGLE_HPD);
break;
case TX_OUTPUT:
// if (it66353_gdev.opts.WaitSymlockBeforeTXOE)
{
if (0 == it66353_rx_is_all_ch_symlock()) {
if (0 == _rx_is_any_ch_symlock()) {
it66353_gdev.vars.count_symlock_lost++;
dev_err(g_it66353->dev,
"RX Symlock lost %d\r\n",
it66353_gdev.vars.count_symlock_lost);
if (it66353_gdev.vars.count_symlock_lost == 100) {
_rx_pll_reset();
it66353_toggle_hpd(1000);
// it66353_set_tx_5v(0);
// it66353_gdev.vars.count_symlock_lost = 0;
// it66353it66353_rx_handle_output_err();
}
} else {
it66353_gdev.vars.count_symlock_unstable++;
dev_err(g_it66353->dev,
"RX Symlock unstable %d\r\n",
it66353_gdev.vars.count_symlock_unstable);
if (it66353_gdev.vars.count_symlock_unstable > 8) {
_rx_pll_reset();
// it66353_fsm_chg(RX_WAIT_CLOCK);
it66353_toggle_hpd(1000);
}
}
} else {
it66353_gdev.vars.count_symlock_lost = 0;
it66353_gdev.vars.count_symlock_unstable = 0;
}
}
if (it66353_rx_monitor_ced_err()) {
it66353it66353_rx_handle_output_err();
}
// _sw_show_hdcp_status();
if (it66353_gdev.opts.active_rx_opt->FixIncorrectHdmiEnc) {
/*
* check if source send incorrect SCDC clock ratio
* after 66353 sent.
*/
if (it66353_gdev.vars.current_hdmi_mode !=
HDMI_MODE_AUTO && it66353_gdev.opts.active_rx_opt->EnRxDDCBypass ==
false) {
_sw_monitor_and_fix_scdc_write();
}
}
if (it66353_gdev.vars.check_for_sdi) {
_sw_sdi_check();
}
// _tx_show_sink_ced();
// _pr_port_info(it66353_gdev.vars.Rx_active_port);
break;
case TX_OUTPUT_PREPARE:
#if EN_AUTO_RS
// check symbol lock before tx output
if (0 == _rx_is_any_ch_symlock()) {
dev_err(g_it66353->dev, "RxChk-SymUnlock\r\n");
it66353_gdev.vars.count_symlock_fail++;
if (it66353_gdev.vars.count_symlock_fail > 3) {
it66353_gdev.vars.count_symlock_fail = 0;
// can not get any channel symbol lock,
// the HDMI encoding may be incorrect
if (it66353_gdev.opts.active_rx_opt->FixIncorrectHdmiEnc) {
if (it66353_gdev.vars.count_try_force_hdmi_mode < 6) {
it66353_gdev.vars.count_try_force_hdmi_mode++;
it66353_fix_incorrect_hdmi_encoding();
} else {
it66353_gdev.vars.count_try_force_hdmi_mode = 0;
it66353_fsm_chg(RX_WAIT_CLOCK);
}
} else {
it66353_eq_reset_state();
it66353_eq_reset_txoe_ready();
it66353_fsm_chg(RX_CHECK_EQ);
}
}
} else {
it66353_eq_set_txoe_ready(1);
if ((it66353_gdev.vars.check_for_hpd_toggle == 1) &&
(it66353_gdev.vars.current_txoe == 0) &&
(_rx_need_hpd_toggle())) {
DBG_TM(AEQ_TOGGLE_HPD);
it66353_set_tx_5v(0);
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_HPD);
it66353_fsm_chg(RX_TOGGLE_HPD);
} else {
#if CHECK_INT_BEFORE_TXOE
u8 reg05 = it66353_h2rxrd(0x05);
u8 reg06 = it66353_h2rxrd(0x06);
u8 reg07 = it66353_h2rxrd(0x07);
if (reg05 == 0 && reg06 == 0 && reg07 == 0) {
prep_count++;
} else {
dev_err(g_it66353->dev,
"RX reg: 05=%02x, 06=%02x 07=%02x\r\n",
reg05, reg06, reg07);
it66353_h2rxwr(0x05, reg05);
it66353_h2rxwr(0x06, reg06);
it66353_h2rxwr(0x07, reg07);
prep_count = 0;
prep_fail_count++;
}
if (prep_count == 1) {
_sw_hdcp_access_enable(0);
}
if (prep_count >= 4) {
prep_count = 0;
it66353_fsm_chg(TX_OUTPUT);
} else {
if (prep_fail_count > 20) {
prep_fail_count = 0;
it66353_fsm_chg(RX_WAIT_CLOCK);
}
}
#else
it66353_fsm_chg(TX_OUTPUT);
#endif
}
}
#endif
break;
case RX_CHECK_EQ:
#if EN_AUTO_RS
it66353_fsm_EQ_check();
#endif
break;
case SETUP_AFE:
prep_count = 0;
prep_fail_count = 0;
if (it66353_gdev.vars.vclk == 0) {
it66353_fsm_chg(RX_WAIT_CLOCK);
} else {
#if EN_AUTO_RS
if (it66353_gdev.vars.try_fixed_EQ) {
it66353_eq_set_txoe_ready(1);
// it66353_fsm_chg(TX_OUTPUT);
it66353_fsm_chg(TX_OUTPUT_PREPARE);
} else {
if (it66353_gdev.opts.active_rx_opt->EnableAutoEQ) {
it66353_fsm_chg(RX_CHECK_EQ);
} else {
it66353_eq_set_txoe_ready(1);
// it66353_fsm_chg(TX_OUTPUT);
it66353_fsm_chg(TX_OUTPUT_PREPARE);
}
}
#else
it66353_eq_set_txoe_ready(1);
it66353_fsm_chg(TX_OUTPUT);
// it66353_fsm_chg(TX_OUTPUT_PREPARE);
#endif
}
break;
case RX_WAIT_CLOCK:
if (it66353_rx_is_clock_stable()) {
it66353_rx_clear_ced_err();
// _sw_enable_txoe_timer_check();
// _sw_hdcp_access_enable(0);
it66353_fsm_chg(SETUP_AFE);
} else {
if (it66353_gdev.vars.RxHPDFlag[it66353_gdev.vars.Rx_active_port] == 0) {
it66353_fsm_chg(RX_UNPLUG);
}
if (it66353_gdev.vars.current_hdmi_mode != HDMI_MODE_AUTO) {
it66353_gdev.vars.count_wait_clock++;
if (it66353_gdev.vars.count_wait_clock > 100) {
it66353_gdev.vars.count_wait_clock = 0;
it66353_auto_detect_hdmi_encoding();
it66353_fsm_chg(RX_UNPLUG);
}
}
}
break;
case RX_HPD:
it66353_fsm_chg(RX_WAIT_CLOCK);
break;
case TX_GOT_HPD:
it66353_fsm_chg(RX_HPD);
// scdcwr(0x30, 0x01);
break;
case TX_WAIT_HPD:
if (0 == _rx_is_5v_active()) {
it66353_fsm_chg_delayed(RX_UNPLUG);
}
if (_tx_is_sink_hpd_high()) {
it66353_fsm_chg(TX_GOT_HPD);
}
break;
case TX_UNPLUG:
if (_rx_is_5v_active()) {
it66353_fsm_chg_delayed(TX_WAIT_HPD);
} else {
it66353_fsm_chg_delayed(RX_UNPLUG);
}
break;
case RX_UNPLUG:
if (_rx_is_5v_active()) {
if (it66353_gdev.vars.force_hpd_state == SW_HPD_LOW) {
break;
}
if (it66353_gdev.vars.state_sys_fsm != RX_TOGGLE_HPD) {
it66353_fsm_chg_delayed(TX_WAIT_HPD);
}
} else {
if (_rx_get_all_port_5v()) {
// it66353_fsm_chg2(TX_WAIT_HPD);
}
}
break;
case IDLE:
break;
}
if (it66353_gdev.vars.fsm_return == 0) {
it66353_gdev.vars.fsm_return = 1;
// goto LOOP_FSM;
}
}
static void it66353_irq(void)
{
u8 sys_int_sts;
u8 currBD = 0;
it66353_detect_ports();
if (it66353_gdev.vars.state_sys_fsm == RX_TOGGLE_HPD) {
return;
}
// static u8 prevBD = 1;
currBD = it66353_h2swrd(0xBD);
// if (currBD != prevBD) {
if (currBD & 0xe0) {
// it66353_gdev.vars.tick_hdcp = get_tick_count();
dev_info(g_it66353->dev, "---HDCP BD=%02x\r\n", currBD);
// prevBD = currBD;
it66353_sw_clear_hdcp_status();
/*
* if (currBD & 0x10) {
* if (prevBD) {
* prevBD = 0;
* _sw_hdcp_access_enable(0);
* } else {
* _sw_hdcp_access_enable(1);
* }
* }
*/
}
sys_int_sts = it66353_h2swrd(0x0C);
if (sys_int_sts == 0x00) {
return;
}
if (sys_int_sts & 0x01) {
it66353_rx_irq();
}
if (sys_int_sts & 0x10) {
it66353_sw_irq(it66353_gdev.vars.Rx_active_port);
it66353_tx_irq();
}
#if EN_CEC
if (it66353_gdev.opts.EnCEC && (sys_int_sts & 0x80)) {
Cec_Irq();
}
if (it66353_gdev.opts.EnCEC) {
CecSys_TxHandler();
CecSys_RxHandler();
}
#endif
}
bool it66353_device_init(void)
{
u8 i;
bool init_done = 0;
switch (it66353_gdev.vars.state_dev_init) {
case 0:
DBG_CLKSTABLE_0();
DBG_SYMLOCK_0();
it66353_set_tx_5v(0);
_sw_reset();
_rx_set_hpd_all(0);
it66353_rx_reset();
_tx_reset();
it66353_txoe(0);
it66353_sw_disable_timer0();
_sw_disable_timer1();
// _sw_config_timer1(50);
// _sw_enable_timer1();
// config default RS
it66353_set_RS(it66353_gdev.opts.active_rx_opt->DefaultEQ[0],
it66353_gdev.opts.active_rx_opt->DefaultEQ[1],
it66353_gdev.opts.active_rx_opt->DefaultEQ[2]);
if (it66353_gdev.opts.tx_opt->CopyEDIDFromSink) {
it66353_set_tx_5v(1);
it66353_gdev.vars.state_dev_init = 1;
it66353_gdev.vars.hpd_wait_count = 0;
} else {
it66353_gdev.vars.state_dev_init = 2;
}
break;
case 1:
if (_tx_is_sink_hpd_high()) {
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
if (it66353_setup_edid_ram(0)) {
it66353_gdev.vars.edid_ready = 1;
}
it66353_gdev.vars.state_dev_init = 3;
} else {
it66353_gdev.vars.edid_ready = 1;
it66353_gdev.vars.state_dev_init = 3;
}
} else {
it66353_gdev.vars.hpd_wait_count++;
if (it66353_gdev.vars.hpd_wait_count > 200) {
// it66353_gdev.vars.state_dev_init = 2;
it66353_gdev.vars.hpd_wait_count = 0;
dev_info(g_it66353->dev, "waiting HPD...\r\n");
}
// it66353_set_tx_5v(_rx_is_5v_active());
}
break;
case 2:
// load FW default EDID
dev_info(g_it66353->dev, "Using internal EDID...\r\n");
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == false) {
it66353_gdev.vars.default_edid[0] = it66353_s_default_edid_port0;
it66353_gdev.vars.default_edid[1] = it66353_s_default_edid_port0;
it66353_gdev.vars.default_edid[2] = it66353_s_default_edid_port0;
it66353_gdev.vars.default_edid[3] = it66353_s_default_edid_port0;
// note:
// The EDID can be different from each port.
// please set default_edid[?] pointer to a desired EDID array
// if you need different EDID.
//
// for example:
// it66353_gdev.vars.default_edid[1] = s_default_edid_port1;
// it66353_gdev.vars.default_edid[2] = s_default_edid_port2;
// it66353_gdev.vars.default_edid[3] = s_default_edid_port3;
for (i = 0; i < RX_PORT_COUNT; i++) {
it66353_setup_edid_ram(i);
}
}
it66353_gdev.vars.edid_ready = 1;
it66353_gdev.vars.state_dev_init = 3;
break;
case 3:
_sw_int_enable_all(1);
_rx_set_hpd_with_5v_all(1);
dev_info(g_it66353->dev, "Active port = P%d\r\n",
it66353_gdev.vars.Rx_active_port);
it66353_set_tx_5v(_rx_is_5v_active());
init_done = 1;
break;
default:
it66353_gdev.vars.state_dev_init = 0;
break;
}
return init_done;
}
void it66353_vars_init(void)
{
// FW Variables init:
it66353_gdev.vars.state_dev_init = 0;
it66353_gdev.vars.VSDBOffset = 0xFF;
it66353_gdev.vars.PhyAdr[0] = 0;
it66353_gdev.vars.PhyAdr[1] = 0;
it66353_gdev.vars.PhyAdr[2] = 0;
it66353_gdev.vars.PhyAdr[3] = 0;
it66353_gdev.vars.RxHPDFlag[0] = -1;
it66353_gdev.vars.RxHPDFlag[1] = -1;
it66353_gdev.vars.RxHPDFlag[2] = -1;
it66353_gdev.vars.RxHPDFlag[3] = -1;
it66353_gdev.vars.Tx_current_5v = -1;
it66353_gdev.vars.count_eq_check = 0;
it66353_gdev.vars.count_fsm_err = 0;
it66353_gdev.vars.count_unlock = 0;
it66353_gdev.vars.state_sys_fsm = RX_UNPLUG;
it66353_gdev.EQ.AutoEQ_state = AEQ_OFF;
it66353_gdev.EQ.sys_aEQ = SysAEQ_RUN;
it66353_gdev.vars.edid_ready = 0;
it66353_gdev.vars.current_txoe = 0xFF;
it66353_gdev.vars.check_for_hpd_toggle = 0;
it66353_gdev.vars.sdi_stable_count = 0;
it66353_gdev.vars.check_for_sdi = 1;
it66353_gdev.vars.force_hpd_state = SW_HPD_AUTO; // 1 : auto, don't modify here
it66353_gdev.vars.vclk_prev = 0;
if (it66353_gdev.opts.active_rx_opt->TryFixedEQFirst) {
it66353_gdev.vars.try_fixed_EQ = 1;
}
it66353_gdev.vars.current_hdmi_mode = HDMI_MODE_AUTO;
it66353_gdev.vars.rx_deskew_err = 0;
it66353_eq_reset_state();
it66353_eq_reset_txoe_ready();
it66353_dump_opts();
}
#if CHECK_DEV_PRESENT
static bool it66353_is_device_lost(void)
{
u8 vendor_id[2] = { 0 };
vendor_id[0] = it66353_h2swrd(0x00);
vendor_id[1] = it66353_h2swrd(0x01);
if (vendor_id[0] == 0x54 && vendor_id[1] == 0x49) {
return FALSE;
}
return TRUE;
}
#endif
static bool it66353_is_device_ready(void)
{
u8 vendor_id[2] = { 0 };
vendor_id[0] = it66353_h2swrd(0x00);
vendor_id[1] = it66353_h2swrd(0x01);
if (vendor_id[0] == 0x54 && vendor_id[1] == 0x49) {
vendor_id[0] = 0;
vendor_id[1] = 0;
vendor_id[1] = it66353_h2swrd(0x03);
if (vendor_id[1] == 0x66) {
vendor_id[0] = it66353_h2swrd(0x02);
if (vendor_id[0] == 0x35) {
it66353_gdev.vars.Rev = it66353_h2swrd(0x04);
dev_info(g_it66353->dev, "Find 6635 %02x !! \r\n",
(int)it66353_gdev.vars.Rev);
return TRUE;
}
} else if (vendor_id[1] == 0x35) {
vendor_id[0] = it66353_h2swrd(0x04);
if (vendor_id[0] == 0x66) {
it66353_gdev.vars.Rev = it66353_h2swrd(0x05);
dev_info(g_it66353->dev, "Find 6635x %02x !! \r\n",
(int)it66353_gdev.vars.Rev);
return TRUE;
}
}
}
dev_info(g_it66353->dev, "Find 6635 fail !!\r\n");
return FALSE;
}
bool it66353_read_edid(u8 block, u8 offset, int length, u8 *edid_buffer)
{
bool result = false;
int off = block * 128 + offset;
u8 reg3C;
int retry = 0;
offset = off % 256;
reg3C = it66353_h2swrd(0x3C);
__RETRY:
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swset(0x38, 0x08, 0x08); // Enable DDC Command Fail Interrupt
it66353_h2swset(0x37, 0x80, 0x80); // Enable DDC Bus Hang Interrupt
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0xA0); // EDID Address
it66353_h2swwr(0x3F, offset); // EDID Offset
it66353_h2swwr(0x40, length); // Read ByteNum[7:0]
it66353_h2swwr(0x41, block/2); // EDID Segment
if (_tx_is_sink_hpd_high()) {
it66353_h2swwr(0x3D, 0x03); // EDID Read Fire
if (_tx_ddcwait()) {
it66353_h2swbrd(0x42, length, edid_buffer);
result = true;
} else {
dev_err(g_it66353->dev, "ERROR: DDC EDID Read Fail !!!\r\n");
if (retry > 0) {
retry--;
msleep(100);
goto __RETRY;
}
}
} else {
dev_err(g_it66353->dev,
"Abort EDID read becasue of detecting unplug !!!\r\n");
}
it66353_h2swwr(0x3C, reg3C); // restore PC DDC Mode
return result;
}
/**
* it66353_read_one_block_edid - will read 128 byte EDID data
*
* @block: EDID block number. (0,1,2 or 3)
*
* @edid_buffer: 128 byte EDID data buffer to store data.
*
* Read 128 byte EDID data from assigned block.
*/
bool it66353_read_one_block_edid(u8 block, u8 *edid_buffer)
{
u8 offset;
u8 i;
u8 read_len = 16;
offset = 0;
for (i = 0; i < 128 / read_len; i++) {
if (it66353_read_edid(block, offset, read_len, edid_buffer)) {
edid_buffer += read_len;
offset += read_len;
continue;
} else {
dev_err(g_it66353->dev,
"ERROR: read edid block 0, offset %d, length %d fail.\r\r\n",
(int)offset, (int)read_len);
return false;
}
}
return true;
}
static void it66353_parse_edid_for_vsdb(u8 *edid)
{
int off;
u8 tag;
u8 len;
// to find HDMI2.0 VSDB in EDID
if (edid[0] == 0x02) { // CTA ext tag
off = 4;
while (off < 0x100) {
tag = (edid[off] >> 5) & 0x7;
len = (edid[off] & 0x1F) + 1;
if (tag == 0x03) { // VSDB
if ((edid[off+1] == 0xD8) &&
(edid[off+2] == 0x5D) &&
(edid[off+3] == 0xC4)) {
it66353_gdev.vars.is_hdmi20_sink = 1;
break;
}
}
if (len == 0)
break;
off += len;
}
}
dev_info(g_it66353->dev, "HDMI2 sink=%d\n", it66353_gdev.vars.is_hdmi20_sink);
}
/**
* it66353_parse_edid_for_phyaddr - parse necessary data for RX
* EDID
*
* @edid: 128 byte EDID data buffer that contains HDMI CEA ext
*
* Before set RX EDID, must call it66353_parse_edid_cea_ext to
* initialize some variables.
*/
void it66353_parse_edid_for_phyaddr(u8 *edid)
{
int off;
u8 tag;
u8 len;
// to find VSDB in EDID
if (edid[0] == 0x02) { // CTA ext tag
off = 4;
while (off < 0x100) {
tag = (edid[off] >> 5) & 0x7;
len = (edid[off] & 0x1F) + 1;
if (tag == 0x03) { // VSDB
if ((edid[off + 1] == 0x03) && (edid[off + 2] == 0x0C) &&
(edid[off + 3] == 0x00)) {
it66353_gdev.vars.VSDBOffset = ((u8)off) + 0x80 + 4;
it66353_gdev.vars.PhyAdr[0] = (edid[off + 4] >> 4) & 0xF;
it66353_gdev.vars.PhyAdr[1] = edid[off + 4] & 0xF;
it66353_gdev.vars.PhyAdr[2] = (edid[off + 5] >> 4) & 0xF;
it66353_gdev.vars.PhyAdr[3] = edid[off + 5] & 0xF;
it66353_gdev.vars.EdidChkSum[1] =
(0x100 - edid[0x7F] -
edid[off + 4] -
edid[off + 5]) & 0xFF;
break;
}
}
if (len == 0)
break;
off += len;
}
}
}
static void it66353_setup_edid_ram_step2(u8 block)
{
int i;
u8 wcount = 16;
u8 phyAB, phyCD;
u8 mask;
u16 sum;
dev_info(g_it66353->dev, "Set RX EDID step2...\r\n");
if (block == 0) {
for (i = 0; i < 4; i++) {
_rx_edid_set_chksum(i, it66353_gdev.vars.EdidChkSum[0]);
}
}
if (block == 1) {
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr | 0x01);
it66353_h2rxset(0x4C, 0x0F, 0x0F);
it66353_h2swwr(0xe9, it66353_gdev.vars.VSDBOffset); // VSDB_Offset
dev_info(g_it66353->dev, "VSDB=%02x\r\n", it66353_gdev.vars.VSDBOffset);
// fill 0xF, if there is no zero address
if (it66353_gdev.vars.PhyAdr[0] && it66353_gdev.vars.PhyAdr[1] &&
it66353_gdev.vars.PhyAdr[2] && it66353_gdev.vars.PhyAdr[3]) {
it66353_gdev.vars.PhyAdr[0] = 0xF;
it66353_gdev.vars.PhyAdr[1] = 0xF;
it66353_gdev.vars.PhyAdr[2] = 0xF;
it66353_gdev.vars.PhyAdr[3] = 0xF;
}
phyAB = (it66353_gdev.vars.PhyAdr[0] << 4) | (it66353_gdev.vars.PhyAdr[1] & 0xF);
phyCD = (it66353_gdev.vars.PhyAdr[2] << 4) | (it66353_gdev.vars.PhyAdr[3] & 0xF);
for (i = 0; i < 4; i++) {
it66353_h2swwr(0xd9 + i * 2, phyAB); // Port0 VSDB_AB
it66353_h2swwr(0xda + i * 2, phyCD); // Port0 VSDB_CD
}
for (i = 0; i < 4; i++) {
if (it66353_gdev.vars.PhyAdr[i] == 0) {
mask = 0xF0 >> (4 * (i & 0x01));
for (wcount = 0; wcount < 4; wcount++) {
phyAB = wcount + 1;
if (mask == 0xF0) {
phyAB = phyAB << 4;
}
it66353_h2swset(0xd9 + wcount * 2 + i / 2,
mask, phyAB);
}
break;
}
}
for (i = 0; i < 4; i++) {
phyAB = it66353_h2swrd(0xd9 + i * 2); // Port(i) VSDB_AB
phyCD = it66353_h2swrd(0xda + i * 2); // Port(i) VSDB_CD
// Port(i) Block1_ChkSum
sum = (0x100 - it66353_gdev.vars.EdidChkSum[1] - phyAB - phyCD) & 0xFF;
it66353_h2swwr(0xe2 + i * 2, (u8)sum);
// if (it66353_gdev.vars.Rev >= 0xC) {
#if 0
switch (i) {
case 0:
mask = 1 << 1;
break;
case 1:
mask = 1 << 2;
break;
case 2:
mask = 1 << 0;
break;
case 3:
mask = 1 << 3;
break;
default:
mask = 1 << 3;
break;
}
#endif
mask = 1 << i;
it66353_h2rxset(0x4C, 0x0F, mask);
it66353_h2rxedidwr(it66353_gdev.vars.VSDBOffset, &phyAB, 1);
it66353_h2rxedidwr(it66353_gdev.vars.VSDBOffset + 1, &phyCD, 1);
phyAB = (u8)sum;
it66353_h2rxedidwr(128 + 127, &phyAB, 1);
// }
}
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr);
}
}
static void it66353_ddc_abort(void)
{
u8 reg3C = it66353_h2swrd(0x3C);
u8 i, j, uc;
it66353_h2swset(0x3C, 0x01, 0x01);
for (i = 0; i < 2; i++) {
it66353_h2swwr(0x3D, 0x0F);
for (j = 0; j < 50; j++) {
uc = it66353_h2swrd(0x1B);
if (uc & 0x80) {
// DDC_FW_Stus_DONE
break;
}
if (uc & 0x38) {
// DDC has something error
dev_err(g_it66353->dev, "ERROR: DDC 0x1B=%02X\r\n", uc);
break;
}
msleep(1);
}
}
it66353_h2swwr(0x3D, 0x0A);
it66353_h2swwr(0x3D, 0x09);
it66353_h2swwr(0x3C, reg3C);
}
static bool it66353_update_edid(u8 block, u8 *edid_buf, u8 flag)
{
u8 i;
bool ret;
u8 retry = 0;
__RETRY_EDID_READ:
if (it66353_gdev.opts.tx_opt->CopyEDIDFromSink) {
ret = it66353_read_one_block_edid(block, edid_buf);
if (false == ret) {
dev_err(g_it66353->dev, "ERROR: read edid block 0\r\n");
if (retry < 3) {
retry++;
it66353_ddc_abort();
goto __RETRY_EDID_READ;
}
}
} else {
u8 *def_edid = it66353_gdev.vars.default_edid[flag];
if (def_edid) {
for (i = 0; i < 128; i++) {
edid_buf[i] = def_edid[i+block*128];
}
ret = true;
} else {
ret = false;
}
}
return ret;
}
bool it66353_setup_edid_ram(u8 flag)
{
u8 edid_tmp[128];
u8 extblock;
u8 i;
it66353_gdev.vars.spmon = 0;
it66353_gdev.vars.is_hdmi20_sink = 0;
if (false == it66353_update_edid(0, edid_tmp, flag)) {
goto __err_exit;
}
if ((edid_tmp[0x08] == 0x5A) &&
(edid_tmp[0x09] == 0x63) &&
(edid_tmp[0x0a] == 0x32) &&
(edid_tmp[0x0b] == 0x0e)) {
it66353_gdev.vars.spmon = 1;
}
if ((edid_tmp[0x71] == 0x4C) &&
(edid_tmp[0x72] == 0x47) &&
(edid_tmp[0x74] == 0x54) &&
(edid_tmp[0x75] == 0x56) &&
(edid_tmp[0x7F] == 0x63)) {
it66353_gdev.vars.spmon = 2;
}
if ((edid_tmp[0x60] == 0x48) &&
(edid_tmp[0x61] == 0x4C) &&
(edid_tmp[0x63] == 0x32) &&
(edid_tmp[0x64] == 0x37) &&
(edid_tmp[0x65] == 0x36) &&
(edid_tmp[0x66] == 0x45) &&
(edid_tmp[0x67] == 0x38) &&
(edid_tmp[0x68] == 0x56)) {
it66353_gdev.vars.spmon = 3;
}
// read Ext block no
extblock = edid_tmp[0x7E];
it66353_gdev.vars.EdidChkSum[0] = edid_tmp[0x7F];
#if FIX_EDID_FOR_ATC_4BLOCK_CTS
if (extblock > 1) {
edid_tmp[0x7E] = 1;
it66353_gdev.vars.EdidChkSum[0] = _rx_calc_edid_sum(edid_tmp);
}
#endif
_pr_buf(edid_tmp, 128);
if (it66353_gdev.opts.tx_opt->CopyEDIDFromSink) {
// update EDID block 0 for all port
it66353_set_internal_EDID(0, edid_tmp, EDID_PORT_ALL);
} else {
// update EDID block 0 for assigned port
it66353_set_internal_EDID(0, edid_tmp, (1 << flag));
}
it66353_setup_edid_ram_step2(0);
if (extblock > 3) {
dev_err(g_it66353->dev, "Warning: Extblock = %d\r\n", extblock);
extblock = 3;
}
for (i = 1; i <= extblock; i++) {
if (false == it66353_update_edid(i, edid_tmp, flag)) {
goto __err_exit;
}
it66353_gdev.vars.VSDBOffset = 0;
it66353_parse_edid_for_vsdb(edid_tmp);
if (i == 1) { // assume our sink has only 2 block EDID
if (it66353_gdev.vars.spmon == 2) {
if (edid_tmp[0x7F] != 0x6A) {
it66353_gdev.vars.spmon = 0;
}
}
_pr_buf(edid_tmp, 128);
if (it66353_gdev.opts.tx_opt->CopyEDIDFromSink) {
// update EDID block 0 for all port
it66353_set_internal_EDID(1, edid_tmp, EDID_PORT_ALL);
} else {
// update EDID block 0 for assigned port
it66353_set_internal_EDID(1, edid_tmp, (1<<flag));
}
if (it66353_gdev.opts.tx_opt->ParsePhysicalAddr) {
it66353_parse_edid_for_phyaddr(edid_tmp);
if (it66353_gdev.vars.VSDBOffset) {
it66353_setup_edid_ram_step2(1);
// break;
// break parsing here make the 4 block EDID CTS fail
}
}
}
} // for i
return true;
__err_exit:
return false;
}
static void it66353_dev_loop(void)
{
#if CHECK_DEV_PRESENT
if (dev_state < DEV_WAIT_DEVICE_READY) {
if (it66353_is_device_lost()) {
dev_state = DEV_FW_VAR_INIT;
}
}
#endif
switch (dev_state) {
case DEV_DEVICE_LOOP:
it66353_fsm();
it66353_irq();
it66353_rx_update_ced_err_from_hw();
break;
case DEV_DEVICE_INIT:
if (it66353_device_init()) {
dev_state = DEV_DEVICE_LOOP;
}
break;
case DEV_WAIT_DEVICE_READY:
if (it66353_is_device_ready()) {
dev_state = DEV_DEVICE_INIT;
}
break;
case DEV_FW_VAR_INIT:
it66353_vars_init();
dev_state = DEV_WAIT_DEVICE_READY;
break;
default:
break;
}
}
// APIs:
void it66353_dump_opts(void)
{
dev_info(g_it66353->dev, ".rx_opt->tag1=%d\r\n",
it66353_gdev.opts.active_rx_opt->tag1);
dev_info(g_it66353->dev, ".rx_opt->EnRxDDCBypass=%d\r\n",
it66353_gdev.opts.active_rx_opt->EnRxDDCBypass);
dev_info(g_it66353->dev, ".rx_opt->EnRxPWR5VBypass=%d\r\n",
it66353_gdev.opts.active_rx_opt->EnRxPWR5VBypass);
dev_info(g_it66353->dev, ".rx_opt->EnRxHPDBypass=%d\r\n",
it66353_gdev.opts.active_rx_opt->EnRxHPDBypass);
dev_info(g_it66353->dev, ".rx_opt->TryFixedEQFirst=%d\r\n",
it66353_gdev.opts.active_rx_opt->TryFixedEQFirst);
dev_info(g_it66353->dev, ".rx_opt->EnableAutoEQ=%d\r\n",
it66353_gdev.opts.active_rx_opt->EnableAutoEQ);
dev_info(g_it66353->dev, ".rx_opt->NonActivePortReplyHPD=%d\r\n",
it66353_gdev.opts.active_rx_opt->NonActivePortReplyHPD);
dev_info(g_it66353->dev, ".rx_opt->DisableEdidRam=%d\r\n",
it66353_gdev.opts.active_rx_opt->DisableEdidRam);
dev_info(g_it66353->dev, ".rx_opt->DefaultEQ=%x %x %x\r\n",
it66353_gdev.opts.active_rx_opt->DefaultEQ[0],
it66353_gdev.opts.active_rx_opt->DefaultEQ[1],
it66353_gdev.opts.active_rx_opt->DefaultEQ[2]);
dev_info(g_it66353->dev, ".rx_opt->FixIncorrectHdmiEnc=%d\r\n",
it66353_gdev.opts.active_rx_opt->FixIncorrectHdmiEnc);
dev_info(g_it66353->dev, ".rx_opt->HPDOutputInverse=%d\r\n",
it66353_gdev.opts.active_rx_opt->HPDOutputInverse);
dev_info(g_it66353->dev, ".rx_opt->HPDTogglePeriod=%d\r\n",
it66353_gdev.opts.active_rx_opt->HPDTogglePeriod);
dev_info(g_it66353->dev, ".rx_opt->TxOEAlignment=%d\r\n",
it66353_gdev.opts.active_rx_opt->TxOEAlignment);
dev_info(g_it66353->dev, ".rx_opt->str_size=%d\r\n",
it66353_gdev.opts.active_rx_opt->str_size);
dev_info(g_it66353->dev, ".tx_opt->tag1=%d\r\n", it66353_gdev.opts.tx_opt->tag1);
dev_info(g_it66353->dev, ".tx_opt->EnTxPNSwap=%d\r\n",
it66353_gdev.opts.tx_opt->EnTxPNSwap);
dev_info(g_it66353->dev, ".tx_opt->EnTxChSwap=%d\r\n",
it66353_gdev.opts.tx_opt->EnTxChSwap);
dev_info(g_it66353->dev, ".tx_opt->EnTxVCLKInv=%d\r\n",
it66353_gdev.opts.tx_opt->EnTxVCLKInv);
dev_info(g_it66353->dev, ".tx_opt->EnTxOutD1t=%d\r\n",
it66353_gdev.opts.tx_opt->EnTxOutD1t);
dev_info(g_it66353->dev, ".tx_opt->CopyEDIDFromSink=%d\r\n",
it66353_gdev.opts.tx_opt->CopyEDIDFromSink);
dev_info(g_it66353->dev, ".tx_opt->ParsePhysicalAddr=%d\r\n",
it66353_gdev.opts.tx_opt->ParsePhysicalAddr);
dev_info(g_it66353->dev, ".tx_opt->TurnOffTx5VWhenSwitchPort=%d\r\n",
it66353_gdev.opts.tx_opt->TurnOffTx5VWhenSwitchPort);
dev_info(g_it66353->dev, ".tx_opt->str_size=%d\r\n",
it66353_gdev.opts.tx_opt->str_size);
dev_info(g_it66353->dev, ".dev_opt->tag1=%d\r\n",
it66353_gdev.opts.dev_opt->tag1);
dev_info(g_it66353->dev, ".dev_opt->SwAddr=%d\r\n",
it66353_gdev.opts.dev_opt->SwAddr);
dev_info(g_it66353->dev, ".dev_opt->RxAddr=%d\r\n",
it66353_gdev.opts.dev_opt->RxAddr);
dev_info(g_it66353->dev, ".dev_opt->CecAddr=%d\r\n",
it66353_gdev.opts.dev_opt->CecAddr);
dev_info(g_it66353->dev, ".dev_opt->EdidAddr=%d\r\n",
it66353_gdev.opts.dev_opt->EdidAddr);
dev_info(g_it66353->dev, ".dev_opt->ForceRxOn=%d\r\n",
it66353_gdev.opts.dev_opt->ForceRxOn);
dev_info(g_it66353->dev, ".dev_opt->RxAutoPowerDown=%d\r\n",
it66353_gdev.opts.dev_opt->RxAutoPowerDown);
dev_info(g_it66353->dev, ".dev_opt->DoTxPowerDown=%d\r\n",
it66353_gdev.opts.dev_opt->DoTxPowerDown);
dev_info(g_it66353->dev, ".dev_opt->TxPowerDownWhileWaitingClock=%d\r\n",
it66353_gdev.opts.dev_opt->TxPowerDownWhileWaitingClock);
dev_info(g_it66353->dev, ".dev_opt->str_size=%d\r\n",
it66353_gdev.opts.dev_opt->str_size);
}
#define BUF_LEN 16
static void it66353_dump_register(u8 addr, char *reg_desc)
{
u8 regbuf[BUF_LEN];
int i, j;
// print reg description
dev_info(g_it66353->dev, reg_desc);
// print table
dev_info(g_it66353->dev, " | ");
for (j = 0; j < BUF_LEN; j++) {
if (j == (BUF_LEN/2)) {
dev_info(g_it66353->dev, "- ");
}
dev_info(g_it66353->dev, "%02X ", j);
}
dev_info(g_it66353->dev, "\n");
// print split line
for (j = 0; j < BUF_LEN + 2; j++) {
dev_info(g_it66353->dev, "---");
}
dev_info(g_it66353->dev, "\n");
// print register values
for (i = 0; i < 256; i += BUF_LEN) {
dev_info(g_it66353->dev, "%02X | ", i);
it66353_i2c_read(addr, i, BUF_LEN, regbuf);
for (j = 0; j < BUF_LEN; j++) {
if (j == (BUF_LEN / 2)) {
dev_info(g_it66353->dev, "- ");
}
dev_info(g_it66353->dev, "%02x ", regbuf[j]);
}
dev_info(g_it66353->dev, "\n");
}
dev_info(g_it66353->dev, "\n");
}
void it66353_dump_register_all(void)
{
it66353_dump_register(it66353_gdev.opts.dev_opt->SwAddr, "\n*** Switch Register:\n");
it66353_dump_register(it66353_gdev.opts.dev_opt->RxAddr, "\n*** RX Register(0):\n");
it66353_chgrxbank(3);
it66353_dump_register(it66353_gdev.opts.dev_opt->RxAddr, "\n*** RX Register(3):\n");
it66353_chgrxbank(5);
it66353_dump_register(it66353_gdev.opts.dev_opt->RxAddr, "\n*** RX Register(5):\n");
it66353_chgrxbank(0);
// dump EDID RAM, enable EDID RAM i2c address
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr | 0x01);
it66353_h2rxset(0x4C, 0x30, 0x00);
it66353_dump_register(it66353_gdev.opts.dev_opt->EdidAddr, "\n*** EDID Port 0:\n");
it66353_h2rxset(0x4C, 0x30, 0x10);
it66353_dump_register(it66353_gdev.opts.dev_opt->EdidAddr, "\n*** EDID Port 1:\n");
it66353_h2rxset(0x4C, 0x30, 0x20);
it66353_dump_register(it66353_gdev.opts.dev_opt->EdidAddr, "\n*** EDID Port 2:\n");
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr); // disable EDID RAM i2c address
#if EN_CEC
it66353_dump_register(it66353_gdev.opts.dev_opt->CecAddr, "\n*** CEC Register:\n");
#endif
}
static bool it66353_write_edid(u8 block, u8 offset, int length, u8 *data_buffer)
{
bool result = false;
int off = block * 128 + offset;
u8 reg3C;
u8 segment = off / 256;
offset = off % 256;
reg3C = it66353_h2swrd(0x3C);
it66353_h2swset(0xF5, 0x80, (1 << 7));
it66353_h2swset(0x3C, 0x01, 0x01); // disable DDCRegen by set RegTxMastersel=1
it66353_h2swset(0x3C, 0x01, 0x01); // Enable PC DDC Mode
it66353_h2swset(0x38, 0x08, 0x08); // Enable DDC Command Fail Interrupt
it66353_h2swset(0x37, 0x80, 0x80); // Enable DDC Bus Hang Interrupt
it66353_h2swwr(0x3D, 0x09); // DDC FIFO Clear
it66353_h2swwr(0x3E, 0xA0); // EDID Address
it66353_h2swwr(0x3F, offset); // EDID Offset
it66353_h2swwr(0x40, length); // Read ByteNum[7:0]
it66353_h2swwr(0x41, segment / 2); // EDID Segment
while (length) {
it66353_h2swwr(0x42, *data_buffer);
length--;
data_buffer++;
}
it66353_h2swwr(0x3D, 0x07); // EDID Write Fire
if (_tx_ddcwait()) {
result = true;
} else {
dev_err(g_it66353->dev, "ERROR: DDC EDID Write Fail !!!\r\n");
}
it66353_h2swwr(0x3C, reg3C); // restore PC DDC Mode
it66353_h2swset(0xF5, 0x80, (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass << 7));
if (it66353_gdev.opts.active_rx_opt->EnRxDDCBypass == 0) {
// enable DDCRegen by set RegTxMastersel=1
it66353_h2swset(0x3C, 0x01, 0x00);
}
return result;
}
bool it66353_write_one_block_edid(u8 block, u8 *edid_buffer)
{
u8 offset;
u8 i;
u8 op_len = 16;
offset = 0;
for (i = 0; i < 128 / op_len; i++) {
if (it66353_write_edid(block, offset, op_len, edid_buffer)) {
edid_buffer += op_len;
offset += op_len;
continue;
} else {
dev_err(g_it66353->dev,
"ERROR: write edid block 0, offset %d, length %d fail.\r\r\n",
(int)offset, (int)op_len);
return false;
}
}
return true;
}
static u8 __maybe_unused it66353_is_5v_present(u8 port)
{
if (port < RX_PORT_COUNT) {
if (it66353_get_port_info0(port, PI_5V)) {
return 1;
}
} else {
dev_err(g_it66353->dev, "Invalid port number:%d\r\n", port);
}
return 0;
}
static u8 __maybe_unused it66353_is_clock_detected(u8 port)
{
if (port < RX_PORT_COUNT) {
if (it66353_get_port_info0(port,
(PI_CLK_STABLE | PI_CLK_VALID | PI_5V))) {
return 1;
}
} else {
dev_err(g_it66353->dev, "Invalid port number:%d\r\n", port);
}
return 0;
}
bool it66353_set_active_port(u8 port)
{
if (port < RX_PORT_COUNT) {
if (it66353_gdev.vars.Rx_active_port != port) {
it66353_gdev.vars.Rx_new_port = port;
it66353_fsm_chg_delayed(RX_PORT_CHANGE);
return 1;
}
} else {
dev_err(g_it66353->dev, "Invalid port number:%d\r\n", port);
}
return 0;
}
u8 it66353_get_active_port(void)
{
return it66353_gdev.vars.Rx_active_port;
}
void it66353_set_option(IT6635_DEVICE_OPTION *Opts)
{
if (Opts) {
// it66353_gdev.opts.EnableAutoEQ = Opts->EnableAutoEQ;
// it66353_gdev.opts.rx_opt->EnRxDDCBypass = Opts->EnRxDDCBypass;
}
}
void it66353_get_option(IT6635_DEVICE_OPTION *Opts)
{
if (Opts) {
// Opts->EnableAutoEQ = it66353_gdev.opts.EnableAutoEQ;
// Opts->EnRxDDCBypass = it66353_gdev.opts.rx_opt->EnRxDDCBypass;
}
}
void it66353_dev_restart(void)
{
// it66353_gdev.vars.Rx_prev_port = -1;
it66353_gdev.vars.state_sys_fsm = RX_UNPLUG;
dev_state = DEV_WAIT_DEVICE_READY;
}
u8 it66353_get_RS(void)
{
// return it66353_gdev.EQ.FixedRsIndex[it66353_gdev.vars.Rx_active_port];
return 0;
}
void it66353_set_RS(u8 rs_idx0, u8 rs_idx1, u8 rs_idx2)
{
u8 rs[3];
if ((rs_idx0 < 14) && (rs_idx1 < 14) && (rs_idx2 < 14)) {
// it66353_gdev.EQ.FixedRsIndex[it66353_gdev.vars.Rx_active_port] = rs_index;
rs[0] = it66353_rs_value[rs_idx0] | 0x80;
rs[1] = it66353_rs_value[rs_idx1] | 0x80;
rs[2] = it66353_rs_value[rs_idx2] | 0x80;
it66353_rx_set_rs_3ch(rs);
it66353_chgrxbank(3);
it66353_h2rxbrd(0x27, 3, rs);
// it66353_h2rxset(0x22, 0x40, 0x00);
it66353_chgrxbank(0);
dev_info(g_it66353->dev, "==> RS set to %02x %02x %02x\r\n",
rs[0], rs[1], rs[2]);
}
}
void it66353_set_ch_RS(u8 ch, u8 rs_index)
{
u8 rs;
if (rs_index < 14) {
rs = it66353_rs_value[rs_index] | 0x80;
it66353_rx_set_rs(ch, rs);
}
}
void it66353_set_rx_hpd(u8 hpd_value)
{
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, hpd_value, TERM_FOLLOW_HPD);
}
/*
* it66353_set_internal_EDID - write data to EDID RAM
*
* @edid: 128 byte EDID data buffer.
*
* @block: EDID block number (0,1 or 2)
*
* target_port is a bitmap from 0x1 to 0xF
*
* ex: set port 1 EDID: target_port = EDID_PORT_1
* set port 1,3 EDID: target_port = EDID_PORT_1|EDID_PORT_3
*/
void it66353_set_internal_EDID(u8 block, u8 *edid, u8 target_port)
{
int i;
u8 wcount = 16;
if (block > 1) {
dev_err(g_it66353->dev, "Invalid block %d\r\n", block);
return;
}
// enable EDID RAM i2c address
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr | 0x01);
// for block 1, select port to be written
it66353_h2rxset(0x4C, 0x0F, target_port);
for (i = 0; i < 128; i += wcount) {
it66353_h2rxedidwr(i + 128 * block, edid, wcount);
edid += wcount;
}
// disable EDID RAM i2c address
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr);
}
/**
* it66353_get_internal_EDID - read data from EDID RAM
*
* @edid: 128 byte EDID data buffer.
*
* @block: EDID block number (0,1,2 or 3)
*
*/
void it66353_get_internal_EDID(u8 block, u8 *edid, u8 target_port)
{
int i;
u8 wcount = 16;
if (block > 1) {
dev_err(g_it66353->dev, "Invalid block %d\r\n", block);
return;
}
if (target_port > 2) {
dev_err(g_it66353->dev, "Invalid port %d\r\n", target_port);
return;
}
// enable EDID RAM i2c address
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr | 0x01);
it66353_h2rxset(0x4C, 0x30, target_port << 4);
for (i = 0; i < 128; i += wcount) {
it66353_h2rxedidrd(i + 128 * block, edid, wcount);
edid += wcount;
}
// disable EDID RAM i2c address
it66353_h2rxwr(0x4B, it66353_gdev.opts.dev_opt->EdidAddr);
}
/*
* it66353_change_default_RS - set the default RS index for each
* port
*
* @port: port number can be P0~P3.
*
* @new_rs_idx: RS index from 0 to 13
*
* @update_hw: 0: only update the vaule in RAM
* 1: update the value to RAM and Hardware register
* (for active port only)
*
*/
void it66353_change_default_RS(u8 port, u8 new_rs_idx0, u8 new_rs_idx1,
u8 new_rs_idx2, u8 update_hw)
{
if (port <= RX_PORT_3) {
it66353_gdev.opts.rx_opt[port]->DefaultEQ[0] = new_rs_idx0;
it66353_gdev.opts.rx_opt[port]->DefaultEQ[1] = new_rs_idx1;
it66353_gdev.opts.rx_opt[port]->DefaultEQ[2] = new_rs_idx2;
if (update_hw && (port == it66353_gdev.vars.Rx_active_port)) {
it66353_set_RS(new_rs_idx0, new_rs_idx1, new_rs_idx2);
}
} else {
dev_err(g_it66353->dev, "Invalid port number:%d\r\n", port);
}
}
/*
* it66353_force_rx_hpd :
* to force active port HPD low or auto control by driver
*
* @hpd_state: 0: Force HPD of active port to low
* 1: HPD of active port is controlled by it6635
* driver
*
* it66353_gdev.vars.force_hpd_state will reset to SW_HPD_AUTO when
* active port changed by it66353_fsm_chg(RX_PORT_CHANGE)
*
*/
void it66353_force_rx_hpd(u8 hpd_state)
{
it66353_gdev.vars.force_hpd_state = hpd_state;
if (hpd_state) { // hpd 0 --> hpd auto
// nothing to do here:
// hpd will be controlled by it66353_fsm()
} else { // hpd auto --> hpd 0
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, hpd_state, TERM_FOLLOW_HPD);
it66353_fsm_chg_delayed(RX_UNPLUG);
}
}
/*
* it66353_toggle_hpd : to make HPD toggle for active port with a
* given duration.
*
* @ms_duration: duration of HPD low in millisecond.
* range from 10ms to 12700ms
*
*/
bool it66353_toggle_hpd(u16 ms_duration)
{
u8 timeout = 0;
bool ret = true;
if (ms_duration <= (0x7F * 10)) {
timeout = ms_duration / 10;
} else if (ms_duration <= (0x7F * 100)) {
timeout = ms_duration/100;
timeout |= (BIT(7));
} else {
ret = false;
}
_rx_set_hpd(it66353_gdev.vars.Rx_active_port, 0, TERM_FOLLOW_HPD);
_tx_scdc_write(0x20, 0x00);
it66353_gdev.vars.hpd_toggle_timeout = timeout;
it66353_fsm_chg(RX_TOGGLE_HPD);
return ret;
}