S5PV210(TQ210)学习笔记——LCD驱动编写

来源:互联网 发布:房地产大数据 编辑:程序博客网 时间:2024/05/10 16:14

分类: 嵌入式开发 638人阅读 评论(0) 收藏 举报
S5PV210TQ210LCD驱动
网上S5PV210内核移植的文章不是很多,而描述2440和6410内核移植的文章多数是讲如何移植,而非手动编写,但是,韦东山老师的视频中讲述了如何从头编写LCD驱动,当然是以2440为例的,我看过视频之后在TQ210平台上进行了实验,实验成功,详细的原理部分以我现在的水平还难以表达清楚。下载是我自己写的代码,适用于TQ210的7寸电容屏。
[cpp] view plaincopy
  1. #include <linux/module.h>  
  2. #include <linux/fb.h>  
  3. #include <linux/dma-mapping.h>  
  4. #include <linux/clk.h>  
  5.   
  6.   
  7. static struct fb_info *lcd_info;  
  8. unsigned long pseudo_palette[16];  
  9.   
  10. unsigned long *display_control;  
  11.   
  12. volatile unsigned long* gpf0con;  
  13. volatile unsigned long* gpf1con;  
  14. volatile unsigned long* gpf2con;  
  15. volatile unsigned long* gpf3con;  
  16. volatile unsigned long* gpd0con;  
  17. volatile unsigned long* gpd0dat;  
  18. volatile unsigned long* vidcon0;  
  19. volatile unsigned long* vidcon1;  
  20. volatile unsigned long* vidtcon0;  
  21. volatile unsigned long* vidtcon1;  
  22. volatile unsigned long* vidtcon2;  
  23. volatile unsigned long* wincon0;  
  24. volatile unsigned long* vidosd0a;  
  25. volatile unsigned long* vidosd0b;  
  26. volatile unsigned long* vidosd0c;  
  27. volatile unsigned long* vidw00add0b0;  
  28. volatile unsigned long* vidw00add1b0;  
  29. volatile unsigned long* shodowcon;  
  30.   
  31. struct clk *lcd_clk;  
  32.   
  33.   
  34. static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)  
  35. {  
  36.     chan &= 0xffff;  
  37.     chan >>= 16 - bf->length;  
  38.     return chan << bf->offset;  
  39. }  
  40.   
  41. static int lcdfb_setcolreg(unsigned int regno, unsigned int red,  
  42.                  unsigned int green, unsigned int blue,  
  43.                  unsigned int transp, struct fb_info *info)  
  44. {  
  45.     unsigned int val;  
  46.       
  47.     if (regno > 16)  
  48.         return 1;  
  49.   
  50.     /* 用red,green,blue三原色构造出val */  
  51.     val  = chan_to_field(red,   &info->var.red);  
  52.     val |= chan_to_field(green, &info->var.green);  
  53.     val |= chan_to_field(blue,  &info->var.blue);  
  54.       
  55.     //((u32 *)(info->pseudo_palette))[regno] = val;  
  56.     pseudo_palette[regno] = val;  
  57.     return 0;  
  58. }  
  59.   
  60. static struct fb_ops lcd_fbops = {  
  61.     .owner      = THIS_MODULE,  
  62.     .fb_setcolreg   = lcdfb_setcolreg,  
  63.     .fb_fillrect    = cfb_fillrect,  
  64.     .fb_copyarea    = cfb_copyarea,  
  65.     .fb_imageblit   = cfb_imageblit,  
  66. };  
  67.   
  68. static int lcd_init(void){  
  69.     int ret;  
  70.   
  71.     /*分配fb_info */  
  72.     lcd_info = framebuffer_alloc(0, NULL);  
  73.     if(lcd_info == NULL){  
  74.         printk(KERN_ERR "alloc framebuffer failed!\n");  
  75.         return -ENOMEM;  
  76.     }  
  77.   
  78.     /* 配置fb_info各成员*/  
  79.     /* fix */  
  80.     strcpy(lcd_info->fix.id, "s5pv210_lcd");  
  81.     lcd_info->fix.smem_len = 800*480*4;  
  82.     lcd_info->fix.type = FB_TYPE_PACKED_PIXELS;  
  83.     lcd_info->fix.visual = FB_VISUAL_TRUECOLOR;  
  84.     lcd_info->fix.line_length = 800*4;  
  85.   
  86.     /* var */  
  87.     lcd_info->var.xres = 800;  
  88.     lcd_info->var.yres = 480;  
  89.     lcd_info->var.xres_virtual = 800;  
  90.     lcd_info->var.yres_virtual = 480;  
  91.     lcd_info->var.bits_per_pixel = 32;  
  92.   
  93.     lcd_info->var.red.offset = 16;  
  94.     lcd_info->var.red.length = 8;  
  95.     lcd_info->var.green.offset = 8;  
  96.     lcd_info->var.green.length = 8;  
  97.     lcd_info->var.blue.offset = 0;  
  98.     lcd_info->var.blue.length = 8;  
  99.     lcd_info->var.activate = FB_ACTIVATE_NOW;  
  100.   
  101.     lcd_info->screen_size = 800*480*4;  
  102.     lcd_info->pseudo_palette = pseudo_palette;  
  103.   
  104.     lcd_info->fbops = &lcd_fbops;  
  105.     /* 配置硬件资源*/  
  106.     /* 映射内存*/  
  107.     display_control = ioremap(0xe0107008,4);  
  108.     gpf0con      = ioremap(0xE0200120, 4);  
  109.     gpf1con      = ioremap(0xE0200140, 4);  
  110.     gpf2con      = ioremap(0xE0200160, 4);  
  111.     gpf3con      = ioremap(0xE0200180, 4);  
  112.       
  113.     gpd0con      = ioremap(0xE02000A0, 4);  
  114.     gpd0dat      = ioremap(0xE02000A4, 4);  
  115.       
  116.     vidcon0      = ioremap(0xF8000000, 4);  
  117.     vidcon1      = ioremap(0xF8000004, 4);  
  118.     vidtcon0     = ioremap(0xF8000010, 4);  
  119.     vidtcon1     = ioremap(0xF8000014, 4);  
  120.     vidtcon2     = ioremap(0xF8000018, 4);  
  121.     wincon0      = ioremap(0xF8000020, 4);  
  122.     vidosd0a     = ioremap(0xF8000040, 4);  
  123.     vidosd0b     = ioremap(0xF8000044, 4);  
  124.     vidosd0c     = ioremap(0xF8000048, 4);  
  125.     vidw00add0b0 = ioremap(0xF80000A0, 4);  
  126.     vidw00add1b0 = ioremap(0xF80000D0, 4);  
  127.     shodowcon    = ioremap(0xF8000034, 4);  
  128.       
  129.     /* 配置GPIO*/  
  130.     *gpf0con = 0x22222222;  
  131.     *gpf1con = 0x22222222;  
  132.     *gpf2con = 0x22222222;  
  133.     *gpf3con = 0x22222222;  
  134.     *gpd0con &= ~0xf;  
  135.     *gpd0con |= 0x1;  
  136.     *gpd0dat |= 1<<0;  
  137.     *display_control = 2<<0;  
  138.     /* 使能时钟*/  
  139.     lcd_clk = clk_get(NULL, "lcd");  
  140.     if (!lcd_clk || IS_ERR(lcd_clk)) {  
  141.         printk(KERN_INFO "failed to get lcd clock source\n");  
  142.     }  
  143.     clk_enable(lcd_clk);  
  144.       
  145.     /* 配置LCD控制器*/  
  146.     *vidcon0 = (4<<6)|(1<<4);  
  147.     *vidcon1 = (1<<6)|(1<<5)|(1<<4);  
  148.   
  149.     *vidtcon0 = (17<<16)|(26<<8)|(4<<0);  
  150.     *vidtcon1 = (40<<16)|(214<<8)|(4<<0);  
  151.     *vidtcon2 = (479<<11)|(799<<0);  
  152.   
  153.     *wincon0 &= ~(0xf<<2);  
  154.     *wincon0 |= (0xb<<2);  
  155.   
  156.     *vidosd0a = (0<<11)|(0<<0);  
  157.     *vidosd0b = (799<<11)|(479<<0);  
  158.     *vidosd0c = 480*800;  
  159.     //物理地址  
  160.     lcd_info->screen_base = dma_alloc_writecombine(NULL,   
  161.         lcd_info->fix.smem_len, (dma_addr_t *)&(lcd_info->fix.smem_start), GFP_KERNEL);  
  162.       
  163.     *vidw00add0b0 = lcd_info->fix.smem_start;  
  164.     *vidw00add1b0 = lcd_info->fix.smem_start + lcd_info->fix.smem_len;  
  165.   
  166.     *shodowcon = 0x1;  
  167.   
  168.     //开启状态  
  169.     *wincon0 |= 1;  
  170.     *vidcon0 |= 3;  
  171.     /* 注册fb_info */  
  172.     ret = register_framebuffer(lcd_info);  
  173.     return ret;  
  174. }  
  175.   
  176. static void lcd_exit(void){  
  177.     unregister_framebuffer(lcd_info);  
  178.     dma_free_writecombine(NULL, lcd_info->fix.smem_len,   
  179.         (void*)lcd_info->screen_base, (dma_addr_t)lcd_info->fix.smem_start);  
  180.   
  181.     iounmap(shodowcon);  
  182.     iounmap(vidw00add1b0);  
  183.     iounmap(vidw00add0b0);  
  184.     iounmap(vidosd0c);  
  185.     iounmap(vidosd0b);  
  186.     iounmap(vidosd0a);  
  187.     iounmap(wincon0);  
  188.     iounmap(vidtcon2);  
  189.     iounmap(vidtcon1);  
  190.     iounmap(vidtcon0);  
  191.     iounmap(vidcon1);  
  192.     iounmap(vidcon0);  
  193.     iounmap(gpd0dat);  
  194.     iounmap(gpd0con);  
  195.     iounmap(gpf3con);  
  196.     iounmap(gpf2con);  
  197.     iounmap(gpf1con);  
  198.     iounmap(gpf0con);  
  199.     framebuffer_release(lcd_info);  
  200. }  
  201.   
  202. module_init(lcd_init);  
  203. module_exit(lcd_exit);  
  204. MODULE_LICENSE("GPL");  

