zoukankan      html  css  js  c++  java
  • linux lcd设备驱动剖析四

    linux lcd设备驱动剖析二文章中,我们详细分析了s3c24xxfb_probe函数。

    文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765

    s3c2410fb.c中s3c24xxfb_probe是非常重要的函数之一,但仅仅分析probe函数,貌似感觉有点不够过瘾,貌似缺少分析了一个非常重要的成员。在probe函数中有一句:fbinfo->fbops   = &s3c2410fb_ops;

    [cpp] view plain?
    1. static struct fb_ops s3c2410fb_ops = {  
    2.     .owner          = THIS_MODULE,  
    3.     .fb_check_var   = s3c2410fb_check_var,      //设置可变参数  
    4.     .fb_set_par     = s3c2410fb_set_par,        //设置固定参数及lcdcon寄存器  
    5.     .fb_blank       = s3c2410fb_blank,          //设置是否使能LCD控制器  
    6.     .fb_setcolreg   = s3c2410fb_setcolreg,      //设置RGB颜色,实现伪颜色表  
    7.     .fb_fillrect    = cfb_fillrect,             //画一个矩形  
    8.     .fb_copyarea    = cfb_copyarea,             //Copy data from area to another  
    9.     .fb_imageblit   = cfb_imageblit,            //Draws a image to the display  
    10. };    

    一、s3c2410fb_check_var函数主要根据tq2440_lcd_cfg实例来设置fb_info结构体的可变参数

    fb_var_screeninfo结构体各个成员,如xres、yres、bits_per_pixel、height、width 等等,具体分析如下:

    [cpp] view plain?
    1. /* 此函数的主要功能是设置可变参数var  */     
    2. static int s3c2410fb_check_var(struct fb_var_screeninfo *var,  
    3.                    struct fb_info *info)  
    4. {     
    5.     struct s3c2410fb_info *fbi = info->par;  
    6.   
    7.     /* platform_data就是tq2440_fb_info结构体实例 */  
    8.     struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;  
    9.     struct s3c2410fb_display *display = NULL;  
    10.   
    11.     /* 在tq2440_fb_info实例里,displays = tq2440_lcd_cfg,default_display = 0 */  
    12.     struct s3c2410fb_display *default_display = mach_info->displays +  
    13.                             mach_info->default_display;  
    14.   
    15.     /* 在tq2440_fb_info实例里,type = S3C2410_LCDCON1_TFT */  
    16.     int type = default_display->type;  
    17.     unsigned i;  
    18.   
    19.     dprintk("check_var(var=%p, info=%p) ", var, info);  
    20.   
    21.     /* validate x/y resolution */  
    22.     /* choose default mode if possible */  
    23.   
    24.     /* 如果参数都等于tq2440_fb_info实例里的参数 
    25.      * 那么赋值给display,此时display指向tq2440_fb_info实例 
    26.      */  
    27.     if (var->yres == default_display->yres &&  
    28.         var->xres == default_display->xres &&  
    29.         var->bits_per_pixel == default_display->bpp)  
    30.         display = default_display;    
    31.   
    32.     /* 否则从tq2440_fb_info结构体实例中循环匹配,num_displays = 1 */  
    33.     else  
    34.         for (i = 0; i < mach_info->num_displays; i++)  
    35.             if (type == mach_info->displays[i].type &&  
    36.                 var->yres == mach_info->displays[i].yres &&  
    37.                 var->xres == mach_info->displays[i].xres &&  
    38.                 var->bits_per_pixel == mach_info->displays[i].bpp) {  
    39.                 display = mach_info->displays + i;  
    40.                 break;  
    41.             }  
    42.   
    43.     /* 如果匹配不成功,display = NULL 则错误 */  
    44.     if (!display) {  
    45.         dprintk("wrong resolution or depth %dx%d at %d bpp ",  
    46.             var->xres, var->yres, var->bits_per_pixel);  
    47.         return -EINVAL;  
    48.     }  
    49.   
    50.     /* it is always the size as the display */  
    51.     /* 找到匹配的display后,将实例中的可变参数赋值 */  
    52.     var->xres_virtual    = display->xres;  
    53.     var->yres_virtual    = display->yres;  
    54.     var->height      = display->height;  
    55.     var->width           = display->width;  
    56.   
    57.     /* copy lcd settings */  
    58.     var->pixclock        = display->pixclock;  
    59.     var->left_margin     = display->left_margin;  
    60.     var->right_margin    = display->right_margin;  
    61.     var->upper_margin    = display->upper_margin;  
    62.     var->lower_margin    = display->lower_margin;  
    63.     var->vsync_len       = display->vsync_len;  
    64.     var->hsync_len       = display->hsync_len;  
    65.   
    66.     fbi->regs.lcdcon5    = display->lcdcon5;  
    67.     /* set display type */  
    68.     fbi->regs.lcdcon1    = display->type;  
    69.   
    70.     var->transp.offset   = 0;  
    71.     var->transp.length   = 0;  
    72.     /* set r/g/b positions */  
    73.     switch (var->bits_per_pixel) {  
    74.     case 1:  
    75.     case 2:  
    76.     case 4:  
    77.         var->red.offset      = 0;  
    78.         var->red.length      = var->bits_per_pixel;  
    79.         var->green           = var->red;  
    80.         var->blue            = var->red;  
    81.         break;  
    82.     ......  
    83.       
    84.     /* TQ2440的LCD就是采用这种模式 */  
    85.     case 32:                  
    86.         /* 24 bpp 888 and 8 dummy */  
    87.         var->red.length      = 8;  
    88.         var->red.offset      = 16;  
    89.         var->green.length    = 8;  
    90.         var->green.offset    = 8;  
    91.         var->blue.length = 8;  
    92.         var->blue.offset = 0;  
    93.         break;  
    94.     }  
    95.     return 0;  
    96. }  
    tq2440_lcd_cfg实例在arch/arm/mach-s3c2440/mach-tq2440.c中定义

    [cpp] view plain?
    1. /* LCD driver info */  
    2. /* tq2440_lcd_cfg在tq2440_fb_info中被设置 */  
    3. static struct s3c2410fb_display tq2440_lcd_cfg __initdata = {  
    4.     .lcdcon5    = S3C2410_LCDCON5_FRM565 |  
    5.               S3C2410_LCDCON5_INVVLINE |  
    6.               S3C2410_LCDCON5_INVVFRAME |  
    7.               S3C2410_LCDCON5_PWREN |  
    8.               S3C2410_LCDCON5_HWSWP,  
    9.     .type       = S3C2410_LCDCON1_TFT,  
    10.       
    11.     ......  
    12.       
    13. /* TQ(LCD W4.3)的配置,config_EmbedSky_W43:CONFIG_FB_S3C24X0_TFT480272=y */  
    14. #elif defined(CONFIG_FB_S3C24X0_TFT480272)    
    15.     .width        = 480,  
    16.     .height       = 272,  
    17.   
    18.     .pixclock     = 40000, /* HCLK 100 MHz, divisor 1 */  
    19.   
    20.     /* VCLK=HCLK/[(CLKVAL+1)x2],HCLK = 100MHz 
    21.      * 根据LCD手册"WXCAT43-TG6#001_V1.0.pdf"的第22页可得,VCLK = 10MHz 
    22.      * 即10 = 100/[(CLKVAL+1)x2],可得CLKVAL = 4 
    23.      */  
    24.     .setclkval    = 0x4,     
    25.     .xres         = 480,  
    26.     .yres         = 272,  
    27.     .bpp          = 16,  
    28.   
    29.     /* 为什么是这样?参考linux/Documentation/fb/framebuffer.txt */  
    30.     .left_margin  = 19,   /* 左边沿  : for HFPD*/    
    31.     .right_margin = 10,   /* 右边沿  : for HBPD*/  
    32.     .hsync_len    = 30,   /* 水平同步: for HSPW*/  
    33.     .upper_margin = 4,    /* 上边沿  : for VFPD*/  
    34.     .lower_margin = 2,    /* 下边沿  : for VBPD*/  
    35.     .vsync_len    = 8,    /* 垂直同步: for VSPW*/  
    36.       
    37.     ......  
    38. };  

    二、s3c2410fb_set_par函数先根据var->bits_per_pixel来选择fix.visual,这里bits_per_pixel = 32,

    故fix.visual = FB_VISUAL_TRUECOLOR,然后计算一行的字节数,最后调用s3c2410fb_activate_var

    函数来激活LCD控制器,即设置各个lcdcon寄存器。

    [cpp] view plain?
    1. static int s3c2410fb_set_par(struct fb_info *info)  
    2. {  
    3.     /* 获得刚被s3c2410fb_check_var函数设置过的var */  
    4.     struct fb_var_screeninfo *var = &info->var;  
    5.   
    6.     switch (var->bits_per_pixel) {  
    7.     case 32:  
    8.     case 16:  
    9.     case 12:  
    10.         info->fix.visual = FB_VISUAL_TRUECOLOR;      /* 真彩色 */  
    11.         break;  
    12.     case 1:  
    13.         info->fix.visual = FB_VISUAL_MONO01;  
    14.         break;  
    15.     default:  
    16.         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;  
    17.         break;  
    18.     }  
    19.   
    20.     /* 一行的字节数 = x*bpp/8 = 480*32/8 = 480*4 */  
    21.     info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;  
    22.   
    23.     /* activate this new configuration */  
    24.     s3c2410fb_activate_var(info);  
    25.       
    26.     return 0;  
    27. }  
    s3c2410fb_activate_var函数先调用s3c2410fb_calc_pixclk函数来计算LCD时钟频率,然后调用s3c2410fb_calculate_tft_lcd_regs函数来设置lcdcon1~lcdcon5,然后调用writel函数将前面s3c2410fb_calculate_tft_lcd_regs函数设置好的lcdconx写入对应寄存器,接着调用s3c2410fb_set_lcdaddr函数来设置LCDSADDR1、LCDSADDR2、LCDSADDR3寄存器,也就是将之前在probe函数通过s3c2410fb_map_video_memory-->dma_alloc_writecombine函数分配好的“显存”告诉LCD控制器,最后使能LCD控制器。
    [cpp] view plain?
    1. static void s3c2410fb_activate_var(struct fb_info *info)  
    2. {  
    3.     /* 在framebuffer_alloc函数里info->par指向了额外多申请 
    4.      * 内存空间的首地址,即info->par指向s3c2410fb_info结构体 
    5.      */  
    6.     struct s3c2410fb_info *fbi = info->par;  
    7.     void __iomem *regs = fbi->io;        /* IO基地址 */  
    8.   
    9.     /* 设置显示模式为: TFT LCD panel */  
    10.     int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;  
    11.   
    12.     /* 通过probe函数后platform_data指向tq2440_fb_info结构体实例 */  
    13.     struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;  
    14.     struct s3c2410fb_display *default_display = mach_info->displays +  
    15.                             mach_info->default_display;  
    16.     struct fb_var_screeninfo *var = &info->var;  
    17.   
    18.   
    19.     /* 计算LCD时钟频率, 在mach_tq2440.c里 pixclock = 40000 */  
    20.     int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;  
    21.   
    22.     dprintk("%s: var->xres  = %d ", __func__, var->xres);  
    23.     dprintk("%s: var->yres  = %d ", __func__, var->yres);  
    24.     dprintk("%s: var->bpp   = %d ", __func__, var->bits_per_pixel);  
    25.   
    26.     if (type == S3C2410_LCDCON1_TFT) {  
    27.         s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);  
    28.         --clkdiv;  
    29.         if (clkdiv < 0)  
    30.             clkdiv = 0;  
    31.     } else {  
    32.         s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);  
    33.         if (clkdiv < 2)  
    34.             clkdiv = 2;  
    35.     }  
    36.   
    37. //  fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);  
    38.     fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(default_display->setclkval);  
    39.   
    40.     /* write new registers */  
    41.   
    42.     dprintk("new register set: ");  
    43.     dprintk("lcdcon[1] = 0x%08lx ", fbi->regs.lcdcon1);  
    44.     dprintk("lcdcon[2] = 0x%08lx ", fbi->regs.lcdcon2);  
    45.     dprintk("lcdcon[3] = 0x%08lx ", fbi->regs.lcdcon3);  
    46.     dprintk("lcdcon[4] = 0x%08lx ", fbi->regs.lcdcon4);  
    47.     dprintk("lcdcon[5] = 0x%08lx ", fbi->regs.lcdcon5);  
    48.   
    49.     /* 禁止视频输出,禁止LCD控制信号 */  
    50.     writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,  
    51.         regs + S3C2410_LCDCON1);  
    52.   
    53.     /* 将前面s3c2410fb_calculate_tft_lcd_regs函数设置好的lcdconx写入对应寄存器 */  
    54.     writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);  
    55.     writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);  
    56.     writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);  
    57.     writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);  
    58.   
    59.     /* set lcd address pointers */  
    60.     s3c2410fb_set_lcdaddr(info);  
    61.   
    62.     /* 最后使能LCD控制器,即使能视频输出 */  
    63.     fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,  
    64.     writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);  
    65. }  
    s3c2410fb_calculate_tft_lcd_regs函数比较简单这里就不分析了,这里只分析s3c2410fb_set_lcdaddr函数

    [cpp] view plain?
    1. static void s3c2410fb_set_lcdaddr(struct fb_info *info)  
    2. {  
    3.     unsigned long saddr1, saddr2, saddr3;  
    4.     struct s3c2410fb_info *fbi = info->par;  
    5.     void __iomem *regs = fbi->io;  
    6.   
    7.     /* LCDSADDR1 = 帧缓冲区起始地址再右移1位 */  
    8.     saddr1  = info->fix.smem_start >> 1;  
    9.   
    10.     /* LCDSADDR2 = 帧缓冲区结束地址再右移1位 */  
    11.     saddr2  = info->fix.smem_start;  
    12.     saddr2 += info->fix.line_length * info->var.yres;  /* 帧缓冲区大小 */  
    13.     saddr2 >>= 1;  
    14.   
    15.     /* LCDSADDR3 = 一行的长度,单位为2字节 */  
    16.     saddr3 = S3C2410_OFFSIZE(0) |  
    17.          S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);  
    18.   
    19.     dprintk("LCDSADDR1 = 0x%08lx ", saddr1);  
    20.     dprintk("LCDSADDR2 = 0x%08lx ", saddr2);  
    21.     dprintk("LCDSADDR3 = 0x%08lx ", saddr3);  
    22.   
    23.     writel(saddr1, regs + S3C2410_LCDSADDR1);  
    24.     writel(saddr2, regs + S3C2410_LCDSADDR2);  
    25.     writel(saddr3, regs + S3C2410_LCDSADDR3);  
    26. }  
    三、s3c2410fb_setcolreg函数主要通过red,green,blue三原色构造出val,然后再将val写入pseudo_palette假调色板中。
    [cpp] view plain?
    1. static int s3c2410fb_setcolreg(unsigned regno,  
    2.                    unsigned red, unsigned green, unsigned blue,  
    3.                    unsigned transp, struct fb_info *info)  
    4. {  
    5.     struct s3c2410fb_info *fbi = info->par;  
    6.     void __iomem *regs = fbi->io;  
    7.     unsigned int val;  
    8.   
    9.     /* TQ2440的LCD是FB_VISUAL_TRUECOLOR,即TFT */  
    10.     switch (info->fix.visual) {  
    11.     case FB_VISUAL_TRUECOLOR:  
    12.         /* true-colour, use pseudo-palette */  
    13.         if (regno < 16) {  
    14.             u32 *pal = info->pseudo_palette;  
    15.   
    16.             /* 用red,green,blue三原色构造出val */  
    17.             val  = chan_to_field(red,   &info->var.red);  
    18.             val |= chan_to_field(green, &info->var.green);  
    19.             val |= chan_to_field(blue,  &info->var.blue);  
    20.   
    21.             pal[regno] = val;  
    22.         }  
    23.         break;  
    24.     ......  
    25.   
    26.     default:  
    27.         return 1;   /* unknown type */  
    28.     }  
    29.     return 0;  
    30. }  
    chan_to_field 函数如下,将具体的RGB数据代入就比较容易理解这个函数了,相应的var.red、var.green、var.blue在s3c2410fb_check_var函数的最后面有设置。

    [cpp] view plain?
    1. static inline unsigned int chan_to_field(unsigned int chan,  
    2.                      struct fb_bitfield *bf)  
    3. {  
    4.     chan &= 0xffff;  
    5.     chan >>= 16 - bf->length;  
    6.     return chan << bf->offset;  
    7. }  

    s3c2410fb_check_var函数设置rgb的部分代码,这里省略了大部分源码,为的是方便参考。

    [cpp] view plain?
    1. static int s3c2410fb_check_var(struct fb_var_screeninfo *var,  
    2.                    struct fb_info *info)  
    3. {     
    4.     .......  
    5.     /* set r/g/b positions */  
    6.     switch (var->bits_per_pixel) {  
    7.     .......  
    8.     /* TQ2440的LCD就是采用这种模式 */  
    9.     case 32:                  
    10.         /* 24 bpp 888 and 8 dummy */  
    11.         var->red.length      = 8;  
    12.         var->red.offset      = 16;  
    13.         var->green.length    = 8;  
    14.         var->green.offset    = 8;  
    15.         var->blue.length = 8;  
    16.         var->blue.offset = 0;  
    17.         break;  
    18.     }  
    19.     return 0;  
    20. }  
     而cfb_fillrect、cfb_copyarea、cfb_imageblit是通用的函数,不用驱动工程师去理会,只需要在加载lcd驱动时,将其对应的模块加载,而要加载模块,必须在编译内核后,再执行make modules,这样就可以得到相应的cfb*.ko了。
    到这里s3c2410fb.c内核自带的lcd驱动基本剖析完毕,这里总结一下难点:

    一、内核自带的lcd驱动是以平台驱动设备模型来编写的,难点不在框架,框架其实很简单,

    1、分配一个fb_info结构体;

    2、设置 fb_info结构体;

    3、注册;

    4、硬件相关的设置

    二、好啦,难点就是如何设置fb_info结构体,而fb_info结构体成员那么多,是不每个成员都要一一设置呢?当然不是,

    主要设置fb_info结构体的固定参数 fb_fix_screeninfo结构体和可变参数fb_var_screeninfo结构体,还有就是硬件相关

    的设置,比如lcd时序参数的设置,也就是要设置lcdcon1~lcdcon5,lcdaddr1~lcdaddr3各个寄存器。

  • 相关阅读:
    联想yoga pro 13 使用Hyper-v蓝屏错误PAGE_FAULT_IN_NONPAGED-AREA的解决办法
    部署 Halo 博客系统
    GiuHub高级搜索关键字
    layer提示框弹出异常抖动,而且不显示
    bootstrap同一页面上多个模态框
    border-radius
    git查看本机ssh公钥
    简单的git连接远程仓库小记
    hexo搭建完成后输入hexo s 没有生成Html静态文件
    application/xml and text/xml的区别
  • 原文地址:https://www.cnblogs.com/alan666/p/8312420.html
Copyright © 2011-2022 走看看