LCD驅動程序


學習目標:熟悉TFT LCD的概念,分層驅動工作原理和程序編寫。

一、LCD 概念

1.  顯示器數據組織格式

1)一幅圖像成為一幀,每幀由多行組成,每行由多個像素組成。每個像素的顏色由若干位表示,對於256色LCD,每個像素由8位表示,稱為8BPP。

2)顯示器呈Z字行的路線進行掃描顯示,使用HSYNC、VSYNC控制掃描和跳轉的路徑;

2、操作過程

1)設置LCD的HSYNC、VSYNC\VCLK等信號的參數,並將幀內存的地址告訴LCD控制器,塔克自動的發起DMA傳輸,從幀內存中得到圖像數據,出現在數據總線VD[23:0]上。我們只需要將顯示的圖像數據寫入幀內存中即可。

2)圖像數據的存儲:

例如:由三原色組建的256色(8BPP)顯示模式,使用8位數據表示一個像素的顏色。但特殊的是,這8位數據用於表示在調色板中的索引值。這里的調色板使用256*16的內存,即使用16BPP的顯示格式來表示對應各個索引值的顏色。因此,最終在LCD顯示的仍為16BPP的數據。

內存數據和像素對應的關系為:

其中,P1、P2...為一個個的像素。

像素在調色板中的數據存放模式16BPP分為兩種格式:5:6:5和5:5:5:1.即:

二、LCD驅動

1、幀緩沖設備

     frambuffer設備層是對顯示設備的一種抽象。其中,幀緩沖是Linux為顯示設備提供的一個接口,它把一些顯示設備描述成一個緩沖區,允許應用程序通過FrameBuffer定義好的接口訪問這些圖形設備,從而不用去關心具體的硬件細節。對於幀緩沖設備而言,只要在顯示緩沖區與顯示點對應的區域寫入顏色值,對應的顏色就會自動的在屏幕上顯示。

2、LCD作為一種幀緩沖設備,也是一種標准的字符型設備,對應於文件系統下/dev/fb%d設備文件。

3、驅動結構

首先分析一下driver/video/fbmem.c

1)進入__init fbmem_init(入口函數),主要創建了字符設備“fb”和類,利用cat 命令查看(cat /proc/devices),可看到該目錄下的fb,主設備號為29。由於還沒有注冊LCD驅動,所以沒有設備節點,

 1 static int __init fbmem_init(void)
 2 {
 3     create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);
 4     //創建字符設備"fb"
 5     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
 6         printk("unable to get major %d for fb devs\n", FB_MAJOR);
 7 
 8     fb_class = class_create(THIS_MODULE, "graphics"); //創建類graphics"
 9     if (IS_ERR(fb_class)) {
10         printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
11         fb_class = NULL;
12     }
13     return 0;
14 }

2)fb_fops結構體及open函數

 1 static const struct file_operations fb_fops = {
 2     .owner =    THIS_MODULE,
 3     .read  =    fb_read,
 4     .write =    fb_write,
 5     .ioctl =    fb_ioctl,
 6 #ifdef CONFIG_COMPAT
 7     .compat_ioctl = fb_compat_ioctl,
 8 #endif
 9     .mmap =       fb_mmap,
10     .open =       fb_open,
11     .release =    fb_release,
12 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
13     .get_unmapped_area = get_fb_unmapped_area,
14 #endif
15 #ifdef CONFIG_FB_DEFERRED_IO
16     .fsync =    fb_deferred_io_fsync,
17 #endif
18 };

-->fb_open函數:

 1 static int fb_open(struct inode *inode, struct file *file)
 2 {
 3     int fbidx = iminor(inode); //取出設備次設備號  4     struct fb_info *info;    //定義一個fb_info結構體  5     int res = 0;
 6 
 7     if (fbidx >= FB_MAX)
 8         return -ENODEV;
 9 #ifdef CONFIG_KMOD
10     if (!(info = registered_fb[fbidx])) // 在次設備里面得到fb_info結構信息(lcd的驅動信息)賦值給info
11 try_to_load(fbidx); 12 #endif /* CONFIG_KMOD */ 13 if (!(info = registered_fb[fbidx])) 14 return -ENODEV; 15 if (!try_module_get(info->fbops->owner)) 16 return -ENODEV; 17 file->private_data = info; 18 if (info->fbops->fb_open) { //如果該設備info結構體有open函數,就執行registered_fb[fbidx]->fbops->fb_open
19 res = info->fbops->fb_open(info,1); 20 if (res) 21 module_put(info->fbops->owner); 22 } 23 return res; 24 }

由於fb設備(幀緩沖設備)主設備號固定,不同設備以次設備號進行區分,執行該設備open函數時,最終指向的是對應設備的open函數。

