高通安卓调试LCD几方面总结(二)


高通安卓调试LCD几方面总结(二)

       在上一篇里写了高通平台android2.3里的kernel和bootloader(LK)里LCD驱动的移植,这一篇主要写一下在4.0里LCD驱动的移植。

  

   (1) kernel

   高通的android4.0和2.3在kernel里的LCD驱动是有区别的,主要在于4.0里少了latedisplay.c这个文件。这里我以renesas公司的r61408这颗IC来介绍。我使用的是RGB565的接口,由于kernel里有高通公司提供的其它家IC的代码,我同样是以truly的来作为参考的,代码的框架不变。首先就是在kernel\drivers\video\msm下以truly的代码为模板,创建一个名为lcdc_r61408.c的文件,文件里的内容照着truly的来做。

   这里先来把Makefile和Kconfig文件修改好。在kernel\drivers\video\msm文件夹下的Makefile里添加:

         

[cpp] view plain copy print ?
  1. obj-$(CONFIG_FB_MSM_LCDC_RENESAS_R61408) += lcdc_renesas_r61408.o 

   在此目录下打开Kconfig文件,在里面添加:

  

[cpp] view plain copy print ?
  1. config FB_MSM_LCDC_RENESAS_R61408 
  2.   bool 
  3.   select FB_MSM_LCDC_PANEL 
  4.   default
  5.  
  6. config FB_MSM_LCDC_RENESAS_R61408_PT_PANEL 
  7.   depends on FB_MSM_LCDC_HW 
  8.   bool "LCDC Renesas PT Panel" 
  9.   select FB_MSM_LCDC_RENESAS_R61408 
  10.   default
  11.   ---help--- 
  12.   Support for LCDC Renesas PT panel 

   还要在kernel\arch\arm\configs目录下的board config文件里添加:

[cpp] view plain copy print ?
  1. CONFIG_FB_MSM_LCDC_RENESAS_R61408_PT_PANEL=y 

   在lcdc_r61408.c文件里主要就是要写向LCD用SPI发指令和发数据的函数,我们的这个项目里使用的是GPIO口模拟的SPI总线,所以SPI的时序一定要写正确,R61408在用SPI发指令的时候data和command是在写8bit数据前通过SDO口的高和低来判别的,但是有些LCD的IC,比如NT35510,需要在发8bit数据之前要发送R/W,H/L,D/C几个信号来区别接下来发的数据表示什么。所以一定要根据所选用的LCD的IC的spec严格的来写SPI的时序,否则有可能造成LCD的初始化都不成功。

   在kernel里,LCD的驱动的调用的流程是:

