?Linux?提供了一套完整的屏幕驱动,支持?RGB,MIPI?DSI,eDP,LVDS,E-INK屏幕,也支持低分辨率的?SPI,IIC?屏幕。具体屏幕的驱动情况,需要根据芯片而确定。本文将通过介绍?D1-H?Kernel?中的?LCD?驱动,讲解配置屏幕驱动的基本方法。
LCD?的驱动
显示驱动可划分为三个层面:驱动层,框架层及底层。底层与图形硬件相接,主要负责将上层配置的功能参数转换成硬件所需要的参数,并配置到相应寄存器中。
显示框架层对底层进行抽象封装成一个个的功能模块。驱动层对外封装功能接口,通过内核向用户空间提供相应的设备结点及统一的接口。
在驱动层,分为三个驱动,分别是framebuffer驱动,display驱动,LCD&HDMI驱动。framebuffer?驱动与?framebuffer?core?对接,实现?linux?标准的framebuffer?接口。display驱动是整个显示驱动中的核心驱动模块,所有的接口都由?display?驱动来提供,包括?lcd?的接口。
LCD?配置
U-Boot
U-Boot?配置
对于?U-Boot?平台,一般推荐直接修改?config?文件,位于
lichee/brandy-2.0/u-boot-2018/configs/xxx_defconfig
其中的?xxx?是芯片的软件代号。可以在?BoardConfig.mk?文件里找到。例如?D1-H?芯片的?BoardConfig.mk?文件,位于:
device/config/chips/d1-h/configs/default/BoardConfig.mk
不过也要注意,有些时候除了?default?文件夹内有?BoardConfig.mk?文件,不同的项目文件夹内也有?BoardConfig.mk?文件。这个时候就需要使用项目文件夹里的?BoardConfig.mk?配置文件。优先使用的是项目文件夹里的配置文件。
在这里,evb1?文件夹为项目文件夹,代表?f133-evb1?这个开发板
default???是缺省文件夹,项目文件夹里找不到配置文件就会去缺省文件夹里寻找
我们打开?BoardConfig.mk?配置文件
可以看到这颗芯片使用的是?sun20iw1p1_defconfig?这个配置文件。就可以在?U-Boot?的?config??文件夹找到这个配置文件。
打开它,可以在文件中找到?SUNXI?LOGO?DISPLAY?后面的部分,看到这两部分配置:
红色框框住的是?DISP?驱动部分,主要部分如下:
CONFIG_CMD_SUNXI_BMP=y??????????#?开机?LOGO?BMP?文件解析器
CONFIG_LZMA=y???????????????????#?开机?LOGO?使用?LZMA?压缩解压工具
CONFIG_DISP2_SUNXI=y????????????#?DISP?驱动
CONFIG_HDMI2_DISP2_SUNXI=y??????#?HDMI?DISP?驱动?
CONFIG_AW_PHY=y?????????????????#?DISP?驱动使用的是全志自研外设
如果希望关闭?U-Boot?的?LCD?DISP?驱动输出,可以注释掉?CONFIG_DISP2_SUNXI=y?这一行。
关闭?HDMI?DISP?驱动也相同,注释即可。
Linux
内核配置
运行?make?kernel_menuconfig?进入内核配置,找到?Video?support?for?sunxi
Device?Drivers?>?Graphics?support?>?Frame?buffer?Devices?>?Video?support?for?sunxi
在显示驱动中最主要的是?<*>?DISP?Driver?Support(sunxi-disp2)?,勾选后可以看到其他的选项。包括驱动支持,调试接口和?LCD?面板的选择。(LCD?panels?select)
进入?LCD?面板选择可以看到许多已经适配了的显示屏可供选择使用。
设备树配置
与其他设备相同,屏幕驱动也使用了两份设备树。第一份配置了显示驱动的地址,时钟等等参数,位于kernel/linux-4.9/arch/arm/boot/dts/sun8iw21p1.dtsi?一般来说这里的参数都不需要修改,默认即可,另外一份配置位于?device/config/chips/v853/configs/vision/board.dts?包括了两个配置节点。第一个是?display?所使用的节点,配置了屏幕的特性与功能,另外一个是?lcd?所使用的节点,配置了?LCD?面板的驱动与参数。
这里的驱动配置非常复杂,具体代表的含义请参考《Linux_LCD_开发指南.pdf》,这里不做过多说明。
LCD?驱动
LCD?显示屏与其他驱动不一样,LCD?屏幕种类繁多,接口丰富,各式各样屏幕参数层出不穷。所以?LCD?屏幕驱动都是以单独的模块存在的,驱动文件位于:
lichee/linux-5.4/drivers/video/fbdev/sunxi/disp2/disp/lcd
从接口上来分,LCD?屏幕可以分为?RGB?屏幕,LVDS?屏幕,MIPI?DSI?屏幕,I8080?屏幕,eDP?屏幕、SPI?屏幕、IIC?屏幕。这里讲解几个常用的屏幕。
(1)RGB?接口
RGB接口在全志平台又称HV接口(Horizontal同步和Vertical同步)。有些LCD屏支持高级的功能比如?gamma,像素格式的设置等,但是?RGB?协议本身不支持图像数据之外的传输,所以无法通过?RGB?管脚进行对?LCD?屏进行配置,所以拿到一款?RGB?接口屏,要么不需要初始化命令,要么这个屏会提供额外的管脚给?SoC?来进行配置,比如?SPI?和?I2C?等。RGB?屏幕有许多格式,不同的位宽,不同的时钟周期。下表是位宽与时钟周期的区别。
位宽 | 时钟周期数 | 颜色数量和格式 | 并行\串行?RGB | 24?bits | 1?cycle | 16.7M?colors,?RGB888 | 并行 | 18?bits | 1?cycle | 262K?colors,?RGB666 | 并行 | 16?bits | 1?cycle | 65K?colors,?RGB565 | 并行 | 6?bits | 3?cycles | 262K?colors,?RGB666 | 串行 | 6?bits | 3?cycles | 65K?colors,?RGB565 | 串行 |
串行?RGB?是相对于并行?RGB?来说,而并不是说它只用一根线来发数据,只要通过多个时钟周期才能把一个像素的数据发完,那么这样的?RGB?接口就是串行?RGB。
(2)MIPI?DSI?接口
MIPI-DSI,即?Mobile?Industry?Processor?Interface?Display?Serial?Interface,移动通信行业处理器接口显示串行接口。MIPI?有?2?种模式:
-
Command?mode,类似MPU接口,需要IC内部有GRAM来缓冲。 -
Video?mode。类似?RGB?接口,没有?GRAM,需要不停往?panel?刷数据。其中?video?mode?又分为三个子?mode:
-
Non-burst?mode?with?sync?pulses -
Non?Burst?mode?with?sync?Events -
Burst?mode。简单理解就是有效数据比率更高,传输效率更高。
MIPI-DSI?的管脚是差分的,分为两种管脚:一种是时钟管脚,另外一种是数据管脚。数据管脚的数量是可变的,数量的单位是?lane?,lane?也指一对差分管脚,每一条?lane?实际包含两条线。一般来说?LCD?屏说明书里面的说的?lane?的数量是指数据管脚的数量不包括时钟管脚。比如说某?4?lane?MIPI-DSI?屏就总共有?(4+1)*2?根脚。
(3)LVDS?屏幕
LVDS?即?Low?Voltage?Differential?Signaling?是一种低压差分信号接口。
由于?LVDS?不具备传输图像数据之外的能力,一般屏端不需要任何初始化,只需要初始化?SoC?端即可。
(4)I8080?屏幕
Intel?8080?接口屏(又称?MCU?接口)很老的协议,一般用在分辨率很小的屏上。
管脚的控制脚有6种:
-
CS?片选信号,决定该芯片是否工作. -
RS?寄存器选择信号,低表示选择?index?或者?status?寄存器,高表示选择控制寄存器。实际场景中一般接SoC的LCD_DE脚(数据使能脚) -
WR?(低表示写数据)?数据命令区分信号,也就是写时钟信号,一般接?SoC?的?LCD_CLK?脚 -
RD?(低表示读数据)数据读信号,也就是读时钟信号,一般接?SoC?的?LCD_HSYNC?脚 -
RESET?复位LCD(?用固定命令系列?0?1?0来复位) -
Data?是双向的数据通路
I8080?根据的数据位宽接口有?8/9/16/18,连哪些脚参考,即使位宽一样,连的管脚也不一样,还要考虑的因素是?RGB?格式。
-
RGB565,总共有?65K?这么多种颜色 -
RGB666,总共有?262K?那么多种颜色 -
9bit?固定为?262K
(5)SPI?屏幕
SPI?LCD?是使用?SPI?总线传输图像数据的屏幕,只会出现在很低分辨率的屏幕上。一般来说开屏前都需要初始化操作。SPI?LCD?屏幕一般不使用?DISP?来驱动屏幕,使用自己独立的驱动即可。例如?FBTFT?驱动。
Linux?Kernel?适配?LCD?屏幕
适配?LCD?屏幕的步骤
-
确保全志显示框架的内核配置有使能 -
前期准备以下资料和信息:
-
屏手册。主要是描述屏基本信息和电气特性等,向屏厂索要。 -
Driver?IC?手册。主要是描述屏?IC?的详细信息。这里主要是对各个命令进行详解,对我们进行初始化定制有用,向屏厂索要。 -
屏时序信息。请向屏厂索要。 -
屏初始化代码,请向屏厂索要。一般情况下?DSI?和?I8080?屏等都需要初始化命令对屏进行初始化。 -
万用表。调屏避免不了测量相关电压。
-
通过第2步屏厂提供的资料,定位该屏的类型,然后选择一个已有同样类型的屏驱动作为模板进行屏驱动添加或者直接在上面修改。 -
修改屏驱动目录下的?panel.c?和?panel.h。在全局结构体变量?panel_array?中新增刚才添加?strcut?__lcd_panel?的变量指针。panel.h?中新增?strcut?__lcd_panel?的声明。 -
修改Makefile。在lcd屏驱动目录的上一级的?Makefile?文件中的disp-objs中新增刚才添加屏驱动.o -
修改?board.dts?中的?lcd0?节点。 -
编译测试
关闭?U-Boot?加快调试
适配屏幕之前,我们可以先关闭?Uboot?的屏幕驱动,保留?Kernel?的驱动方便调试
先前往?Uboot?配置文件夹修改配置文件关闭屏幕驱动。
brandy/brandy-2.0/u-boot-2018/configs/sun8iw21p1_defconfig
把?CONFIG_DISP2_SUNXI=y?注释了
适配?RGB?屏幕
下面简述如何移植一款?RGB?屏幕,使用的开发板是?V853?开发板,屏幕型号是【D500T7009VC】,此?RGB?屏幕无需初始化。使用40Pin?排线,RGB666?连接开发板(可以看见,开发板的?RGB?LCD?有一根飞线,飞线后文有说明)
由于此?RGB?屏幕不需要初始化,所以直接使用?default_lcd?驱动即可,在?kernel?内?default_lcd?是默认启用的,所以只需要修改设备树即可。
硬件连接
硬件连接如电路图与?LCD?屏幕手册所示:
可以看到,屏厂提供的手册中的?31?脚是?DISP?脚,默认?LOW?模式也就是关闭显示输出。而开发板侧的?LCD?屏幕连接器的?DISP?脚是悬空的。如果不飞线会导致屏幕关闭显示输出而不显示。不过也有的屏幕不需要?DISP?信号控制。根据屏幕而定。
设备树配置
首先,我们在?pio?节点内增加?rgb18_pin?作为?LCD?屏幕的?Pin?绑定。
&pio?{
????rgb18_pins_a:?rgb18@0?{
????????allwinner,pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,pname?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,function?=?"lcd";
????????allwinner,muxsel?=?<2>;
????????allwinner,drive?=?<3>;
????????allwinner,pull?=?<0>;
????};
????rgb18_pins_b:?rgb18@1?{
????????allwinner,pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,pname?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,function?=?"rgb18_suspend";
????????allwinner,muxsel?=?<0xf>;
????????allwinner,drive?=?<1>;
????????allwinner,pull?=?<0>;
????};
};
然后修改?lcd0?节点。这里将其分为几个部分,下文会对这几个部分着重说明。另外比较重要的配置项添加了注释。
&lcd0?{
????lcd_used????????????=?<1>;????????????#?启用lcd
????lcd_driver_name?????=?"default_lcd";??#?使用?default_lcd?驱动
????lcd_if??????????????=?<0>;????????????#?0:rgb?4:dsi
????#?Part?1
????lcd_x???????????????=?<800>;??????????#?宽度
????lcd_y???????????????=?<480>;??????????#?高度
????lcd_width???????????=?<108>;??????????#?屏幕物理宽度,单位?mm
????lcd_height??????????=?<65>;???????????#?屏幕物理高度,单位?mm
????#?Part?2
????lcd_pwm_used????????=?<1>;????????????#?启用背光?PWM
????lcd_pwm_ch??????????=?<9>;????????????#?使用?PWM?通道?9?
????lcd_pwm_freq????????=?<50000>;????????#?PWM?频率,单位?Hz
????lcd_pwm_pol?????????=?<0>;????????????#?背光?PWM?的极性
????lcd_pwm_max_limit???=?<255>;??????????#?背光?PWM?的最大值(<=255)
????#?Part?3
????lcd_dclk_freq???????=?<24>;???????????#?屏幕时钟,单位?MHz
????lcd_ht??????????????=?<816>;??????????#?hsync?total?cycle(pixel)
????lcd_hbp?????????????=?<12>;???????????#?hsync?back?porch(pixel)?+?hsync?plus?width(pixel);
????lcd_hspw????????????=?<4>;????????????#?hsync?plus?width(pixel)
????lcd_vt??????????????=?<496>;??????????#?vsync?total?cycle(line)
????lcd_vbp?????????????=?<12>;???????????#?vsync?back?porch(line)?+?vysnc?plus?width(line)
????lcd_vspw????????????=?<4>;????????????#?vsync?plus?width(pixel)
????#?Part?4
????lcd_lvds_if?????????=?<0>;
????lcd_lvds_colordepth?=?<1>;???????????
????lcd_lvds_mode???????=?<0>;
????lcd_frm?????????????=?<0>;????????????#?0:关闭;?1:启用rgb666抖动;?2:启用rgb656抖动
????lcd_io_phase????????=?<0x0000>;
????lcd_gamma_en????????=?<0>;
????lcd_bright_curve_en?=?<0>;
????lcd_cmap_en?????????=?<0>;
????deu_mode????????????=?<0>;
????lcdgamma4iep????????=?<22>;
????smart_color?????????=?<90>;
????#?Part?5
????pinctrl-0?=?<&rgb18_pins_a>;
????pinctrl-1?=?<&rgb18_pins_b>;
};
Part?1
在这一部分中,我们设置了屏幕的像素宽度、高度,物理宽度、高度,这些数据都可以在屏厂提供的数据手册中查询到。有小数点的可以遵循四舍五入原则。
Part?2
在这一部分,我们配置了?LCD?屏幕的背光相关属性,使用?PWM?背光实现动态调整背光,可以在电路图中看到?LCD-PWM?使用的是?PWM9,所以配置?lcd_pwm_ch?=?<9>;
Part?3
这一部分是非常重要的一部分,能不能点亮?LCD?屏幕都靠这一部分。
我们打开屏幕手册,找到时序介绍这一部分。
(1)DCLK
首先是?DCLK,这里显示的?DCLK?值是?25,需要注意的是,如果直接设置?lcd_dclk_freq?=?<25>;?会导致实际的频率变为?48MHz。这是因为当?DCLK?的频率小于?48MHz?时,其频率是从?288MHz?的主时钟分频到实际频率的。而?25MHz?无法被完整分频导致其使用下一个频点?48M,从而导致屏幕被超频的情况。
这里简单说明下分频系数与得到的频率的计算方法:
在开机时,如果使用的是?RGB?屏幕,我们可以看到这样的输出:
disp?0,?clk:?pll(144000000),clk(144000000),dclk(24000000)?dsi_rate(144000000)
?????clk?real:pll(288000000),clk(288000000),dclk(48000000)?dsi_rate(0)
实际芯片的?LCD?是通过?PLL?时钟分频得到的。所以在这里会计算分频的分频值。使用?dclk(24000000)的?24MHz?乘上倍频系数?6,得到?pll(144000000)也就是144MHz,使用?pll(144000000)?去申请最近的时钟,这里申请到的是?real:pll(288000000)?也就是?288MHz。此时使用?(int)(real:pll?/?pll)?即可获得分频系数。由于不执行浮点运算,输出的结果会向下取整。例如这里的分频系数是?288MHz?/?144MHz?=?2。使用分频系数乘上倍频系数即可得到?288MHz?的实际分频结果,这里则是?288MHz?/?(2?*?6)?=?24MHz?刚刚好。
如果我们设置为25MHz,按照上面的计算可知分频系数为?1,分频结果则是?288MHz?/?(1?*?6)?=?48MHz
由于?Display?框架不单单需要支持?RGB?显示屏,也需要支持?MIPI-DSI,LVDS?等等接口的显示屏,并且不同的频率区段所使用的倍频系数也是不同的,所以倍频系数是不可以随意修改的。
在这里,最好的方法就是选取一个最接近的?DCLK?值即可。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 48MHz | 24MHz | 16MHz | 12MHz | 8MHz | 6MHz | 4MHz |
而对于?48MHz?以上的屏幕,由于使用了不同的时钟源,其分频更加精准,无需按照此处方法调优。
(2)HT(Hsync?Total)
查询手册可知,HT?的值是?816,这个值不需要修改。填入即可。
(3)HBP(Hsync?Back?Porch)
查询手册可知,HBP?值为?8,不过由于全志平台的?HBP?的含义是?HBP?+?HSPW,所以需要加上?HSPW?的值填入。这里便是?8?+?4?=?12
(4)HSPW(Hsync?Plus?Width)
查询手册可知,HSPW?的值是?4,这个值不需要修改。填入即可。
(5)VT(Vsync?Total)
查询手册可知,HT?的值是?496,这个值不需要修改。填入即可。
(6)VBP(Vsync?Back?Porch)
查询手册可知,VBP?值为?8,不过由于全志平台的?VBP?的含义是?VBP?+?VSPW,所以需要加上?VSPW?的值填入。这里便是?8?+?4?=?12
(7)VSPW(Vsync?Plus?Width)
查询手册可知,VSPW?的值是?4,这个值不需要修改。填入即可。
可以看到手册还提供了?HFP(Hsync?Front?Porch)和?VFP(Vsync?Front?Porch)的值。这个值不需要填写,因为驱动可以通过计算得出实际的值。
Part?4
这一部分是?LCD?屏幕扩展功能的区域,包括?LCD?屏幕的各类功能,可以参照手册内容设置。
Part?5
这一部分是?Pin?的绑定,绑定上面创建的两个?RGB?节点。GPIO?与?Pin?绑定相关请查阅:【GPIO?-?V853】
驱动勾选
不需要初始化的?RGB?LCD?的驱动比较简单,勾选Video?support?for?sunxi?即可
Device?Drivers?>?Graphics?support?>?Frame?buffer?Devices?>?Video?support?for?sunxi
测试屏幕
首先编译,打包。烧录系统。
先?ls?命令打印?/dev/?目录看看有没有出现?fb0?这个节点
可以看到这里有?fb0`?节点。那就进行下最简单的花屏测试。
cat?/dev/urandom?>?/dev/fb0
这个测试一定会显示?cat:?write?error:?No?space?left?on?device?,这样才是正常情况,因为?FB0?可以类比为屏幕的显存,显存是固定大小的可以被消耗完,当显存填满的时候就会报错?No?space?left?on?device。如果执行这一行命令一直没出现这个报错则有可能底层显示驱动配置有问题。
另外也可以使用?colorbar?测试
echo?8?>?/sys/class/disp/disp/attr/colorbar
也可以使用,获取屏幕的图层信息,帧率等等。
cat?/sys/class/disp/disp/attr/sys
适配?RGB?+?SPI?初始化?屏幕
有些?RGB?屏幕需要使用?SPI?进行初始化,设置屏幕的?GAMMA,亮度,工作模式等。开屏前需要使用?SPI?发送数据初始化屏幕的寄存器,再使用?RGB?进行图像的输出。
适配前的准备
这里示例的开发板是本次大赛所参赛的板子?【YuzukiRuler?Pro?随身Linux小尺子】,其硬件连接如下所示:
这里使用的屏幕是?【TL032FWV01-I1440A】驱动是?ST7701s,询问厂家得到了三份资料。
lcd.h
在这个文件中,定义了屏幕的一些时序信息。
TL032FWV01-I1440A_specification(3).pdf
在这个文件中,定义了屏幕的长宽高,分辨率等等
TL032FWV01-I1440A_ST7701S(SPI9bit+RGB16bit)G2.2_V1.0(1).INI
这个文件里定义了?SPI?的初始化时序信息,这里的?W_C?代表的写命令,W_D?代表写数据。?Delay?代表延时。
编写驱动程序
想要驱动需要初始化的屏幕,就需要使用屏幕驱动了。屏幕驱动位于以下文件夹内
lichee/linux-5.4/drivers/video/fbdev/sunxi/disp2/disp/lcd
从现成的驱动开始
由于是?RGB?+?SPI?的屏幕,可以到屏幕驱动中找一份现成的相同驱动方式的驱动文件来修改,这里我们使用的是?st7789v.c?驱动,查看驱动可知他使用的是?spi?驱动模式
具体的?SPI?通讯的方法是使用函数模拟?SPI?的时序。并没有实际使用?SPI?外设。这样的好处是可以使用任意?GPIO?引脚,且初始化完成即可释放?GPIO。
首先为了方便区分,我们复制一份?st7789v?驱动,重命名为?TL032FWV01
并且把驱动内的?st7789v?全部改为?TL032FWV01
头文件也别忘记修改
配置屏幕驱动
第一部分是?LCD_cfg_panel_info?这一部分一般不需要修改,保留即可
第二部分是?LCD?上电下电相关,默认即可
第三部分是屏幕开启关闭与背光相关,对于这款屏幕也不需要修改
第四部分是?软件模拟?SPI?的部分,这里需要对照?ST7701s?数据手册修改为?ST7701s?的?SPI?时序。
主要添加了延时函数,因为?ST7701s?的?SPI?时钟较低,并修改对应的上升、下降触发方式。
//three?line?9bit?mode
static?void?TL032FWV01_spi_write_cmd(u32?value)
{
????int?i;
????spi_cs_0;
????spi_sdi_0;
????spi_scl_0;
????sunxi_lcd_delay_us(10);
????spi_scl_1;
????for?(i?=?0;?i?<?8;?i++)?{
????????sunxi_lcd_delay_us(10);
????????if?(value?&?0x80)
????????????spi_sdi_1;
????????else
????????????spi_sdi_0;
????????spi_scl_0;
????????sunxi_lcd_delay_us(10);
????????spi_scl_1;
????????value?<<=?1;
????}
????sunxi_lcd_delay_us(10);
????spi_cs_1;
}
static?void?TL032FWV01_spi_write_data(u32?value)
{
????int?i;
????spi_cs_0;
????spi_sdi_1;
????spi_scl_0;
????sunxi_lcd_delay_us(10);
????spi_scl_1;
????for?(i?=?0;?i?<?8;?i++)?{
????????sunxi_lcd_delay_us(10);
????????if?(value?&?0x80)
????????????spi_sdi_1;
????????else
????????????spi_sdi_0;
????????value?<<=?1;
????????sunxi_lcd_delay_us(10);
????????spi_scl_0;
????????spi_scl_1;
????}
????sunxi_lcd_delay_us(10);
????spi_cs_1;
}
另外这里的?SPI?操作均定义在驱动头部,其中的?sunxi_lcd_gpio_set_value?中,1,2,3?来自设备树的?lcd_gpio_1,lcd_gpio_2,lcd_gpio_3。
例如:设备树配置了?lcd_gpio_1?=?<&pio?PG?13?GPIO_ACTIVE_HIGH>;表示上电时PG13?脚默认高电平,在屏幕驱动里可以使用?sunxi_lcd_gpio_set_value(0,?1,?1)?将?PG13?脚设置为高电平,sunxi_lcd_gpio_set_value(0,?1,?0)?将?PG13?脚设置为低电平。
第五部分是开关屏函数,原来的开关屏函数包括读取屏幕信息,这里我们不需要读取信息所以直接删除即可。开屏?init?部分调用?SPI?初始化屏幕参数即可。
static?void?LCD_panel_init(u32?sel)
{
????lcd_panel_TL032FWV01_init();
????return;
}
static?void?LCD_panel_exit(u32?sel)
{
????return;
}
接下来是最重要的一部分,屏幕初始化参数部分。这一部分使用?SPI?对屏幕进行初始化,打开屏厂提供的初始化代码,一一对应即可。
static?void?lcd_panel_TL032FWV01_init(void)
{
????sunxi_lcd_delay_ms(100);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x13);
????TL032FWV01_spi_write_cmd(0xEF);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x10);
????TL032FWV01_spi_write_cmd(0xC0);
????TL032FWV01_spi_write_data(0xE5);
????TL032FWV01_spi_write_data(0x02);
????TL032FWV01_spi_write_cmd(0xC1);
????TL032FWV01_spi_write_data(0x0C);
????TL032FWV01_spi_write_data(0x0A);
????TL032FWV01_spi_write_cmd(0xC2);
????TL032FWV01_spi_write_data(0x07);
????TL032FWV01_spi_write_data(0x0F);
????TL032FWV01_spi_write_cmd(0xC3);
????TL032FWV01_spi_write_data(0x02);
????TL032FWV01_spi_write_cmd(0xCD);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_cmd(0xB0);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_data(0x51);
????TL032FWV01_spi_write_data(0x0D);
????TL032FWV01_spi_write_data(0xCE);
????TL032FWV01_spi_write_data(0x06);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_data(0x1D);
????TL032FWV01_spi_write_data(0x02);
????TL032FWV01_spi_write_data(0xD0);
????TL032FWV01_spi_write_data(0x0F);
????TL032FWV01_spi_write_data(0x6F);
????TL032FWV01_spi_write_data(0x36);
????TL032FWV01_spi_write_data(0x3F);
????TL032FWV01_spi_write_cmd(0xB1);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x10);
????TL032FWV01_spi_write_data(0x4F);
????TL032FWV01_spi_write_data(0x0C);
????TL032FWV01_spi_write_data(0x11);
????TL032FWV01_spi_write_data(0x05);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x07);
????TL032FWV01_spi_write_data(0x07);
????TL032FWV01_spi_write_data(0x1F);
????TL032FWV01_spi_write_data(0x05);
????TL032FWV01_spi_write_data(0xD3);
????TL032FWV01_spi_write_data(0x11);
????TL032FWV01_spi_write_data(0x6E);
????TL032FWV01_spi_write_data(0x34);
????TL032FWV01_spi_write_data(0x3F);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x11);
????TL032FWV01_spi_write_cmd(0xB0);
????TL032FWV01_spi_write_data(0x4D);
????TL032FWV01_spi_write_cmd(0xB1);
????TL032FWV01_spi_write_data(0x1C);
????TL032FWV01_spi_write_cmd(0xB2);
????TL032FWV01_spi_write_data(0x87);
????TL032FWV01_spi_write_cmd(0xB3);
????TL032FWV01_spi_write_data(0x80);
????TL032FWV01_spi_write_cmd(0xB5);
????TL032FWV01_spi_write_data(0x47);
????TL032FWV01_spi_write_cmd(0xB7);
????TL032FWV01_spi_write_data(0x85);
????TL032FWV01_spi_write_cmd(0xB8);
????TL032FWV01_spi_write_data(0x21);
????TL032FWV01_spi_write_cmd(0xB9);
????TL032FWV01_spi_write_data(0x10);
????TL032FWV01_spi_write_cmd(0xC1);
????TL032FWV01_spi_write_data(0x78);
????TL032FWV01_spi_write_cmd(0xC2);
????TL032FWV01_spi_write_data(0x78);
????TL032FWV01_spi_write_cmd(0xD0);
????TL032FWV01_spi_write_data(0x88);
????sunxi_lcd_delay_ms(100);
????TL032FWV01_spi_write_cmd(0xE0);
????TL032FWV01_spi_write_data(0x80);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x02);
????TL032FWV01_spi_write_cmd(0xE1);
????TL032FWV01_spi_write_data(0x04);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x05);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x60);
????TL032FWV01_spi_write_data(0x60);
????TL032FWV01_spi_write_cmd(0xE2);
????TL032FWV01_spi_write_data(0x30);
????TL032FWV01_spi_write_data(0x30);
????TL032FWV01_spi_write_data(0x60);
????TL032FWV01_spi_write_data(0x60);
????TL032FWV01_spi_write_data(0x3C);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x3D);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0xE3);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x33);
????TL032FWV01_spi_write_data(0x33);
????TL032FWV01_spi_write_cmd(0xE4);
????TL032FWV01_spi_write_data(0x44);
????TL032FWV01_spi_write_data(0x44);
????TL032FWV01_spi_write_cmd(0xE5);
????TL032FWV01_spi_write_data(0x06);
????TL032FWV01_spi_write_data(0x3E);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_data(0x40);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x0A);
????TL032FWV01_spi_write_data(0x42);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x0C);
????TL032FWV01_spi_write_data(0x44);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_cmd(0xE6);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x33);
????TL032FWV01_spi_write_data(0x33);
????TL032FWV01_spi_write_cmd(0xE7);
????TL032FWV01_spi_write_data(0x44);
????TL032FWV01_spi_write_data(0x44);
????TL032FWV01_spi_write_cmd(0xE8);
????TL032FWV01_spi_write_data(0x07);
????TL032FWV01_spi_write_data(0x3F);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x09);
????TL032FWV01_spi_write_data(0x41);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x0B);
????TL032FWV01_spi_write_data(0x43);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0x0D);
????TL032FWV01_spi_write_data(0x45);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_data(0xA0);
????TL032FWV01_spi_write_cmd(0xEB);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x4E);
????TL032FWV01_spi_write_data(0x4E);
????TL032FWV01_spi_write_data(0xEE);
????TL032FWV01_spi_write_data(0x44);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0xED);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0x04);
????TL032FWV01_spi_write_data(0x56);
????TL032FWV01_spi_write_data(0x72);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0x27);
????TL032FWV01_spi_write_data(0x65);
????TL032FWV01_spi_write_data(0x40);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_data(0xFF);
????TL032FWV01_spi_write_cmd(0xEF);
????TL032FWV01_spi_write_data(0x10);
????TL032FWV01_spi_write_data(0x0D);
????TL032FWV01_spi_write_data(0x04);
????TL032FWV01_spi_write_data(0x08);
????TL032FWV01_spi_write_data(0x3F);
????TL032FWV01_spi_write_data(0x1F);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x13);
????TL032FWV01_spi_write_cmd(0xE8);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x0E);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0x11);
????sunxi_lcd_delay_ms(120);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x13);
????TL032FWV01_spi_write_cmd(0xE8);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x0C);
????sunxi_lcd_delay_ms(10);
????TL032FWV01_spi_write_cmd(0xE8);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0xFF);
????TL032FWV01_spi_write_data(0x77);
????TL032FWV01_spi_write_data(0x01);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0x3A);
????TL032FWV01_spi_write_data(0x66);
????TL032FWV01_spi_write_cmd(0x36);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0x35);
????TL032FWV01_spi_write_data(0x00);
????TL032FWV01_spi_write_cmd(0x29);
????return;
}
最后一部分,定义了用户自己的操作与屏幕的操作信息,可以在这里增加用户自定义的操作,这里没有操作就留空了,同时修改结构体内为对应的操作即可。
添加屏幕驱动到内核中
屏幕驱动修改完成了,现在需要把屏幕驱动添加到内核中。
首先在屏幕驱动文件夹内找到?Kconfig?文件
编辑这个文件,新增屏幕的引索。配置名称叫做?LCD_SUPPORT_TL032FWV01
config?LCD_SUPPORT_TL032FWV01
????bool?"LCD?support?TL032FWV01?panel"
????default?n
????---help---
????????If?you?want?to?support?TL032FWV01?panel?for?display?driver,?select?it.
同时在相同文件夹内找到?panel.c?和?panel.h?两个文件,修改?panel.c?增加屏幕指针。并使用?ifdef?宏来确定只有启用这款屏幕的时候才会编译这个屏幕的驱动。指针的声明位于刚才修改的头文件中,需要确认声明与?panel.c?中的名称是否一致。
#ifdef?CONFIG_LCD_SUPPORT_TL032FWV01
????&TL032FWV01_panel,
#endif????
头文件也一致,引用结构体
#ifdef?CONFIG_LCD_SUPPORT_TL032FWV01
extern?struct?__lcd_panel?TL032FWV01_panel;
#endif
最后,到屏幕面板驱动的上一层文件夹,找到?Makefile?添加驱动编译选项。
disp-$(CONFIG_LCD_SUPPORT_TL032FWV01)?+=?lcd/TL032FWV01.o
至此,屏幕驱动就添加到内核中了。
内核配置
make?kernel_menuconfig?后,到?Device?Drivers?>?Graphics?support?>?Frame?buffer?Devices?>?Video?support?for?sunxi?>?LCD?panels?select?找到屏幕,勾选即可。
可以在编译的时候查看驱动是否被编译上了
配置设备树
首先,我们在?pio?节点内增加?rgb18_pins?作为?LCD?屏幕的?Pin?绑定。
rgb18_pins_a:?rgb18@0?{
????pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?\
????????"PD6",?"PD7",?"PD8",?"PD9",?"PD10",?"PD11",?\
????????"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?\
????????"PD18",?"PD19",?"PD20",?"PD21";
????function?=?"lcd0";
????drive-strength?=?<30>;
????bias-disable;
};
rgb18_pins_b:?rgb18@1?{
????pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?\
????????"PD6",?"PD7",?"PD8",?"PD9",?"PD10",?"PD11",?\
????????"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?\
????????"PD18",?"PD19",?"PD20",?"PD21";
????function?=?"io_disabled";
????bias-disable;
};
设备树类似的,按照屏厂提供的参数填写即可。lcd_driver_name?记得与之前驱动中的?.name="TL032FWV01"?相同。
测试屏幕
没啥好说的,自己就亮了
适配?MIPI?DSI?屏幕
MIPI?DSI?屏幕的适配较为简单,一样的只需要给定时序参数与初始化即可。不过部分?MIPI?屏幕包括其他功能,例如?OLED?屏幕的背光调节等,需要在驱动里实现相关的功能。这里不对这些操作做过多叙述,仅仅点亮屏幕即可。
开发板使用的是【基于?D1s?的带屏旁路由?Yuzuki?RV?Router】,屏幕使用的是【D310T9362V1】,2?lane?MIPI?DSI,硬件连接如下:
适配前的准备
同样的,向屏厂索要屏幕资料。这次提供的资料如下:
一份数据手册与一份屏幕初始化代码。
在数据手册中,我们可以知道这个屏幕使用的是?ST7701s?驱动芯片,分辨率?800x320.
屏幕初始化代码中,配置了?MIPI?的时序,屏幕的亮度,GAMMA等等信息。
编写驱动程序
同样的,这次是?MIPI?DSI?屏幕,就使用?ST7701s.c?这个驱动修改即可。复制一份驱动文件夹,改名?d310t9362v1?,并全局替换?st7701s?到?d310t9362v1。
接下来修改源码,编辑?d310t9362v1.c?文件,修改源码,这里只讲述需要修改的部分,其他部分不做修改即可。
(1)开关屏幕部分
这里按照数据手册里的开关屏幕时序设置即可,ST7701s?的驱动开屏较慢,需要适当增加延时。
static?s32?lcd_open_flow(u32?sel)
{
????LCD_OPEN_FUNC(sel,?lcd_power_on,?10);
????LCD_OPEN_FUNC(sel,?lcd_panel_init,?120);
????LCD_OPEN_FUNC(sel,?sunxi_lcd_tcon_enable,?120);
????LCD_OPEN_FUNC(sel,?lcd_bl_open,?0);
????return?0;
}
static?s32?lcd_close_flow(u32?sel)
{
????LCD_CLOSE_FUNC(sel,?lcd_bl_close,?0);
????LCD_CLOSE_FUNC(sel,?sunxi_lcd_tcon_disable,?0);
????LCD_CLOSE_FUNC(sel,?lcd_panel_exit,?200);
????LCD_CLOSE_FUNC(sel,?lcd_power_off,?500);
????return?0;
}
static?void?lcd_power_on(u32?sel)
{
????sunxi_lcd_pin_cfg(sel,?1);
????sunxi_lcd_delay_ms(50);
????panel_reset(sel,?1);
????sunxi_lcd_delay_ms(5);
????panel_reset(sel,?0);
????sunxi_lcd_delay_ms(10);
????panel_reset(sel,?1);
????sunxi_lcd_delay_ms(120);
}
static?void?lcd_power_off(u32?sel)
{
????sunxi_lcd_pin_cfg(sel,?0);
????sunxi_lcd_delay_ms(20);
????panel_reset(sel,?0);
????sunxi_lcd_delay_ms(5);
}
(2)屏幕初始化部分
这也是最重要的部分,首先定义两个屏幕初始化中没有使用的值作为标志位使用,REGFLAG_DELAY?与?REGFLAG_END_OF_TABLE?。
然后依照屏厂提供的初始化参数编写初始化表
这里需要一些转换,不能像之前的复制粘贴了,需要以每一个写命令(或延时)作为分隔,并与屏厂提供的命令一一对应即可。编写方法具体如下:
(1)写命令,没有数据
{DSICMDCMD,0x01},??----->?{0x01,?1,?{0x00}},
(2)写命令,带有数据
{DSICMD_CMD,0xFF},?
{DSICMD_DATA,0x77},??
{DSICMD_DATA,0x01},??
{DSICMD_DATA,0x00},?----->?{0xFF,?5,?{0x77,?0x01,?0x00,?0x00,?0x11}},
{DSICMD_DATA,0x00},??
{DSICMD_DATA,0x11},?
其中的含义为:
{0xFF,????5,????????{0x77,???????0x01,?0x00,?0x00,?0x11}},
^?命令地址?^?数据个数??^?第一个数据??^?第二个数据?....
(3)延时
{CMDDELAY_MS,120},?----->?{REGFLAG_DELAY,?REGFLAG_DELAY,?{120}},
转换完成的初始化表如下
static?struct?LCM_setting_table?lcm_initialization_setting[]?=?{
????{0x01,?1,?{0x00}?},
????{REGFLAG_DELAY,?REGFLAG_DELAY,?{120}?},
????{0x11,?1,?{0x00}?},
????{REGFLAG_DELAY,?REGFLAG_DELAY,?{120}?},
????{0xff,?5,?{0x77,?0x01,?0x00,?0x00,?0x11}?},
????{0xd1,?1,?{0x11}?},
????{0x55,?1,?{0xb0}?},
????{0xff,?5,?{0x77,?0x01,?0x00,?0x00,?0x10}?},
????{0xc0,?2,?{0x63,?0x00}?},?//?SCNL?=?(0x63?+?1)?*?8?=?800
????{0xc1,?2,?{0x09,?0x02}?},?//?VFB=0x09??VBF=0x02
????{0xc2,?2,?{0x37,?0x08}?},?//?PCLK=?512?+?(0x08?*?16)?=?640
????{0xc7,?1,?{0x00}?},?//?x-dir??rotate?0?:?0x00?????rotate?180?:0x04
????{0xcc,?1,?{0x38}?},
????{0xb0,?16,?{0x00,?0x11,?0x19,?0x0c,?0x10,?0x06,?0x07,?0x0a,?0x09,?0x22,
???????????0x04,?0x10,?0x0e,?0x28,?0x30,?0x1c}?},
????{0xb1,?16,?{0x00,?0x12,?0x19,?0x0d,?0x10,?0x04,?0x06,?0x07,?0x08,?0x23,
????????????0x04,?0x12,?0x11,?0x28,?0x30,?0x1c}?},
????{0xff,?5,?{0x77,?0x01,?0x00,?0x00,?0x11}?},?//??enable??bk?fun?of??command?2??BK1
????{0xb0,?1,?{0x4d}?},
????{0xb1,?1,?{0x5b}?},?//?0x56??0x4a??0x5b
????{0xb2,?1,?{0x07}?},
????{0xb3,?1,?{0x80}?},
????{0xb5,?1,?{0x47}?},
????{0xb7,?1,?{0x8a}?},
????{0xb8,?1,?{0x21}?},
????{0xc1,?1,?{0x78}?},
????{0xc2,?1,?{0x78}?},
????{0xd0,?1,?{0x88}?},
????{REGFLAG_DELAY,?REGFLAG_DELAY,?{100}?},
????{0xe0,?3,?{0x00,?0x00,?0x02}?},
????{0xe1,?11,?{0x01,?0xa0,?0x03,?0xa0,?0x02,?0xa0,?0x04,?0xa0,?0x00,?0x44,
????????????0x44}?},
????{0xe2,?12,?{0x00,?0x00,?0x00,?0x00,?0x00,?0x00,?0x00,?0x00,?0x00,?0x00,
????????????0x00,?0x00}?},
????{0xe3,?4,?{0x00,?0x00,?0x33,?0x33}?},
????{0xe4,?2,?{0x44,?0x44}?},
????{0xe5,?16,?{0x01,?0x26,?0xa0,?0xa0,?0x03,?0x28,?0xa0,?0xa0,?0x05,?0x2a,
????????????0xa0,?0xa0,?0x07,?0x2c,?0xa0,?0xa0}?},
????{0xe6,?4,?{0x00,?0x00,?0x33,?0x33}?},
????{0xe7,?2,?{0x44,?0x44}?},
????{0xe8,?16,?{0x02,?0x26,?0xa0,?0xa0,?0x04,?0x28,?0xa0,?0xa0,?0x06,?0x2a,
????????????0xa0,?0xa0,?0x08,?0x2c,?0xa0,?0xa0}?},
????{0xeb,?7,?{0x00,?0x01,?0xe4,?0xe4,?0x44,?0x00,?0x40}?},
????{0xed,?16,?{0xff,?0xf7,?0x65,?0x4f,?0x0b,?0xa1,?0xcf,?0xff,?0xff,?0xfc,
????????????0x1a,?0xb0,?0xf4,?0x56,?0x7f,?0xff}?},
????{0xff,?5,?{0x77,?0x01,?0x00,?0x00,?0x00}?},
????{0x36,?1,?{0x00}?},?//?U&D??Y-DIR??rotate?0:?0x00?:?rotate?180?:0x10
????{0x3a,?1,?{0x55}?},
????{0x29,?1,?{0x00}?},
????{REGFLAG_END_OF_TABLE,?REGFLAG_END_OF_TABLE,?{}?}
};
(3)屏幕初始化、退出部分
这一部分也不需要修改,默认即可。
(4)屏幕定义部分
这一部分定义了屏幕的用户操作,屏幕的名称等等。
将屏幕驱动添加到内核中
与之前的一样,编辑屏幕驱动文件夹内的?Kconfig?文件,添加引索
config?LCD_SUPPORT_D310T9362V1
????bool?"LCD?support?D310T9362V1?panel"
????default?n
????---help---
????????If?you?want?to?support?D310T9362V1?panel?for?display?driver,?select?it.
然后编辑同目录中的?panel.c,添加结构体
#ifdef?CONFIG_LCD_SUPPORT_D310T9362V1
????&d310t9362v1_panel,
#endif
编辑同目录下的?panel.h?添加结构体
#ifdef?CONFIG_LCD_SUPPORT_D310T9362V1
extern?struct?__lcd_panel?d310t9362v1_panel;
#endif
到上一级目录中,找到?Makefile?文件,增加编译项
disp-$(CONFIG_LCD_SUPPORT_D310T9362V1)?+=?lcd/d310t9362v1.o
内核配置
make?kernel_menuconfig?后,到?Device?Drivers?>?Graphics?support?>?Frame?buffer?Devices?>?Video?support?for?sunxi?>?LCD?panels?select?找到屏幕,勾选即可。
勾选后编译可以看到驱动被编译了
配置设备树
首先,我们在?pio?节点内增加?dsi2lane_pins?作为?LCD?屏幕的?Pin?绑定。
dsi2lane_pins_a:?dsi2lane@0?{
????pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5";
????function?=?"dsi";
????drive-strength?=?<30>;
????bias-disable;
};
dsi2lane_pins_b:?dsi2lane@1?{
????pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5";
????function?=?"io_disabled";
????bias-disable;
};
设备树类似的,按照屏厂提供的参数填写即可。lcd_driver_name?记得与之前驱动中的?.name="d310t9362v1"?相同。
&lcd0?{
????lcd_used????????????=?<1>;
????lcd_driver_name?????=?"d310t9362v1";
????lcd_backlight???????=?<50>;
????lcd_if??????????????=?<4>;
????lcd_x???????????????=?<480>;
????lcd_y???????????????=?<800>;
????lcd_width???????????=?<40>;
????lcd_height??????????=?<67>;
????lcd_pwm_used????????=?<1>;
????lcd_pwm_ch??????????=?<2>;
????lcd_pwm_freq????????=?<1000>;
????lcd_pwm_pol?????????=?<0>;
????lcd_pwm_max_limit???=?<255>;
????lcd_dclk_freq???????=?<34>;
????lcd_hbp?????????????=?<120>;
????lcd_ht??????????????=?<624>;
????lcd_hspw????????????=?<48>;
????lcd_vbp?????????????=?<28>;
????lcd_vt??????????????=?<908>;
????lcd_vspw????????????=?<12>;
????lcd_dsi_if??????????=?<0>;
????lcd_dsi_lane????????=?<2>;
????deu_mode????????????=?<0>;
????lcdgamma4iep????????=?<22>;
????smart_color?????????=?<90>;
????lcd_gpio_0?=??<&pio?PD?9?GPIO_ACTIVE_HIGH>;
????pinctrl-0?=?<&dsi2lane_pins_a>;
????pinctrl-1?=?<&dsi2lane_pins_b>;
};
测试屏幕
开机就点亮了,下文介绍如何显示命令行到屏幕上。
屏幕驱动移植?U-Boot
对于?U-Boot,需要对屏幕驱动进行修改。这里我们以上面移植过的【TL032FWV01】屏幕驱动作为示例,演示如何移植?Linux?的驱动到?U-Boot?内。首先找到屏幕驱动的路径:
brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd
我们先把在?Linux?内测试?OK?的驱动复制到?U-Boot?屏幕驱动目录中。
驱动源码的修改
找到屏幕驱动头文件?TL032FWV01.h?把
extern?struct?__lcd_panel_t?TL032FWV01_panel;
改为
extern?__lcd_panel_t?TL032FWV01_panel;
找到源文件?TL032FWV01.c?把
static?void?LCD_cfg_panel_info(struct?panel_extend_para?*info)
改为
static?void?LCD_cfg_panel_info(panel_extend_para?*info)
找到源文件末尾,把
struct?__lcd_panel?TL032FWV01_panel?=?{
改为
__lcd_panel_t?TL032FWV01_panel?=?{
增加?U-Boot?引索
打开屏幕驱动同目录下的?Kconfig,增加引索
config?LCD_SUPPORT_TL032FWV01
????bool?"LCD?support?TL032FWV01?panel"
????default?n
????---help---
????????If?you?want?to?support?TL032FWV01?panel?for?display?driver,?select?it.
找到同目录下的?panel.c?增加结构体。
#ifdef?CONFIG_LCD_SUPPORT_TL032FWV01
????&TL032FWV01_panel,
#endif????
找到同目录下的?panel.h?增加指针。
#ifdef?CONFIG_LCD_SUPPORT_TL032FWV01
extern?__lcd_panel_t?TL032FWV01_panel;
#endif
前往上一级目录,找到?Makefile?增加编译选项。
disp-$(CONFIG_LCD_SUPPORT_TL032FWV01)?+=?lcd/TL032FWV01.o
启用屏幕驱动
找到?U-Boot?所使用的?defconfig?文件,增加一行
CONFIG_LCD_SUPPORT_TL032FWV01=y
配置?U-Boot?设备树
打开项目对应的?uboot-board.dts?将设备树替换为?Linux?相同的即可。
编译测试
使用命令?muboot?编译?U-Boot,可以在编译的时候看到编译完成了。
开机?LOGO
开机?LOGO?是由?UBOOT?所提供的支持,所以需要配置?UBOOT?的显示屏驱动。
在这之前,先前往?Uboot?检查是否开启了屏幕驱动。
brandy-2.0/u-boot-2018/configs/sun8iw21p1_defconfig
把?CONFIG_DISP2_SUNXI=y?取消注释
然后如同?Kernel?一样,修改?Uboot?的设备树即可。
device/config/chips/v853/configs/vision/uboot-board.dts
&pio?{
????rgb18_pins_a:?rgb18@0?{
????????allwinner,pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,pname?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,function?=?"lcd";
????????allwinner,muxsel?=?<2>;
????????allwinner,drive?=?<3>;
????????allwinner,pull?=?<0>;
????};
????rgb18_pins_b:?rgb18@1?{
????????allwinner,pins?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,pname?=?"PD0",?"PD1",?"PD2",?"PD3",?"PD4",?"PD5",?"PD6",?"PD7",?"PD8",?"PD9",?\
????????"PD10",?"PD11",?"PD12",?"PD13",?"PD14",?"PD15",?"PD16",?"PD17",?"PD18",?"PD19",?\
????????"PD20",?"PD21";
????????allwinner,function?=?"rgb18_suspend";
????????allwinner,muxsel?=?<0xf>;
????????allwinner,drive?=?<1>;
????????allwinner,pull?=?<0>;
????};
};
&lcd0?{
????lcd_used????????????=?<1>;
????lcd_driver_name?????=?"default_lcd";
????lcd_if??????????????=?<0>;
????lcd_x???????????????=?<800>;
????lcd_y???????????????=?<480>;
????lcd_width???????????=?<108>;
????lcd_height??????????=?<64>;
????lcd_dclk_freq???????=?<24>;
????lcd_pwm_used????????=?<1>;
????lcd_pwm_ch??????????=?<9>;
????lcd_pwm_freq????????=?<50000>;
????lcd_pwm_pol?????????=?<0>;
????lcd_pwm_max_limit???=?<255>;
????lcd_ht??????????????=?<816>;
????lcd_hbp?????????????=?<12>;
????lcd_hspw????????????=?<4>;
????lcd_vt??????????????=?<496>;
????lcd_vbp?????????????=?<12>;
????lcd_vspw????????????=?<4>;
????lcd_lvds_if?????????=?<0>;
????lcd_lvds_colordepth?=?<1>;
????lcd_lvds_mode???????=?<0>;
????lcd_frm?????????????=?<0>;
????lcd_io_phase????????=?<0x0000>;
????lcd_gamma_en????????=?<0>;
????lcd_bright_curve_en?=?<0>;
????lcd_cmap_en?????????=?<0>;
????deu_mode????????????=?<0>;
????lcdgamma4iep????????=?<22>;
????smart_color?????????=?<90>;
????pinctrl-0?=?<&rgb18_pins_a>;
????pinctrl-1?=?<&rgb18_pins_b>;
};
替换开机?LOGO?文件
如果希望修改开机?LOGO,可以修改下面路径里的这个图片文件
device/config/chips/d1-h/configs/nezha/configs
注意,图片不宜过大,太大的图片容易导致加载缓慢。而且图片需要?BMP?格式,24位深
打包烧写即可,替换?bootlogo?不需要编译
显示命令行到屏幕上
这一部分仅?5.4?内核可以使用,4.9?内核不适用
Linux?自带有?FBCON?驱动,可以显示命令行到屏幕上作为小电脑使用。
配置?Kernel?选项
首先?make?kernel_menuconfig?勾选以下配置
(1)选中?TTY?设备
Device?Drivers?>?Character?devices
????[*]???Virtual?terminal
(2)启用?FBCON
Device?Drivers?>?Graphics?support?>?Console?display?driver?support
????[*]?VGA?text?console
????[*]?Framebuffer?Console?support
(3)配置启动?console?输出
打开项目文件夹内的?env.cfg?在?console=${console}?前加入?console=tty0,对应自己的?setargs,mmc?启动使用?setargs_mmc?,nand?启动使用?setargs_nand,以此类推。
即可开机显示命令行
|