zoukankan      html  css  js  c++  java
  • TQ2440平台上LCD驱动的移植

    参考:

    http://liu1227787871.blog.163.com/blog/static/205363197201242393031250/

    http://blog.csdn.net/cumtgao/article/details/8649006

    http://www.360doc.com/content/12/0424/17/9159905_206213245.shtml

    http://blog.csdn.net/yj4231/article/details/7878762

     

    硬件平台:TQ2440

    LCD型号:WXCAT43,分辨率480*270

    U-boot版本:u-boot-2015.04

    内核版本:Linux-3.14

     

    作者:彭东林

    邮箱:pengdonglin137@163.com

     

    下面主要完成Linux下面的LCD驱动的移植。

     

    首先我们需要知道一些LCD的知识:

    我们使用的LCD是TFT类型,下面是S3C2440的LCD控制器的信号引脚:

    image

     

    下面是TQ2440的LCD原理图:

    image

     

    下面介绍一下这些引脚的功能:

    VCLK:发出lcd时钟信号,每来一个时钟,就会在屏幕上显示一个像素

    VLINE:发出lcd行扫描信号

    VFRAME:发出lcd桢扫描信号

    VM:VDEN,有效时才会在屏幕上显示象素

    LCD_PWREN:发出lcd面板电源使能控制信号

    VD[3]——VD[7]   :lcd数据总线

    VD[10]——VD[15] :lcd数据总线

    VD[19]——VD[23] :lcd数据总线

     

    各信号的含义

    VSYNC:帧同步信号

    每发出一个脉冲,表示新的一屏图像数据开始传送。

    HSYNC:行同步信号

    每发出一个脉冲,表示新的一行图像数据开始传送。

    VCLK:像素时钟信号

    每发出一个脉冲,表示新的一个点图像数据开始传送。

    LEND:行结束信号

     

    VBPD:表示在一帧图像开始时,帧同步信号以后的无效的行数,对应驱动中的upper_margin

    VFBD:表示在一帧图像结束后,帧同步信号以前的无效的行数,对应驱动中的lower_margin

    VSPW:表示垂直同步脉冲的宽度,单位是行数

    HBPD:表示从水平同步信号开始到一行的有效数据开始之间的vclk的个数,对应驱动中的left_margin;

    HFPD:表示一行的有效数据结束到下一个水平同步信号开始之间的vclk的个数,对应驱动中的right_margin;

    HSPW:表示水平同步信号的宽度,单位是vclk的个数

     

    下面我们结合两张图理解一下:

    image

     

    0_1332308765VvL6.gif

    下面我们结合TQ2440上的LCD芯片手册分析:

    image

    注意上面这幅图下面的文字中对CLK和H的说明,其中:CLK表示的是像素时钟周期,H表示的是行同步时钟周期。可以发现上面图中的规律:

    525=480+2+41+2   他们的单位都是CLK

    286=272+2+10+2   他们的单位都是H

    所以帧率就是: 1/(525*286*像素时钟周期) Hz

    这样我们也容易理解S3C2440上的两个公式:

    VCLK(Hz) = HCLK/[(CLKVAL+1)x2]   像素时钟频率(Hz)

    Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)+ (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]   帧率(Hz)

    image

    对照上面的两幅图,我们采用典型值来填充这个结构体

    static struct s3c2410fb_display tq2440_lcd_cfg __initdata = {
     
        .lcdcon5    = S3C2410_LCDCON5_FRM565 |
                  S3C2410_LCDCON5_INVVLINE |
                  S3C2410_LCDCON5_INVVFRAME |
                  S3C2410_LCDCON5_PWREN |
                  S3C2410_LCDCON5_HWSWP,
     
        .type        = S3C2410_LCDCON1_TFT,
     
        .width        = 480,
        .height        = 272,
     
        .pixclock    = 100000,
        .xres        = 480,
        .yres        = 272,
        .bpp        = 16,
        .left_margin    = 2,   /* For HBPD+1, 这里我们取的都是典型值  */
        .right_margin    = 2,  /* For HFPD+1  */
        .hsync_len    = 41,    /* For HSPW+1  */
        .upper_margin    = 2,  /* For VBPD+1  */
        .lower_margin    = 2,  /* For VFPD+1  */
        .vsync_len    = 10,    /* For VSPW+1  */
    };
     
    #define S3C2410_GPCCON_MASK(x)    (3 << ((x) * 2))
    #define S3C2410_GPDCON_MASK(x)    (3 << ((x) * 2))
     
    static struct s3c2410fb_mach_info tq2440_fb_info __initdata = {
        .displays    = &tq2440_lcd_cfg,
        .num_displays    = 1,
        .default_display = 0,
     
        /* Enable VD[2..7], VD[10..15], VD[18..23] and VCLK, syncs, VDEN
         * and disable the pull down resistors on pins we are using for LCD
         * data. */
     
        .gpcup        = (0xf << 1) | (0x3f << 10),
     
        .gpccon        = (S3C2410_GPC1_VCLK   | S3C2410_GPC2_VLINE |
                   S3C2410_GPC3_VFRAME | S3C2410_GPC4_VM |
                   S3C2410_GPC8_VD0   | S3C2410_GPC9_VD1 |
                   S3C2410_GPC10_VD2   | S3C2410_GPC11_VD3 |
                   S3C2410_GPC12_VD4   | S3C2410_GPC13_VD5 |
                   S3C2410_GPC14_VD6   | S3C2410_GPC15_VD7),
     
        .gpccon_mask    = (S3C2410_GPCCON_MASK(1)  | S3C2410_GPCCON_MASK(2)  |
                   S3C2410_GPCCON_MASK(3)  | S3C2410_GPCCON_MASK(4)  |
                   S3C2410_GPCCON_MASK(8) | S3C2410_GPCCON_MASK(9) |
                   S3C2410_GPCCON_MASK(10) | S3C2410_GPCCON_MASK(11) |
                   S3C2410_GPCCON_MASK(12) | S3C2410_GPCCON_MASK(13) |
                   S3C2410_GPCCON_MASK(14) | S3C2410_GPCCON_MASK(15)),
     
        .gpdup        = (0x3f << 2) | (0x3f << 10),
     
        .gpdcon        = (S3C2410_GPD2_VD10  | S3C2410_GPD3_VD11 |
                   S3C2410_GPD4_VD12  | S3C2410_GPD5_VD13 |
                   S3C2410_GPD6_VD14  | S3C2410_GPD7_VD15 |
                   S3C2410_GPD10_VD18 | S3C2410_GPD11_VD19 |
                   S3C2410_GPD12_VD20 | S3C2410_GPD13_VD21 |
                   S3C2410_GPD14_VD22 | S3C2410_GPD15_VD23),
     
        .gpdcon_mask    = (S3C2410_GPDCON_MASK(2)  | S3C2410_GPDCON_MASK(3) |
                   S3C2410_GPDCON_MASK(4)  | S3C2410_GPDCON_MASK(5) |
                   S3C2410_GPDCON_MASK(6)  | S3C2410_GPDCON_MASK(7) |
                   S3C2410_GPDCON_MASK(10) | S3C2410_GPDCON_MASK(11)|
                   S3C2410_GPDCON_MASK(12) | S3C2410_GPDCON_MASK(13)|
                   S3C2410_GPDCON_MASK(14) | S3C2410_GPDCON_MASK(15)),
     
    //    .lpcsel        = ((0xCE6) & ~7) | 1<<4,  // 禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。
    };
     

     

    接下来要说的参数是pixclock,他是用来计算像素频率的。

    在driver/video/s3c2410fb.c中:

    static void s3c2410fb_activate_var(struct fb_info *info)
    {
        struct s3c2410fb_info *fbi = info->par;
        void __iomem *regs = fbi->io;
        int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
        struct fb_var_screeninfo *var = &info->var;
        int clkdiv;
     
        clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);
     
        dprintk("%s: var->xres  = %d
    ", __func__, var->xres);
        dprintk("%s: var->yres  = %d
    ", __func__, var->yres);
        dprintk("%s: var->bpp   = %d
    ", __func__, var->bits_per_pixel);
     
        if (type == S3C2410_LCDCON1_TFT) {
            s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
            --clkdiv;
            if (clkdiv < 0)
                clkdiv = 0;
        } else {
            s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
            if (clkdiv < 2)
                clkdiv = 2;
        }
     
        fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
     
         ......
    }

    上面计算得到的clkdiv就是VCLK(Hz) = HCLK/[(CLKVAL+1)x2] 中的CLKVAL.

    static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
                          unsigned long pixclk)
    {
        unsigned long clk = fbi->clk_rate;
        unsigned long long div;
     
        /* pixclk is in picoseconds, our clock is in Hz
         *
         * Hz -> picoseconds is / 10^-12
         */
     
        div = (unsigned long long)clk * pixclk;
        div >>= 12;            /* div / 2^12 */
        do_div(div, 625 * 625UL * 625); /* div / 5^12 */
     
        dprintk("pixclk %ld, divisor is %ld
    ", pixclk, (long)div);
        return div;
    }

    首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后

    clkdiv  = (clk * pixclk  / 10^12 + (2 - 1))/ 2。

    随后由于是采用TFT模式,将clkdiv-1。最后得:

    clkdiv  = (clk * pixclk  / 10^12 + (2 - 1))/ 2 - 1,

    这里的clk即为HCLK,LCD模块使用HCLK作为时钟源,从内核启动代码中可以看到HCLK是100MHz

    为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。

    我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 从LCD的芯片手册上可以看到VCLK的典型值是9MHz,我们取10MHz。

    因此,pixclk=100000。

    其实在内核的参考文档中有这样一段话:

    The speed at which the electron beam paints the pixels is determined by the
    dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
    of cycles per second), each pixel is 35242 ps (picoseconds) long:
        1/(28.37516E6 Hz) = 35.242E-9 s

    也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。

     

    完。

  • 相关阅读:
    十分钟构建双十一交互分析大盘
    数字IT基础-数据采集总线
    阿里云文件存储(NAS)助力业务系统承载双十一尖峰流量
    如何在网络视听行业建一扇内容安全大门?
    Frost & Sullivan权威报告:阿里云再次领跑云WAF大中华区市场
    赋能时空云计算,阿里云数据库时空引擎Ganos上线
    点播转码相关常见问题及排查方式
    《边缘云计算技术及标准化白皮书》
    内存性能的正确解读
    《倡议书——节约用电,从我做起》
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/4604913.html
Copyright © 2011-2022 走看看