接下來看一下fb_read函數:

 1 static ssize_t
 2 fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 3 {
 4     unsigned long p = *ppos;
 5     struct inode *inode = file->f_path.dentry->d_inode;
 6     int fbidx = iminor(inode);           //取出設備次設備號  7     struct fb_info *info = registered_fb[fbidx];  //定義並獲取設備的fb_info結構體  8     u32 *buffer, *dst;
 9     u32 __iomem *src;
10     int c, i, cnt = 0, err = 0;
11     unsigned long total_size;
12 
13     if (!info || ! info->screen_base)
14         return -ENODEV;
15 
16     if (info->state != FBINFO_STATE_RUNNING)
17         return -EPERM;
18 
19     if (info->fbops->fb_read)
20         return info->fbops->fb_read(info, buf, count, ppos); //如果該設備fb_info結構體有read函數,就執行registered_fb[fbidx]->fbops->fb_read 21     
22     total_size = info->screen_size;
23 
24     if (total_size == 0)
25         total_size = info->fix.smem_len;
26 
27     if (p >= total_size)
28         return 0;
29 
30     if (count >= total_size)
31         count = total_size;
32 
33     if (count + p > total_size)
34         count = total_size - p;
35 
36     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
37              GFP_KERNEL);
38     if (!buffer)
39         return -ENOMEM;
40 
41     src = (u32 __iomem *) (info->screen_base + p);
42 
43     if (info->fbops->fb_sync)
44         info->fbops->fb_sync(info);
45 
46     while (count) {
47         c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
48         dst = buffer;
49         for (i = c >> 2; i--; )
50             *dst++ = fb_readl(src++);
51         if (c & 3) {
52             u8 *dst8 = (u8 *) dst;
53             u8 __iomem *src8 = (u8 __iomem *) src;
54 
55             for (i = c & 3; i--;)
56                 *dst8++ = fb_readb(src8++);
57 
58             src = (u32 __iomem *) src8;
59         }
60 
61         if (copy_to_user(buf, buffer, c)) {
62             err = -EFAULT;
63             break;
64         }
65         *ppos += c;
66         buf += c;
67         cnt += c;
68         count -= c;
69     }
71     kfree(buffer);
73     return (err) ? err : cnt;
74 }

由以上程序可知,read的調用和open類似。都依賴於對應設備的fb_info結構體info,在程序中是由registered_fb[fbidx]數組獲取的。
3)最后,看一下registered_fb[fbidx]數組的定義,位於register_framebuffer函數中。

 1 int register_framebuffer(struct fb_info *fb_info)
 2 {
 3     int i;
 4     struct fb_event event;
 5     struct fb_videomode mode;
 6 
 7     if (num_registered_fb == FB_MAX)
 8         return -ENXIO;
 9     num_registered_fb++;
10     for (i = 0 ; i < FB_MAX; i++)
11         if (!registered_fb[i])
12             break;
13     fb_info->node = i;
14 
15     fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), "fb%d", i);//創建設備節點,名稱為fbi,主設備號為FB_MAJOR 29,次設備號為i 17     if (IS_ERR(fb_info->dev)) {
18         /* Not fatal */
19         printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
20         fb_info->dev = NULL;
21     } else
22         fb_init_device(fb_info);
23 
24     if (fb_info->pixmap.addr == NULL) {
25         fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
26         if (fb_info->pixmap.addr) {
27             fb_info->pixmap.size = FBPIXMAPSIZE;
28             fb_info->pixmap.buf_align = 1;
29             fb_info->pixmap.scan_align = 1;
30             fb_info->pixmap.access_align = 32;
31             fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
32         }
33     }    
34     fb_info->pixmap.offset = 0;
35 
36     if (!fb_info->pixmap.blit_x)
37         fb_info->pixmap.blit_x = ~(u32)0;
38 
39     if (!fb_info->pixmap.blit_y)
40         fb_info->pixmap.blit_y = ~(u32)0;
41 
42     if (!fb_info->modelist.prev || !fb_info->modelist.next)
43         INIT_LIST_HEAD(&fb_info->modelist);
44 
45     fb_var_to_videomode(&mode, &fb_info->var);
46     fb_add_videomode(&mode, &fb_info->modelist);
47     registered_fb[i] = fb_info; //賦值到registered_fb[i]數組中 48 
49     event.info = fb_info;
50     fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
51     return 0;
52 }

接下來看一下fb硬件驅動程序,以/drivers/video/s3c2410fb.c為例。

