基于MTK65xx平台lcm和背光驱动的编写

一、 与LCD驱动相关的主要文件路径
\mediatek\platform\mt6573\uboot\mt6573_disp_drv.c
\mediatek\platform\mt6573\uboot\mt6573_disp_drv_dpi.c
\mediatek\platform\mt6573\uboot \mt6573_disp_drv_dbi.c
\mediatek\platform\mt6573\uboot \mt6573_dpi_drv.c
\mediatek\platform\mt6573\uboot \mt6573_dsi_drv.c
\mediatek\platform\mt6573\uboot \mt6573_lcd_drv.c
\mediatek\source\kernel\drivers\video\mtkfb.c
\mediatek\source\kernel\drivers\video\disp_drv.c
\mediatek\source\kernel\drivers\video\disp_drv_dpi.c
\mediatek\source\kernel\drivers\video\disp_drv_dbi.c
\mediatek\platform\mt6573\kernel\drivers\video\lcd_drv.c
\mediatek\platform\mt6573\kernel\drivers\video\dpi_drv.c
\mediatek\platform\mt6573\kernel\drivers\video\dsi_drv.c
\mediatek\custom\common\kernel\lcm\LCM_NAME\LCM_NAME.c

\mediatek\platform\mt6573\uboot \mt6573_pwm.c
\mediatek\platform\mt6573\uboot \mt65xx_leds.c
\mediatek\source\kernel\drivers\leds\leds.c
\mediatek\custom\ginwave73_gb\kernel\leds\mt65xx\cust_leds.c

二、 怎样新建一个LCD驱动
LCD模组主要包括LCD显示屏和驱动IC。比如LF040DNYB16a模组的驱动IC型号为NT35510。要在MTK6573平台上新建这个lcd的驱动,步骤如下:
1、 在mediatek\custom\common\kernel\lcm目录下新建文件夹nt35510,在此文件夹中新建nt35510.c。这就是LCM硬件层驱动文件。
2、 修改\mediatek\custom\common\kernel\lcm\ mt65xx_lcm_list.c, 在 lcm_driver_list [ lcm_count ] 中增加nt35510_lcm_drv。
3、 打开mediatek\config\ginwave73_gb\ProjectConfig.mk,修改CUSTOM_UBOOT_LCM = nt35510, CUSTOM_KERNEL_LCM = nt35510;修改LCM_WIDTH、LCM_HEIGHT、BOOT_LOGO为正确的值。

三、 驱动文件(nt35510.c) 主要任务是实现
LCM_DRIVER   nt35510_lcm_drv =
{
    .name = "nt35510",
.set_util_funcs = lcm_set_util_funcs,
.get_params     = lcm_get_params,
.init           = lcm_init,
.suspend        = lcm_suspend,
.resume         = lcm_resume,
.compare_id     = lcm_compare_id,
};
(1)lcm_get_params主要是设置LCM相关的参数,数据结构如下:
typedef struct
{
    LCM_TYPE type;
LCM_CTRL ctrl;               //! how to control LCM registers  
     unsigned int width;
    unsigned int height;
unsigned int io_select_mode;     //DBI or DPI should select IO mode according to chip spec
  
/* particular parameters */
    LCM_DBI_PARAMS dbi;
    LCM_DPI_PARAMS dpi;
    LCM_DSI_PARAMS dsi;
} LCM_PARAMS;

LCM_TYPE 定义LCM与HOST间的接口,主要分为3种,DBI, DPI, DSI。其中DBI又分为parallel DBI 和serial DBI。
parallel DBI(B型DBI)的命令和数据都在数据总线D[17:0]上传输。CSX为低时数据有效。WRX线控制D[17:0]为写时序,RDX控制D[17:0]为读时序。D/CX指示D[17:0]上传输的是命令还是数据。注:MT6573使用LPA0线作为D/CX线,LPCE线作为CSX线,LWRB线作为WRX线,LRDB线作为RDX线。

