LCD模块功能主要是LCD液晶显示
以helitech ili9881d为例,首先需要准备东西如下:
1、屏IC Data Sheet
2、初始化代码 —>获得初始化屏幕ic的命令,用于编写屏的.h文件(lk)和.dtsi文件(kernel)
首先从Data Sheet或者初始化代码中可以获得以下一些信息:
1、水平脉冲宽度(qcom,mdss-dsi-h-pulse-width,Hsync)为68
2、水平后沿值(qcom,mdss-dsi-h-back-porch,HBP)为120
3、水平前沿值(qcom,mdss-dsi-h-front-porch,HFP)为88
4、垂直脉冲宽度(qcom,mdss-dsi-v-pulse-width,Vsync)为8
5、垂直后沿值(qcom,mdss-dsi-v-back-porch,VBP)为24
6、垂直前沿值(qcom,mdss-dsi-v-front-porch,VFP)为16
7、面板高度(qcom,mdss-dsi-panel-height,VAdr)为1280
8、面板宽度(qcom,mdss-dsi-panel-width,HAdr)为720
首先准备好LCD的.h(命名规则panel_ic_vendor_platform_res_mod)
因为ILI9881D型号的LCD在高通的推荐供应商列表中,所以可以在高通官网中找到类似的patch,里面有相关的.h代码,仿照来合成需要的.h文件。
在这个.h文件中,需要修改的地方有如下:
static struct panel_resolution ili9881d_hlt_a8_720p_video_panel_res = {
720, 1280, 88, 120, 68, 0, 16, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
这个结构体包含了显示屏的许多参数,在panel.h中可以找到其结构体如下:
typedef struct panel_resolution{
uint16_t panel_width;
uint16_t panel_height;
uint16_t hfront_porch;
uint16_t hback_porch;
uint16_t hpulse_width;
uint16_t hsync_skew;
uint16_t vfront_porch;
uint16_t vback_porch;
uint16_t vpulse_width;
uint16_t hleft_border;
uint16_t hright_border;
uint16_t vtop_border;
uint16_t vbottom_border;
uint16_t hactive_res;
uint16_t vactive_res;
uint16_t invert_data_polarity;
uint16_t invert_vsync_polarity;
uint16_t invert_hsync_polarity;
};
根据屏的信息,把数据填入相应的位置。
const uint32_t ili9881d_hlt_a8_720p_video_timings[] = {
0x86, 0x1E, 0x14, 0x00, 0x42, 0x46, 0x18, 0x20, 0x16, 0x03, 0x04, 0x00
};
指定面板 PHY 定时设置的长度为 12 的数组。这组数据一般是根据计算得到.
static struct panel_timing ili9881d_hlt_a8_720p_video_timing_info = {
0, 4, 0x04, 0x1B
};
0x04是指qcom,mdss-dsi-t-clk-post,指定模式切换后的字节时钟周期。
0x1B是指 qcom,mdss-dsi-t-clk-pre,指定模式切换前的字节时钟周期。
上面两组数据通过DSI_Timing_Parameters.xlsm计算得来,方法如下:
将从规格书中获得信息依次填入上表,然后ctrl+j;ctrl+k;得到
DSI PHY registers和DSI Registers,如下图
`
Static struct panel_reset_sequence ili9881d_hlt_a8_720p_video_panel_reset_seq = {
{ 1, 0, 1, }, { 20, 20, 20, }, 2
};
对应的结构体如下:
typedef struct panel_reset_sequence {
//引脚的状态,数据对应的含义分别为,拉低,拉高,拉低
uint8_t pin_state[TOTAL_RESET_GPIO_CTRL];
//拉高、拉低的状态持续的时间,分别为20、20和20毫秒
uint32_t sleep[TOTAL_RESET_GPIO_CTRL];
//pin脚的两个方向
uint8_t pin_direction;
};
static struct backlight ili9881d_hlt_a8_720p_video_backlight = {
2, 1, 4095, 100, 1, "PMIC_8909"
};
对应的结构体如下:
typedef struct backlight {
uint16_t bl_interface_type;
uint16_t bl_min_level;
uint16_t bl_max_level; //背光的最大值为4096
uint16_t bl_step;
uint16_t bl_pmic_controltype;
char *bl_pmic_model; //背光的模式
};
看一下厂家给的初始化代码:
REGISTER,FF,03,98,81,03
REGISTER,02,01,00
REGISTER,03,01,56
REGISTER,04,01,13
REGISTER,05,01,00
………
REGISTER,FF,03,98,91,00
REGISTER,35
REGISTER,11
Delayms(120);
REGISTER,29
Delayms(10);
参考已有的LCD屏幕,可以模仿有:
厂家提供的初始化代码中没有点亮背光的代码,自己Data Sheet中找到ILI9881d的背光控制为0x51和0x53;
mipi_dsi_cmd对应的结构体如下:
struct mipi_dsi_cmd {
int size; //大小
char *payload; //命令
int wait; //等待时间
uint8_t cmds_post_tg;
};
从中可以看出,每一条命令写入一个ili9881d_hlt_a8_720p_video_on_command[]中,最后在汇总到
static struct mipi_dsi_cmd结构体中。
至此,.h文件完成了。
接下来的步骤是使得lk代码可以调用这个.h文件
1、在msm_panel.h中,添加这个屏的LCM_ID
2、在bootloader/lk/platform/msm_shared/mipi_dsi.c 中的mdss_dsi_panel_initialize函数中可以通get_panel_rdid来获取屏幕的LCMID,给panel_ic_id赋值;
3、在bootable/bootloader/lk/target/msm8909/oem_panel.c中
首先,添加lcd头文件#include “include/xxx.h” —>上一步的头文件
接着在init_panel_data函数中,利用switch (panel_ic_id),找到相应的地方,给panelstruct结构体赋值
//panelstruct结构体
typedef struct panel_struct{
struct panel_config *paneldata;
struct panel_resolution *panelres;
struct color_info *color;
struct videopanel_info *videopanel;
struct commandpanel_info *commandpanel;
struct command_state *state;
struct lane_configuration *laneconfig;
struct panel_timing *paneltiminginfo;
struct panel_reset_sequence *panelresetseq;
struct backlight *backlightinfo;
struct fb_compression fbcinfo;
};
至此lk中的移植就完成了.
在Kernel中移植lcd ,流程大致和在lk中移植lcd差不多
首先要写好.dts文件
创建arch/arm/boot/dts/qcom/dsi-panel-xxx-video.dtsi文件
文件就一个节点,&mdss_mdp,这其中有显示屏的节点dsi_xxx,其中很多节点的数值都在LK中有提及
&mdss_mdp {
dsi_ili9881d_hlt_a8_hd_vid: qcom,mdss_dsi_ili9881d_hlt_a8_hd_video
(跟LK添加的显示屏的节点名称(panel_config->panel_node_id)一样)
{
//qcom,mdss-dsi-panel-name = " ic vendor platform res mod dsi panel ";
qcom,mdss-dsi-panel-name = "ili9881d hlt a8 hd video mode dsi panel";
qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
qcom,mdss-dsi-panel-type = "dsi_video_mode";
qcom,mdss-dsi-panel-destination = "display_1";
qcom,mdss-dsi-panel-framerate = <60>;
qcom,mdss-dsi-virtual-channel-id = <0>;
qcom,mdss-dsi-stream = <0>;
qcom,mdss-dsi-panel-width = <720>;//面板宽度
qcom,mdss-dsi-panel-height = <1280>;//面板高度
qcom,mdss-dsi-h-front-porch = <88>;//水平前沿值
qcom,mdss-dsi-h-back-porch = <120>;//水平后沿值
qcom,mdss-dsi-h-pulse-width = <68>;//水平脉冲宽度
qcom,mdss-dsi-h-sync-skew = <0>;
qcom,mdss-dsi-v-back-porch = <24>;//垂直后沿值
qcom,mdss-dsi-v-front-porch = <16>;//垂直前沿值
qcom,mdss-dsi-v-pulse-width = <8>;//垂直脉冲宽度
qcom,mdss-dsi-h-left-border = <0>;
qcom,mdss-dsi-h-right-border = <0>;
qcom,mdss-dsi-v-top-border = <0>;
qcom,mdss-dsi-v-bottom-border = <0>;
qcom,mdss-dsi-bpp = <24>;
qcom,mdss-dsi-color-order = <0>;
qcom,mdss-dsi-underflow-color = <0xff>;
qcom,mdss-dsi-border-color = <0>;
qcom,ulps-enabled;
//厂商初始化改为以下属性,每一行一条命令
//39--写,格式,04--写的字节,命令
qcom,mdss-dsi-on-command =
[
39 01 00 00 00 00 04 FF 98 81 03
39 01 00 00 00 00 02 02 00
39 01 00 00 00 00 02 03 56
39 01 00 00 00 00 02 04 13
.....
39 01 00 00 00 00 03 51 0F FF
39 01 00 00 00 00 02 53 24
39 01 00 00 00 00 02 35 00
05 01 00 00 80 00 02 11 00
05 01 00 00 20 00 02 29 00
];
qcom,mdss-dsi-off-command = [
05 01 00 00 32 00 02 28 00
05 01 00 00 78 00 02 10 00
];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
qcom,mdss-dsi-h-sync-pulse = <0>;
qcom,mdss-dsi-traffic-mode = "burst_mode";
qcom,mdss-dsi-lane-map ="lane_map_0123";
qcom,mdss-dsi-bllp-eof-power-mode;
qcom,mdss-dsi-bllp-power-mode;
qcom,mdss-dsi-lane-0-state;
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
qcom,mdss-dsi-lane-3-state;
//指定面板 PHY 定时设置的长度为 12 的数组,跟lk一样
qcom,mdss-dsi-panel-timings =[86 1E 14 00 42 46 18 20 16 03 04 00];
//指定模式切换后的字节时钟周期,跟lk一样
qcom,mdss-dsi-t-clk-post = <0x04>;//1f>;
//指定模式切换前的字节时钟周期,跟lk一样
qcom,mdss-dsi-t-clk-pre = <0x1b>;//x2d>;
qcom,mdss-dsi-bl-min-level = <20>;
//背光最大电流,4095
qcom,mdss-dsi-bl-max-level = <4095>;
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
//屏幕的上电时序,1代表拉高,0代表拉低,20代表20ms时间
qcom,mdss-dsi-reset-sequence = <1 20>,<0 2>, <1 20>;
qcom,mdss-dsi-lp11-init;
qcom,cont-splash-enabled;
qcom,mdss-dsi-hbp-power-mode;
qcom,mdss-dsi-panel-status-check-mode = "bta_check";
};
};
在对应的msm89-qrd-skue.dtsi加上dsi-panel-xxx-video.dtsi,以在A8添加ILI9881d为例,则在msm8909-qrd-skue.dtsi中添加
dsi-panel-ili9881d-holitech-a8-hd-video.dtsi;