将上面的代码在自己的内核环境下编译,然后下载到开发板上试运行即可。

在安装驱动程序前执行指令:

[cpp] view plaincopy
  1. ls /dev/fb*  
如果有fb0或者其他fb*存在,应该修改内和配置,取消其他fb的配置,如果看不到fb*设备,则可以按照如下步骤进行测试。
测试前还需要修改下内核配置,有两个原因:

(1) 内核默认配置下不支持Frame buffer

(2) 我们的驱动程序中用到了三个函数:

[cpp] view plaincopy
  1. .fb_fillrect    = cfb_fillrect,  
  2. .fb_copyarea    = cfb_copyarea,  
  3. .fb_imageblit   = cfb_imageblit,  
这三个函数是引用的内核中的函数,不是我们自行实现的。

鉴于上面两个原因,我们需要配置内核支持Frame buffer和列举出的三个函数,另外,内核中并没有直接配置支持这三个函数的选项,权宜之计,修改下drivers/video目录下的Kconfig文件,在config FB项中添加

[cpp] view plaincopy
  1. select FB_CFB_FILLRECT  
  2. select FB_CFB_COPYAREA  
  3. select FB_CFB_IMAGEBLIT  
添加时一定保证格式正确,参考下该文件下的其他配置项即可。配置完成后执行make menuconfig作如下配置:
[cpp] view plaincopy
  1. Device Drivers  --->  
  2.     Graphics support  --->  
  3.         <*> Support for frame buffer devices  --->  
