屏的接口种类非常多,常见的包括RGB、HDMI、VGA、LVDS、EDP、MIPI等接口。其中,在Android移动设备上,大多采用的是MIPI接口。某些时候,由于某种需求,需要将 Android设备上的MIPI数据显示到其他接口的屏上,此时,则需要利用相关转换芯片将MIPI接口的数据转换成其他接口的数据。 比如家庭中常用的TV多数都是HDMI接口类型,为了满足这一些项目设计需求,本文中以MSM8916平台LT8912转换芯片为调试对象,实现这一功能。
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可以同时传送音频和影像信号。
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控制器上发送出去。
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的设备节点中定义了该设备的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);
}
}
电源管理接口:
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;
}
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;