linux LCD驱动

来源:互联网 发布:沙发品牌推荐 知乎 编辑:程序博客网 时间:2024/05/16 12:04

  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 <mach/regs-lcd.h>  
  23. #include <mach/regs-gpio.h>  
  24. #include <mach/fb.h>  
  25.   
  26. static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,  
  27.                  unsigned int green, unsigned int blue,  
  28.                  unsigned int transp, struct fb_info *info);  
  29.   
  30.   
  31. struct lcd_regs {  
  32.     unsigned long   lcdcon1;  
  33.     unsigned long   lcdcon2;  
  34.     unsigned long   lcdcon3;  
  35.     unsigned long   lcdcon4;  
  36.     unsigned long   lcdcon5;  
  37.         unsigned long   lcdsaddr1;  
  38.         unsigned long   lcdsaddr2;  
  39.         unsigned long   lcdsaddr3;  
  40.         unsigned long   redlut;  
  41.         unsigned long   greenlut;  
  42.         unsigned long   bluelut;  
  43.         unsigned long   reserved[9];  
  44.         unsigned long   dithmode;  
  45.         unsigned long   tpal;  
  46.         unsigned long   lcdintpnd;  
  47.         unsigned long   lcdsrcpnd;  
  48.         unsigned long   lcdintmsk;  
  49.         unsigned long   lpcsel;  
  50. };  
  51.   
  52. static struct fb_ops s3c_lcdfb_ops = {  
  53.     .owner      = THIS_MODULE,  
  54.     .fb_setcolreg   = s3c_lcdfb_setcolreg,  
  55.     .fb_fillrect    = cfb_fillrect,  
  56.     .fb_copyarea    = cfb_copyarea,  
  57.     .fb_imageblit   = cfb_imageblit,/* linux内核自带的驱动,需要加载,后面使用的时候会讲到 */  
  58. };  
  59.   
  60.   
  61. static struct fb_info *s3c_lcd;  
  62.   
  63. static volatile unsigned long *gpccon;  
  64. static volatile unsigned long *gpdcon;  
  65. static volatile unsigned long *gpgcon;  
  66. static volatile struct lcd_regs* lcd_regs;  
  67. static u32 pseudo_palette[16];  
  68.   
  69.   
  70. /* from pxafb.c */  
  71. /* 这里使用的是linux自带的函数,直接copy的 */  
  72. static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)  
  73. {  
  74.     chan &= 0xffff;  
  75.     chan >>= 16 - bf->length;  
  76.     return chan << bf->offset;  
  77. }  
  78.   
  79.   
  80. static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,  
  81.                  unsigned int green, unsigned int blue,  
  82.                  unsigned int transp, struct fb_info *info)  
  83. {  
  84.     unsigned int val;  
  85.       
  86.     if (regno > 16)  
  87.         return 1;  
  88.   
  89.     /* 用red,green,blue三原色构造出val */  
  90.     val  = chan_to_field(red,   &info->var.red);  
  91.     val |= chan_to_field(green, &info->var.green);  
  92.     val |= chan_to_field(blue,  &info->var.blue);  
  93.       
  94.     //((u32 *)(info->pseudo_palette))[regno] = val;  
  95.     pseudo_palette[regno] = val;  
  96.     return 0;  
  97. }  
  98.   
  99. static int lcd_init(void)  
  100. {  
  101.     /* 1. 分配一个fb_info */  
  102.     s3c_lcd = framebuffer_alloc(0, NULL);  
  103.   
  104.     /* 2. 设置 */  
  105.     /* 2.1 设置固定的参数 */  
  106.     strcpy(s3c_lcd->fix.id, "tq2440_480_272_lcd");  
  107.     s3c_lcd->fix.smem_len = 480*272*16/8;  
  108.     s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;  
  109.     s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR; /* TFT */  
  110.     s3c_lcd->fix.line_length = 480*2;  
  111.       
  112.     /* 2.2 设置可变的参数 */  
  113.     s3c_lcd->var.xres           = 480;  
  114.     s3c_lcd->var.yres           = 272;  
  115.     s3c_lcd->var.xres_virtual   = 480;  
  116.     s3c_lcd->var.yres_virtual   = 272;  
  117.     s3c_lcd->var.bits_per_pixel = 16;  
  118.   
  119.     /* RGB:565 */  
  120.     s3c_lcd->var.red.offset     = 11;  
  121.     s3c_lcd->var.red.length     = 5;  
  122.       
  123.     s3c_lcd->var.green.offset   = 5;  
  124.     s3c_lcd->var.green.length   = 6;  
  125.   
  126.     s3c_lcd->var.blue.offset    = 0;  
  127.     s3c_lcd->var.blue.length    = 5;  
  128.   
  129.     s3c_lcd->var.activate       = FB_ACTIVATE_NOW;  
  130.       
  131.       
  132.     /* 2.3 设置操作函数 */  
  133.     s3c_lcd->fbops              = &s3c_lcdfb_ops;  
  134.       
  135.     /* 2.4 其他的设置 */  
  136.     s3c_lcd->pseudo_palette = pseudo_palette;  
  137.     /*s3c_lcd->screen_base  = ;  后面再设置显存的虚拟地址 */   
  138.     s3c_lcd->screen_size   = 480*272*16/8;  
  139.   
  140.     /* 3. 硬件相关的操作 */  
  141.     /* 3.1 配置GPIO用于LCD */  
  142.   
  143.     gpccon = ioremap(0x56000020, 4);  
  144.     gpdcon = ioremap(0x56000030, 4);  
  145.     gpgcon = ioremap(0x56000060, 4);  
  146.   
  147.         *gpccon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */  
  148.     *gpdcon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[23:8] */  
  149.   
  150.     *gpgcon |= (3<<8); /* GPG4用作LCD_PWREN */  
  151.       
  152.     /* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */  
  153.     lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));  
  154.   
  155.     /* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2], LCD手册 
  156.      *            10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2] 
  157.      *            CLKVAL = 4 
  158.      * bit[6:5]: 0b11, TFT LCD 
  159.      * bit[4:1]: 0b1100, 16 bpp for TFT 
  160.      * bit[0]  : 0 = Disable the video output and the LCD control signal. 
  161.      */  
  162.     lcd_regs->lcdcon1  = (4<<8) | (3<<5) | (0x0c<<1);  
  163.   
  164.     /* 垂直方向的时间参数 
  165.      * bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据 
  166.      *             LCD手册 VBPD=2 
  167.      * bit[23:14]: 多少行, 272, 所以LINEVAL=272-1=271 
  168.      * bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC 
  169.      *             LCD手册 VFPD=2 
  170.      * bit[5:0]  : VSPW, VSYNC信号的脉冲宽度, LCD手册T1=1, 所以VSPW=1-1=0 
  171.      */  
  172.     lcd_regs->lcdcon2  = (2<<24) | (271<<14) | (2<<6) | (10<<0);  
  173.   
  174.     /* 水平方向的时间参数 
  175.      * bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据 
  176.      *             LCD手册 HBPD=2 
  177.      * bit[18:8]: 多少列, 480, 所以HOZVAL=480-1=479 
  178.      * bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC 
  179.      *             LCD手册 HFPD=2 
  180.      */  
  181.     lcd_regs->lcdcon3 = (2<<19) | (479<<8) | (2);  
  182.   
  183.     /* 水平方向的同步信号 
  184.      * bit[7:0] : HSPW, HSYNC信号的脉冲宽度, LCD手册 HSPW=41  
  185.      */   
  186.     lcd_regs->lcdcon4 = (41<<0);  
  187.   
  188.     /* 信号的极性  
  189.      * bit[11]: 1 = 565 format 
  190.      * bit[10]: 0 = The video data is fetched at VCLK falling edge 
  191.      * bit[9] : 1 = HSYNC信号要反转,即低电平有效  
  192.      * bit[8] : 1 = VSYNC信号要反转,即低电平有效  
  193.      * bit[6] : 0 = VDEN不用反转 
  194.      * bit[3] : 0 = PWREN输出0 
  195.      * bit[1] : 0 = BSWP 
  196.      * bit[0] : 1 = HWSWP 2440手册P413 
  197.      */  
  198.     lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);  
  199.       
  200.     /* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */  
  201.     s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);  
  202.       
  203.     lcd_regs->lcdsaddr1  = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);  
  204.     lcd_regs->lcdsaddr2  = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;  
  205.     lcd_regs->lcdsaddr3  = (480*16/16);  /* 一行的长度(单位: 2字节) */      
  206.       
  207.     //s3c_lcd->fix.smem_start = xxx;  /* 显存的物理地址 */  
  208.     /* 启动LCD */  
  209.     lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */  
  210.     lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本身 */   
  211.   
  212.     /* 4. 注册 */  
  213.     register_framebuffer(s3c_lcd);  
  214.       
  215.     return 0;  
  216. }  
  217.   
  218. static void lcd_exit(void)  
  219. {  
  220.     unregister_framebuffer(s3c_lcd);  
  221.     lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本身 */  
  222.     dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);  
  223.     iounmap(lcd_regs);  
  224.     iounmap(gpccon);  
  225.     iounmap(gpdcon);  
  226.     iounmap(gpgcon);  
  227.     framebuffer_release(s3c_lcd);  
  228. }  
  229.   
  230. module_init(lcd_init);  
  231. module_exit(lcd_exit);  
  232.   
  233. MODULE_LICENSE("GPL");  


