欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RK3399 Linux-SDK mipi屏幕驱动及调试

程序员文章站 2022-07-14 16:42:37
...

一,流程及通路

    我接触到的三款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屏幕常用于安卓平台,有些还直接用单片机驱动,故厂家给出的序列极少会是直接满足要求的,都需要进行一定的改写。

    现举例说明改写方式。一般厂家给出的文档可能是这样的:

RK3399 Linux-SDK 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 改写举例

    以上图作为依据,分别将三种类型的写法做个举例:

    一、多个数据情况

RK3399 Linux-SDK mipi屏幕驱动及调试

    上图中红框,按数个数方式可确定有四个数据,需要用39指令。本次发送无延时,故延时数量为0.数据长度为4.数据内容为FF 98 81 00.改写后结果为:

39 00 04 FF 98 81 00

    二、两个数据情况(带延时)

RK3399 Linux-SDK mipi屏幕驱动及调试

    上图中红框,按数个数方式可确定有两个数据,需要用15指令。本次发送延时为1.数据长度为2.改写后结果为:

15 01 02 3A 77

    三、单个数据情况

RK3399 Linux-SDK mipi屏幕驱动及调试

    上图中红框,按数个数方式可确定有一个数据,需要用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的延时。之后就再没出现过灰屏问题了。