展锐Android-Q LCD调试
模块功能描述
LCD模块功能主要是LCD液晶显示
LCD移植准备
以 skyworth ili9881c hd为例,首先需要准备东西如下:
1、屏IC Data Sheet
2、初始化代码 —>获得初始化屏幕ic的命令,用于编写u-boot初始化文件和.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
u-boot移植
增加LCD驱动文件
参考已有lcd驱动文件,添加要增加lcd的驱动文件,命名规则panel_ic_vendor_platform_res_mod.c
从原理图中获取gpio LCM_GPIO_VCC和LCM_GPIO_POWER
#include "sprd_panel.h"
#include "sprd_dsi.h"
#include "dsi/mipi_dsi_api.h"
#include "sprd_dphy.h"
static uint8_t init_data[] = {
0x39,0x00,0x00,0x04, 0xFF,0x98,0x81,0x03,
0x23,0x00,0x00,0x02, 0x01,0x00,
0x23,0x00,0x00,0x02, 0x02,0x00,
......
0x39,0x00,0x00,0x04, 0xFF,0x98,0x81,0x00,
0x23,0x00,0x00,0x02, 0x35,0x00,
0x39,0x00,0x00,0x03, 0x51,0xff,0xff,
0x23,0x00,0x00,0x02, 0x53,0x2c,
0x23,0x78,0x00,0x02, 0x11,0x00,
0x23,0x0A,0x00,0x02, 0x29,0x00,
CMD_END
};
//发送命令
static int mipi_dsi_send_cmds(struct sprd_dsi *dsi, void *data)
{
uint16_t len;
struct dsi_cmd_desc *cmds = data;
if ((cmds == NULL) || (dsi == NULL))
return -1;
for (; cmds->data_type != CMD_END;) {
len = (cmds->wc_h << 8) | cmds->wc_l;
mipi_dsi_gen_write(dsi, cmds->payload, len);
if (cmds->wait)
msleep(cmds->wait);
cmds = (struct dsi_cmd_desc *)(cmds->payload + len);
}
return 0;
}
static int ili9881c_init(void)
{
struct sprd_dsi *dsi = &dsi_device;
struct sprd_dphy *dphy = &dphy_device;
mipi_dsi_lp_cmd_enable(dsi, true);
mipi_dsi_send_cmds(dsi, init_data);
mipi_dsi_set_work_mode(dsi, SPRD_MIPI_MODE_VIDEO);
mipi_dsi_state_reset(dsi);
mipi_dphy_hs_clk_en(dphy, true);
return 0;
}
//读取LCD屏id
static int ili9881c_readid(void)
{
struct sprd_dsi *dsi = &dsi_device;
uint8_t cmd[] = {0xFF, 0x98, 0x81, 0x00};
uint8_t read_buf[4] = {0};
mipi_dsi_lp_cmd_enable(dsi, true);
mipi_dsi_gen_write(dsi, cmd, ARRAY_SIZE(cmd));
mipi_dsi_set_max_return_size(dsi, 1);
mipi_dsi_dcs_read(dsi, 0xda, &read_buf[0], 1);
//mipi_dsi_dcs_read(dsi, 0x01, &read_buf[1], 1);
printk("lcd_ili9881c_mipi read id 0xda value is 0x%x !\n", read_buf[0]);
if((0x05 == read_buf[0])) {
pr_info("ili9881c read id success!\n");
return 0;
}
pr_err("ili9881c read id failed!\n");
return -1;
}
static int ili9881c_power(int on)
{
sprd_gpio_request(NULL, LCM_GPIO_VCC);
sprd_gpio_request(NULL, LCM_GPIO_POWER);
sprd_gpio_request(NULL, CONFIG_LCM_GPIO_RSTN);
printk("lcd_ili9881c_hd_mipi reset vcc=%d power=%d rst=%d !\n", LCM_GPIO_VCC, LCM_GPIO_POWER, CONFIG_LCM_GPIO_RSTN);
sprd_gpio_direction_output(NULL, LCM_GPIO_VCC, 1);
mdelay(5);
sprd_gpio_direction_output(NULL, LCM_GPIO_POWER, 1);
mdelay(5);
if (on) {
sprd_gpio_request(NULL, CONFIG_LCM_GPIO_RSTN);
sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 1);
mdelay(5);
sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 0);
mdelay(5);
sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 1);
mdelay(20);
} else {
sprd_gpio_direction_output(NULL, CONFIG_LCM_GPIO_RSTN, 0);
mdelay(5);
}
return 0;
}
/*
static int ili9881c_set_brightness(int brightness)
{
unsigned char set_bl_seq[] = {0x51, 0x0F,0xFF};
struct sprd_dsi *dsi = &dsi_device;
set_bl_seq[1] = brightness >> 4 & 0x0F;
set_bl_seq[2] = brightness;
mipi_dsi_gen_write(dsi,&set_bl_seq[0], 3);
return 0;
}
*/
static struct panel_ops ili9881c_ops = {
.init = ili9881c_init,
.read_id = ili9881c_readid,
.power = ili9881c_power,
//.set_brightness = ili9881c_set_brightness,
};
static struct panel_info ili9881c_info = {
/* common parameters */
.lcd_name = "lcd_ili9881c_skyworth_hd_mipi_hd",
.type = SPRD_PANEL_TYPE_MIPI,
.bpp = 24,
.width = 720,
.height = 1280,
/* DPI specific parameters */
.pixel_clk = 64000000,
.rgb_timing = {
.hfp = 88, /* unit: pixel */
.hbp = 120,
.hsync = 64,
.vfp = 16, /*unit: line*/
.vbp = 24,
.vsync = 8,
},
/* MIPI DSI specific parameters */
.phy_freq = 552*1000,
.lane_num = 4,
.work_mode = SPRD_MIPI_MODE_VIDEO,
.burst_mode = PANEL_VIDEO_BURST_MODE,
.nc_clk_en = false,
};
struct panel_driver lcd_ili9881c_skyworth_hd_mipi_driver = {
.info = &ili9881c_info,
.ops = &ili9881c_ops,
};
添加编译规则
在bsp/bootloader/u-boot15/drivers/video/sprd/lcd/Makefile中添加
obj-$(CONFIG_FB_LCD_ILI9881C_SKYWORTH_HD_MIPI) += lcd_ili9881c_skyworth_hd_mipi.o
配置LCD编译选项
在bsp//bootloader/u-boot15/include/configs/8541e_1h10.h
#define LCM_GPIO_VCC 72
#define LCM_GPIO_POWER 73
#define CONFIG_FB_LCD_ILI9881C_SKYWORTH_HD_MIPI
关联到内核
在bsp/bootloader/u-boot15/drivers/video/sprd/lcd/panel_cfg.h中修改
extern struct panel_driver lcd_ili9881c_skyworth_hd_mipi_driver;
static struct panel_cfg supported_panel[] = {
#ifdef CONFIG_FB_LCD_ILI9881C_SKYWORTH_HD_MIPI
{
.lcd_id = 0x988105,
.drv = &lcd_ili9881c_skyworth_hd_mipi_driver,
}
}
配置LCD电源LDO
在bsp/bootloader/u-boot15/board/spreadtrum/8541e_1h10/ldo_sleep.c是能ldo
void DCDC_ldo_power_on()
{
regulator_enable("vddgen");
regulator_enable("vdd28");
}
在bspbootloader/u-boot15/board/spreadtrum/8541e_1h10/regulator_init.c中设置ldo电流强度
static int power_on_voltage_init(void)
{
regulator_set_voltage("vddgen",1800);
regulator_set_voltage("vdd28",2800);
}
编译uboot并烧录,查看LCD是否被点亮。如果没有,添加适当log查看,同时查看porch值、clk、freq是否配置正确。
配置kernel
参考已有lcd,添加dtsi文件
arch/arm/boot/dts/lcd/lcd_ili9881c_mipi_skyworth_hd.dtsi
/ { lcds {
lcd_ili9881c_skyworth_hd_mipi_hd: lcd_ili9881c_skyworth_hd_mipi_hd {
sprd,dsi-work-mode = <1>;
sprd,dsi-lane-number = <4>;
sprd,dsi-color-format = "rgb888";
sprd,phy-bit-clock = <552000>;
sprd,phy-escape-clock = <20000>;
sprd,width-mm = <68>;
sprd,height-mm = <121>;
sprd,esd-check-enable = <0>;
sprd,esd-check-mode = <1>;
sprd,esd-check-period = <1000>;
sprd,reset-on-sequence = <1 5>, <0 5>, <1 20>;
sprd,reset-off-sequence = <0 20>;
sprd,initial-command = [
39 00 00 04 FF 98 81 03
23 00 00 02 01 00
23 00 00 02 02 00
......
13 78 00 01 11
13 14 00 01 29
];
sprd,sleep-in-command = [
13 0A 00 01 28
13 78 00 01 10
];
sprd,sleep-out-command = [
13 78 00 01 11
13 64 00 01 29
];
display-timings {
timing0 {
clock-frequency = <64000000>;
hactive = <720>;
vactive = <1280>;
hback-porch = <120>;
hfront-porch = <88>;
vback-porch = <24>;
vfront-porch = <16>;
hsync-len = <68>;
vsync-len = <8>;
};
};
oled-backlight {
default-brightness = <25>;
max-level = <255>;
brightness-levels = [
39 14 00 03 51 00 00
39 00 00 03 51 00 10
......
39 00 00 03 51 0F E0
39 00 00 03 51 0F F0
];
};
};
};
};
引用dtsi文件
在bsp/kernel/kernel4.14/arch/arm/boot/dts/8541e-1h10.dts中添加
#include "lcd/lcd_ili9881c_mipi_skyworth_hd.dtsi"
编译kernel并烧录,查看LCD是否被点亮。如果没有,添加适当log查看,同时查看porch值、clk、freq是否配置正确。
问题总结
问题1:u-boot阶段lcd未被点亮
解决方法:
1、添加log,查看lcd id是否读取正常,保证走正常的init流程
2、查看porch值、clk、freq是否正常,
phy_feq 理论计算
(pixelclk * 24 * 1.2)/lane_num
pixelclk理论计算
(width+hfp +hbp +hsync )*(height +vfp +vbp +vsync )*fps
问题2:kernel阶段lcd未被点亮
解决方案:
查看porch值、clk、freq是否正常,
phy_feq 理论计算
(pixelclk * 24 * 1.2)/lane_num
pixelclk理论计算
(width+hfp +hbp +hsync )*(height +vfp +vbp +vsync )*fps
问题3:背光调节有问题
解决方案
1、现有lcd背光调节方式为发命令给ic,ic自己调节,展锐默认背光调节为pwm调节,
2、修改kernel/kernel4.14/arch/arm/boot/dts/8541e-1h10-overlay.dts
将 pwm_backlight: sprd_backlight 修改为 pwm_backlight: sprd_pwm_backlight
问题3:调节背光值到最大,死机
解决方案:
背光调节时,调用的为/kernel/kernel4.14/drivers/gpu/drm/sprd/sprd_panel.c中的static int sprd_oled_set_brightness(struct backlight_device *bdev)函数,添加log发现,背光为最大值时,数据溢出,查看,发现背光brightness-levels个数为255,而struct sprd_oled中定义struct dsi_cmd_desc *cmds[255],当背光为255时,确实溢出了,因此,修改struct dsi_cmd_desc *cmds[255]为struct dsi_cmd_desc *cmds[256].
来源:CSDN
作者:悲伤的小强
链接:https://blog.csdn.net/qq_29890089/article/details/104041365