MIPI-DSI转HDMI驱动调试(lt8912)
Overview
屏的接口种类非常多,常见的包括RGB、HDMI、VGA、LVDS、EDP、MIPI等接口。其中,在Android移动设备上,大多采用的是MIPI接口。某些时候,由于某种需求,需要将 Android设备上的MIPI数据显示到其他接口的屏上,此时,则需要利用相关转换芯片将MIPI接口的数据转换成其他接口的数据。 比如家庭中常用的TV多数都是HDMI接口类型,为了满足这一些项目设计需求,本文中以MSM8916平台LT8912转换芯片为调试对象,实现这一功能。
1、mipi DSI简介
MIPI(Mobile Industry Processor Interface)是2003年由ARM, Nokia, ST ,TI等公司成立的一个联盟,目的是把手机内部的接口如摄像头、显示屏接口、射频/基带接口等标准化,从而减少手机设计的复杂程度和增加设计灵活性。 MIPI联盟下面有不同的WorkGroup,分别定义了一系列的手机内部接口标准,比如摄像头接口CSI、显示接口DSI、射频接口DigRF、麦克风/喇叭接口SLIMbus等。DSI 定义了一个位于处理器和显示模组之间的高速串行接口。
D- PHY的物理层支持HS(High Speed)和LP(Low Power)两种工作模式
HS模式:低压查分信号 功耗大 高速率(80M -1Gbps) 信号幅值(100mv-300mv)
LP模式:单端信号 功耗小,速率低(< 10Mbps) 信号幅值(0-1.2V)
在高速模式下,通道状态是差分的0或1,定义P比N高时定义为1,P比N低时定义为0,此时线上典型电压为差分200mv
链路层的模式分为:Command模式和Video模式。
当链路层选择Command模式时,物理层可以为HS模式,也可以为LP模式;
但链路层选择Video模式时,物理层只能选择HS模式。
数据包格式:
short packet : 4 bytes(固定长度,实际传输用户数据2字节)
DI(1字节) + packetData(1字节) + packetData(1字节) + ECC(1字节)
long packet : 6-65541 bytes(变长,实际传输用户数据0-65535字节)
DI(1字节) + 字节长度(2字节)+ ECC(1字节) + packetData(0-65535字节) + Checksum(2字节) = 4 + (0~65535) + 2 =6 ~ 65541
2002年4月,日立、松下、飞利浦、Silicon Image、索尼、汤姆逊、东芝七家公司共同组建了HDMI高清多媒体接口组织,开始着手制定一种符合高清时代标准的全新数字化视频/音频接口技术。
HDMI可用于机顶盒、DVD播放机、个人电脑、电视游乐器、综合扩大机、数字音响与电视机。HDMI可以同时传送音频和影像信号。
3、LT8912简介
LT8912是龙迅半导体(lontium semiconductor)设计的一款单通道mipi-dsi转HDMI、LVDS、MHL芯片。其基本的特性如下:
- DSI输入支持1clock lane和4 data lanes
- DSI 输入80Mb/s-1.5Gb/s 每lane的数据传输
- ++DSI只支持Non-burst sync events的数据格式++
- HDMI最高支持8bit 60HZ 1080P的数据输出
- 支持I2c接口对芯片进行配置。
软件设计流程
LT8912的驱动流程是首先从FAE处获得芯片寄存器配置说明,通过AP在LT8912复位脚产生一个下拉150毫秒的信号,然后拉高使芯片工作起来。然后通过AP端的I2C接口把FAE给的配置发送到芯片中。配置完成后,需要在配置HOST MIPI端输出Non-Burst模式,sync events的video steam,以及连续的时钟信号。LT8912会DSI端获取这些数据包后转换成r/g/b hs/vs/de的信号,在转换成HDMI信号发送到HDMI控制器上发送出去。
reset PIN申请与控制
qualcomm msm8916 & android L 版本使用的DTS管理设备树,包括GPIO。所以我们首先在msm8916-pinctrl.dtsi中申请一个reset GPIO端口gpio14,并定义端口的默认状态和休眠状态。
/* add lt8912 driver elio shao */
lt8912_reset_pin {
qcom,pins = <&gp 14>;
qcom,pin-func = <0>;
qcom,num-grp-pins = <1>;
label = "lt8912_reset_pin";
lt_default: lt_default {
drive-strength = <6>;
bias-pull-up;
};
lt_sleep: lt_sleep {
drive-strength = <2>;
bias-pull-down;
};
};
并加入到LT8912设备中,在msm8916-qrd-skui-slm755-public.dtsi里面创建LT8912设备节点:
&i2c_0 {
lt8912 {
compatible = "qcom,lt8912";
reg = <0x48>;
pinctrl-names = "default","sleep";
pinctrl-0 = <<_default>;
pinctrl-1 = <<_sleep>;
lt,gpio_rstn = <&msm_gpio 14 0x0>;
};
};
在LT8912的驱动文件中,probe阶段申请这个GPIO,并使用。在文件mdss_i2c_interface.c代码:
/*Get GPIO*/
if (client->dev.of_node) {
my_mipi_i2c->gpio_rstn = of_get_named_gpio_flags(client->dev.of_node,
"lt,gpio_rstn", 0, NULL);
}
/* request GPIO */
err = gpio_request(my_mipi_i2c->gpio_rstn, "lt8912_rsrn");
if (err < 0) {
mutex_unlock(&my_mipi_i2c->lock);
printk("Failed to request GPIO:%d, ERRNO:%d",
my_mipi_i2c->gpio_rstn, err);
err = -ENODEV;
}else{/*use reset GPIO*/
gpio_direction_output(my_mipi_i2c->gpio_rstn, 0);
mdelay(150);
gpio_direction_output(my_mipi_i2c->gpio_rstn, 1);
}
LT8912-I2C驱动及接口
在LT8912的设备节点中定义了该设备的compatible应和驱动的id_table相匹配,才能probe设备。还定义了该节点使用的I2C adapter为i2c_0,且设备的默认地址是reg = <0x48> ,reset脚为GPIO14等信息。
在驱动中使用如下接口注册该设备驱动类型为I2C设备和probe函数:
static const struct of_device_id mipi_i2c_of_match[] = {
{ .compatible = "qcom,lt8912",},
{},
};
static struct i2c_driver mipi_i2c_driver = {
.driver = {
.name = "qcom,lt8912",
.owner = THIS_MODULE,
.of_match_table = mipi_i2c_of_match,
.pm = &mipi_i2c_pm_ops,
},
.probe = mipi_i2c_probe,
.remove = mipi_i2c_remove,
.id_table = mipi_i2c_id,
};
module_i2c_driver(mipi_i2c_driver);
MODULE_LICENSE("GPL");
在probe函数中要做的主要是获取I2C client,测是I2C收发是否成功,并完成一次对LT8912寄存器的初始化工作。
下面主要列出I2C部分代码:
int HDMI_WriteI2C_Byte(int reg, int val)
{
int rc = 0;
rc = i2c_smbus_write_byte_data(my_mipi_i2c->mdss_mipi_i2c_client,reg,val);
if (rc < 0) {
printk("eliot :HDMI_WriteI2C_Byte fail \n");
return rc;
}
return rc ;
}
int HDMI_ReadI2C_Byte(int reg)
{
int val = 0;
val = i2c_smbus_read_byte_data(my_mipi_i2c->mdss_mipi_i2c_client, reg);
if (val < 0) {
dev_err(&my_mipi_i2c->mdss_mipi_i2c_client->dev, "i2c read fail: can't read from %02x: %d\n", 0, val);
return val;
}
return val ;
}
另外一点需要注意的是,为了防止系统休眠在唤醒LT8912出现掉电的情况,在唤醒的时候还需要对LT8912在重新复位,通过I2C刷入初始化数据。
需要在kernel/drivers/video/msm/mdss/mdss_dsi_host.c 修改如下代码:
void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
{
……
/*diff two register access type*/
if (ctrl->cmd_access == CMD_ACCESS_DSI)
ctrl->cmdlist_commit = mdss_dsi_cmdlist_commit;
else if (ctrl->cmd_access == CMD_ACCESS_I2C)
ctrl->cmdlist_commit = mdss_i2c_cmdlist_commit;
}kernel/drivers/video/msm/mdss/mdss_dsi_panel.c 如下代码:
static int mdss_dsi_panel_on(struct mdss_panel_data *pdata)
{
……
if (ctrl->cmd_access == CMD_ACCESS_DSI) {
if (ctrl->on_cmds.cmd_cnt)
mdss_dsi_panel_cmds_send(ctrl, &ctrl->on_cmds);
} else if (ctrl->cmd_access == CMD_ACCESS_I2C) {
if (ctrl->i2c_on_cmds.cmds_cnt)
ctrl->cmdlist_commit(ctrl, 0);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
电源管理接口:
static int mipi_i2c_resume(struct device *tdev) {
Reset_chip();
return 0;
}
static int mipi_i2c_suspend(struct device *tdev) {
gpio_direction_output(my_mipi_i2c->gpio_rstn, 0);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
MIPI DSI HOST配置
qualcomm的display使用dtsi文件配置mipi-dsi和panel的一些参数。从LT8912的spec中可知,其DSI信号要求:
- 1,non-burst mode(continue mode)
- 2,Video mode
- 3,sync event
- 4,MIPI DSI
LT8912只支持Non-Burst模式,sync events的video steam,以及连续的时钟信号。所以需要对AP端的DSI进行一些配置,需要注意调整项:
qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
qcom,mdss-dsi-force-clock-lane-hs;
qcom,mdss-dsi-always-on;
- 1
- 2
- 3
调试中遇到的问题及办法
- mipi-dsi在传输数据时使用的是差分信号对,特别是hs-video模式时候,速度较快,所以克服信号干扰是非常重要的,在LT8912的调试过程,在中后期一直没有图像显示出来,最终发现是由于AP端的mipi-dsi接口一共有两路输出,虽然一路到EVB板没有接LCD,但是已经对mipi信号产生了很大的干扰,造成不能显示图像。后期通过把核心板和EVB版分离测试,达到要求,有图像显示。
- 在软件调试过程中,发送I2C数据到从设备中使用了互斥锁,保证发送数据过程不被中断,造成寄存器数值不对。但是由于在使用互斥锁的时候没有对锁进行初始化就使用造成系统运行经常崩溃,甚至无法进入系统等奇怪问题。后面初始化后,问题消除。
- 前期I2C信号不通,通过缩短I2C线长,修改供电方式解决,猜测和供电方式不稳定有关。