serial DBI(C型DBI)的命令和数据都在SPI接口上传输。CSX为低时SDA有效。SCL提供时钟,DIN输入,DOUT输出。当SDA_EN=1时,DIN线成为双向的SDA线,即可输入又可输出,DOUT线不用。C型DBI分为3线(没有D/CX线)和4线(有D/CX线)两种。3线的使用一个D/CX bit来区分命令/数据,4线的使用D/CX线来区分命令/数据。注:MT6573使用LSA0线作为D/CX线,LSCE线作为CSX线。LSCK线作为SCL线,LSDA线作为SDA线。

DPI的命令在SPI上传输,Pixel data(RGB data)在D[17:0]上传输。其命令传输方式与serial DBI相同。对于Pixel data(RGB data),需要自己的4条控制线:DPICK_PIN(RGB时钟) 、DPIDE_PIN(RGB数据有效)、 DPIVSYNC(场同步)、 DPIHSYNC(行同步) 。

typedef enum
{
    LCM_CTRL_NONE = 0,
    LCM_CTRL_SERIAL_DBI,
    LCM_CTRL_PARALLEL_DBI,
    LCM_CTRL_GPIO
}
LCM_CTRL定义LCM与HOST之间传递command的方式,有SERIAL_DBI、PARALLEL_DBI、GPIO几种控制方式。如果是DPI接口,其LCM_CTRL可以选择SERIAL_DBI或者GPIO。

Width和height定义LCM的宽度和高度。

io_select_mode有这些选项:0(LCD_IO_SEL_16CPU_24RGB),1(LCD_IO_SEL_18CPU_18RGB),2(LCD_IO_SEL_24CPU_8RGB),3(LCD_IO_SEL_24CPU_ONLY)。根据driver IC 的定义填写。

LCM_×××_PARAMS根据不同的LCM_TYPE取值,这是针对DBI/DPI/DSI接口类型的详细参数定义。比如LCM_DPI_PARAMS定义如下:
typedef struct
{
    unsigned int mipi_pll_clk_ref;   // 0..1
    unsigned int mipi_pll_clk_div1;  // 0..63
    unsigned int mipi_pll_clk_div2;  // 0..15
    unsigned int dpi_clk_div;        // 2..32
    unsigned int dpi_clk_duty;       // (dpi_clk_div - 1) .. 31

    /* polarity parameters */
    LCM_POLARITY clk_pol;
    LCM_POLARITY de_pol;
    LCM_POLARITY vsync_pol;
    LCM_POLARITY hsync_pol;

    /* timing parameters */
    unsigned int hsync_pulse_width;
    unsigned int hsync_back_porch;
    unsigned int hsync_front_porch;
    unsigned int vsync_pulse_width;
    unsigned int vsync_back_porch;
    unsigned int vsync_front_porch;
   
    /* output format parameters */
    LCM_DPI_FORMAT format;
    LCM_COLOR_ORDER rgb_order;
    unsigned int  is_serial_output;

    /* intermediate buffers parameters */
    unsigned int intermediat_buffer_num; // 2..3

    /* iopad parameters */
    LCM_DRIVING_CURRENT io_driving_current;
   
} LCM_DPI_PARAMS;

其中,第一段前4项用于控制DPI时钟,计算公式如下:
Pixel Clock Frequency = 26MHz * mipi_pll_clk_div1 / (mipi_pll_clk_ref + 1)/ (2 * mipi_pll_clk_div2)/ dpi_clk_div
第二段4个参数设置DPICK_PIN(RGB时钟) 、DPIDE_PIN(RGB数据有效)、 DPIVSYNC(场同步)、 DPIHSYNC(行同步)线是上升沿还是下降沿有效。
行同步脉冲开始前和开始后的几个时钟周期,是行信号消隐期;场同步开始前和开始后的几个行周期,是场信号消隐期。消隐期不传递图像信号。消隐期特性由第三段六个参数定义:hsync_pulse_width;    hsync_back_porch; hsync_front_porch;  vsync_pulse_width;  vsync_back_porch;  vsync_front_porch;
LCM_DPI_FORMAT指定每个像素中RGB各占几个bit.
LCM_COLOR_ORDER指定RGB的顺序。
上述参数的值,均依照LCM spec及驱动IC datasheet中的定义。

