5 linux lcd驱动程序编写

1 lcd硬件操作原理

Lcd显示的过程如下:

  1. 从显存中输出显示颜色的数据,在屏幕左上角的第一个点开始显示,每间隔一个像素时钟VCLK,向右移动一个点,当移到最右边时,会根据水平同步信号HSYNC跳到下一行的最左边;
  2. 又重复步骤1的操作,直到显示到右下角最后一个点为止,这时根据垂直同步信号YSYNC,又跳回到左上角第一个点开始下一帧图片的显示。

2 编写驱动

在上一章节结尾已经理出了lcd驱动程序的大致流程。首先来实现入口函数lcd_init函数。

  1. 分配一个fb_info空间;
  2. 对fb_info指向的结构体进行配置;
    1. 设置可变参数;
    2. 设置固定参数;
    3. 设置fops;
  3. 设置其他参数。
  4. 硬件相关的配置;
    1. 设置lcd相关的引脚;
    2. 设置lcd控制器的寄存器;
    3. 设置显存,并将显存的地址通知到lcd控制器;
    4. 开启lcd,lcd控制器以及背光。
  5. 注册framebuffer。

出口函数lcd_exit主要实现对资源的释放,具体操作如下:

  1. 卸载framebuffer;
  2. 关闭lcd控制器,背光;
  3. 释放显存空间;
  4. 解除所有io资源的映射;
  5. 释放申请的fb_info空间。

