RK3568平台(显示篇)HDMI交换机芯片PI3HDX231

一.简介

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");

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值