1)驅動入口

 1 static struct platform_driver s3c2410fb_driver = {
 2     .probe        = s3c2410fb_probe,
 3     .remove        = s3c2410fb_remove,
 4     .suspend    = s3c2410fb_suspend,
 5     .resume        = s3c2410fb_resume,
 6     .driver        = {
 7         .name    = "s3c2410-lcd",
 8         .owner    = THIS_MODULE,
 9     },
10 };
11 
12 int __devinit s3c2410fb_init(void)
13 {
14     return platform_driver_register(&s3c2410fb_driver);
15 }
16 
17 static void __exit s3c2410fb_cleanup(void)
18 {
19     platform_driver_unregister(&s3c2410fb_driver);
20 }

2)當平台設備的驅動和設備匹配后,會直接調用prob函數。

  1 static int __init s3c2410fb_probe(struct platform_device *pdev)
  2 {
  3     struct s3c2410fb_info *info;
  4     struct fb_info       *fbinfo;
  5     struct s3c2410fb_hw *mregs;
  6     int ret;
  7     int irq;
  8     int i;
  9     u32 lcdcon1;
 10 
 11     mach_info = pdev->dev.platform_data;  //獲取lcd設備信息  12     if (mach_info == NULL) {
 13         dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");
 14         return -EINVAL;
 15     }
 25     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); //分配fb_info結構體  26     if (!fbinfo) {
 27         return -ENOMEM;
 28     }
     //設置fb_info結構體
31 info = fbinfo->par; 32 info->fb = fbinfo; 33 info->dev = &pdev->dev; 34 35 platform_set_drvdata(pdev, fbinfo); 37 dprintk("devinit\n"); 39 strcpy(fbinfo->fix.id, driver_name); 41 memcpy(&info->regs, &mach_info->regs, sizeof(info->regs)); 43 /* Stop the video and unset ENVID if set */ 44 info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID; 45 lcdcon1 = readl(S3C2410_LCDCON1); 46 writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); 47 48 info->mach_info = pdev->dev.platform_data; 49 50 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 51 fbinfo->fix.type_aux = 0; 52 fbinfo->fix.xpanstep = 0; 53 fbinfo->fix.ypanstep = 0; 54 fbinfo->fix.ywrapstep = 0; 55 fbinfo->fix.accel = FB_ACCEL_NONE;
64 fbinfo->fbops = &s3c2410fb_ops; 65 fbinfo->flags = FBINFO_FLAG_DEFAULT; 66 fbinfo->pseudo_palette = &info->pseudo_pal; 67 73 74 fbinfo->var.upper_margin = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1; 75 fbinfo->var.lower_margin = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1; 76 fbinfo->var.vsync_len = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1; 77 78 fbinfo->var.left_margin = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1; 79 fbinfo->var.right_margin = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1; 80 fbinfo->var.hsync_len = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1; 81 90 fbinfo->fix.smem_len = mach_info->xres.max * 91 mach_info->yres.max * 92 mach_info->bpp.max / 8; 93 94 for (i = 0; i < 256; i++) 95 info->palette_buffer[i] = PALETTE_BUFF_CLEAR; 96 97 if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) { 98 ret = -EBUSY; 99 goto dealloc_fb; 100 } 103 dprintk("got LCD region\n"); 104 //硬件相關的操作,中斷、時鍾.... 105 ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); 106 if (ret) { 107 dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); 108 ret = -EBUSY; 109 goto release_mem; 110 } 111 112 info->clk = clk_get(NULL, "lcd"); 113 if (!info->clk || IS_ERR(info->clk)) { 114 printk(KERN_ERR "failed to get lcd clock source\n"); 115 ret = -ENOENT; 116 goto release_irq; 117 } 118 119 clk_enable(info->clk); 120 dprintk("got and enabled clock\n"); 121 122 msleep(1); 123 124 /* Initialize video memory */ 125 ret = s3c2410fb_map_video_memory(info); 126 if (ret) { 127 printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret); 128 ret = -ENOMEM; 129 goto release_clock; 130 } 137 ret = register_framebuffer(fbinfo);//注冊fb_info結構體 138 if (ret < 0) { 139 printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret); 140 goto free_video_memory; 141 } 142 143 /* create device files */ 144 device_create_file(&pdev->dev, &dev_attr_debug); 145 146 printk(KERN_INFO "fb%d: %s frame buffer device\n", 147 fbinfo->node, fbinfo->fix.id); 148 149 return 0; 150 151 free_video_memory: 152 s3c2410fb_unmap_video_memory(info); 153 release_clock: 154 clk_disable(info->clk); 155 clk_put(info->clk); 156 release_irq: 157 free_irq(irq,info); 158 release_mem: 159 release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD); 160 dealloc_fb: 161 framebuffer_release(fbinfo); 162 return ret; 163 }

小結:

根據驅動結構和程序源碼分析可知,lcd驅動程序需要完成以下幾部分:

1)分配一個fb_info結構體:由函數framebuffer_alloc() 完成 ;

2)設置fb_info結構體;

3)注冊fb_inforegister_framebuffer();

4)硬件相關的操作 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM