核心文件:/drivers/video/fbmem.c
18.2.3.Linux帧缓冲相关数据结构与函数
1. fb_info结构体(最关键)
-
- struct fb_info {
- int node;
- int flags;
- struct mutex lock;
- struct mutex mm_lock;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- struct fb_monspecs monspecs;
- struct work_struct queue;
- struct fb_pixmap pixmap;
- struct fb_pixmap sprite;
- struct fb_cmap cmap;
- struct list_head modelist;
- struct fb_videomode *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;
- struct device *device;
- struct device *dev;
- int class_flag;
- #ifdef CONFIG_FB_TILEBLITTING
- struct fb_tile_ops *tileops;
- #endif
- char __iomem *screen_base;
- unsigned long screen_size;
- void *pseudo_palette;
- #define FBINFO_STATE_RUNNING 0
- #define FBINFO_STATE_SUSPENDED 1
- u32 state;
- void *fbcon_par;
-
- void *par;
-
-
-
- resource_size_t aperture_base;
- resource_size_t aperture_size;
- };
2.fb_ops结构体
fb_info成员变量fb_ops为指向底层操作的函数指针,这些函数需要驱动程序猿自己编写。
-
- struct fb_ops {
-
- struct module *owner;
- int (*fb_open)(struct fb_info *info, int user);
- int (*fb_release)(struct fb_info *info, int user);
-
-
- ssize_t (*fb_read)(struct fb_info *info, char __user *buf,size_t count, loff_t *ppos);
- ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,size_t count, loff_t *ppos);
-
-
- int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
-
-
- 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);
-
-
- 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);
-
-
- 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);
- };
fb_ops 的 fb_check_var() 成员函数用于检查可以修改的屏幕参数并调整到合适的值,而 fb_set_par() 则使得用户设置的屏幕参数在硬件上有效。
3. fb_var_screeninfo 和 fb_fix_screeninfo 结构体
fb_var_screeninfo 记录用户可以修改的显示控制器参数,包括屏幕分辨率和每个像素点的比特数。
fb_fix_screeninfo 记录用户不能修改的显示控制器参数,包括屏幕缓冲区的物理地址、长度。当对帧缓冲舍内进行映射操作的时候,就是从fb_fix_screeninfo中取得缓冲区的物理地址。上述结构体都需要在驱动程序中初始化和设置。
-
- struct fb_var_screeninfo {
- __u32 xres;
- __u32 yres;
- __u32 xres_virtual;
- __u32 yres_virtual;
- __u32 xoffset;
- __u32 yoffset;
-
- __u32 bits_per_pixel;
- __u32 grayscale;
-
-
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-
- __u32 nonstd;
-
- __u32 activate;
-
- __u32 height;
- __u32 width;
-
- __u32 accel_flags;
-
-
- __u32 pixclock;
- __u32 left_margin;
- __u32 right_margin;
- __u32 upper_margin;
- __u32 lower_margin;
- __u32 hsync_len;
- __u32 vsync_len;
- __u32 sync;
- __u32 vmode;
- __u32 rotate;
- __u32 reserved[5];
- };
-
- struct fb_fix_screeninfo {
- char id[16];
- unsigned long smem_start;
- __u32 smem_len;
- __u32 type;
- __u32 type_aux;
-
-
-
-
-
-
- __u32 visual;
- __u16 xpanstep;
- __u16 ypanstep;
- __u16 ywrapstep;
- __u32 line_length;
- unsigned long mmio_start;
- __u32 mmio_len;
- __u32 accel;
- __u16 reserved[3];
- };
4.fb_bitfield 结构体
fb_bitfield描述每一像素显示缓冲区的组织方式,包含位域偏移、位域长度和MSB(最高有效位)指示。
-
- struct fb_bitfield {
- __u32 offset;
- __u32 length;
- __u32 msb_right;
- };
5.fb_cmap结构体
fb_cmap结构体记录设备无关的颜色表信息,用户空间可以通过 ioctl() 的 FBIOGETCMAP 和 FBIOPUTCMAP 命令读取或设定 颜色表。
-
- struct fb_cmap {
- __u32 start;
- __u32 len;
- __u16 *red;
- __u16 *green;
- __u16 *blue;
- __u16 *transp;
- };
-
-
-
-
-
-
- if( (vinfo.bits_per_pixel == 8) || (vinfo.bits_per_pixel == 4) ){
- screencols = (vinfo.bits_per_pixel == 8) ? 256 : 16;
- int loopc;
- startcmap = new fb_cmap;
- startcmap->start = 0;
- startcmap->len = screencols;
-
- startcmap->red = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
- startcmap->green = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
- startcmap->blue = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
- startcmap->transp = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);;
-
- ioctl(df, FBIOGETCMAP, startcmap);
- for( loopc = 0; loopc < screencols; loopc++ ){
- screenclut[loopc] = qRgb(startcmap->red[loopc] >> 8, startcmap->green[loopc] >> 8, startcmap->blue[loopc] >> 8)
- }
- }
对于一个256色(BPP=8)的800*600的图像,若使用红绿蓝分别用一个字节描述,则需要800*600*3=1 440 000Byte,若使用颜色表,需要800*600*1+256*3=480 768Byte的空间。
7.文件操作结构体
-
- static const struct file_operations fb_fops = {
- .owner = THIS_MODULE,
- .read = fb_read,
- .write = fb_write,
- .unlocked_ioctl = fb_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = fb_compat_ioctl,
- #endif
- .mmap = fb_mmap,
- .open = fb_open,
- .release = fb_release,
- #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
- .get_unmapped_area = get_fb_unmapped_area,
- #endif
- #ifdef CONFIG_FB_DEFERRED_IO
- .fsync = fb_deferred_io_fsync,
- #endif
- };
7.注册于注销帧缓冲设备
int register_framebuffer(struct fb_info *fb_info); /* 注册成功返回0, 超过设备最大数目FB_MAX(32)返回-ENXIO */
int unregister_framebuffer(struct fb_info *fb_info);
18.3帧缓冲设备驱动结构
18.4帧缓冲设备驱动的模块加载与卸载函数
帧缓冲设备驱动的模块加载函数:
(1)申请FBI结构体的内存空间,初始化FBI结构体中固定和可变的屏幕参数。
(2)根据具体的LCD屏幕特点,完成LCD控制器硬件的初始化。
(3)申请帧缓冲设备的显示缓冲区空间。
(4)注册帧缓冲设备。
在帧缓冲设备驱动的模块加载函数中完成的工作只是注册平台驱动,初始化FBI结构体中的固定和可变参数、LCD控制器硬件的初始化、申请帧缓冲设备的显示缓冲区空间和注册帧缓冲设备的工作在平台驱动的探测函数中完成。
-
-
- static struct platform_driver xxxfb_driver = {
- .probe = xxxfb_probe,
- .remove = xxxfb_remove,
- .suspend = xxxfb_suspend,
- .resume = xxxfb_resume,
- .driver = {
- .name = "xxx-lcd",
- .owner = THIS_MODULE,
- },
- };
-
- static int __init xxxfb_probe(...)
- {
- struct fb_info* info;
-
-
- info = framebuffer_alloc(...);
-
- info->screen_base = framebuffer_virtual_memory;
- info->var = xxxfb_var;
- info->fix = xxxfb_fix;
-
-
- alloc_dis_buffer(...);
-
-
- lcd_init(...);
-
-
- xxxfb_check_var(&info->var, info);
-
-
- if( register_framebuffer(info)<0 )
- return -EINVAL;
-
- return 0;
- }
-
-
- static void __exit xxxfb_remove(...)
- {
- struct fb_info* info = dev_get_drv_data(dev);
-
- if(info){
- unregister_framebuffer(info);
- dealloc_dis_buffer(...);
- framebuffer_release(info);
- }
- }
-
- int __init xxxfb_init(void)
- {
- return platform_driver_register(&xxxfb_driver);
- }
-
- static void __exit xxxfb_cleanup()
- {
- platform_driver_unregister(&xxxfb_driver);
- }
- module_init(xxxfb_init);
- module_exit(xxxfb_cleanup);
18.5 帧缓冲设备显示缓冲区的申请与释放
在分配缓冲区时一定要考虑cache的一致性问题,因为系统往往会通过DMA方式搬移显示数据。合适的方式是使用 dma_alloc_writecombine() 函数分配一段 writecombining 区域,对应的 writecombining 区域由 dma_free_writecombine() 函数释放。
writecombing 意味着“写合并”,它允许写入的数据被合并,并临时保存在写合并缓冲区(WCB)中,直到进行一次 burst 传输而不再需要多次 single 传输。通过 dma_alloc_writecombine() 分配的显示缓冲区不会出现 cache 一致性问题,这一点类似 dma_alloc_coherent()。
-
- static int __init xxxfb_map_video_memory(struct xxxfb_info *fbi)
- {
- fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);
- fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, &fbi->map_dma, GFP_KERNEL);
-
- fbi->map_size = fbi->fb->fix.smem_len;
-
- if( fbi->map_cpu ){
- memset(fbi->map_cpu, 0xf0, fbi->map_size);
-
- fbi->screen_dma = fbi->map_dma;
- fbi->fb->screen_base = fbi->map_cpu;
- fbi->fb->fix.smem_start = fbi->screen_dma;
- }
- return fbi->map_cpu ? 0 : -ENOMEM;
- }
-
- static inline void xxxfb_unmap_video_memory(struct s3c2410fb_info* fbi)
- {
-
- dma_free_writecombine(fbi->dev, fbi->map_size, fbi->map_cpu, fbi->map_dma);
- }
18.6 帧缓冲设备的参数设置
18.6.1 定时参数
FBI结构体可变参数var中的left_margin、right_margin、upper_margin、lower_margin、hsync_len和vsync_len直接查LCD数据手册就可以得到。
18.6.2 像素时钟
FBI可变参数var中的pixclock意味着像素时钟,例如,如果为28.37516MHz,那么画一个像素需要35242ps(皮秒):
1/(28.37516MHz) = 35.242E-9s
如果屏幕的分辨率是640*480,显示一行的时间是:640*35.242E-9s = 22.555E-6 s
每条扫描线是640,但是水平回扫和水平同步也需要时间,假设水平回扫和同步需要272个像素时钟,因此,画一条扫描线完整的时间是:(640+272)* 35.242E-9 s - 9 s = 32.141E-6 s
可以计算出水平扫描率大约是32kHz:1/(32.141E-6 s) = 31.113E3 Hz
完整的屏幕有480条线,但是垂直回扫和垂直同步也需要时间,假设垂直回扫和垂直同步需要49个像素时钟,因此,画一个完整的屏幕的时间是:(480+49)*32.141E-6 s = 17.002E-3 s
可以计算出垂直扫描率大约是59kHz:1/(17.002E-3 s) = 58.815 Hz
这意味着屏幕数据每秒钟大约刷新59次。
18.6.3 颜色位域
FBI可变参数var中的red、green、blue、位域的设置直接由显示缓冲区与显示点的对应关系决定,例如,对于RGB565模式,red占居5位,偏移11位,green占居6位,偏移5位,blue占居5位,偏移0位,即:
- fbinfo->var.red.offset = 11;
- fbinfo->var.green.offset = 5;
- fbinfo->var.blue.offset = 0;
- fbinfo->var.transp.offset = 0;
- fbinfo->var.red.length = 5;
- fbinfo->var.green.length = 6;
- gbinfo->var.blue.length = 5;
18.6.4 固定参数
FBI固定参数 fix 中 smem_start 指示帧缓冲设备显示缓冲区的首地址,smem_len 为帧缓冲设备显示缓冲区的大小,计算公式为:
smem_len = max_xres * max_yres * max_bpp;
即:帧缓冲设备显示缓冲区的大小 = 最大的 x 解析度 * 最大的 y 解析度 * 最大的 BPP;
18.7 帧缓冲设备驱动的 fb_ops 成员函数
fb_check_var() 用于调整可变参数,并修正为硬件所支持的值;fb_set_par() 则根据屏幕参数设置具体读写LCD控制器的寄存器以使得LCD控制器进入相应的工作状态。
对于 fb_ops 中的 fb_fillrect() 、 fb_copyarea() 和 fb_imageblit() 成员函数,通常直接使用对应的通用的 cfb_fillrect() 、cfb_copyarea() 和 cfb_imageblit() 函数即可。cfb_fillrect() 函数定义在 drivers/video/cfbfillrect.c 文件中,cfb_copyarea() 定义在 /drivers/video/cfbcopyarea.c 文件中, cfb_imageblit() 定义在 /drivers/cfbimagblt.c 文件中。
-
- static int xxxfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info * info)
- {
- struct xxxfb_info* fbi = info->par;
- unsigned int val;
-
- switch( fbi->fb->fix.visual ){
- case FB_VISUAL_TRUECOLOR:
-
- if( regno < 16 ){
- u32 * pal = fbi->fb->pseudo_palette;
-
- val = chan_to_field(red, &fbi->fb->var.red);
- val |= chan_to_field(green, &fbi->fb->var.green);
- val |= charn_to_field(blue, &fbi->fb->var.blue);
-
- pal[regno] = val;
- }
- break;
- case FB_VISUAL_PSEUDOCOLOR:
- if( regno < 256 ){
-
- val = ( (red >> 0) & 0xf800 );
- val |= ( (green >> 5) & 0x07e0 );
- val |= ( (blue >> 11) & 0x001f );
-
- writel(val, XXX_TFTPAL(regno));
- schedule_palette_update(fbi, regno, val);
- }
- break;
- ...
- }
- return 0;
- }
18.8 LCD设备驱动的读写、mmap 和 ioctl 函数
(仔细分析)
18.9 帧缓冲设备的用户空间访问
通过/dev/fbn,应用程序可进行的针对帧缓冲设备的操作主要有:
(1)读/写 /dev/fbn : 相当于读写帧缓冲区。例如 cp /dev/fb0 tmp 可以将当前屏幕的内容复制到一个文件中。cp tmp > /dev/fb0则讲图像文件tmp显示在屏幕上。
(2)映射操作:对于帧缓冲设备,可通过 mmap() 映射操作将屏幕缓冲区的物理地址映射到用户控件的一段虚拟地址中,之后用户控件就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图。而且若干个进程可以映射到同一个显示缓冲区。实际上,使用帧缓冲设备的应用程序都是通过映射操作来显示图形的。
(3)I/O控制:对于帧缓冲设备,对设备文件的 ioctl() 操作可读取/设置显示设备及屏幕的参数,如分辨率、显示颜色数、屏幕大小等。
应用程序操作/dev/fbn的一般步骤:
(1)打开 /dev/fbn 设备文件。
(2)用 ioctl() 操作取得当前显示屏幕的参数,如分辨率、每个像素点的比特位数和偏移。根据屏幕参数课计算屏幕缓冲区的大小。
(3)将屏幕缓冲区映射到用户空间。
(4)映射后就可以直接读/写屏幕缓冲区进行绘图和图片显示了。
-
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <linux/fb.h>
- #include <sys/mman.h>
-
- int main()
- {
- int fbfd = 0;
- struct fb_var_screeninfo vinfo;
- unsigned long screensize = 0;
- char* fbp = 0;
- int x = 0, y = 0;
- int i = 0;
-
- fbfd = open("/dev/fb0", O_RDWR);
- if( !fbfd ){
- printf("Error:cannot open framebuffer device.\n");
- exit(1);
- }
- printf("The framebuffer device was opened successfully.\n");
-
-
- if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)){
- printf("Error reading variable information.\n");
- exit(1);
- }
-
- printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
- if(vinfo.bits_per_pixel != 16){
- printf("Error:not supported bits_per_pixel,it only supports 16 bit color\n");
- exit(1);
- }
-
-
- screensize = vinfo.xres * vinfo.yres * 2;
-
-
- fbp = (char*)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
- if( (int)fbp == -1 ){
- printf("Error:failed to map framebuffer device to memory.\n");
- exit(4);
- }
- printf("The framebuffer device was mapped to memory successfully.\n");
-
-
- for(i = 0; i < 3; i++ ){
- for(y = i * (vinfo.yres / 3); y < (i + 1) * (vinfo.yres / 3); y++ ){
- for(x = 0; x < vinfo.xres; x++ ){
- long location = x * 2 + y * vinfo.xres * 2;
- int r = 0, g = 0, b = 0;
- unsigned short rgb;
- if(i == 0)
- r = ((x * 1.0) / vinfo.xres) * 32;
- if(i == 1)
- g = ((x * 1.0) / vinfo.xres) * 64;
- if(i == 2)
- b = ((x * 1.0) / vinfo.xres) * 32;
-
- rgb = (r << 11) | (g << 5) | b;
- *((unsigned short*)(fbp + location)) = rgb;
- }
- }
- }
- munmap(fbp, screensize);
- close(fbfd);
- return 0;
- }