嵌入式Linux中文站

linux framebuffer 驱动


        framebuffer 设备即帧缓冲设备(简写fb)提供了显示接口的抽象描述。他同时代表着显示接口的存储区,应用程序通过定义好的函数访问,不需要知道底层的任何操作。 Framebuffer 驱动使用的设备节点,通常位于/dev 目录,如/dev/fb*.从用户角度看,fb 设备和其他/dev 下面的设备类似:普通的字符设备,主设备号29,次设备号定义fb 的索引。通常,使用如下方式(前面的数字表示次设备号)0 = /dev/fb0 第一个fb 设备,1 = /dev/fb1 第二个fb 设备,fb 也是一种普通的内存设备,可以读写其内容。例如,屏幕抓屏:cp /dev/fb0 myfile,fb 虽然可以像内存设备(/dev/mem)一样,对其read,write,seek 以及mmap。但区别在于fb 使用的不是整个内存区,而是显存部分。通过ioctl 可以读取或设定fb 设备参数,很重要的一点,颜色表(cmap)也要通过Ioctl 设定。你可以获取设备一些不变的信息,如设备名,屏幕的组织(平面,象素,...)对应内存区的长度和起始地址。也可以获取能够改变的信息,例如位深,颜色格式,时序等。如果你改变这些值,驱动程序将对值进行优化,以满足设备特性。framebuffer是LCD控制器相关的驱动,CPU可以通过寻址的方式访问LCD控制器,所以framebuffer一般是platform driver,所以用platform driver的架构来编写framebuffer驱动。     

        当我们想编写一个FB设备驱动时,一个比较好的方法是注册platform设备,然后将FB设备的注册,IO映射操作,硬件初始化等操作放在在probe中进行,从而整体上结构清晰。

     一. 重要数据结构

           1. 帧缓冲区描述符 fb_info

            struct fb_info {
                    atomic_t count;
                    int node;
                    int flags;
                    struct mutex lock;  /* Lock for open/release/ioctl funcs */
                    struct mutex mm_lock;  /* Lock for fb_mmap and smem_* fields */
                    struct fb_var_screeninfo var; /* Current var */     //缓冲区可变参数
                    struct fb_fix_screeninfo fix; /* Current fix */         //缓冲区固定参数
                    struct fb_monspecs monspecs; /* Current Monitor specs */    //当前显示器
                    struct work_struct queue; /* Framebuffer event queue */     //事件工作队列
                    struct fb_pixmap pixmap; /* Image hardware mapper */      //图像硬件映射
                    struct fb_pixmap sprite; /* Cursor hardware mapper */        //光标硬件映射
                    struct fb_cmap cmap;  /* Current cmap */
                    struct list_head modelist;      /* mode list */            //视频模式链表
                    struct fb_videomode *mode; /* current mode */    //当前视频模式      
            #ifdef CONFIG_FB_BACKLIGHT           //支持背光的参数
                    struct backlight_device *bl_dev;
                    struct mutex bl_curve_mutex; 
                    u8 bl_curve[FB_BACKLIGHT_LEVELS];
            #endif
            #ifdef CONFIG_FB_DEFERRED_IO
                    struct delayed_work deferred_work;
                    struct fb_deferred_io *fbdefio;
            #endif
                    struct fb_ops *fbops;      //帧缓冲区操作函数,framebuffer驱动的核心数据结构
                    struct device *device;  /* This is the parent */    //父设备
                    struct device *dev;  /* This is this fb device */    //FB设备
                    int class_flag;                    /* private sysfs flags */
            #ifdef CONFIG_FB_TILEBLITTING
                    struct fb_tile_ops *tileops;    /* Tile Blitting */
            #endif
                    char __iomem *screen_base; /* Virtual address */
                    unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
                    void *pseudo_palette;  /* Fake palette of 16 colors */ 
            #define FBINFO_STATE_RUNNING 0
            #define FBINFO_STATE_SUSPENDED 1
                    u32 state;   /* Hardware state i.e suspend */
                    void *fbcon_par;                /* fbcon use-only private area */
                    void *par;
                    struct apertures_struct {
                            unsigned int count;
                            struct aperture {
                                     resource_size_t base;
                                     resource_size_t size;
                             } ranges[0];
                    } *apertures;
            };

            2. 帧缓冲区操作函数表 fb_ops

                struct fb_ops {
                       struct module *owner;          //模块所有者
                       int (*fb_open)(struct fb_info *info, int user);        //FB打开操作
                       int (*fb_release)(struct fb_info *info, int user);   //FB释放操作
                       ssize_t (*fb_read)(struct fb_info *info, char __user *buf,size_t count, loff_t *ppos);    //FB读操作
                       ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,size_t count, loff_t *ppos); //FB写操作
                       int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);//检查FB可变变量,并调整为可用值
                       int (*fb_set_par)(struct fb_info *info);          //设置视频模式
                       int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info); //设置颜色寄存器
                       int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); //批量设置颜色寄存器,并设置颜色表
                       int (*fb_blank)(int blank, struct fb_info *info);     //空白显示
                       int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); //面板显示
                       void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);              //画矩形
                       void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);//把数据拷贝到另外一个区域
                       void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);//在帧缓冲去中显示一个图片
                       int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);                    //绘制光标
                       void (*fb_rotate)(struct fb_info *info, int angle);                                              //旋转显示
                       int (*fb_sync)(struct fb_info *info);                                                      //等待并同步图像显示完成
                       int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,unsigned long arg);//FB的IOCTL操作
                       int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,unsigned long arg);    
                       int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);  //FB的内存映射MMAP操作
                       void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var);
                       void (*fb_destroy)(struct fb_info *info);
                       int (*fb_debug_enter)(struct fb_info *info);
                       int (*fb_debug_leave)(struct fb_info *info);
              };

              3. 固定参数描述符

                  struct fb_fix_screeninfo {
                           char id[16];                                           /* identification string eg "TT Builtin" *///FB ID
                           unsigned long smem_start;            /* Start of frame buffer mem */       //FB缓存开始位置
                                                                                          /* (physical address) */                   //物理地址相关
                           __u32 smem_len;                             /* Length of frame buffer mem */   //FB缓存长度
                           __u32 type;                                          /* see FB_TYPE_*  */                       //FB类型
                           __u32 type_aux;                                 /* Interleave for interleaved Planes *///FB分界
                           __u32 visual;                                       /* see FB_VISUAL_*  */                   //FB色彩模式
                           __u16 xpanstep;                                 /* zero if no hardware panning  */  //没有硬件PAN则赋0
                           __u16 ypanstep;                                 /* zero if no hardware panning  */            
                           __u16 ywrapstep;                               /* zero if no hardware ywrap    */
                           __u32 line_length;                             /* length of a line in bytes    */       //1行的字节数
                           unsigned long mmio_start;              /* Start of Memory Mapped I/O*///内存IO映射的开始地址
                                                                                          /* (physical address) */
                           __u32 mmio_len;                               /* Length of Memory Mapped I/O  *///IO 长度
                           __u32 accel;                                        /* Indicate to driver which */
                                                                                          /*  specific chip/card we have */
                           __u16 capabilities;                             /* see FB_CAP_*   */
                           __u16 reserved[2];                             /* Reserved for future compatibility */
                  };

              4. 可变参数描述符

                  struct fb_var_screeninfo {
                          __u32 xres;   /* visible resolution  */                           //可见解析度,分辨率的X轴宽度
                          __u32 yres;                                                                     //可见解析度,分辨率的Y轴宽度
                          __u32 xres_virtual;  /* virtual resolution  */                //虚拟解析度X轴宽度
                          __u32 yres_virtual;                                                        //虚拟解析度Y轴宽度
                          __u32 xoffset;   /* offset from virtual to visible */       //虚拟与可见解析度之间X轴偏移
                          __u32 yoffset;   /* resolution   */                                   //虚拟与可见解析度之间Y轴偏移
                          __u32 bits_per_pixel;  /* guess what   */                   //每像素点的位数
                          __u32 grayscale;  /* 0 = color, 1 = grayscale, */        //灰度级别,0表示彩色
                                                                  /* >1 = FOURCC   */
                          struct fb_bitfield red;  /* bitfield in fb mem if true color, */ //RGB位域的红色
                          struct fb_bitfield green; /* else only length is significant */ //RGB位域的绿色
                          struct fb_bitfield blue;                                                      //RGB位域的蓝色
                          struct fb_bitfield transp; /* transparency   */                 //透明色
                          __u32 nonstd;   /* != 0 Non standard pixel format */   //如果该值不为0,非标准像素格式
                          __u32 activate;   /* see FB_ACTIVATE_*  */
                          __u32 height;   /* height of picture in mm    */             //图像在屏幕中的高度
                          __u32 width;   /* width of picture in mm     */                //图像在屏幕中的宽度
                          __u32 accel_flags;  /* (OBSOLETE) see fb_info.flags */
                          __u32 pixclock;   /* pixel clock in ps (pico seconds) */  //像点时钟
                          __u32 left_margin;  /*time from sync to picture*///行切换时,显示与同步间的延时左边空隙
                          __u32 right_margin; /*time from picture to sync*///行切换时,显示与同步间的延时右边空隙
                          __u32 upper_margin;/*time from sync to picture*///帧切换时,显示与同步间的延时上边空隙
                          __u32 lower_margin;                                             //帧切换时,显示与同步间的延时上边空隙
                          __u32 hsync_len;  /* length of horizontal sync */   //行同步长度
                          __u32 vsync_len;  /* length of vertical sync */        //帧同步长度
                          __u32 sync;   /* see FB_SYNC_*  */                                                          
                          __u32 vmode;   /* see FB_VMODE_*  */
                          __u32 rotate;   /* angle we rotate counter clockwise */   //顺时针旋转角度
                          __u32 colorspace;  /* colorspace for FOURCC-based modes *///颜色空间
                          __u32 reserved[5];  /* Reserved for future compatibility */
                   };

              5. 调色板描述符

                   struct fb_cmap {
                           __u32 start;   /* First entry */                                             //第一个颜色入口
                           __u32 len;   /* Number of entries */                                //颜色总数
                           __u16 *red;   /* Red values */                                          //RGB红色值
                           __u16 *green;                                                                     //RGB红绿值
                           __u16 *blue;                                                                       //RGB红蓝值
                           __u16 *transp;   /* transparency, can be NULL */        //透明度值
                    };

     二. 主要函数

           1. framebuffer_alloc, 为FB驱动分配内存空间并初始化,这个函数的核心的作用是申请一块空间,并转换为struct fb_info 指针返回,在这个申请过程私有数据空间size并非是必须的,需要的话可以有但并非必须。

               struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
               {
                        #define BYTES_PER_LONG (BITS_PER_LONG/8)
                        #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
                        int fb_info_size = sizeof(struct fb_info);
                        struct fb_info *info;
                        char *p;
                        if (size)
                               fb_info_size += PADDING;
                        p = kzalloc(fb_info_size + size, GFP_KERNEL);
                        if (!p)
                              return NULL;
                        info = (struct fb_info *) p;
                        if (size)
                               info->par = p + fb_info_size;
                        info->device = dev;
                        #ifdef CONFIG_FB_BACKLIGHT
                                mutex_init(&info->bl_curve_mutex);
                        #endif
                                 return info;
                        #undef PADDING
                        #undef BYTES_PER_LONG
               }

           2. register_framebuffer,向系统注册FB。

                int register_framebuffer(struct fb_info *fb_info)
                {
                      int ret;
                      mutex_lock(&registration_lock);
                      ret = do_register_framebuffer(fb_info);
                      mutex_unlock(&registration_lock);
                      return ret;
                }

     三. 实现framebuffer驱动程序的一个例子的步骤

                1. 包含头文件

                     #include <linux/fb.h>

                     #include <linux/device.h>

                     #include <linux/platform_device.h>

                2. 定义及初始化相关FB参数结构体

                     struct fb_var_screeninfo dnfb_var__devinitdata = {
                             .xres  = 1280,
                             .yres  = 1024,
                             .xres_virtual = 2048,
                             .yres_virtual = 1024,
                             .bits_per_pixel = 1,
                             .height  = -1,
                             .width  = -1,
                             .vmode  = FB_VMODE_NONINTERLACED,
                      };

                      static struct fb_fix_screeninfo dnfb_fix__devinitdata = {
                              .id  = "Apollo Mono",
                              .smem_start = (FRAME_BUFFER_START + IO_BASE),
                              .smem_len = FRAME_BUFFER_LEN,
                              .type  = FB_TYPE_PACKED_PIXELS,
                              .visual  = FB_VISUAL_MONO10,
                              .line_length = 256,
                      };

                3. 定义及实现结构 fb_ops与它的成员函数

                      static int dnfb_blank(int blank, struct fb_info *info)
                      {
                             if (blank)
                                    out_8(AP_CONTROL_3A, 0x0);
                             else
                                    out_8(AP_CONTROL_3A, 0x1);
                             return 0;
                      }

                      void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
                      {
                               unsigned long pat, pat2, fg;
                               unsigned long width = rect->width, height = rect->height;
                               int bits = BITS_PER_LONG, bytes = bits >> 3;
                               u32 bpp = p->var.bits_per_pixel;
                               unsigned long __iomem *dst;
                               int dst_idx, left;
                               if (p->state != FBINFO_STATE_RUNNING)
                                      return;
                               if (p->fix.visual == FB_VISUAL_TRUECOLOR || p->fix.visual == FB_VISUAL_DIRECTCOLOR )
                                      fg = ((u32 *) (p->pseudo_palette))[rect->color];
                               else
                                      fg = rect->color;
                               pat = pixel_to_pat(bpp, fg);
                               dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
                               dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
                               dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
                               left = bits % bpp;
                               if (p->fbops->fb_sync)
                                       p->fbops->fb_sync(p);
                               if (!left) {
                                       u32 bswapmask = fb_compute_bswapmask(p);
                                       void (*fill_op32)(struct fb_info *p,unsigned long __iomem *dst, int dst_idx,unsigned long pat, unsigned n, int bits,u32 bswapmask) = NULL;

                                       switch (rect->rop) {
                                       case ROP_XOR:
                                                fill_op32 = bitfill_aligned_rev;
                                                break;
                                       case ROP_COPY:
                                                fill_op32 = bitfill_aligned;
                                                break;
                                       default:
                                                printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
                                                fill_op32 = bitfill_aligned;
                                                break;
                                        }
                                        while (height--) {
                                                dst += dst_idx >> (ffs(bits) - 1);
                                                dst_idx &= (bits - 1);
                                                fill_op32(p, dst, dst_idx, pat, width*bpp, bits, bswapmask);
                                                dst_idx += p->fix.line_length*8;
                                        }
                                } else {
                                        int right, r;
                                        void (*fill_op)(struct fb_info *p, unsigned long __iomem *dst,int dst_idx, unsigned long pat, int left,int right, unsigned n, int bits) = NULL;
                                        right = bpp - left;
                                        switch (rect->rop) {
                                               case ROP_XOR:
                                                        fill_op = bitfill_unaligned_rev;
                                                        break;
                                               case ROP_COPY:
                                                        fill_op = bitfill_unaligned;
                                                        break;
                                               default:
                                                        printk(KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
                                                        fill_op = bitfill_unaligned;
                                                        break;
                                         }
                                         while (height--) {
                                                 dst += dst_idx / bits;
                                                 dst_idx &= (bits - 1);
                                                  r = dst_idx % bpp;
                                                  pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
                                                  fill_op(p, dst, dst_idx, pat2, left, right,width*bpp, bits);
                                                  dst_idx += p->fix.line_length*8;
                                         }
                                  }
                          }

                          static void dnfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
                          {

                          }

                          void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
                         {
                                u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
                                u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
                                u32 width = image->width;
                                u32 dx = image->dx, dy = image->dy;
                                u8 __iomem *dst1;
                                if (p->state != FBINFO_STATE_RUNNING)
                                     return;
                                bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
                                start_index = bitstart & (32 - 1);
                                pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
                                bitstart /= 8;
                                bitstart &= ~(bpl - 1);
                                dst1 = p->screen_base + bitstart;
                                if (p->fbops->fb_sync)
                                      p->fbops->fb_sync(p);
                                if (image->depth == 1) {
                                        if (p->fix.visual == FB_VISUAL_TRUECOLOR || p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
                                               fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
                                               bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
                                        } else {
                                               fgcolor = image->fg_color;
                                               bgcolor = image->bg_color;
                                 } 
                                 if (32 % bpp == 0 && !start_index && !pitch_index && ((width & (32/bpp-1)) == 0) && bpp >= 8 && bpp <= 32)    
                                        fast_imageblit(image, p, dst1, fgcolor, bgcolor);
                                 else 
                                        slow_imageblit(image, p, dst1, fgcolor, bgcolor,start_index, pitch_index);
                            } else
                                     color_imageblit(image, p, dst1, start_index, pitch_index);
                      }

                      static struct fb_ops dn_fb_ops = {
                             .owner  = THIS_MODULE,
                             .fb_blank = dnfb_blank,
                             .fb_fillrect = cfb_fillrect,
                             .fb_copyarea = dnfb_copyarea,
                             .fb_imageblit = cfb_imageblit,
                      };

                4. 定义及实现总线驱动结构体及成员函数,如果FB驱动是挂载在platform总线上,则定义platform_driver结构,如果是别的总线,比如PCI总线,在定义pci_driver结构。一般会在总线驱动成员的probe函数中调用FB的函数framebuffer_alloc与register_framebuffer来申请FB设备及注册到系统。

                     static int __devinit dnfb_probe(struct platform_device *dev)
                     {
                            struct fb_info *info;
                            int err = 0;
                            info = framebuffer_alloc(0, &dev->dev);
                            if (!info)
                                  return -ENOMEM;
                            info->fbops = &dn_fb_ops;
                            info->fix = dnfb_fix;
                            info->var = dnfb_var;
                            info->var.red.length = 1;
                            info->var.red.offset = 0;
                            info->var.green = info->var.blue = info->var.red;
                            info->screen_base = (u_char *) info->fix.smem_start;
                            err = fb_alloc_cmap(&info->cmap, 2, 0);
                            if (err < 0) {
                                   framebuffer_release(info);
                                   return err;
                            }
                            err = register_framebuffer(info);
                            if (err < 0) {
                                   fb_dealloc_cmap(&info->cmap);
                                   framebuffer_release(info);
                                   return err;
                             }
                             platform_set_drvdata(dev, info);
                             out_8(AP_CONTROL_3A, RESET_CREG);
                             out_be16(AP_WRITE_ENABLE, 0x0);
                             out_8(AP_CONTROL_0, NORMAL_MODE);
                             out_8(AP_CONTROL_1, (AD_BLT | DST_EQ_SRC | NORM_CREG1));
                             out_8(AP_CONTROL_2, S_DATA_PLN);
                             out_be16(AP_ROP_1, SWAP(0x3));
                             return err;
                       }

                       static struct platform_driver dnfb_driver = {
                             .probe = dnfb_probe,
                             .driver = {
                                     .name = "dnfb",
                              },
                       };

                       static struct platform_device dnfb_device = {
                              .name = "dnfb",
                       };

                5. 调用总线驱动注册函数把FB驱动注册到系统。

                     int __init dnfb_init(void)
                     {
                           int ret;
                           if (!MACH_IS_APOLLO)
                                 return -ENODEV;
                           if (fb_get_options("dnfb", NULL))
                                 return -ENODEV;
                           ret = platform_driver_register(&dnfb_driver);
                           if (!ret) {
                                 ret = platform_device_register(&dnfb_device);
                           if (ret)
                                 platform_driver_unregister(&dnfb_driver);
                           }
                           return ret;
                     }
                     module_init(dnfb_init);

本文永久更新链接:http://embeddedlinux.org.cn/emb-linux/kernel-driver/201710/25-7674.html



分享:

评论