配置后保存配置,编译内核并将编译好的内核下载到开发板或者NFS运行,同时将编译好的LCD驱动程序拷贝到开发板运行环境中进行安装,如果驱动文件名为lcd.ko,则执行:
[cpp] view plaincopy
  1. insmod lcd.ko  
这时,你可以看到屏幕被重新初始化了。虽然LCD已经初始化了,但是不知道如何进行测试,可以按照韦东山老师视频中讲述的方法进行LCD驱动测试,但是我们移植的3.8.3内核默认不支持字库,还需要作其他配置,我是用画线的方式测试的屏幕,这里我讲一下我用的测试方法:

(1) 在Linux主机上编译下面的C++程序

[cpp] view plaincopy
  1. #include <iostream>  
  2.   
  3. unsigned long buffer[480][800] = {0};  
  4.   
  5. void put_long_hex(unsigned long v){   
  6.     for(int i = 0; i != 4; ++i){  
  7.         std::cout.put(static_cast<char>(0xff&(v>>(8*(3-i)))));  
  8.     }     
  9. }  
  10.   
  11. int main(){  
  12.     for(int i = 0; i != 480; ++i){  
  13.         buffer[i][0]   = 0x00ff0000;  
  14.         buffer[i][799] = 0x0000ff00;  
  15.     }     
  16.   
  17.     for(int i = 0; i != 800; ++i){  
  18.         buffer[0][i]   = 0xff000000;  
  19.         buffer[479][i] = 0x00ffff00;  
  20.     }     
  21.   
  22.     for(int i = 0; i != 480; ++i){  
  23.         for(int j = 0; j != 800; ++j){  
  24.             put_long_hex(buffer[i][j]);   
  25.         }     
  26.     }     
  27. }  