这里要说说一个特别之处。NT35510使用DPI接口,SERIAL_DBI ctrl 方式时,没有D/CX线,datasheet上定义的传输方式是9 bits,即在数据byte前加个D/CX bit。但使用了NT35510的LCM——LF040DNYB16a,其spec中却另外定义了其串口使用16 bits 模式传输。Byte1 是标志byte, 前3位分别为 R/W bit, D/CX bit, High/Low bit;Byte2 则是命令或数据。NT35510的每个command长度为2byte,还可能带有若干bytes的参数,比如命令F001,参数AA,应该这样传输:0x20 0xF0 0x00 0x01 0x40 0xAA。

(2)lcm_init主要实现LCM的初始化,包括如下步骤:
config_gpio——配置GPIO。
发送reset信号。RESET pin low和RESET pin high需要持续的时间一般为若干ms, 以datasheet 为准。
init_lcm_registers——初始化LCM的寄存器。具体可以厂家提供的初始代码为参考。一般在此函数末尾,都会使用唤醒命令组(见后文),使LCM进入工作状态。
(3)lcm_suspend 使LCM休眠,使用特定的命令,并遵守datasheet定义的时间特性。常用命令组如下:
0X2800 (or  0X28) —— set display off
0X1000 (or  0X10) —— enter sleep mode
对nt35510而言,还有一种更深睡眠的状态——deep standby mode,使用如下命令进入:
0X4F00  0X01
(4)lcm_resume使LCM苏醒,使用特定的命令,并遵守datasheet定义的时间特性。
唤醒命令组:
0X1100 (or 0X11) —— exit sleep mode
0X2900 (or 0X29) —— set display on
对nt35510而言,如果在lcm_suspend中使LCM enter deep standby mode,则不能使用唤醒命令组,需要使用reset信号并要重新init_lcm_registers。

四、 驱动 nt35510_lcm_drv怎样被上层使用
Mtkfb.c中实现了LCM的platform driver:
static struct platform_driver   mtkfb_driver =
{
    .driver = {
        .name    = “mtk-fb”,
        .bus     = &platform_bus_type,
        .probe   = mtkfb_probe,
        .remove  = mtkfb_remove,   
        .suspend = mtkfb_suspend,
        .resume  = mtkfb_resume,
    },   
};
mtkfb_probe会调用函数mtkfb_find_lcm_driver来发现LCM的硬件层驱动。mtkfb_find_lcm_driver——DISP_SelectDevice——disp_drv_get_lcm_driver,检查lcm_driver_list[], 得到当前使用的LCM及其驱动名称。

Mt6573_devs.c中,定义了framebuffer型的platform device,这个设备在mt6573_board_init()调用时被注册。它所对应的驱动就是上文提到的mtkfb_driver
static struct platform_device   mt6573_device_fb = {
    .name = "mtkfb",
    .id   = 0,
    .num_resources = ARRAY_SIZE(resource_fb),
    .resource      = resource_fb,
    .dev = {
        .dma_mask = &mtkfb_dmamask,
        .coherent_dma_mask = 0xffffffff,
    },
};

