4535 lines
		
	
	
		
			108 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| }
 |