详细的代码如下所示。

  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/errno.h>  
  4. #include <linux/string.h>  
  5. #include <linux/mm.h>  
  6. #include <linux/slab.h>  
  7. #include <linux/delay.h>  
  8. #include <linux/fb.h>  
  9. #include <linux/init.h>  
  10. #include <linux/dma-mapping.h>  
  11. #include <linux/interrupt.h>  
  12. #include <linux/workqueue.h>  
  13. #include <linux/wait.h>  
  14. #include <linux/platform_device.h>  
  15. #include <linux/clk.h>  
  16.     
  17. #include <asm/io.h>  
  18. #include <asm/uaccess.h>  
  19. #include <asm/div64.h>  
  20.     
  21. #include <asm/mach/map.h>  
  22. #include <asm/arch/regs-lcd.h>  
  23. #include <asm/arch/regs-gpio.h>  
  24. #include <asm/arch/fb.h>  
  25.     
  26. static volatile unsigned long *gpbcon;  
  27. static volatile unsigned long *gpbdat;  
  28. static volatile unsigned long *gpccon;  
  29. static volatile unsigned long *gpdcon;  
  30. static volatile unsigned long *gpgcon;  
  31. static u32 pseudo_pal[16];  
  32.     
  33. struct lcd_regs{  
  34.     unsigned long lcdcon1;  
  35.     unsigned long lcdcon2;  
  36.     unsigned long lcdcon3;  
  37.     unsigned long lcdcon4;  
  38.     unsigned long lcdcon5;  
  39.     unsigned long lcdsaddr1;  
  40.     unsigned long lcdsaddr2;  
  41.     unsigned long lcdsaddr3;  
  42.     unsigned long redlut;  
  43.     unsigned long greenlut;  
  44.     unsigned long bluelut;  
  45.     unsigned long reserved[9];  
  46.     unsigned long dithmode;  
  47.     unsigned long tpal;  
  48.     unsigned long lcdintpnd;  
  49.     unsigned long lcdsrcpnd;  
  50.     unsigned long lcdintmask;  
  51.     unsigned long tconsel;  
  52. };  
  53. static volatile struct lcd_regs *lcdregs;  
  54.         
  55. static struct fb_info      *s3cxx_lcd;  
  56. static int s3cfb_setcolreg(unsigned regno,  
  57.                    unsigned red, unsigned green, unsigned blue,  
  58.                    unsigned transp, struct fb_info *info);  
  59.     
  60. static struct fb_ops s3c_lcdfb_ops = {  
  61.     .owner      = THIS_MODULE,  
  62.     .fb_setcolreg   = s3cfb_setcolreg,  
  63.     .fb_fillrect    = cfb_fillrect,  
  64.     .fb_copyarea    = cfb_copyarea,  
  65.     .fb_imageblit   = cfb_imageblit,  
  66. };  
  67.     
  68. static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)  
  69. {  
  70.     chan &= 0xffff;  
  71.     chan >>= 16 - bf->length;  
  72.     return chan << bf->offset;  
  73. }  
  74.     
  75. static int s3cfb_setcolreg(unsigned regno,  
  76.                    unsigned red, unsigned green, unsigned blue,  
  77.                    unsigned transp, struct fb_info *info)  
  78. {  
  79.     unsigned int val;  
  80.         
  81.     if(regno > 16)  
  82.         return 1;  
  83.     
  84.     /* red green blue三原色构造出val */  
  85.     val  = chan_to_field(red,   &info->var.red);  
  86.     val |= chan_to_field(green, &info->var.green);  
  87.     val |= chan_to_field(blue,  &info->var.blue);  
  88.     
  89.     pseudo_pal[regno] = val;  
  90.     return 0;     
  91. }  
  92.     
  93. static int lcd_init(void)  
  94. {  
  95.     /* 1、分配一个fb_info空间 */  
  96.     s3cxx_lcd = framebuffer_alloc(0, NULL);  
  97.         
  98.     /* 2、配置 */  
  99.     /* 2.1、设置可变参数 */  
  100.     s3cxx_lcd->var.xres          = 480;  
  101.     s3cxx_lcd->var.yres          = 272;  
  102.     s3cxx_lcd->var.xres_virtual  = 480;  
  103.     s3cxx_lcd->var.yres_virtual  = 272;  
  104.     s3cxx_lcd->var.bits_per_pixel    = 16;  
  105.     /* RGB:565 */  
  106.     s3cxx_lcd->var.red.offset        = 11;  
  107.     s3cxx_lcd->var.red.length        = 5;  
  108.     s3cxx_lcd->var.green.offset      = 5;  
  109.     s3cxx_lcd->var.green.length      = 6;  
  110.     s3cxx_lcd->var.blue.offset       = 0;  
  111.     s3cxx_lcd->var.blue.length       = 5;      
  112.     s3cxx_lcd->var.activate          = FB_ACTIVATE_NOW;  
  113.             
  114.     /* 2.2、设置固定参数 */  
  115.     strcpy(s3cxx_lcd->fix.id, "mylcd");  
  116.     s3cxx_lcd->fix.smem_len      = 480*272*16/8;  
  117.     s3cxx_lcd->fix.type          = FB_TYPE_PACKED_PIXELS;  
  118.     s3cxx_lcd->fix.visual            = FB_VISUAL_TRUECOLOR;  
  119.     s3cxx_lcd->fix.line_length       = 480*2;  
  120.         
  121.     /* 2.3、设置fbops */  
  122.     s3cxx_lcd->fbops             = &s3c_lcdfb_ops;  
  123.     /* 2.4、设置其他参数 */  
  124.     s3cxx_lcd->pseudo_palette        = pseudo_pal;  
  125.     s3cxx_lcd->screen_size           = 480*272*16/8;  
  126.         
  127.     /* 3、硬件相关的操作 */  
  128.     /* 3.1、设置lcd的引脚 */  
  129.     gpbcon = ioremap(0x56000010, 8);  
  130.     gpbdat = gpbcon + 1;  
  131.     gpccon = ioremap(0x56000020, 4);  
  132.     gpdcon = ioremap(0x56000030, 4);  
  133.     gpgcon = ioremap(0x56000060, 4);  
  134.     
  135.     /* GPIOc管脚用于VD[7:0]LCD_LPCREVBLCD_LPCREV LCD_LPCOEVMVFRAMEVLINEVCLKLEND */  
  136.     *gpccon = 0Xaaaaaaaa;  
  137.     /*GPIOd管脚用于VD[23:8] */  
  138.     *gpdcon = 0Xaaaaaaaa;  
  139.     /* GPIOB0管脚设为输出引脚 */  
  140.     *gpbcon &= ~0x03;  
  141.     *gpbcon |= 0x01;  
  142.     *gpbdat &= ~0x01;  
  143.     /*gpiog4管脚用于LCD_PWRDN */  
  144.     *gpgcon |= (3<<8);  
  145.         
  146.     /* 3.2、设置lcd控制器的寄存器 */  
  147.     lcdregs = ioremap(0X4D000000, sizeof(struct lcd_regs));  
  148.     /* lcdcon1 
  149.      * bit[17:8] vclk = hclk /[(clkval + 1)*2] 
  150.               10000000Hz(100nS) = 100000000/[(clkval + 1)*2] 
  151.               clkval = 4 
  152.      * bit[6:5]  11 = TFT LCD panel 
  153.      * bit[4:1]  1100 = 16 bpp for TFT 
  154.      * bit[0]    0 = Disable the video output and the LCD control signal 
  155.      */  
  156.     lcdregs->lcdcon1 = (4 << 8)| (3 << 5) | (0x0c << 1) ;  
  157.     /* 
  158.      * lcdcon2 垂直方向的时间参数 
  159.      * bit[31:24] VBPD VSYNC之后多久才能发出第一行数据 
  160.      *            LCD手册上  T0-T2-T1 = VBPD + 1 = 4; 
  161.                   VBPD=3 
  162.      * bit[23:14] LINEVAL 显示的行数 320 = LINEVAL + 1; 
  163.      *            LINEVAL = 319; 
  164.      * bit[13:6]  VFPD 最有一行数据发出多久后,再发出VSYNC 
  165.      *            VFPD  T2-T5 = VFPD + 1; 
  166.      *            VFPD=1; 
  167.      * bit[5:0]   VSPW VSYNC信号的脉冲宽度  
  168.      *            VSPW + 1 = T1 = 1 
  169.      *            VSPW = 0; 
  170.      */  
  171.     lcdregs->lcdcon2 = (1 << 24) | (271 << 14) | (1 << 6) |(9);  
  172.     /* 
  173.      * lcdcon3 水平方向的时间参数 
  174.      * bit[25:19] HBPD HSYNC之后多久才能发出第一行数据 
  175.      *            LCD手册上  T6-T8-T7 = HBPD + 1 =273-251-5=17; 
  176.                   HBPD=17 
  177.      * bit[18:8]  HOZVAL 显示的列数 240 = HOZVAL + 1; 
  178.      *            LINEVAL = 239; 
  179.      * bit[7:0]   HFPD 最后一行的最后一个数据发出多久后,再发出HSYNC 
  180.      *            VFPD  T8-T11 = 251-240=11=VFPD+1; 
  181.      *            VFPD=10; 
  182.      */  
  183.      lcdregs->lcdcon3 = (1 << 19) | (479 << 8) | (1 << 0);  
  184.     /* 
  185.      * lcdcon4 水平方向的时间参数 
  186.      * bit[7:0]   HSPW HSYNC信号的脉冲宽度 
  187.      *            HSPW  T7 = HSPW+1 = 5; 
  188.      *            HSPW=4; 
  189.      */  
  190.     lcdregs->lcdcon4 = (40 << 0);  
  191.     /* 
  192.      * 信号的极性 
  193.      * bit[11] 1 = 5:6:5 Format 
  194.      * bit[10] 0 = The video data is fetched at VCLK falling edge 
  195.      * bit[9]  This bit indicates the VLINE/HSYNC pulse polarity. 1 = Inverted 
  196.      * bit[8]  This bit indicates the VFRAME/VSYNC pulse polarity. 1 = Inverted 
  197.      * bit[6]  This bit indicates the VDEN signal polarity. 0 = Normal 
  198.      * bit[3]  PWREN  LCD_PWREN output signal enable/disable.0 = Disable PWREN signal 1 = Enable PWREN signal 
  199.      * bit[1]  BSWP  =0 
  200.      * bit[0]  HWSWP = 1 参照2440手册 
  201.      */  
  202.     lcdregs->lcdcon5 = (1 << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (1 << 0);  
  203.         
  204.     /* 3.3、设置显存,并将显存的地址通知到lcd控制器*/  
  205.     s3cxx_lcd->screen_base = dma_alloc_writecombine(NULL, s3cxx_lcd->fix.smem_len, &s3cxx_lcd->fix.smem_start, GFP_KERNEL);  
  206.     lcdregs->lcdsaddr1 = (s3cxx_lcd->fix.smem_start >> 1) &~(3<<30);  
  207.     lcdregs->lcdsaddr2 = ((s3cxx_lcd->fix.smem_start + s3cxx_lcd->fix.smem_len) >> 1) & 0x1fffff;  
  208.     lcdregs->lcdsaddr3 = (480*16/16); /* 一行的长度(单位:2字节) */  
  209.     
  210.     lcdregs->lcdcon1 |= (1<<0);    /* 使能LCD控制器 */  
  211.     lcdregs->lcdcon5 |= (1<<3);    /* 使能lcd */  
  212.     *gpbdat |= 1;               /*输出高电平,打开背光 */  
  213.         
  214.     //s3cxx_lcd->fix.smem_start          /* 显存的物理地址 */  
  215.     /* 4、注册 */  
  216.     register_framebuffer(s3cxx_lcd);  
  217.         
  218.     return 0;  
  219. }  
  220.     
  221.     
  222. static void lcd_exit()  
  223. {  
  224.     unregister_framebuffer(s3cxx_lcd);  
  225.     lcdregs->lcdcon1 &= ~(1<<0);  
  226.     *gpbdat &= ~1;  
  227.     dma_free_writecombine(NULL, s3cxx_lcd->fix.smem_len, s3cxx_lcd->screen_base, s3cxx_lcd->fix.smem_start);  
  228.         
  229.     iounmap(lcdregs);  
  230.     iounmap(gpbcon);  
  231.     iounmap(gpccon);  
  232.     iounmap(gpdcon);  
  233.     iounmap(gpgcon);  
  234.     framebuffer_release(s3cxx_lcd);  
  235. }  
  236.     
  237.     
  238. module_init(lcd_init);  
  239. module_exit(lcd_exit);  
  240.     
  241. MODULE_LICENSE("GPL");  

 

3 编译调试

  1. 去掉内核中原有的lcd驱动

    使用make menuconfig命令,对内核重新配置。

    如上图所示,由->Device Drivers进入,选择->Graphics support,最终将sc2410 lcd framebuffer support设置成M,作为模块编译。因为在fb_ops结构体中,cfb_fillrect、cfb_copyarea、cfb_imageblit三个模块被调用了,所以需要将sc2410 lcd framebuffer作为模块进行编译,方便我们后面对这三个模块进行挂载。

  2. 对内核设置完成后,使用make uImage命令编译内核,使用make modules命令编译模块。编译结束后,将uImage和cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko分别拷贝到可被NFS挂接的目录下/work/nfs_root/...。
  3. 通过nfs服务,使用新的uImage启动系统。
  4. 装载cfbfillrect.ko、cfbcopyarea.ko、cfbimageblit.ko和lcd.ko驱动模块。

  5. 测试。

    使用echo hello > /dev/tty1命令,将hello输出到lcd终端。会发现lcd屏幕上打印出"hello"字符。

    使用cat lcd.ko > /dev/fb0命令,将lcd.ko的内容显示到lcd上。这里显示的是花屏的效果。

     

视频中使用的是3.5寸的lcd屏,这里需要将对应的参数修改成4.3寸屏的,才能看到正确的实验现象。

猜你喜欢

转载自www.cnblogs.com/beijiqie1104/p/11511916.html