LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

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的图形框架和测试工具》。

参考资料《Linux字符设备驱动框架:Linux内核的framebuffer驱动框架》。

posted on 2024-03-01 23:59  ArnoldLu  阅读(301)  评论(0编辑  收藏  举报

导航