Sc8810 DataSheet分析LCD初始化
一、基础条件
依赖代码:Sc8810_fb.c
LCD型号:NT35510_A45026N00HN
pdf中的内容如下:
二、初始化分析
1、初始化部分从static int sc8810fb_probe(void * lcdbase)开始
LCD最后Application Notes中提到如下:
对应代码static void hw_early_init(struct sc8810fb_info *fb),如下:
//select LCD clock source
__raw_bits_and(~(1<<6), GR_PLL_SRC); //pll_src=96M
__raw_bits_and(~(1<<7), GR_PLL_SRC);
//set LCD divdior
__raw_bits_and(~(1<<0), GR_GEN4); //div=0
__raw_bits_and(~(1<<1), GR_GEN4);
__raw_bits_and(~(1<<2), GR_GEN4);
//enable LCD clock
__raw_bits_or(1<<3, AHB_CTL0);
//LCD soft reset
__raw_bits_or(1<<3, AHB_SOFT_RST);
mdelay(10);
__raw_bits_and(~(1<<3), AHB_SOFT_RST);
//LCD enable LCDC global interrupt
__raw_bits_and(~(1<<0), LCDC_IRQ_EN);
配置好上述寄存器之后,执行到显示模式,datasheet描述拉如下:
对应代码如下:
__raw_bits_or((1<<0), LCDC_IRQ_CLR);
执行子函数lcdc_mcu_init,其内容如下:
//panel reset
panel_reset();
//LCDC module enable
reg_val |= (1<<0);
/*FMARK mode*/
reg_val |= (1<<1); // no f-mark
/*FMARK pol*/
//reg_val |= (1<<2);
__raw_writel(reg_val, LCDC_CTRL);
2、为了区分同一个厂家的LCD模块,由于其读取的ID是相同的,因此需要读取电压AD值来区分不同型号
ADC_Init(); //为了读取电压值
ANA_REG_OR(ANA_AGEN, AGEN_ADC_EN);
ANA_REG_OR(ANA_CLK_CTL, ACLK_CTL_AUXAD_EN | ACLK_CTL_AUXADC_EN);
ANA_REG_OR(ADC_CTRL, ADC_EN_BIT);
3、find_adapt_from_readid读取LCD的ID值,并进行挂载,从而才能实现特定的LCD屏幕初始化。
for循环依次挂载,并相应初始化后,再读取ID值。
int i;
uint32_t id;
for(i = 0;i<(sizeof(lcd_panel))/(sizeof(lcd_panel[0]));i++) {
//first ,try mount
mount_panel(fb,lcd_panel[i].panel);
//hw init to every panel
hw_init(fb);
//readid
if(fb->panel->ops->lcd_readid) {
id = fb->panel->ops->lcd_readid(fb->panel);
FB_PRINT("[The panel id is 0x%x]\n", id);
} else {
id = lcd_readid_default(fb->panel);
}
//if the id is right?
if(id == lcd_panel[i].lcd_id) {
FB_PRINT("[The lcd panel return id is 0x%x]\n", id);
save_lcd_id_to_kernel(id);
return i;
}
}
return -1;
4、分配显存
fb->smem_start = (uint32_t)lcdbase;
fb->smem_len = fb->panel->width * fb->panel->height;
5、这里根据datasheet的操作如下:
初始化代码中是先进行LCM参数配置,再进行图层信息的配置,个人觉得这里初始化顺序允许颠倒。
代码中LCM参数初始化对应static void hw_init(struct sc8810fb_info *fb),如下:
/* only MCU mode is supported currently */
if (LCD_MODE_RGB == fb->panel->mode)
return;
//panel reset
panel_reset(fb->panel);
/* set lcdc-lcd interface parameters */
lcdc_lcm_configure(fb);
/* set timing parameters for LCD */
lcdc_update_lcm_timing(fb->register_timing);
其中lcdc_lcm_configure(fb);内容如下:
uint32_t reg_val = 0;
/* CS1 bus mode [BIT0]: 8080/6800 */
switch (fb->panel->info.mcu->bus_mode) {
case LCD_BUS_8080:
break;
case LCD_BUS_6800:
reg_val |= (1 << 8);
break;
default:
break;
}
/* CS1 bus width [BIT1:0] */
switch (fb->panel->info.mcu->bus_width) {
case 8:
break;
case 9:
reg_val |= ((1 << 9) | (1 << 12));
break;
case 16:
reg_val |= (2 << 9);
break;
case 18:
reg_val |= ((3 << 9) | (1 << 12));
break;
case 24:
reg_val |= ((4 << 9) | (2 << 12));
break;
default:
break;
}
reg_val |= (1 << 16);
__raw_writel(reg_val, LCM_CTRL);
其中lcdc_update_lcm_timing(fb->register_timing);内容如下:
__raw_writel(value, LCM_PARAMETER1);
6、以上初始化LCM,根据之前的datasheet知道接着初始化OSD,这之前需要先初始化屏幕,然后再初始化OSD, fb->panel->ops->lcd_init(fb->panel);
7、初始化OSD和IMG总共6层
__raw_bits_and(~(1<<0),LCDC_IMG_CTRL);
__raw_bits_and(~(1<<0),LCDC_OSD2_CTRL);
__raw_bits_and(~(1<<0),LCDC_OSD3_CTRL);
__raw_bits_and(~(1<<0),LCDC_OSD4_CTRL);
__raw_bits_and(~(1<<0),LCDC_OSD5_CTRL);
reg_val |= (1 << 0);
reg_val |= (1 << 2);
reg_val |= (5 << 3); //RGB565
reg_val |= (2 << 7); //B2B3B0B1
__raw_writel(reg_val, LCDC_OSD1_CTRL);
/* OSD1 layer base */
reg_val = fb->smem_start;
/*OSD1 layer alpha value*/
__raw_writel(0xff, LCDC_OSD1_ALPHA);
/*OSD1 layer size*/
reg_val = ( fb->panel->width & 0xfff) | (( fb->panel->height & 0xfff )<<16);
__raw_writel(reg_val, LCDC_OSD1_SIZE_XY);
/*OSD1 layer start position*/
__raw_writel(0, LCDC_OSD1_DISP_XY);
/*OSD1 layer pitch*/
reg_val = ( fb->panel->width & 0xfff) ;
__raw_writel(reg_val, LCDC_OSD1_PITCH);
8、最后设置LCDC和LCM的尺寸
/*LCDC workplane size*/
set_lcdsize(fb->panel);
/*LCDC LCM rect size*/
set_lcmrect(fb->panel);
9、清除LCD,并设置开机Logo
lcd_clear (NULL, 1, 1, NULL); /* dummy args */
lcd_enable ();//未实现
10、通过阅读datasheet后面的屏幕初始化,刷屏如下:
通过代码查找,定位到static void real_refresh(struct sc8810fb_info *fb)
fb->panel->ops->lcd_invalidate(fb->panel);
/* set timing parameters for LCD */
lcdc_update_lcm_timing(fb->gram_timing);
__raw_bits_or((1<<3), LCDC_CTRL); /* start refresh */
while(!(__raw_readl(LCDC_IRQ_RAW) & (1<<0))); // wait util done
__raw_bits_or((1<<0), LCDC_IRQ_CLR);
/* set timing parameters for LCD */
lcdc_update_lcm_timing(fb->register_timing);
三、总结
不难看出,整个屏幕初始化程序的编写基本都是依据datasheet中的lcd application notes编写的,因此如果以后需要编写初始化程序,完全可以按照datasheet中的应用来编写或者分析。