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

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. /* TQ(LCD W4.3)的配置,config_EmbedSky_W43:CONFIG_FB_S3C24X0_TFT480272=y */
  12. #elif defined(CONFIG_FB_S3C24X0_TFT480272)
  13. .width        = 480,
  14. .height       = 272,
  15. .pixclock     = 40000, /* HCLK 100 MHz, divisor 1 */
  16. /* VCLK=HCLK/[(CLKVAL+1)x2],HCLK = 100MHz
  17. * 根据LCD手册"WXCAT43-TG6#001_V1.0.pdf"的第22页可得,VCLK = 10MHz
  18. * 即10 = 100/[(CLKVAL+1)x2],可得CLKVAL = 4
  19. */
  20. .setclkval    = 0x4,
  21. .xres         = 480,
  22. .yres         = 272,
  23. .bpp          = 16,
  24. /* 为什么是这样?参考linux/Documentation/fb/framebuffer.txt */
  25. .left_margin  = 19,   /* 左边沿  : for HFPD*/
  26. .right_margin = 10,   /* 右边沿  : for HBPD*/
  27. .hsync_len    = 30,   /* 水平同步: for HSPW*/
  28. .upper_margin = 4,    /* 上边沿  : for VFPD*/
  29. .lower_margin = 2,    /* 下边沿  : for VBPD*/
  30. .vsync_len    = 8,    /* 垂直同步: for VSPW*/
  31. ......
  32. };

二、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. switch (var->bits_per_pixel) {
  6. case 32:
  7. case 16:
  8. case 12:
  9. info->fix.visual = FB_VISUAL_TRUECOLOR;      /* 真彩色 */
  10. break;
  11. case 1:
  12. info->fix.visual = FB_VISUAL_MONO01;
  13. break;
  14. default:
  15. info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
  16. break;
  17. }
  18. /* 一行的字节数 = x*bpp/8 = 480*32/8 = 480*4 */
  19. info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
  20. /* activate this new configuration */
  21. s3c2410fb_activate_var(info);
  22. return 0;
  23. }

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. /* 设置显示模式为: TFT LCD panel */
  9. int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
  10. /* 通过probe函数后platform_data指向tq2440_fb_info结构体实例 */
  11. struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
  12. struct s3c2410fb_display *default_display = mach_info->displays +
  13. mach_info->default_display;
  14. struct fb_var_screeninfo *var = &info->var;
  15. /* 计算LCD时钟频率, 在mach_tq2440.c里 pixclock = 40000 */
  16. int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
  17. dprintk("%s: var->xres  = %d\n", __func__, var->xres);
  18. dprintk("%s: var->yres  = %d\n", __func__, var->yres);
  19. dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
  20. if (type == S3C2410_LCDCON1_TFT) {
  21. s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
  22. --clkdiv;
  23. if (clkdiv < 0)
  24. clkdiv = 0;
  25. } else {
  26. s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
  27. if (clkdiv < 2)
  28. clkdiv = 2;
  29. }
  30. //  fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
  31. fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(default_display->setclkval);
  32. /* write new registers */
  33. dprintk("new register set:\n");
  34. dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
  35. dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
  36. dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
  37. dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
  38. dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
  39. /* 禁止视频输出,禁止LCD控制信号 */
  40. writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
  41. regs + S3C2410_LCDCON1);
  42. /* 将前面s3c2410fb_calculate_tft_lcd_regs函数设置好的lcdconx写入对应寄存器 */
  43. writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
  44. writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
  45. writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
  46. writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
  47. /* set lcd address pointers */
  48. s3c2410fb_set_lcdaddr(info);
  49. /* 最后使能LCD控制器,即使能视频输出 */
  50. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
  51. writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
  52. }

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. /* LCDSADDR1 = 帧缓冲区起始地址再右移1位 */
  7. saddr1  = info->fix.smem_start >> 1;
  8. /* LCDSADDR2 = 帧缓冲区结束地址再右移1位 */
  9. saddr2  = info->fix.smem_start;
  10. saddr2 += info->fix.line_length * info->var.yres;  /* 帧缓冲区大小 */
  11. saddr2 >>= 1;
  12. /* LCDSADDR3 = 一行的长度,单位为2字节 */
  13. saddr3 = S3C2410_OFFSIZE(0) |
  14. S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
  15. dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
  16. dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
  17. dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
  18. writel(saddr1, regs + S3C2410_LCDSADDR1);
  19. writel(saddr2, regs + S3C2410_LCDSADDR2);
  20. writel(saddr3, regs + S3C2410_LCDSADDR3);
  21. }