[cpp] view plain copy print ?
  1. msm_fb_open()  (msm_fb.c) 
  2.   -> msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable) 
  3.     -> if (!mfd->panel_power_on) { 
  4.            ret = pdata->on(mfd->pdev); //这里的on指向的就是lcdc.c里的lcdc_on()函数 
  5.                 -> lcdc_on() /*这个函数里开启LCDC时钟,并且打开LCD*/ 
  6.               -> clk_prepare_enable(pixel_mdp_clk); 
  7.                  clk_prepare_enable(pixel_lcdc_clk); 
  8.                  if (lcdc_pdata && lcdc_pdata->lcdc_power_save) 
  9.                    lcdc_pdata->lcdc_power_save(1);/*函数指针,指向board-display.c里的msm_lcdc_power_save()*/ 
  10.                  if (lcdc_pdata && lcdc_pdata->lcdc_gpio_config)/*这里lcdc_gpio_config指针是空,所以不会接着调 下一步*/ 
  11.                        ret = lcdc_pdata->lcdc_gpio_config(1); 
  12.                  ret = panel_next_on(pdev);/*函数指针,指向lcdc_renesas_r61408.c里的lcdc_renesas_panel_on()函数*/ 
[cpp] view plain copy print ?
  1. panel_next_on(pdev)  (lcdc.c) 
  2.   -> lcdc_renesas_panel_on() (lcdc_renesas_r61408.c) 
  3.     -> renesas_disp_reginit()    /*初始化LCD的寄存器*/ 
  4.       -> renesas_spi_write_bytes() /*设用SPI写函数*/ 
[cpp] view plain copy print ?
  1. lcdc_renesas_panel_init(void) (lcdc_renesas_r61408.c) 
  2. -> platform_driver_register(&this_driver);/*注册LCD驱动,此时renesas_probe函数会调一次,主要是为了从board-display.c里获取SPI和背光各管脚的定义值*/ 
  3.    /*设置与LCD相关的参数,具体各参数意义参考上一篇*/ 
  4.    pinfo->xres = 480; 
  5.    pinfo->yres = 800; 
  6.    MSM_FB_SINGLE_MODE_PANEL(pinfo); 
  7.    pinfo->type = LCDC_PANEL; 
  8.    pinfo->pdest = DISPLAY_1; 
  9.    pinfo->wait_cycle = 0; 
  10.    pinfo->bpp = 16; 
  11.    pinfo->fb_num = 2; 
  12.    pinfo->clk_rate = 30720000; 
  13.    pinfo->bl_max = 32; 
  14.    pinfo->bl_min = 1; 
  15.    pinfo->lcdc.h_back_porch = 10;/* hsw = 8 + hbp=16 */ 
  16.    pinfo->lcdc.h_front_porch = 150;////100 
  17.    pinfo->lcdc.h_pulse_width = 10; 
  18.    pinfo->lcdc.v_back_porch = 22;///12/* vsw=1 + vbp = 7 */ 
  19.    pinfo->lcdc.v_front_porch = 10; 
  20.    pinfo->lcdc.v_pulse_width = 2; 
  21.    pinfo->lcdc.border_clr = 0;   /* blk */ 
  22.    pinfo->lcdc.underflow_clr = 0xff;/* blue */ 
  23.    pinfo->lcdc.hsync_skew = 0; 
  24.  
  25.    platform_device_register(&this_device);/*这里又注册了一个device,这个device和board-display.c里 的device是通过id来区别的,然后会再次调用renesas_probe函数*/ 
  26.      -> msm_fb_add_device(pdev)  /*向高通的FB架构里将上面的LCD信息注册进去*/ 

   在关闭LCD的时候,kernel里驱动调用流程刚好是和上面反向的。

   这里有一点,因为我们使用的背光是通过PMIC的一个1号脚来控制的,使用PMIC管脚产生PWM的使用方法是,在board-display.c里初始化GPIO的函数里,加上这么一句:

[cpp] view plain copy print ?
  1. pmapp_disp_backlight_init(); 

   然后在board-display.c里加上如下配置:

[cpp] view plain copy print ?
  1. static int lcdc_set_bl(int level) 
  2.   int ret; 
  3.   //PRINTK("lcdc_renesas_set_bl,level = %d\r\n",level); 
  4.   ret = pmapp_disp_backlight_set_brightness(level); 
  5.   if (ret) 
  6.     pr_err("%s: can't set lcd backlight!\n", __func__); 
  7.   return ret; 
  8. static struct msm_panel_common_pdata lcdc_panel_data = { 
  9.   .panel_config_gpio = NULL, 
  10.   .pmic_backlight = lcdc_set_bl, 
  11.   .gpio_num   = lcdc_gpio_table, 

   最后在lcdc_renesas_r61408.c的pmic_backlight这个函数指针就可以实现背光的控制和调节。

  

   (2) bootloader(lk)

   和上一篇一样,首先要在目录/bootable/bootloader/lk/target/$(Projec)下更改lcd的makefile文件rules.mk,在rules.mk下添加:

[cpp] view plain copy print ?
  1. DEFINES += DISPLAY_LCDC_PANEL_R61408=1 

   代码的流程可以跟着truly的例子来做,这里简单的介绍一下流程:

[cpp] view plain copy print ?
  1. void kmain()(/bootable/bootloader/lk/kernel/main.c) 
  2.   -> thread_resume (thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); 
  3.     -> bootstrap2 
  4.       -> target_init(); (bootable\bootloader\lk\target\$(project)\Init.c) 
  5.         -> /*这里是bootloader里按键的驱动*/ 
  6.           #if (!ENABLE_NANDWRITE) 
  7.           keys_init(); 
  8.           keypad_init(); 
  9.           #endif 
  10.  
  11.           display_init();   /*初台化LCD驱动*/ 
  12.           dprintf(INFO, "Diplay initialized\n"); 
  13.            display_image_on_screen(); /*向LCD里发送图像数据*/ 
[cpp] view plain copy print ?
  1. display_init()  (/bootable/bootloader/lk/platform/msm7627a/platform.c) 
  2. /*这里lcdc_init()和panel_lcdc_init()的顺序如果调过来LCD显示就不正常,我也很奇怪,不知道是为什么*/ 
  3. -> fb_config = lcdc_init(); 
  4.   -> mdp_lcdc_clock_init();/*初始化LCDC的时钟*/ 
  5.       lcd_timing = get_lcd_timing();/*获取LCD相关的硬件设置*/ 
  6.       fb_cfg = lcdc_init_set(lcd_timing);/*根据上面得到的设置数据来设置CPU的寄存器*/ 
  7.  
  8. -> panel_lcdc_init();    /*初始化LCD,要完成的有RESET,SPI的读写函数等等*/ 

   LCDC接口的LCD驱动在LK里相对MIPI的来说流程比较简单,SPI的时序参考KERNEL里的一般就没有什么问题的。

   到这里,LCDC和MIPI的两种LCD在高通平台上的驱动也算是写完了,小弟也正处在学习的过程当中,写得不好。我再写一下在调试LCDC接口的LCD驱动时的一些心得(也是之前在网上看到的):

1)调试lcd背光,背光主要分为PMIC自带的和单独的DCDC,如果为PMIC自带的背光,一般平台厂商已经做好,直接调用接口即可,如果为单独的DCDC驱动,则需要用GPIO控制DCDC的EN端

2)确认lcd的模拟电,io电是否正常