3.使用



MAKEFILE: 驱动文件名为sk_lcd.c

KERN_DIR = /home/stevenking/workspace/code/linux-2.6.39  //修改为自己的内核路径

all:
make -C $(KERN_DIR) M=`pwd` modules 

clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order

obj-m += sk_lcd.o


1.首先先从原先内核中make menuconfig去掉原来的驱动程序,不然会有冲突

-> Device Drivers
-> Graphics support
<M> S3C2410 LCD framebuffer support


2.编译内核,下载新的内核

make zImage
make modules 

3.重建根文件系统

将linux文件夹下/driver/video/下面的
cfbfillrect.ko 
cfbimgblt.ko
cfbcopyarea.ko 
以及新编译的 
sk_lcd.ko
放入/root/lib/文件夹下
重新 mkyaffs2image root root.bin,下载新的文件系统

4.启动之后:

cd /lib
insmod cfbcopyarea.ko
insmod cfbfillrect.ko 
insmod cfbimgblt.ko 
insmod sk_lcd.ko
echo hello > /dev/tty1
cat /proc/devices > /dev/tty1
就能在LCD上看到输出的文字了

补充:添加到内核
1.driver/video/kconfig

config FB_TQ2440_LCD_SK
tristate "TQ2440 LCD framebuffer support by Steven King"
depends on FB && ARCH_S3C2410
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
---help---
 LCD driver by steven king 8/18/2013.

2.makefile

obj-$(CONFIG_FB_TQ2440_LCD_SK) += tq2440_lcd_sk.o

3.文件复制过去,加上选项,编译,即可。
0 0