在linux 内核中lcd 设备驱动所使用的是framebuffer设备类型,framebuffer设备驱动程序的核心数据结构是fb_ops;用户空间就是通过此结构体,调用其中的函数来对LCD实现控制。Mtkfb.c中定义并实现了fb_ops类型的mtkfb_ops。一个使用mtkfb_ops的例子见\mediatek\source\kernel\drivers\gpu\pvr\services4\3rdparty\mtklfb\ mtklfb_displayclass.c。
static struct  fb_ops  mtkfb_ops = {
    .owner          = THIS_MODULE,
    .fb_open        = mtkfb_open,
    .fb_release     = mtkfb_release,
    .fb_setcolreg   = mtkfb_setcolreg, //批量配置颜色参数
    .fb_pan_display = mtkfb_pan_display_proxy, //虚拟屏幕内容显示
    .fb_fillrect    = cfb_fillrect, //填充区域显示
    .fb_copyarea    = cfb_copyarea, //复制区域显示
    .fb_imageblit   = cfb_imageblit, //显示图象
    .fb_cursor      = mtkfb_soft_cursor, //光标显示
    .fb_check_var   = mtkfb_check_var, //检查并配置fb_var_screeninfo参数
    .fb_set_par     = mtkfb_set_par, //change display mode and set parameter
    .fb_ioctl       = mtkfb_ioctl, //特定ioctl配置LCD屏幕特性
};

五、 Frame Buffer 设备驱动
mtkfb_driver中的各个函数会调用到DISP_xxx函数(DISP_drv.c),而DISP_xxx会调用到LCD_xxx函数(lcd_drv.c)以及LCM硬件层驱动。
mtkfb_probe的主要工作如下:
* find lcm driver
* Register interrupt handler (call back function), Init screen update wait queue, create screen update kThread
* Allocate and initialize frame buffer device (fb_info , mtkfb_device), select panel type according to machine type
*  Initialize Display Driver PDD Layer (DISP_init)
*  Initialize fb_info struct (mtkfb_fbinfo_init)
*  Register mtkfb_device fs  to system (mtkfb_register_sysfs)
*  Register fb_info to system (register_framebuffer)

fb_info结构定义如下:
struct fb_info {
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct  fb_var_screeninfo  var; /* Current var */
struct  fb_fix_screeninfo  fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist;      /* mode list */
struct fb_videomode *mode; /* current mode */

#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev;
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif

#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif

struct fb_ops *fbops;
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag;                    /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops;    /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32  state; /* Hardware state i.e suspend */
void *fbcon_par;                /* fbcon use-only private area */

/* From here on everything is device dependent */
void *par;
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
};
 1)fb_var_screeninfo
  这个结构描述了显示卡的特性:
  NOTE:  __u32 是表示 unsigned 不带符号的 32 bits 的数据类型,其余类推。这是 Linux 内核中所用到的数据类型,如果是开发用户空间(user-space)的程序,可以根据具体计算机平台的情况,用 unsigned long 等等来代替
  struct fb_var_screeninfo
  {
  __u32 xres; /* visible resolution */ //可视区域
  __u32 yres;
  __u32 xres_virtual; /* virtual resolution */ //可视区域
  __u32 yres_virtual;
  __u32 xoffset; /* offset from virtual to visible resolution */ //可视区域的偏移
  __u32 yoffset;
  __u32 bits_per_pixel; /* guess what */ //每一象素的bit数
  __u32 grayscale; /* != 0 Gray levels instead of colors *///等于零就成黑白
  struct fb_bitfield red; /* bitfield in fb mem if true color, */真彩的bit机构
  struct fb_bitfield green; /* else only length is significant */
  struct fb_bitfield blue;
  struct fb_bitfield transp; /* transparency */ 透明
  __u32 nonstd; /* != 0 Non standard pixel format */ 不是标准格式
  __u32 activate; /* see FB_ACTIVATE_* */
  __u32 height; /* height of picture in mm */ 内存中的图像高度
  __u32 width; /* width of picture in mm */ 内存中的图像宽度
  __u32 accel_flags; /* acceleration flags (hints) */ 加速标志
  /* Timing: All values in pixclocks, except pixclock (of course) */
  时序-_-这些部分就是显示器的显示方法了,可以找相关的资料看看
  __u32 pixclock; /* pixel clock in ps (pico seconds) */
  __u32 left_margin; /* time from sync to picture */
  __u32 right_margin; /* time from picture to sync */
  __u32 upper_margin; /* time from sync to picture */
  __u32 lower_margin;
  __u32 hsync_len; /* length of horizontal sync */ 水平可视区域
  __u32 vsync_len; /* length of vertical sync */ 垂直可视区域
  __u32 sync; /* see FB_SYNC_* */
  __u32 vmode; /* see FB_VMODE_* */
  __u32 reserved[6]; /* Reserved for future compatibility */ 备用-以后开发
  };
  2) fb_fix_screeninfon
  这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。
  struct fb_fix_screeninfo {
  char id[16]; /* identification string eg "TT Builtin" */ID
  unsigned long smem_start; /* Start of frame buffer mem */ 内存起始
  /* (physical address) */ 物理地址
  __u32 smem_len; /* Length of frame buffer mem */ 内存大小
  __u32 type; /* see FB_TYPE_* */
  __u32 type_aux; /* Interleave for interleaved Planes */插入区域?
  __u32 visual; /* see FB_VISUAL_* */
  __u16 xpanstep; /* zero if no hardware panning */没有硬件设备就为零
  __u16 ypanstep; /* zero if no hardware panning */
  __u16 ywrapstep; /* zero if no hardware ywrap */
  __u32 line_length; /* length of a line in bytes */ 一行的字节表示
  unsigned long mmio_start; /* Start of Memory Mapped I/O */内存映射的I/O起始
  /* (physical address) */
  __u32 mmio_len; /* Length of Memory Mapped I/O */ I/O的大小
  __u32 accel; /* Type of acceleration available */ 可用的加速类型
  __u16 reserved[3]; /* Reserved for future compatibility */
  };

