framebuffer设备驱动

framebuffer设备介绍

  • 帧缓冲的概念
    Linux系统为显示设备提供的一个接口;
    显示缓冲区抽象,屏蔽图像硬件的底层差异;
    上层应用程序在图形模式下直接对显示缓冲区进行读写操作;
    用户不必关心物理显示缓冲区的具体位置及存放方式;
    只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示;
  • 帧缓冲设备为标准字符设备
    主设备号为29,对应于/dev/fb%d设备文件
  • 帧缓冲驱动的应用
    桌面系统,Xwindow服务器就是利用帧缓冲进行窗口的绘制。
    嵌入式系统中的Qt/Embedded等图形用户界面环境也基于帧缓冲而设计
    通过帧缓冲可支持汉字点阵的显示,因此帧缓冲也成为Linux汉化的可行方案。

framebuffer驱动框架

  • framebuffer驱动的核心在fbmem.c文件中实现;
    这个文件的模块入口函数如下:



    1.在这个模块入口函数中,注册一个"fb"字符设备
    2.主设备号是FB_MAJOR 29
    3.操作函数集合是fb_fops
    4.fb设备的操作函数集合如下:


  • 分析fb_open方法,简化代码如下:



    fb_open方法:以次设备号为索引,调用get_fb_info方法取得struct fb_info结构;get_fb_info方法如下:



    get_fb_info()方法功能是以次设备号为数组registered_fb[] 的下标,找到对应的struct fb_info结构指针;
    数组registered_fb[]是一个指针数组,在头文件fb.h中定义:
  • 再分析fb_read,fb_write,fb_ioctl等方法;这些操作函数的开始处都是以次设备号为下标,在数组registered_fb[]中,找到一个struct fb_info结构体,根据这个结构体来进行操作。
  • 将一个struct fb_info注册到registered_fb[]数组的函数是:
    int register_framebuffer(struct fb_info *fb_info)
    注销:
    int unregister_framebuffer(struct fb_info *fb_info)
  • 从上面的分析我们可以得出编写framebuffer驱动的主要步骤:
    初始化一个struct fb_info结构;
    将这个结构注册到系统中(register_framebuffer);

相关数据结构

  • fb_info
struct fb_var_screeninfo var; /* 可变参数 */
struct fb_fix_screeninfo fix; /* 固定参数 */
struct fb_monspecs monspecs; /* 显示器标准 */
struct work_struct queue; /* 帧缓冲事件队列 */
struct fb_pixmap pixmap; /* 图像硬件mapper */
struct fb_pixmap sprite; /* 光标硬件mapper */
struct fb_cmap cmap;/* 目前的颜色表 */
struct list_head modelist;
struct fb_videomod *mode; /* 目前的video模式 */
struct fb_ops *fops; /* fb_ops,帧缓冲操作 */
char *screen_base;                  /* 虚拟基地址 */
void *pseudo_palette;      /* 虚拟调色板,用于真彩色没有调色板情况 */
  • fb_ops结构体
 /* 打开/释放 */
int (*fb_open)(struct fb_info *info,int user);
int (*fb_release)(struct fb_info *info,int user);        
  /* 对于非线性布局的/常规内存映射无法工作的帧缓冲设备需要 */
ssize_t (*fb_read)(struct file *file,char __user *buf,size_t count,loff_t *ppos);
ssize_t (*fb_write)(struct file *file,const char __user *buf,size_t count,loff_t *ppos);/* 检测可变参数,并调整到支持的值 */
int (*fb_check_var)(struct fb_var_screeninfo *var,struct fb_info *info);/* 根据info->var设置video模式 */
int (*fb_set_par)(struct fb_info *info);    
/* 设置color寄存器 */
int (*fb_setcolreg)(unsigned regno,unsigned read,unsigned green,unsigned blue,unsigned transp,struct fb_info *info);     
/* 批量设置color寄存器,设置颜色表 */
int (*fb_setcmap)(struct fb_cmap *cmap,struct fb_info *info);
. . .
/*fb特定的ioctl(可选)*/
int (*fb_ioctl)(struct fb_info *info,unsigned int cmd,unsigned long arg);
. . .
/* fb特定的mmap */
int (*fb_mmap)(struct fb_info *info,struct vm_area_struct *vma);
  • fb_var_screeninfo 结构体
/* 可见清晰度 */
  __u32 xres;
  __u32 yres;
  /* 虚拟解析度 */
  __u32 xres_virtual;
  __u32 yres_virtual;
  /* 虚拟到可见之间的偏移 */
  __u32 xoffset;
  __u32 yoffset;
  __u32 bits_per_pixel; /* 每像素位数,BPP */
  __u32 grayscale;       /* 非0时指灰度 */
 
  /* fb缓存的R\G\B位域 */    
  struct fb_bitfield red;
  struct fb_bitfield green;
  struct fb_bitfield blue;
  struct fb_bitfield transp; /* 透明度 */
