Framebuffer简介
在Linux设备中,LCD显示采用了帧缓冲(framebuffer)技术,所以LCD驱动也叫Framebuffer驱动,所以LCD驱动框架就是围绕帧缓冲展开工作。帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象出来,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备, 主设备号为29,对应于/dev/fbn。
Framebuffer框架结构
核心层代码fbmem.c向上提供了完整的字符设备操作接口,也就是实现注册字符设备,提供通用的open,read,write,ioctl,mmap等接口;向下给硬件设备驱动层提供标准的驱动编程接口。
在linux系统中,一个硬件控制器(显卡)抽象为一个fb_info结构体,要实现一个LCD驱动就是要实现这个结构,并且使用核心层提供的注册函数注册。
重要结构体
内核版本:4.20.12
- fb_info
// include/linux/fb.hstructfb_info { atomic_tcount; intnode; intflags; intfbcon_rotate_hint; structmutexlock; /* open/release/ioctl中使用*/structmutexmm_lock; /*fb_mmap和smem_*数据域中使用 */structfb_var_screeninfovar; /* LCD屏可变参数 */structfb_fix_screeninfofix; /* LCD屏固定参数 */structfb_monspecsmonspecs; /* Current Monitor specs */structwork_structqueue; /* Framebuffer event queue */structfb_pixmappixmap; /* Image hardware mapper */structfb_pixmapsprite; /* Cursor hardware mapper */structfb_cmapcmap; /* Current cmap */structlist_headmodelist; /* mode list */structfb_videomode*mode; /* current mode */structbacklight_device*bl_dev; //背光设备structmutexbl_curve_mutex; u8bl_curve[FB_BACKLIGHT_LEVELS]; //背光水平曲线structdelayed_workdeferred_work; structfb_deferred_io*fbdefio; structfb_ops*fbops; /*真正操作硬件寄存器的方法集合*/structdevice*device; structdevice*dev; /* fb设备*/intclass_flag; /* private sysfs flags */structfb_tile_ops*tileops; /* Tile Blitting */union { char__iomem*screen_base; /* LCD虚拟显存地址 */char*screen_buffer; }; unsignedlongscreen_size; /* LCD虚拟显存大小 */void*pseudo_palette; /* 指向16种颜色的调试板,其实就是一块内存*/u32state; /* Hardware state i.e suspend */void*fbcon_par; void*par; //私有数据,用来存放自己的数据的结构地址structapertures_struct { unsignedintcount; structaperture { resource_size_tbase; resource_size_tsize; } ranges[0]; } *apertures; boolskip_vt_switch; /* no VT switch on suspend/resume required */};
使用标准的LCD框架编写,var, fix, fbops, screen_base这四个成员是一定要实现的。
- fb_var_screeninfo
structfb_var_screeninfo { __u32xres; /* 可见屏幕一行有多少像素点 */__u32yres; /*可见屏幕一屏有多少行,也就是列*/__u32xres_virtual; /* 虚拟屏幕一行有多少像素点*/__u32yres_virtual; /*虚拟屏幕一屏有多少行*///显存大小并不一定等于实际屏幕显示对应的区域__u32xoffset; /* 虚拟屏到实际屏的水平偏移量 */__u32yoffset; /* 虚拟屏到实际屏的垂直偏移量*/__u32bits_per_pixel; /* 每个像素的位数即BPP,比如:RGB565则填入16*/__u32grayscale; /* 0 = 彩色, 1 = 灰度屏*//* >1 = FOURCC */structfb_bitfieldred; /* 红色的长度和偏移信息 */structfb_bitfieldgreen; /* 绿色的长度和偏移信息 */structfb_bitfieldblue; /* 蓝色的长度和偏移信息 */structfb_bitfieldtransp; /* 透明色的长度和偏移信息*/__u32nonstd; /* 不等于0则为非标准像素格式 *///定义修改参数生效时刻,一般马上生效,对应值是0,宏名是 FB_ACTIVATE_NOW__u32activate; /* see FB_ACTIVATE_* *///存放物理屏的物理尺寸,是外观尺寸,单位是mm,可选择填充的项 非重点__u32height; /* height of picture in mm */__u32width; /* width of picture in mm */__u32accel_flags; /* (OBSOLETE) see fb_info.flags *///下面是LCD屏的工作时序,参数从datasheet来__u32pixclock; /* pixel clock in ps (pico seconds) */__u32left_margin; /* time from sync to picture */__u32right_margin; /* time from picture to sync */__u32upper_margin; /* time from sync to picture */__u32lower_margin; __u32hsync_len; /* length of horizontal sync */__u32vsync_len; /* length of vertical sync */__u32sync; /* see FB_SYNC_* */__u32vmode; /* see FB_VMODE_* */__u32rotate; /* angle we rotate counter clockwise */__u32colorspace; /* colorspace for FOURCC-based modes */__u32reserved[4]; /* Reserved for future compatibility */};
上面结构体的参数大部分都是要从LCD屏的datasheet中获取。
- fb_fix_screeninfo
structfb_fix_screeninfo { charid[16]; /* LCD标识名 填写一个16字符以内字符串即可 */unsignedlongsmem_start; /* 显存的物理起始地址,不是虚拟地址*//* (physical address) */__u32smem_len; /* 显存的内存长度 */__u32type; /* 表示像素类型 see FB_TYPE_* */__u32type_aux; /* Interleave for interleaved Planes */__u32visual; /* 表示颜色类型 see FB_VISUAL_* */__u16xpanstep; /* 如果没有硬件panning就赋值为0 */__u16ypanstep; /* 如果没有硬件panning就赋值为0 */__u16ywrapstep; /* 如果没有硬件panning就赋值为0 *///一行的字节数 ,例:(RGB565)240*320,那么这里就等于240*16/8 __u32line_length; /* length of a line in bytes */unsignedlongmmio_start; /* Start of Memory Mapped I/O *//* (physical address) *///独立显卡相关的,基本不用__u32mmio_len; /* Length of Memory Mapped I/O */__u32accel; /* Indicate to driver which *//* specific chip/card we have */__u16capabilities; /* see FB_CAP_* */__u16reserved[2]; /* Reserved for future compatibility */};
- fb_ops
structfb_ops { /* open/release and usage marking */structmodule*owner; int (*fb_open)(structfb_info*info, intuser); int (*fb_release)(structfb_info*info, intuser); /* For framebuffers with strange non linear layouts or that do not* work with normal memory mapped access*/ssize_t (*fb_read)(structfb_info*info, char__user*buf, size_tcount, loff_t*ppos); ssize_t (*fb_write)(structfb_info*info, constchar__user*buf, size_tcount, loff_t*ppos); /* checks var and eventually tweaks it to something supported,* DO NOT MODIFY PAR */int (*fb_check_var)(structfb_var_screeninfo*var, structfb_info*info); /* set the video mode according to info->var */int (*fb_set_par)(structfb_info*info); /* set color register */int (*fb_setcolreg)(unsignedregno, unsignedred, unsignedgreen, unsignedblue, unsignedtransp, structfb_info*info); /* set color registers in batch */int (*fb_setcmap)(structfb_cmap*cmap, structfb_info*info); /* blank display */int (*fb_blank)(intblank, structfb_info*info); /* pan display */int (*fb_pan_display)(structfb_var_screeninfo*var, structfb_info*info); /* Draws a rectangle */void (*fb_fillrect) (structfb_info*info, conststructfb_fillrect*rect); /* Copy data from area to another */void (*fb_copyarea) (structfb_info*info, conststructfb_copyarea*region); /* Draws a image to the display */void (*fb_imageblit) (structfb_info*info, conststructfb_image*image); /* Draws cursor */int (*fb_cursor) (structfb_info*info, structfb_cursor*cursor); /* wait for blit idle, optional */int (*fb_sync)(structfb_info*info); /* perform fb specific ioctl (optional) */int (*fb_ioctl)(structfb_info*info, unsignedintcmd, unsignedlongarg); /* Handle 32bit compat ioctl (optional) */int (*fb_compat_ioctl)(structfb_info*info, unsignedcmd, unsignedlongarg); /* perform fb specific mmap */int (*fb_mmap)(structfb_info*info, structvm_area_struct*vma); /* get capability given var */void (*fb_get_caps)(structfb_info*info, structfb_blit_caps*caps, structfb_var_screeninfo*var); /* teardown any resources to do with this framebuffer */void (*fb_destroy)(structfb_info*info); /* called at KDB enter and leave time to prepare the console */int (*fb_debug_enter)(structfb_info*info); int (*fb_debug_leave)(structfb_info*info); };
常用的重要成员:
- fb_open:当你的lcd不需要做什么特殊的初始化操作,这个方法可以不实现
- fb_release:当你的应用程序不使用lcd设备的时候,需要做的事情在这里实现
- fb_read:当你的lcd控制器使用的内存是独立显存的时候才需要使用,直接使用核心层read
- fb_write:当你的lcd控制器使用的内存是独立显存的时候才需要使用,直接使用核心层write
- fb_check_var:检测应用程序传递下来的可变参数是否合法。当你不提供给应用程序通过ioctl命令动态修改LCD可变参数时不需要实现。
- fb_set_par:实现的功能是把可变参数设置到硬件寄存器中去
- fb_blank:实现屏幕的黑屏白屏模式(开屏,关屏)
- fb_fillrect:实现功能是填充矩形
- fb_copyarea:实现功能是区域复制
- fb_imageblit:实现功能是绘制位图
- fb_mmap:实现的功能是把内核空间的分配的显存映射到用户空间中对应的mmap系统调用,当你的控制器是独显的时候才需要
API函数
/*函数功能:注册fb_info*/intregister_framebuffer(structfb_info*fb_info) /*函数功能:注销fb_info*/intunregister_framebuffer(structfb_info*fb_info) /*函数功能:动态分配DMA内存,同时可以得到分配内存的虚拟地址和物理地址 参数说明: dev:设备指针如果没有赋值NULL size:内存大小 dma_handle:作为输出参数使用,存放分配到的内存对应的物理地址 flag:内存分配方式 返回值:分配到的内存的首地址*/void*dma_alloc_writecombine(structdevice*dev, size_tsize, dma_addr_t*dma_handle, gfp_tflag) /*功能:释放由dma_alloc_writecombine分配的dma内存 参数: dev:设备指针如果没有赋值NULL size:内存大小 cpu_addr:dma_alloc_writecombine得到的虚拟地址首地址 dma_handle:作为输出参数使用,存放分配到的内存对应的物理地址 */voiddma_free_writecombine(structdevice*dev, size_tsize, void*cpu_addr, dma_addr_tdma_handle)
lcd每屏显示的数据量很大,所以lcd一般是利用DMA来搬运数据,而DMA模块只会使用物理地址,所以LCD驱动中需要记录物理地址。
总结
上面的结构体中有很多参数,对于一些普通的LCD屏,需要用到的参数并不多。主要就是从数据手册中获取屏幕参数信息,填充fb_info,然后注册。另一部分就LCDC的寄存器配置。目前很多原厂会将这两部分进行分离,这样就更方便客户进行定制。