Mtkfb_device结构定义如下:
struct mtkfb_device {
    int             state;
    void           *fb_va_base;             /* MPU virtual address */
    dma_addr_t      fb_pa_base;             /* Bus physical address */
    unsigned long   fb_size_in_byte;

    unsigned long   layer_enable;
    MTK_FB_FORMAT   layer_format[HW_OVERLAY_COUNT];
    unsigned int    layer_config_dirty;

    int             xscale, yscale, mirror;    /* transformations. rotate is stored in fb_info->var */
    u32             pseudo_palette[17];

    struct fb_info  *fb_info;                /* Linux fbdev framework data */
    struct device   *dev;
};

六、 uBoot阶段
Mt6573_board.c中,board_init函数执行硬件先期初始化工作,它调用mt65xx_disp_init函数(Mt6573_disp_drv.c)。
board_init——mt65xx_disp_init——DISP_Init——disp_drv_init_context——DISP_DetectDevice——disp_drv_get_lcm_driver——lcm_driver_list[ ]
disp_drv_init_context——DISP_GetDriverDPI / DISP_GetDriverDBI / DISP_GetDriverDSI
DISP_GetDriverDPI (mt6573_disp_drv_dpi.c)——DPI_DISP_DRV. dpi_init——init_dpi——DPI_Init (Mt6573_dpi_drv.c)
DISP_GetDriverDBI  (mt6573_disp_drv_dbi.c)——DBI_DISP_DRV. dbi_init——init_lcd——LCD_xxx(Mt6573_lcd_drv.c)
DISP_GetDriverDSI  (mt6573_disp_drv_dsi.c)——DSI_DISP_DRV. dsi_init——init_lcd,init_dsi——DSI_Init(Mt6573_dsi_drv.c)
DISP_Init——LCD_Init  (Mt6573_lcd_drv.c)
DISP_UpdateScreen——LCD_StartTransfer  (Mt6573_lcd_drv.c)