__u32 nonstd; /* !=0非标准像素格式 */
  __u32 activate;
  __u32 height; /* 高度 */
  __u32 width; /* 宽度 */
  __u32 accel_flags; /* 看fb_info.flags */
    
  /* 定时:除了pixclock本身外,其他的都以像素时钟为单位 */
  __u32 pixclock; /* 像素时钟(皮秒) */
  __u32 lef_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]; /* 保留 */
  • fix_screeninfo结构体
char id[16]; /* 字符串形式的标识符 */
  unsigned long smem_start; /* fb缓存的开始位置 */
  __u32 smem_len; /* fb缓存的长度 */
  __u32 type; /* FB_TYPE_ */
  __u32 type_aux; /* 分界 */
  __u32 visual; /* FB_VISUAL_ */
  __u16 xpanstep; /* 如果没有硬件panning,赋0 */
  __u16 ypanstep;
  __u16 ywrapstep; 
  __u32 line_length; /* 1行的字节数 */
  unsigned long mmio_start; /* 内存映射I/O的开始位置 */
  __u32 mmio_len; /* 内存映射I/O的长度 */
  __u32 accel;
  __u16 reserved[3];  /* 保留 */
  • fb_bitfield结构体
  __u32 offset; /* 位域偏移 */
  __u32 length; /* 位域长度 */
  __u32 msb_right; /* MSB */
  • fb_cmap结构体
  __u32 start; /* 第1个元素入口 */
  __u32 len; /* 元素数量 */
  __u16 *red; 
  /* R、G、B 透明度*/
  __u16 *green;
  __u16 *blue;
  __u16 *transp;

fb_info结构成员的设置

  • 帧缓冲设备显示缓冲区的申请与释放
  • 系统往往会通过DMA方式搬移显示数据。
    dma_alloc_writeconlbine()函数分配一段writecombining
    writecombining意味着“写合并”,它允许写入的数据被合并,并临时保存在写合并缓冲区(WCB)中,直到进行一次burst传输而不再需要多次single传输
    dma_free_wfitecombine()函数释放
  • 可变参数
    left_margin、right_margin、upper_margin、lowermargin、hsy和vsync_len直接查LCD的数据手册


  • 颜色位域
    • RGB565:
      Red 占5 bits, offset = 11
      Green 占 6 bits, offset = 5
      Blue 占 5 bits, offset=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;
fbinfo->var.blue.length = 5;
  • 固定参数
    smem_start指示帧缓冲设备显示缓冲区的首地址
    smem_len为帧缓冲设备显示缓冲区的大小
    smem_len=max_xresmax_yersmax_bpp
  • 帧缓冲设备驱动的fb_ops成员函数
    • fb_fillrect()
      通常直接使用通用的cfb_fillrect()
      定义在 drivers/video/cfbfillrect.c
    • fb_copyarea()
      cfb_copyarea()
      定义在drivers/video/cfbcopyarea.c文件中
    • fb_imageblit()
      cfb_imageblit()函数
      定义在drivers/video/cfbimgblt.c
    • fb_setcolreg()成员函数实现
      伪颜色表(针对FB_VISUAL_TRUECOLOR、FB_VISUAL DIRECTCOLOR模式)
      颜色表的填充

代码实战

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>

#include <mach/gpio.h>
#include <mach/map.h>
#include <plat/gpio-cfg.h>

static struct fb_info   *my_fbinfo;
static struct clk       *bus_clk;
static struct clk       *lcd_clk;;

/** * 定义寄存器 * **/
static void __iomem     *regs_base; 
#define VIDCON0     (regs_base+0x0)
#define VIDCON1     (regs_base+0x4)
#define VIDTCON0    (regs_base +0x10)
#define VIDTCON1    (regs_base +0x14)
#define VIDTCON2    (regs_base +0x18)
#define WINCON0     (regs_base +0x20)
#define SHADOWCON   (regs_base +0x34)

#define VIDOSD0A    (regs_base +0x40)
#define VIDOSD0B    (regs_base +0x44)
#define VIDOSD0C    (regs_base +0x48)

#define VIDW00ADD0B0 (regs_base +0xA0)
#define VIDW00ADD1B0 (regs_base +0xD0)
#define VIDW00ADD2  (regs_base +0x100)

#define LCDBLK_CFG (S3C_VA_SYS + 0x0210)


static struct fb_ops my_fb_ops = {
    .owner          = THIS_MODULE,
    .fb_fillrect    = cfb_fillrect,
    .fb_copyarea    = cfb_copyarea,
    .fb_imageblit   = cfb_imageblit,
};

