最近刚调试了基于rk3128平台的lcd驱动,顺便记录总结一下,有不足之处,望各位不吝赐教!
关于framebuffer(帧缓冲设备)的架构这里就不多罗嗦了,不会的可以去网上百度,这个很多的
1.代码架构
drivers/video/rockchip/transmitter/
|_ rk32_mipi_dsi.c /* MIPI 驱动主体文件 */
|_ rk32_mipi_dsi.h /* 寄存器以及结构体的定义 */
|_ mipi_dsi.c /* 封装的函数指针接口函数, 供 lcd_mipi.c 调用, 函数的具体实现
在 rk32_mipi_dsi.c 中 */
|_ mipi_dsi.h /* mipi 协议相关的宏定义以及函数指针结构体定义 */
drivers/video/rockchip/screen/
|_ lcd_mipi.c /* 屏参 dtsi 文件的解析 */
由于rk3128平台使用了3.10以后的内核,所以采用了dts的形式来管理驱动,刚开始的时候会觉得不顺手,用多了还是相当方便的
2.代码架构图
无论是正常的开机流程以及休眠唤醒的流程,显示相关的模块都是要和 fb 以及
vop(lcdc)交互的。rk32_dsi_enable()和 rk32_dsi_disable()函数作为 MIPI 和 vop 之
间 的 交 互 的 总 入 口 函 数 。 另 外 还 有 一 对 rk32_mipi_power_up_DDR() 和
rk32_mipi_power_down_DDR()函数是单独供 ddr 变频的时候使用,
正常的开机以及休
眠唤醒流程不走这两个函数。开机流程图如下图所示:
dsi_init() : 该函数主要实现 mipi host 和 phy 的上电以及初始化工作。
rk_mipi_screen_standby() : 屏的供电以及屏的初始化工作(屏初始化命令的发送)
dsi_enable_video_mode() : command 模式和 video 模式的切换,发送命令一般是在 command 模式下,正常数据传输是在 video 模下。
dsi_is_enable() : mipi host 的关闭与开启。
3.移植过程
a.配置menuconfig
b.添加关于lcd的dtsi文件(屏参文件)
在kernel/arch/arm/boot/dts中随便找一个其他mipi屏的dtsi文件参考其写法(3.10内核的新变化,也可以不用)
屏参文件包含四个部分:mipi host 配置、屏电源控制配置、屏初始化序列和屏参。
b.1 mipi host的配置
disp_mipi_init: mipi_dsi_init{
compatible = "rockchip,mipi_dsi_init";
rockchip,screen_init = <1>;
rockchip,dsi_lane = <4>;
rockchip,dsi_hs_clk = <310>;
rockchip,mipi_dsi_num = <1>;
};
screen_init : 表示屏是否需要初始化,如果需要则置为 1.
dsi_lane : mipi 数据传输需要几条数据 lane,这个一般根据原理图和 mipi 屏的规格书来配置。这个指的是每个 mipi 的数据 lane 数。譬如如果是双 mipi,每个 mipi 为 4 lane。那么此处仍然设置为 4。
dsi_hs_clk : 屏 ddr clk,表示一条数据 lane 的传输速率,单位为 Mbits/s。有个大概的计算公式:100+H_total*V_total*fps*3*8/lanes。H_total,V_total 包括 active,bp,fp 和 sync-len 的和;fps 为帧率,刚调试一款屏时,fps 为 50 多帧就好,然后慢慢抬高;3 为一个像素点为 rgb 3 个字节;8 为 8 bits;lanes 为(dsi_lane*mipi_dsi_num) ;100 为实际的结果要比理论值大 100M 左右。上面计算得到的值只是大概值并非精确的值,但是对于一般的屏都适用,对于部分屏需要微调该值。
mipi_dsi_num : 单 mipi 还是双 mipi,也是根据原理图和屏幕规格书来配置的。如果是双 mipi 则置为 2。单 MIPI 的屏 SDK 代码默认设置的是 MIPI_TX,所以单 MIPI 是接MIPI_TX 这一路,双 MIPI 接法:MIPI_TX 这路接左屏,MIPI_TX/RX 这一路接右屏。
b.2 屏电源的配置
&lcdc {
status = "okay";
backlight = <&backlight>;
pinctrl-names = "default", "gpio";
pinctrl-0 = <&lcdc0_lcdc>;
pinctrl-1 = <&lcdc0_gpio>;
rockchip,fb-win-map =
power_ctr: power_ctr {
rockchip,debug = <0>;
lcd_cs: lcd_cs {
rockchip,power_type =
gpios = <&gpio1 GPIO_B4 GPIO_ACTIVE_HIGH>;
rockchip,delay = <10>;
};
lcd_rst:lcd_rst{
rockchip,power_type =
gpios = <&gpio0 GPIO_D0 GPIO_ACTIVE_LOW>;
rockchip,delay = <20>;
};
lcd_en:lcd_en {
rockchip,power_type =
gpios = <&gpio0 GPIO_B0 GPIO_ACTIVE_LOW>;
rockchip,delay = <20>;
};
};
};
电源配置的 gpios 需要根据原理图来配置 lcd_en 等各对应哪路 gpio
b.3 屏的初始化序列
disp_mipi_init_cmds: screen-on-cmds {
compatible = "rockchip,screen-on-cmds";
rockchip,cmd_debug = <1>;
rockchip,on-cmds1 {
compatible = "rockchip,on-cmds";
rockchip,cmd_type =
rockchip,dsi_id = <0>;
rockchip,cmd = <0x15 0x80 0xAC>;
rockchip,cmd_delay = <0>;
};
rockchip,on-cmds2 {
compatible = "rockchip,on-cmds";
rockchip,cmd_type =
rockchip,dsi_id = <0>;
rockchip,cmd = <0x15 0x81 0xB8>;
rockchip,cmd_delay = <0>;
};
。
。
。
rockchip,on-cmds8 {
compatible = "rockchip,on-cmds";
rockchip,cmd_type =
rockchip,dsi_id = <0>;
rockchip,cmd = <0x05 0x11>;
rockchip,cmd_delay = <10>;
};
rockchip,on-cmds9 {
compatible = "rockchip,on-cmds";
rockchip,cmd_type =
rockchip,dsi_id = <0>;
rockchip,cmd = <0x05 0x29>;
rockchip,cmd_delay = <10>;
};
};
cmd_type : 命令是在 low power(LPDT)还是 high speed(HSDT)下发送。
dsi_id : 命令通过哪个 mipi 发送。0 表示在 mipi0 发送,1 表示在 mipi1 发送,2 表示双 mipi 同时发送。因为很少出现单独使用 mipi1 的情况,所以对于单 mipi,这个值默认是 0;对于双 mipi,这个值 是 2。
cmd : 初始化命令。格式:命令类型(如 0x05/0x15/0x39)+命令+参数。具体细节见 mipi 协议文档。
注:最后两条命令是mipi协议规定的,一定要加的0x11: Exit_sleep_mode,0x29:Set_display_on 表示告诉显示设备(屏)可以开始显示图像数据了。
b.4 屏的参数
disp_timings: display-timings {
native-mode = <&timing0>;
compatible = "rockchip,display-timings";
timing0: timing0 {
screen-type =
lvds-format =
out-face =
clock-frequency = <51668640>;
hactive = <1024>;
vactive = <600>;
hback-porch = <160>;
hfront-porch = <160>;
vback-porch = <23>;
vfront-porch = <12>;
hsync-len = <10>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <0>;
pixelclk-active = <0>;
swap-rb = <0>;
swap-rg = <0>;
swap-gb = <0>;
};
};
lvds-format : lvds 数据格式。对于 mipi 来说是无效参数,不用配置
out-face : 屏幕接线格式
上述三个参数的取值在 include/dt-bindings/rkfb/rk_fb.h 中定义。
clock-frequency : dclk 频率,单位为 HZ,一般屏的规格书中有,如果没有可以通过公式计算:H*V(包括同步信号)*fps
Hactive : 水平有效像素
Vactive : 垂直有效像素
hback-porch/hfront-porch/hsync-len : 水平同步信号
vback-porch/vfront-porch/vsync-len : 垂直同步信号
hsync-active 、 vsync-active 、 de-active 、 pixelclk-active :分 别 为 hync,vsync,DEN,dclk 的极性控制。置 1 将对极性进行翻转。
swap-rb、swap-rg、swap-gb : 设 1 将对对应的颜色进行翻转。
b.5 板级文件配置
屏参文件配置好以后,需要在板级文件中包含这个屏参文件:
#include "lcd-xxxxx-mipi.dtsi"
因为在芯片 dtsi 中默认是关闭的,所以需要在板级板级文件中开启 mipi host
&dsihost0 {
status = "okay";
};
因为是单mipi,只要配置一个就可以了
4.调试遇到的问题与总结
1.开机后lcd屏闪
问题定位:修改代码,提高dclk到一个较高的频率;
(1)若提高pclk后,屏闪现象消失
现象分析:说明此现象是刷新频率较低引起的;
解决方法:将dclk提高到一个合适的较高频率;
(2) 若提高dclk后,屏闪现象仍然存在
现象分析:说明此现象不是刷新频率较低引起的,这种情况是LCD极性设置不正确造成的;
解决方法:在dtsi文件中修改LCD的极性;
2.休眠唤醒,只有背光亮,屏不亮
问题定位:查看唤醒时的log,代码跑崩了,重新上电,屏是可以起来的,可能是唤醒屏时,en,rst的配置有问题
自己其实当时用了一个很low的方法来获取rst,en脚的管脚号,在lcd_mipi.c中通过解析dtsi的那个文件的那个函数,获得了gpio号,
然后在用gpio_direction_output的方法设置管脚号(这也是我刚开始不习惯的原因),后来在rk_fb.c的文件中发现有现成的接口,做了修改,代码倒是不崩溃了,但是屏还是不亮,通过log发现休眠唤醒时,居然没有走我的初始化序列,一看,发现少了0x11和0x29两条指令,加上后,问题解决
解决方法:修改获得pin脚号的方法,加上0x11和0x29命令