Linux显示(一):Framebuffer子系统
Framebuffer是向应用层提供统一标准显示设备的接口,将显示设备硬件结构抽象为一系列数据结构。应用程序打开Framebuffer设备后,通过ioctl进行配置,将内存通过mmap映射后直接操作。
Linux中Framebuffer是一个字符设备,一个LCD往往对应多个Framebuffer设备,多层叠加显示到LCD上。
1 Linux Framebuffer配置
Device Drivers ->Graphics support ->Frame buffer Devices
1.1 Frambuffer子系统初始化
fbmem_init()初始化framebuffer子系统:
fbmem_init
->proc_create_seq--创建/proc/fb,显示当前fb设备序号和名称。
->register_chrdev--注册fb字符设备,操作函数集为fb_fops。
->class_create--创建graphics类。
->fb_console_init
->device_create--创建创建fbcon设备。
->fbcon_init_device--创建rotate、rotate_all、cursor_blink节点。
->fbcon_start--注册并启动fbcon。
对于/dev/fbX设备的操作映射到内核是fb_fpops:
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, #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ !defined(CONFIG_MMU)) .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO .fsync = fb_deferred_io_fsync, #endif .llseek = default_llseek, };
fbcon设备包含如下属性节点:
static struct device_attribute device_attrs[] = { __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all), __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink, store_cursor_blink), };
实例如下:
/sys/devices/virtual/graphics/fbcon |-- cursor_blink
|-- rotate |-- rotate_all
2 Framebuffer驱动文件
drivers/video/fbdev/core/ ├── bitblit.c ├── cfbcopyarea.c ├── cfbfillrect.c ├── cfbimgblt.c ├── fbcmap.c--framebuffer colormap处理。 ├── fb_cmdline.c ├── fbcon.c--基于framebuffer的console实现。 ├── fbcvt.c--VESA Coordinated Video Timing实现。 ├── fb_defio.c ├── fbmem.c--framebuffer模块初始化,以及framebuffer注册去注册等,以及fb设备操作函数实现。 ├── fbmon.c ├── fb_notify.c ├── fb_sys_fops.c-- ├── fbsysfs.c--fb设备属性节点实现。 ├── modedb.c ├── softcursor.c ├── syscopyarea.c ├── sysfillrect.c ├── sysimgblt.c
3 Framebuffer数据结构和API
3.1 Framebuffer数据结构
struct fb_info用于描述一个Frambuffer,通过register_framebuffer()注册到Framebuffer子系统中。
struct fb_info { atomic_t count; int node; int flags; int fbcon_rotate_hint; 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 */ ... struct fb_ops *fbops;--Framebuffer操作函数集。 struct device *device; /* This is the parent */ struct device *dev; /* This is this fb device */ int class_flag; /* private sysfs flags */ #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */ #endif union { char __iomem *screen_base; /* Virtual address */--Framebuffer显存虚拟地址。 char *screen_buffer; }; unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ --Framebuffer显存大小。 ... };
struct fb_fix_screeninfo表示不可修改的Framebuffer属性参数:
struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */--Framebuffer名称。 unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */--Framebuffer显存起始物理地址。 __u32 smem_len; /* Length of frame buffer mem */--Framebuffer显存大小。 __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */--Framebuffer一行所占字节数。 unsigned long mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Indicate to driver which */ /* specific chip/card we have */ __u16 capabilities; /* see FB_CAP_* */ __u16 reserved[2]; /* Reserved for future compatibility */ };
struct fb_var_screeninfo表示可修改的Framebuffer参数:
struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible */ __u32 yoffset; /* resolution */ __u32 bits_per_pixel; /* guess what */--位深,表示一个像素占用多少个位宽。 __u32 grayscale; /* 0 = color, 1 = grayscale, */ ... __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */--下面6个参数是LCD时序参数。 __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 */... };
3.2 Framebuffer API
Framebuffer注册和去注册函数:
extern int register_framebuffer(struct fb_info *fb_info); extern void unregister_framebuffer(struct fb_info *fb_info);
register_framebuffer()注册Framebuffer设备,同时创建设备节点:
register_framebuffer
->do_register_framebuffer
->do_remove_conflicting_framebuffers
->device_create--创建/dev/fbX设备,对应操作函数集为fp_ops。
->fb_init_device--创建framebuffer设备属性。
->fb_var_to_videomode
->fb_add_videomode
->fbcon_fb_registered
framebuffer创建的属性节点包括:
static struct device_attribute device_attrs[] = { __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank), __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console), __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor), __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes), __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan), __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual), __ATTR(name, S_IRUGO, show_name, NULL), __ATTR(stride, S_IRUGO, show_stride, NULL), __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate), #if IS_ENABLED(CONFIG_FB_BACKLIGHT) __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve), #endif };
实例如下:
/sys/devices/platform/soc/5a001000.display-controller/graphics/fb0 |-- bits_per_pixel |-- blank |-- console |-- cursor |-- mode |-- modes |-- name |-- pan|-- rotate |-- state |-- stride `-- virtual_size
unregister_framebuffer()执行Framebuffer去注册。
4 Framebuffer驱动(STM32 LCDC)
STM32 LCDC驱动程序首先注册DRM设备,然后注册一个Framebuffer作为DRM Kernel Client。
stm_drm_platform_probe ->drm_fbdev_generic_setup ->drm_fbdev_client_hotplug ->drm_fb_helper_initial_config ->__drm_fb_helper_initial_config_and_unlock ->register_framebuffer--注册Framebuffer设备。
5 Framebuffer测试
下面是编写Framebuffer测试程序示例:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/ioctl.h> #include <sys/mman.h> #define FBDEVICE "/dev/fb0" #define WHITE 0xffffffff #define BLACK 0x00000000 void draw_back(unsigned int *pfb, unsigned int width, unsigned int height, unsigned int color) { unsigned int x, y; for (y=0; y<height; y++) { for (x=0; x<width; x++) { *(pfb + y * width + x) = color; } } } int main(void) { int fd = -1; unsigned int *pfb = NULL; unsigned int width; unsigned int height; struct fb_fix_screeninfo finfo = {0}; struct fb_var_screeninfo vinfo = {0}; /************************第1步:打开设备**************************/ fd = open(FBDEVICE, O_RDWR); if (fd < 0) { perror("open"); return -1; } printf("open %s success.\n", FBDEVICE); /*******************第2步:获取设备的硬件信息********************/ ioctl(fd, FBIOGET_FSCREENINFO, &finfo);//获取LCD的固定属性参数 /* *finfo.smem_start:LCD显存的起始地址 *finfo.smem_len:LCD显存的字节大小 */ printf("smem_start = 0x%x, smem_len = %u.\n", finfo.smem_start, finfo.smem_len); ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);//获取LCD的可变参数 /* *vinfo.xres:水平分辨率 *vinfo.yres:垂直分辨率 *vinfo.xres_virtual:虚拟水平分辨率 *vinfo.yres_virtual:虚拟垂直分辨率 *vinfo.bits_per_pixel:像素深度 */ printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres); printf("xres_virtual = %u, yres_virtual = %u.\n", vinfo.xres_virtual, vinfo.yres_virtual); printf("bpp = %u.\n", vinfo.bits_per_pixel); width = vinfo.xres; height = vinfo.yres; /*************************第3步:进行mmap***********************/ //计算LCD显存大小(单位:字节) unsigned long len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8; printf("len = %ld\n", len); pfb = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); printf("pfb = %p.\n", pfb); /********************第4步:进行lcd相关的操作********************/ draw_back(pfb, width, height, RED); /***********************第五步:释放显存************************/ munmap(pfb, len); /***********************第六步:关闭文件************************/ close(fd); return 0; }
更多关于Framebuffer的测试参考《Linux显示(二):基于Framebuffer的图形框架和测试工具》。