三、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. /* TQ2440的LCD是FB_VISUAL_TRUECOLOR,即TFT */
  9. switch (info->fix.visual) {
  10. case FB_VISUAL_TRUECOLOR:
  11. /* true-colour, use pseudo-palette */
  12. if (regno < 16) {
  13. u32 *pal = info->pseudo_palette;
  14. /* 用red,green,blue三原色构造出val */
  15. val  = chan_to_field(red,   &info->var.red);
  16. val |= chan_to_field(green, &info->var.green);
  17. val |= chan_to_field(blue,  &info->var.blue);
  18. pal[regno] = val;
  19. }
  20. break;
  21. ......
  22. default:
  23. return 1;   /* unknown type */
  24. }
  25. return 0;
  26. }

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各个寄存器。

linux lcd设备驱动剖析四的更多相关文章

  1. linux lcd设备驱动剖析一

    s3c2440 lcd驱动源码文件是:drivers/video/s3c2410fb.c 看驱动源码首先当然是先看入口函数,这里是s3c2410fb_init函数 [cpp] view plain? ...

  2. linux lcd设备驱动剖析三

    上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体 [cpp] view plain? static struct fb_ops s3 ...

  3. linux lcd设备驱动剖析二

    上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构. 上一节文章链接:http://blog.csdn.net/lwj103862095/ ...

  4. linux 块设备驱动(四)——简单的sbull实例

    #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> # ...

  5. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  6. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  7. 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(二)

    我们在 浅谈Linux PCI设备驱动(一)中(以下简称 浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设 ...

  8. 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(一)

    要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分.不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI.US ...

  9. (57)Linux驱动开发之三Linux字符设备驱动

    1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...

随机推荐

  1. js 验证 -身份证等

    js验证身份证: function isIdCard(idCard) { var num = idCard.toLowerCase().match(/\w/g); if (idCard.match(/ ...

  2. html5适应屏幕的方案

          适应屏幕的方案: 1.css3 Media queries (针对多版本设计稿) 2.设计稿不复杂的时候 通过宽度自适应用百分比 3.通过更新meta:viewport标签,通过设计稿尺寸 ...

  3. python学习笔记(xlwt/xlrd下载安装)

    python支持处理Excel 可以使用xlwt xlrd 模块 分别在https://pypi.python.org/pypi/xlwt  和 https://pypi.python.org/pyp ...

  4. .Net Core使用jexus配置https

    今天搞了一下怎么从http换成https,写一篇博客记录该过程.关于jexus的安装和使用请看我之前的一篇博客<Jexus部署Asp.Net Core项目>,唯一的不同是,将jexus升级 ...

  5. 20165332实验二 Java面向对象程序设计

    20165332实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验要求 ...

  6. 列出远程git的全部分支

    列出全部分支 git ls-remote --heads your.git | awk 'sub(/refs\/heads\//,""){print $2}' 其中awk中sub替 ...

  7. POJ 2502 最短路

    http://poj.org/problem?id=2502 同一条地铁线上的站点相邻点间按照v2建边,然后所有点之间按照v1更新建边,所有的边都是双向边,both directions. 然后直接跑 ...

  8. 使用ZooKeeper实现Java跨JVM的分布式锁(读写锁)

    一.使用ZooKeeper实现Java跨JVM的分布式锁 二.使用ZooKeeper实现Java跨JVM的分布式锁(优化构思) 三.使用ZooKeeper实现Java跨JVM的分布式锁(读写锁) 读写 ...

  9. Django restfull规范

    一. 什么是RESTful REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移” REST从资源的角 ...

  10. Activiti 教程

    Activiti入门教程:http://blog.csdn.net/column/details/activitizhou.html Activiti 5.15 用户手册:http://www.cnb ...