• LCD驱动程序


    学习目标:熟悉TFT LCD的概念,分层驱动工作原理和程序编写。

    一、LCD 概念

    1.  显示器数据组织格式

    1)一幅图像成为一帧,每帧由多行组成,每行由多个像素组成。每个像素的颜色由若干位表示,对于256色LCD,每个像素由8位表示,称为8BPP。

    2)显示器呈Z字行的路线进行扫描显示,使用HSYNC、VSYNC控制扫描和跳转的路径;

    2、操作过程

    1)设置LCD的HSYNC、VSYNCVCLK等信号的参数,并将帧内存的地址告诉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
    ", 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
    ", 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
    ", 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
    ");
     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 "); 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 "); 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 ", 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 "); 115 ret = -ENOENT; 116 goto release_irq; 117 } 118 119 clk_enable(info->clk); 120 dprintk("got and enabled clock "); 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 ", 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 ", 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 ", 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)硬件相关的操作 

  • 相关阅读:
    学习笔记之05-printf和scanf函数
    学习笔记之04-函数
    学习笔记之03-第一个C程序代码分析
    学习笔记之02-第一个C程序
    学习笔记之01-C语言概述
    Internal Server Error with LAMP
    Git学习总结
    Git-多人协作
    素数之和
    数列之和
  • 原文地址:https://www.cnblogs.com/lxl-lennie/p/10248889.html
Copyright © 2020-2023  润新知