3)根据lcd的分辨率,RGB/CPU/MIPI等不同的接口,配置控制寄存器接口

4)根据lcd spec配置PCLK的频率,配置PCLK,VSYNC,HSYNC,DE等控制线的极性

5)使用示波器测试所有clk的波形,确认频率,极性是否符合要求

6)使用示波器测试data线,看是否有数据输出,bpp的设置是否正确

7)如果lcd需要初始化,配置spi的接口,一般分为cpu自带的spi控制器,和gpio模拟的spi。

8)根据lcd spec中的初始化代码进行lcd的初始化

9)用示波器测量lcd的spi clk及数据线,确认是否正常输出

10)正常情况下,此时lcd应该可以点亮。如果没有点亮,按照上述步骤1到9,逐项进行检查测试,重点检查第5项,clk的极性

11)如果lcd点亮,但是花屏。则需要先确认数据格式是否正确,然后确认fb里的数据是否正常,有以下几种方法确认fb里的数据

i)cat /dev/graphics/fb0 > /sdcard/fb0,然后将/sdcard/fb0 >到另一台相同分辨率及相同格式的手机上,看图片显示是否正常 ii)使用irfanview软件显示cat /dev/graphics/fb0出来的raw数据,注意要正确设置分辨率及格式,否则显示花屏 iii)如果adb连接正常,可以使用豌豆莢等软件,查看fb中的数据是否正常

通过以上三种途径,如果确认fb中的数据正常显示,则很可能为lcd初始化代码的问题,或者clk极性的问题,如果fb数据不正常,则可能为lcd控制寄存器配置不正常导致

你可能感兴趣的:(高通安卓调试LCD几方面总结(二))