编译指令如下:
[cpp] view plaincopy
  1. g++ -o main main.cpp  
然后如下方式执行程序:
[cpp] view plaincopy
  1. ./main > /nfsroot/rootfs/test.img  
我是直接将文件生成在NFS的根文件系统下了,你也可以用其他方式将生成的文件拷贝到开发板运行环境内,然后执行如下指令:
[cpp] view plaincopy
  1. cat test.img > /dev/fb0  
这时,就可以在屏幕上看到一个矩形且矩形的四条边颜色不相同。

如果想将驱动编译进内核,并在启动时可以看到小企鹅,可以将上面的驱动拷贝到内核的drivers/video/目录下,命名为tq210_fb.c,然后在该目录下做如下修改:

(1)修改Kconfig,添加TQ210的LCD驱动配置选项

在config FB_S4C项的后面添加如下内容:

[cpp] view plaincopy
  1. config FB_TQ210  
  2.     tristate "TQ210 lcd support"  
  3.     depends on FB  
  4.     select FB_CFB_FILLRECT  
  5.     select FB_CFB_COPYAREA  
  6.     select FB_CFB_IMAGEBLIT  
  7.     ---help---  
  8.     Currently the suport is only for the TQ210  
(2)修改Makefile,添加如下内容:
[cpp] view plaincopy
  1. obj-$(CONFIG_FB_TQ210)        += tq210_fb.o  

(3)退回到内核根目录下,执行make menuconfig并按如下方式配置内核

[cpp] view plaincopy
  1. Device Drivers  --->  
  2.     Graphics support  --->  
  3.         <*> Support for frame buffer devices  --->  
  4.             <*>   TQ210 lcd support  
  5.         [*] Bootup logo  --->  
  6.             [*]   Standard black and white Linux logo   
  7.             [*]   Standard 16-color Linux logo  
  8.             [*]   Standard 224-color Linux logo  


然后执行指令make zImage或者make uImage来编译内核,将编译好的内核烧写到开发板或者是放到NFS下即可正常运行。

如果您在开发或配置过程遇到什么问题可以留言讨论。


本文链接:http://blog.csdn.net/girlkoo/article/details/8743732


原创粉丝点击