static int __init tiny4412_fb_init(void)
{
    u32  data;
    unsigned int buffer_size;
    dma_addr_t map_dma;
/**创建并初始化一个struct fb_info  **/
    my_fbinfo =framebuffer_alloc(0,NULL);       // fb_info分配内存
    my_fbinfo->fbops        = &my_fb_ops;   // fb操作函数集合
    /*初始化fb_info.var成员*/
    my_fbinfo->var.xres = 800;  // 分辨率
    my_fbinfo->var.yres = 480;  //分辨率
    my_fbinfo->var.xres_virtual = 800;  //分辨率
    my_fbinfo->var.yres_virtual = 480;  //分辨率
    my_fbinfo->var.xoffset = 0;         //分辨率
    my_fbinfo->var.yoffset = 0;     //分辨率
    my_fbinfo->var.pixclock = 33036;
    my_fbinfo->var.left_margin = 36;
    my_fbinfo->var.right_margin = 80;
    my_fbinfo->var.upper_margin = 15;
    my_fbinfo->var.lower_margin = 22;
    my_fbinfo->var.hsync_len = 10;
    my_fbinfo->var.vsync_len = 8;
    my_fbinfo->var.activate = FB_ACTIVATE_NOW;
    my_fbinfo->var.bits_per_pixel   = 32;
    my_fbinfo->var.red.offset       = 16;
    my_fbinfo->var.red.length       = 8;
    my_fbinfo->var.green.offset = 8;
    my_fbinfo->var.green.length = 8;
    my_fbinfo->var.blue.offset  = 0;
    my_fbinfo->var.blue.length  = 8;
    /*初始化fb_info.fix成员*/
    my_fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
    my_fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
    my_fbinfo->fix.line_length = 800*32/8;  //每行的字节数
    /*分配显示缓冲区内存*/
    buffer_size = 800*480*32/8;
    my_fbinfo->fix.smem_len = buffer_size;
    my_fbinfo->screen_base = dma_alloc_writecombine(NULL,  //DMA内存申请
        buffer_size, &map_dma, GFP_KERNEL);
    memset(my_fbinfo->screen_base, 0x0, buffer_size);
    my_fbinfo->fix.smem_start = map_dma;
    
/* 硬件初始化  */
    /*使能时钟*/
    bus_clk = clk_get_sys("exynos4-fb.0", "lcd");
    clk_enable(bus_clk);
    lcd_clk =  clk_get_sys("exynos4-fb.0", "sclk_fimd");
    clk_enable(lcd_clk);
    
    /* 寄存器基地址进行映射*/
    regs_base = ioremap(EXYNOS4_PA_FIMD0,SZ_32K);
        /* 设置GPF为LCD pin功能  */
    s3c_gpio_cfgrange_nopull(EXYNOS4_GPF0(0), 8, S3C_GPIO_SFN(2));
    s3c_gpio_cfgrange_nopull(EXYNOS4_GPF1(0), 8, S3C_GPIO_SFN(2));
    s3c_gpio_cfgrange_nopull(EXYNOS4_GPF2(0), 8, S3C_GPIO_SFN(2));
    s3c_gpio_cfgrange_nopull(EXYNOS4_GPF3(0), 4, S3C_GPIO_SFN(2));

    /* 设置LCD的数据接口为 RGB */ 
    data = __raw_readl(LCDBLK_CFG);
    data |= (1 << 1);
    __raw_writel(data, LCDBLK_CFG);

    /*设置VIDCON0, 1寄存器*/
    data =(1<<17)|(25<<6)| (1<<4)|(1<<1)|(1<<0);
    writel(data, VIDCON0);
    data = (1<<7)|(1<<6)|(1<<5);
    writel(data, VIDCON1);
    /*设置时钟信号寄存器*/
    data = (14<<16)|(21<<8)|(7<<0);
    writel(data, VIDTCON0);     
    data = (35<<16)|(79<<8)|(9<<0);
    writel(data, VIDTCON1);     
    data = (479<<11)|(799<<0);
    writel(data, VIDTCON2); 
    /* 设置缓冲区地址寄存器 */
    data = my_fbinfo->fix.smem_start;
    writel(data, VIDW00ADD0B0); 
    data = my_fbinfo->fix.smem_start + 800*480*32/8;
    writel(data, VIDW00ADD1B0); 
    data = 800*32/4;
    writel(data, VIDW00ADD2);   
    /* write 'OSD' registers to control position of framebuffer */
    data =  0;
    writel(data, VIDOSD0A); 
    data = (799<<11) |(479<<0) ;
    writel(data, VIDOSD0B); 
    /*使用channel 0 通道*/
    data = readl(SHADOWCON);
    data |= (1<<0) ;
    writel(data, SHADOWCON);
    /*配置WINCON0寄存器*/
    data = (0xb << 2)| (1 << 15)| (0x0 << 9)| (1<<0);
    writel(data, WINCON0);  
/* 注册fb_info */
    register_framebuffer(my_fbinfo);
    return 0;
}

static void __exit tiny4412_fb_exit(void)
{
    unregister_framebuffer(my_fbinfo);  //注销fb_info
    dma_free_writecombine(NULL, PAGE_ALIGN(my_fbinfo->fix.smem_len), //dma内存释放
                    my_fbinfo->screen_base, my_fbinfo->fix.smem_start);
    framebuffer_release(my_fbinfo);//fb_info内存释放
    
    clk_disable(lcd_clk);
    clk_put(lcd_clk);
    clk_disable(bus_clk);
    clk_put(bus_clk);
}

module_init(tiny4412_fb_init);
module_exit(tiny4412_fb_exit);
MODULE_LICENSE("GPL");
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容