一.简介
HDMI交换机芯片是一款可以同时输入几路HDMI的芯片,通过设计交换机芯片的寄存器值,已选择
其中一路作为输出。
I3HDX231是3:1 HDMI线性ReDriver交换机,支持每通道6 Gbps的数据速率,4096 x 2160像素分辨率,彩色12位YCbCr 4:2:2格式。
线性ReDriver技术提供了2倍改进的附加抖动性能,组件放置和布局灵活,易于使用。线性ReDriver被用作信号调节生态系统的一部分,旨在帮助高速数字信号的传输和数字反馈均衡(DFE)接收。它支持引脚和I2C编程模式、端口选择、可编程终端电阻器、空闲模式的静噪检测、DDC开关缓冲器和输入/输出电压电平选择。
PI3HDX231是3:1 HDMI线性ReDriver交换机,支持每通道6 Gbps的数据速率,4096 x 2160像素分辨率,彩色12位YCbCr 4:2:2格式。
线性ReDriver技术提供了2倍改进的附加抖动性能,组件放置和布局灵活,易于使用。线性ReDriver被用作信号调节生态系统的一部分,旨在帮助高速数字信号的传输和数字反馈均衡(DFE)接收。它支持引脚和I2C编程模式、端口选择、可编程终端电阻器、空闲模式的静噪检测、DDC开关缓冲器和输入/输出电压电平选择。
PI3HDX231是3:1 HDMI线性ReDriver交换机,支持每通道6 Gbps的数据速率,4096 x 2160像素分辨率,彩色12位YCbCr 4:2:2格式。
线性ReDriver技术提供了2倍改进的附加抖动性能,组件放置和布局灵活,易于使用。线性ReDriver被用作信号调节生态系统的一部分,旨在帮助高速数字信号的传输和数字反馈均衡(DFE)接收。它支持引脚和I2C编程模式、端口选择、可编程终端电阻器、空闲模式的静噪检测、DDC开关缓冲器和输入/输出电压电平选择。
特征
→符合HDMI 2.0标准的线性ReDriver交换机,具有6Gbps数据速率
→I2C控制3:1 TMDS端口切换
→支持直流耦合TMDS或交流耦合DP++差分输入信号,用于DP++电平移位器应用
独立支持输入均衡器、平坦增益和电压摆动控制,以实现优化的HDMI信号完整性
线性ReDriver提供了比ReDriver CMOS技术提高两倍的相加抖动
作为信号调节生态系统的一部分,使用接收机侧DFE(决策反馈均衡器)提高TMDS链路裕度
自动断电模式的输入时钟检测(静噪)
带DDC VOL/VIL调节的可选有源缓冲器或无源DDC开关
可编程接收器和驱动器块端接电阻器
当端口选择控制或HPD_SINK=0关闭所有三个端口时,待机电流<50uA
→HPD1/2/3开漏或双端接输出选择
→8KV触点符合IEC61000-4-2,等级3
电源:3.3V。
二.port口寄存器配置
通过设置寄存器0x00的第5 6位选择需要哪个port口。
三.驱动代码实现
#include <linux/miscdevice.h>
#include <linux/cdev.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>
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00)
#define DRIVER_NAME "PI3HDX231"
/*control reg*/
#define RX_SEL_CONTROL 0x00
/*define logging*/
#define PI3HDX231_DEBUG 1
#if PI3HDX231_DEBUG
#define DBG(format, args...) \
printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ##args)
#define ERR(format, args...) \
printk(KERN_ERR "%s: " format, DRIVER_NAME, ##args)
#define WARNING(format, args...) \
printk(KERN_WARN "%s: " format, DRIVER_NAME, ##args)
#define INFO(format, args...) \
printk(KERN_INFO "%s: " format, DRIVER_NAME, ##args)
#else
#define DBG(format, args...)
#define ERR(format, args...)
#define WARNING(format, args...)
#define INFO(format, args...)
#endif
struct pi3hdx231_dev {
struct device *dev;
struct miscdevice miscdev;
struct i2c_client *client;
struct mutex confctl_mutex;
struct timer_list timer;
struct work_struct work_i2c_poll;
bool auto_switch_en;
bool power_up_chip_en;
bool cec_switch_en;
bool nosignal;
u32 hdmi_rx_sel;
};
struct pi3hdx231_dev *g_pi3hdx231;
struct pi3hdx231_dev *g_frontboard_pi3hdx231;
struct pi3hdx231_dev *pi3hdx231;
static void i2c_wr(struct pi3hdx231_dev *pi3hdx231, u16 reg, u8 *val, u32 n)
{
struct i2c_msg msg;
struct i2c_client *client = pi3hdx231->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) {
ERR("%s: writing register 0x%x from 0x%x failed\n", __func__,
reg, client->addr);
} else {
switch (n) {
case 1:
INFO("I2C write 0x%02x = 0x%02x\n", reg, data[1]);
break;
case 2:
INFO("I2C write 0x%02x = 0x%02x%02x\n", reg, data[2],
data[1]);
break;
case 4:
INFO("I2C write 0x%02x = 0x%02x%02x%02x%02x\n", reg,
data[4], data[3], data[2], data[1]);
break;
default:
INFO("I2C write %d bytes from address 0x%02x\n", n,
reg);
}
}
}
static void i2c_rd(struct pi3hdx231_dev *pi3hdx231, u16 reg, u8 *val, u32 n)
{
struct i2c_msg msg[2];
struct i2c_client *client = pi3hdx231->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)) {
ERR("%s: reading register 0x%x from 0x%x failed\n", __func__,
reg, client->addr);
}
}
static void i2c_wr8(struct pi3hdx231_dev *pi3hdx231, u16 reg, u8 buf)
{
i2c_wr(pi3hdx231, reg, &buf, 1);
}
static long pi3hdx231_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
{
return 0;
}
static ssize_t pi3hdx231_write(struct file *file, const char __user *buf,
size_t size, loff_t *ppos)
{
return 1;
}
static ssize_t pi3hdx231_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
return 1;
}
static ssize_t pi3hdx231_hdmirxsel_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pi3hdx231_dev *pi3hdx231 = g_pi3hdx231;
struct pi3hdx231_dev *pi3hdx231frontboard = g_frontboard_pi3hdx231;
DBG("%s: hdmi rx select state: %d\n", __func__, pi3hdx231->hdmi_rx_sel);
return sprintf(buf, "pi3hdx231->hdmi_rx_sel:%d, pi3hdx231frontboard->hdmi_rx_sel:%d\n", pi3hdx231->hdmi_rx_sel, pi3hdx231frontboard->hdmi_rx_sel);
}
static ssize_t pi3hdx231_hdmirxsel_write(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pi3hdx231_dev *pi3hdx231 = g_pi3hdx231;
struct pi3hdx231_dev *pi3hdx231frontboard = g_frontboard_pi3hdx231;
u32 hdmirxstate = 0;
u32 frontboard = 2;
int ret;
ret = kstrtouint(buf, 10, &hdmirxstate);
if (ret) {
ERR("%s: write hdmi_rx_sel failed!!!, hdmirxstate:%d \n", __func__, hdmirxstate);
} else if(hdmirxstate >= 0 && hdmirxstate <= 2){
pi3hdx231->hdmi_rx_sel = hdmirxstate ;
i2c_wr8(pi3hdx231, RX_SEL_CONTROL, pi3hdx231->hdmi_rx_sel << 5);
printk("%s: pi3hdx231_hdmirxsel_write state: %d\n", __func__, hdmirxstate);
}else {
ERR("%s: write hdmi_rx_sel failed!!!, hdmirxstate:%d \n", __func__, hdmirxstate);
}
return count;
}
static DEVICE_ATTR(hdmirxsel, 0644,
pi3hdx231_hdmirxsel_read, pi3hdx231_hdmirxsel_write);
static void pi3hdx231_init(struct pi3hdx231_dev *pi3hdx231)
{
pi3hdx231->hdmi_rx_sel = 1;
i2c_wr8(pi3hdx231, RX_SEL_CONTROL, pi3hdx231->hdmi_rx_sel << 5);
}
static const struct file_operations pi3hdx231_fops = {
.owner = THIS_MODULE,
.read = pi3hdx231_read,
.write = pi3hdx231_write,
.unlocked_ioctl = pi3hdx231_ioctl,
};
struct miscdevice pi3hdx231_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pi3hdx231_dev",
.fops = &pi3hdx231_fops,
};
static int pi3hdx231_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pi3hdx231_dev *pi3hdx231;
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;
DBG("chip found @ 0x%x (%s)\n", client->addr << 1,
client->adapter->name);
pi3hdx231 = devm_kzalloc(dev, sizeof(struct pi3hdx231_dev), GFP_KERNEL);
if (!pi3hdx231)
return -ENOMEM;
pi3hdx231->client = client;
client->flags |= I2C_CLIENT_SCCB;
if(client->addr == 0x57){
ret = misc_register(&pi3hdx231_miscdev);
if (ret) {
ERR(" pi3hdx231 ERROR: could not register pi3hdx231 device\n");
return ret;
}
mutex_init(&pi3hdx231->confctl_mutex);
ret = device_create_file(pi3hdx231_miscdev.this_device,
&dev_attr_hdmirxsel);
if (ret) {
dev_err(pi3hdx231->dev, " failed to create attr hdmirxsel!\n");
return ret;
}
printk(" pi3hdx231_init +");
pi3hdx231_init(pi3hdx231);
g_pi3hdx231 = pi3hdx231;
}else{
pi3hdx231_init(pi3hdx231);
g_frontboard_pi3hdx231 = pi3hdx231;
}
INFO("%s found @ 0x%x (%s)\n", client->name, client->addr << 1,
client->adapter->name);
return 0;
}
static int pi3hdx231_remove(struct i2c_client *client)
{
del_timer(&pi3hdx231->timer);
flush_work(&pi3hdx231->work_i2c_poll);
mutex_destroy(&pi3hdx231->confctl_mutex);
misc_deregister(&pi3hdx231_miscdev);
kfree(pi3hdx231);
return 0;
}
static const struct of_device_id pi3hdx231_of_match[] = {
{ .compatible = "pericom,pi3hdx231" },
{}
};
MODULE_DEVICE_TABLE(of, pi3hdx231_of_match);
static struct i2c_driver pi3hdx231_driver = {
.probe = pi3hdx231_probe,
.remove = pi3hdx231_remove,
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(pi3hdx231_of_match),
},
};
static int __init pi3hdx231_driver_init(void)
{
return i2c_add_driver(&pi3hdx231_driver);
}
static void __exit pi3hdx231_driver_exit(void)
{
i2c_del_driver(&pi3hdx231_driver);
}
device_initcall_sync(pi3hdx231_driver_init);
module_exit(pi3hdx231_driver_exit);
MODULE_DESCRIPTION("PI3HDX231 3 HDMI in switch driver");
MODULE_AUTHOR("Bin Yang <yangbin@rock-chips.com>");
MODULE_LICENSE("GPL v2");