七、 背光控制
Cust_leds.c中定义了cust_mt65xx_led类型的数组cust_led_list[],列出了平台的所有led设备,包括不同颜色的led灯,轨迹球、键盘、按键、LCD等的背光灯。不同led设备可以有各自不同的背光控制方式,比如PWM(脉冲宽度调制)方式,GPIO方式,PMIC方式,用户自定义方式等。如下所示:
static struct  cust_mt65xx_led   cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
{"red",               MT65XX_LED_MODE_PWM, PWM3},
{"green",             MT65XX_LED_MODE_PWM, PWM2},
{"blue",              MT65XX_LED_MODE_PWM, PWM1},
{"jogball-backlight",    MT65XX_LED_MODE_NONE, -1},
{"keyboard-backlight",  MT65XX_LED_MODE_NONE, -1},
{"button-backlight",    MT65XX_LED_MODE_PWM, PWM7},
{"lcd-backlight",       MT65XX_LED_MODE_CUST, (int)Cust_SetBacklight},
};
这个数组通过get_cust_led_list函数被mt65xx_leds_probe调用,传递给mt65xx_leds_driver,并且mt65xx_leds_probe 将数组中的设备一一注册。(\mediatek\source\kernel\drivers\leds\ leds.c)
static struct platform_driver mt65xx_leds_driver = {
.driver = {
.name = "leds-mt65xx",
.owner = THIS_MODULE,
},
.probe = mt65xx_leds_probe,
.remove = mt65xx_leds_remove,
//.suspend = mt65xx_leds_suspend,
.shutdown   = mt65xx_leds_shutdown,
};

static struct platform_device mt65xx_leds_device = {
.name = "leds-mt65xx",
.id = -1
};
以lcd-backlight为例,其控制方式为自定义,背光控制IC为SN3228B,控制函数为Cust_SetBacklight。MT6573的GPIO49作为控制脚,连接到SN3228B的EN/SET脚。该引脚对收到的上升沿脉冲计数(1~14个),调整自己的输出电流,从而控制Leds的亮度。所以Cust_SetBacklight根据要达到的亮度level,在GPIO49上输出对应数量的上升沿脉冲,时间特性需满足SN3228的定义。
如果采用PWM控制方式,则需要使用MT6573的PWM controller。函数led_set_pwm实现了用PWM控制led灯的亮、灭、闪烁时对PWM controller 相关寄存器的配置;函数backlight_set_pwm实现了用PWM控制lcd backlight时对PWM controller 相关寄存器的配置,随lcd-backlight brightness level不同,相应的PWM脚输出对应数量的脉冲(level:0~64)
mt65xx_led_set_cust根据不同的控制方式,调用相应的配置函数,如下:
MT65XX_LED_MODE_PWM:  backlight_set_pwm / led_set_pwm
MT65XX_LED_MODE_CUST: Cust_SetBacklight
MT65XX_LED_MODE_GPIO: brightness_set_gpio
MT65XX_LED_MODE_PMIC: brightness_set_pmic

mt65xx_leds_probe 为cust_led_list[]中的每个led设备初始化一个任务队列,任务处理函数为 mt65xx_led_work,它再调用mt65xx_led_set_cust实现控制。每个led设备的亮度设置(brightness_set)函数为mt65xx_led_set,它对lcd-backlight设备直接调用mt65xx_led_set_cust,对其他设备则用任务队列调度。
Uboot阶段,其函数调用及led和backlight控制方式与启动后基本相同。增加了各种充电状态下对红、绿、蓝led的控制。如下是backlight的调用关系:
(mt65xx_leds.c)mt65xx_backlight_on——mt65xx_leds_brightness_set(MT65XX_LED_TYPE_LCD, LED_FULL)——mt65xx_led_set_cust——brightness_set_pwm / Cust_SetBacklight

基于MTK65xx平台lcm和背光驱动的编写_第1张图片
 
 基于MTK65xx平台lcm和背光驱动的编写_第2张图片
 
基于MTK65xx平台lcm和背光驱动的编写_第3张图片

你可能感兴趣的:(驱动)