RK3399 Linux-SDK mipi屏幕驱动及调试
一,流程及通路
我接触到的三款mipi屏幕,基本的点亮流程都是很一致的,就是背光使能-背光点亮-屏幕使能-reset引脚按指定时序/波形拉高或拉低-初始化序列命令发送。
3399的linuxSDK中,包含一种类似通用的屏幕驱动。本文档以使用此驱动为前提,不包含原理内容(硬件基础实在太差,原理自己也没有搞很通),只描述如何尽快的完成屏幕配置并最终将屏幕点亮。给自己留一个记录,也希望其中的内容能对刚上手的朋友们有一点帮助。
流程上分为以下几步:
(1)需要预先向屏幕厂商讨要一些屏幕关键参数及资料。
(2)进行关键引脚对应
(3)进行DTS配置
(4)固件编译,烧写调试
文档会按顺序说明以上几步的关键位置及步骤。
二,参数及配置
2.1 关键引脚对应
按上述流程说明,我们需要对应以下几个引脚:
1.屏幕使能;2.屏幕reset;3.背光使能;4.背光pwm
需要根据核心板原理图,底板原理图(如果存在转接板还要看转接板部分的原理图),屏幕接线端接线的原理图三个确定内核中引脚的对应。
按3399来讲,一般是有4个GPIO分组。常见的原理图写法,应该是类似GPIO4_D5这种写法。各个引脚完成对应后记录好名称,需要配置到DTS中,下面文档会提到DTS中引脚的改写方式。
2.2 向屏幕厂商讨要一些屏幕关键参数及资料
主要包括初始化序列,display off序列,reset波形,timing参数
其实除去序列外,波形及timing参数都可以通过屏幕的spec或datasheet文件中读取,如需要磨练此部分的个人技能,可以尝试进行自己的对应。不过由于我硬件基础差,文档多数是英文且含有较多专业次会,时序和参数对应学习成本也比较高,这部分屏幕厂商应该会有的。讨要一下会减少很多的时间花费,也会更准确一点。
2.3 屏幕初始化序列/display off序列及Linux平台改写
屏幕在点亮后会涉及到初始化序列发送,这部分序列厂家会提供。不过一般由于mipi屏幕常用于安卓平台,有些还直接用单片机驱动,故厂家给出的序列极少会是直接满足要求的,都需要进行一定的改写。
现举例说明改写方式。一般厂家给出的文档可能是这样的:
不同厂家提供的文档写法可能有些不同,不过基本还是相通的.
2.3.1 改写方法/公式
以图中内容来讲,有三种指令:GP_COMMAND_PA,SPI_WriteData,Delay。Delay很好理解,延时嘛,一般输入的参数就是延时数量,延时单位一般是毫秒(ms).另两种其实不用区别,只需要掌握以下方式:
1.数个数;以GP_COMMAND_PA为开始,下一个GP_COMMAND_PA(不包含这次的GP_COMMAND_PA和Delay)为结束作为一次数据发送,数一共有几个数据。
2.看延时。看是否存在延时。
改写格式上是这样的:
命令类型+延时数量+数据长度+数据
命令类型根据上文提到的数个数来确定,只讲三种:一个数据,两个数据,多于两个数据
如果只有一个数据,对应的命令类型是0x05;如果有两个数据,对应的命令类型是0x15;如果多于两个数据,对应的命令类型是0x39.
延时数量就是根据代码中的延时,转换为16进制就好。
数据长度,就是数个数的结果,不改写前一次数据发送有几个数据,转换为16进制填入此位置。
数据就是把函数中除延时外的内容按从上到下顺序接在后面就好。注意全部数据为16进制,0x要求省略。
2.3.2 改写举例
以上图作为依据,分别将三种类型的写法做个举例:
一、多个数据情况
上图中红框,按数个数方式可确定有四个数据,需要用39指令。本次发送无延时,故延时数量为0.数据长度为4.数据内容为FF 98 81 00.改写后结果为:
39 00 04 FF 98 81 00
二、两个数据情况(带延时)
上图中红框,按数个数方式可确定有两个数据,需要用15指令。本次发送延时为1.数据长度为2.改写后结果为:
15 01 02 3A 77
三、单个数据情况
上图中红框,按数个数方式可确定有一个数据,需要用05指令。本次发送延时为200.数据长度为1.改写后结果为:
05 C8 01 11
全部数据按顺序改写完成后,先保存在一个文件中,后续配置DTS时会使用到。
2.4 DTS配置
这一部分是重点,全部之前的工作全为此处进行准备。
首先,进行DTS文件对应。dts文件存于 sdk目录/kernel/arch/arm64/boot/dts/rockchip/ 中。由于瑞芯微平台提供两种编译内核的方式,故请在对应前确认到底使用的是哪一个DTS文件。
DTS写法这里不赘述(主要我还没有对写法全通),主要描述需要添加的内容和具体放置位置。
共需要以下几个重点内容:dsi,route_dsi,backlight,vcc_lcd,dsi_in_vopb,dsi_in_vopl,vopb。
另注意,由于DTS文件设计到层层包含(DTS文件可以包含后缀为.dtsi的文件,作用就像C语言中的.h文件),故建议重要配置及板卡特性配置写到最后一级的DTS文件中,防止由于在较高层级的dtsi配置后手误在后面又进行了配置,导致配置被错误覆盖。
2.4.1 dsi
dsi部分的内容较多,只说明重点内容:
backlight = <&backlight>;
power-supply = <&vcc_lcd>;
enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
enable-gpios表示屏幕使能引脚,要求拉高,GPIO_ACTIVE_HIGH意为拉高。reset-gpios表示屏幕reset引脚,具体拉高拉低,要根据内核配置和时序要求来进行对应修改。
说明以下引脚对应,以reset引脚来举例。对应芯片引脚为GPIO4_D5。写法上如上述写法,引脚分组写为&gpio4,对应引脚的GPIO4。D5的数字是这么对应的。一般每个引脚分组再细分为ABCD四组,按顺序对应数字0,1,2,3.D5对应的数字方式,是字母对应数字乘以8再加上字母后数字,D5的话就是3*8+5=29.
reset-delay-ms = <1>;
disable-delay-ms = <10>;
init-delay-ms = <10>;
enable-delay-ms = <20>;
prepare-delay-ms = <10>;
unprepare-delay-ms = <10>;
dsi,lanes = <4>;
status = "okay";
上述配置中有一些延时,我暂时了解到的相对重要的是reset-delay-ms和init-delay-ms。reset-delay-ms是在屏幕初始化过程中,第一次操作reset引脚之前的延时。init-delay-ms,是在屏幕初始化过程中,第一次操作reset引脚之后的延时。如有特殊的reset时序要求,可分别在uboot和kernel中进行修改,具体代码分别为:
uboot:
u-boot\drivers\video\drm\rockchip_panel.c
修改时序:panel_simple_prepare
kernel:
kernel\drivers\gpu\drm\panel\panel-simple.c
修改时序:panel_simple_prepare
需注意,reset引脚时序配置,如DTS中route_dsi开启,则无特殊情况开机过程kernel部分的reset引脚控制不调用,kernel部分的reset引脚配置,只在屏幕出现休眠唤醒时使用。
dsi,lanes = <4>;
dsi,lanes = <4>;是配置当前mipi是几通道的,需根据屏幕实际情况配置。
panel-init-sequence = [];
panel-init-sequence填写刚刚改写好的初始化序列。
panel-exit-sequence
panel-exit-sequence填写display off序列,一般为两条,也需要厂家提供。
disp_timings: display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <59000000>;
hactive = <720>;
vactive = <1280>;
hback-porch = <40>;
hfront-porch = <60>;
vback-porch = <32>;
vfront-porch = <28>;
hsync-len = <8>;
vsync-len = <6>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <1>;
};
};
这一部分也很重要,是屏幕的一些参数。hactive和vactive就是水平数值的像素,也就是屏幕分辨率了。
hback-porch,hfront-porch,vback-porch,vfront-porch按顺序简写为HBP,HFP,VBP,VFP,这个跟厂家讨要后,根据简写字母对应即 可。
hsync-len,vsync-len也请与厂家沟通确定
clock-frequency 像素时钟频率,厂家如不给出,可以通过公式计算出。公式为:
像素时钟频率 = (hactive+hbp+hfp+hsync-len)x (vactive+vbp+vfp+vsync-len)xfps
然后保留两位有效数字(不要四舍五入),后面数据直接填0即可。
最后附上整体的dsi配置,供参考。
&dsi {
status = "okay";
dsi_panel: panel {
compatible ="simple-panel-dsi";
reg = <0>;
backlight = <&backlight>;
//power-supply = <&vcc_lcd>;
enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;//此处我自己改了内核驱动代码
//做了特殊时序
//不建议参考,通常配置为GPIO_ACTIVE_LOW
dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
dsi,format = <MIPI_DSI_FMT_RGB888>;
reset-delay-ms = <1>;
disable-delay-ms = <10>;
init-delay-ms = <10>;
enable-delay-ms = <20>;
prepare-delay-ms = <10>;
unprepare-delay-ms = <10>;
dsi,lanes = <4>;
status = "okay";
panel-init-sequence = [
39 00 04 FF 98 81 03
15 00 02 01 00
15 00 02 02 00
15 00 02 03 72
15 00 02 04 00
15 00 02 05 00
... ... ... ... //这部分内容太多了,每个屏幕都有不同,全部省略方便查看
05 C8 01 11
05 C8 01 29
];
panel-exit-sequence = [
05 14 01 28
05 78 01 10
];
disp_timings: display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <59000000>;
hactive = <720>;
vactive = <1280>;
hback-porch = <40>;
hfront-porch = <60>;
vback-porch = <32>;
vfront-porch = <28>;
hsync-len = <8>;
vsync-len = <6>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
};
};
};
};
2.4.2 backlight
根据翻译,此处为背光配置。
&backlight {
status = "okay";
pwms = <&pwm1 0 25000 0>;
enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
};
注意,pwms要与芯片引脚对应,确认具体是哪一组;enable-gpios表示使能引脚,使能引脚如硬件级别保证了拉高,就可以不进行配置了。另注意,使能与PWM是两个引脚,使能引脚千万不要配置错误,如错误会导致异常的系统内核循环,无法正常启动系统。
另,需注意对应的pwm在DTS中是否已启用。
启用写法如下:
&pwm1 {
status = "okay";
};
2.4.3 route_dsi
这个配置,作用是是否经由mipi接口在uboot和kernel阶段显示过度图片(图片可更换)。开启此配置的方式为:
&route_dsi{
status = "okay";
};
这个配置的生效,还仰仗uboot阶段使用kernel的dtb。uboot开启使用kernel的dtb功能的配置相对简单,只需在使用的config文件中添加CONFIG_USING_KERNEL_DTB=y即可。(注意此配置有一项项配置依赖,OF_LIVE).
其实此项配置还与mipi的驱动链路相关。理论上就我现有了解,HOST链接为VOP->MIPI-DSI->Panel,VOP在kernel中分为vopb和vopl,route_dsi这个配置在rk3399.dtsi中配置了一个connect,也就是链接到哪一个vop,之后也会讲到将DSI具体配置到哪个链路中。要求此项配置中connect选择的vop与实际配置的vop要一致,才能打通这个通路。
如配置为vopb,配置方式为在此配置中添加connect = <&vopb_out_dsi>;
2.4.4 dsi通路
通路问题在上一节有讲到,假定我们将dsi配置到vopb中,则共需要进行dsi_in_vopl,vopb,dsi_in_vopb这三项的配置。
首先,vopb要打开:
&vopb {
status = "okay";
};
其次,dsi_in_vopl要关闭
&dsi_in_vopl{
status = "disabled";
};
最后,dsi_in_vopb要打开
&dsi_in_vopb{
status = "okay";
};
以上全部完成修改,通路就已经配置完毕了。剩余其他基本在dts文件中都有默认配置
三、调试
说明一些调试内容。
内容上如完成以上配置,屏幕应是成功点亮的。背光不亮,有可能是该组pwm没有使能;如使能无背光,检查以下enable引脚及pwm引脚配置。如以上全都正确,多数是硬件问题了(pwm是独立于屏幕配置之外的,故只要将该组pwm使能,背光上电就会亮,无论是否有uboot部分的配置及启用)。
之后如屏幕无法点亮,可以跟进内核查看uboot部分及kernel部分打印,查看错误原因。
建议开启uboot阶段的显示,能屏蔽系统问题直接去定位驱动及配置问题。
如参数配置后屏幕背光亮但无显示,可能是驱动链路问题,需要进行dsi的链路检查,重点查看route_dsi的初始配置链路,和2.4.4 dsi通路对应内容。
如链路正确,驱动正确且内核显示已绑定,仍有背光无显示,可以查看一下reset引脚配置是否正确,如正确,抓取以下启动时的波形,看是否reset波形时间上有问题。
类似花屏等的问题,可以参考相对有经验的大神博客:https://me.csdn.net/Shushan1,个人受益匪浅。
另调试过程中出现过一种奇怪现象,如启动uboot显示logo,保持系统级别默认屏幕显示方向,屏幕初始化只在uboot阶段进行,内核阶段不进行。如进行默认屏幕显示方向翻转后,内核运行的最后,会重新初始化一遍屏幕(官方请教后好像是一种休眠唤醒)。这种情况下屏幕会短暂的灭-亮,之后显示系统界面。
奇怪在哪儿呢,奇怪在内核进行屏幕初始化后,屏幕有几率灰屏。现象上灰屏时内核无其他错误打印,但灭-亮的时间会短很多。个人由于不通原理,考虑到有可能是系统界面显示较快与屏幕初始化有冲突,在发送完初始化序列后,内核代码中再添加固定50ms的延时。之后就再没出现过灰屏问题了。