framebuffer驱动

2013-03-12 23:15

分析1

帧缓冲(framebuffer)是linux系统显示设备的框架,将显示缓冲区抽象,屏蔽具体细节,用户可以直接操作帧缓冲,达到显示的目的。
常见的xwindow和qte也是基于帧缓冲而搭建,只需将图形的颜色值写入帧缓冲对应点,就能达到具体的显示效果。在嵌入式linux系统中,
LCD驱动都是基于framebuffer框架,以达到轻量级的图形接口。

帧缓冲设备是标准的字符设备,对应主设备号位29,设备节点为/dev/fb*,最大支持32个设备。帧缓冲与像素点的具体关系根据色彩位不同而不同,
常见的RGB565,对应一个short int 型,低5位为BULE,中间6位为GREEN,高5位为RED。
既然帧缓冲设备是一种字符设备,那必然存在一个file_operation结构,该结构在drivers/video/fbmem.c中,编写驱动时不用关心,只需注册一个
帧缓冲设备的struct fb_info信息即可。
819 struct fb_info {
 820         int node;
 821         int flags;
 822         struct mutex lock;              /* Lock for open/release/ioctl funcs */
 823         struct mutex mm_lock;           /* Lock for fb_mmap and smem_* fields */
 824         struct fb_var_screeninfo var;   /* Current var*/
 825         struct fb_fix_screeninfo fix;   /* Current fix */
/* fb_var_screeninfo和fb_fix_screeninfo是驱动程序中最重要的,这是与LCD驱动器相关的参数,分为可变的参数和不可变参数 */
 826         struct fb_monspecs monspecs;    /* Current Monitor specs */
 827         struct work_struct queue;       /* Framebuffer event queue */
 828         struct fb_pixmap pixmap;        /* Image hardware mapper */
 829         struct fb_pixmap sprite;        /* Cursor hardware mapper */
 830         struct fb_cmap cmap;            /* Current cmap */
/* fb_cmap,是当前的颜色表 大于16BPP的不使用该字段*/
 831         struct list_head modelist;      /* mode list */
 832         struct fb_videomode *mode;      /* current mode */
 833 
 834 #ifdef CONFIG_FB_BACKLIGHT /* 背光控制*/
 835         /* assigned backlight device */
 836         /* set before framebuffer registration, 
 837            remove after unregister */
 838         struct backlight_device *bl_dev;
 839 
 840         /* Backlight level curve */
 841         struct mutex bl_curve_mutex;
 842         u8 bl_curve[FB_BACKLIGHT_LEVELS];
 843 #endif
 844 #ifdef CONFIG_FB_DEFERRED_IO
 845         struct delayed_work deferred_work;
 846         struct fb_deferred_io *fbdefio;
 847 #endif
 848 
 849         struct fb_ops *fbops; /* 这是帧缓冲设备的操作函数集,根据具体的硬件实现,很重要的结构 */
 850         struct device *device;          /* This is the parent */
 851         struct device *dev;             /* This is this fb device */
 852         int class_flag;                    /* private sysfs flag */
 854         struct fb_tile_ops *tileops;    /* Tile Blitting */
 855 #endif
 856         char __iomem *screen_base;      /* Virtual address */
 857         unsigned long screen_size;      /* Amount of ioremapped VRAM or 0 */
/* screen_base,screen_size是帧缓冲映射后的虚拟地址和大小,这个地址并不是帧显示的地址*/
 858         void *pseudo_palette;           /* Fake palette of 16 colors */
 859 #define FBINFO_STATE_RUNNING    0
 860 #define FBINFO_STATE_SUSPENDED  1
 861         u32 state;                      /* Hardware state i.e suspend */
 862         void *fbcon_par;                /* fbcon use-only private area */
 863         /* From here on everything is device dependent */
 864         void *par;
 865         /* we need the PCI or similiar aperture base/size not
 866            smem_start/size as smem_start may just be an object
 867            allocated inside the aperture so may not actually overlap */
 868         resource_size_t aperture_base;
 869         resource_size_t aperture_size;
 870 };

struct fb_info 记录一个帧缓冲设备的所有信息,一个设备对应一个fb_info结构题,通过register_framebuffer向帧换冲框架注册。
int register_framebuffer(struct fb_info *fb_info);

fb_var_screeninfo 结构体记录着用户可以修改的设备参数,具体如下:
 237 struct fb_var_screeninfo {
 238         __u32 xres;                     /* visible resolution           */
 239         __u32 yres;
/* 可见的分辨率,比如800*480的7寸屏,800*480是可见的,但实际会比这个大,用户同步和消隐 */
 240         __u32 xres_virtual;             /* virtual resolution           */
 241         __u32 yres_virtual;
/* 对应虚拟的分辨率 */ 
 242         __u32 xoffset;                  /* offset from virtual to visible */
 243         __u32 yoffset;                  /* resolution                   */
 244 /* 虚拟到可见的偏移,如果xres 和xres_virtual相等,那么该段为0 */
 245         __u32 bits_per_pixel;           /* guess what                   */
/* 每个像素点位宽,比如RGB565这个就应该是16;
 246         __u32 grayscale;                /* != 0 Graylevels instead of colors */
 247 /* 表示灰度 */
 248         struct fb_bitfield red;         /* bitfield in fb mem if true color, */
 249         struct fb_bitfield green;       /* else only length is significant */
 250         struct fb_bitfield blue;
 251         struct fb_bitfield transp;      /* transparency                 */
 252 
 253         __u32 nonstd;                   /* != 0 Non standard pixel format */
 254 
 255         __u32 activate;                 /* see FB_ACTIVATE_*            */
 256 
 257         __u32 height;                   /* height of picture in mm    */
 258         __u32 width;                    /* width of picture in mm     */
 259 
 260         __u32 accel_flags;              /* (OBSOLETE) see fb_info.flags */
 261 
 262         /* Timing: All values in pixclocks, except pixclock (of course) */
 263         __u32 pixclock;                 /* pixel clock in ps (pico seconds) */
/* 像素时钟,单位是皮秒1s=10^-12ps */
 264         __u32 left_margin;              /* time from sync to picture    */
/* 从同步到绘图之间的延时,或者叫水平后沿 */
 265         __u32 right_margin;             /* time from picture to sync    */
/* 水平前沿 */
 266         __u32 upper_margin;             /* time from sync to picture    */
/* 垂直后沿 */ 
 267         __u32 lower_margin;
/* 垂直前沿 */ 
 268         __u32 hsync_len;                /* length of horizontal sync    */
 269         __u32 vsync_len;                /* length of vertical sync      */
/* 水平,垂直同步长度,即在水平后沿和垂直后沿前面的同步时钟长度 */
/* 以上所有同步时间,都是以像素时钟作为单位,即同步时间=同步宽度*pixclock */
 270         __u32 sync;                     /* see FB_SYNC_*                */
 271         __u32 vmode;                    /* see FB_VMODE_*               */
 272         __u32 rotate;                   /* angle we rotate counter clockwise */
 273         __u32 reserved[5];              /* Reserved for future compatibility */
 274 };
以上是可变参数结构,另外还有struct fb_fix_screeninfo不可变参数:
 154 struct fb_fix_screeninfo {
 155         char id[16];                    /* identification string eg "TT Builtin" */
 156         unsigned long smem_start;       /* Start of frame buffer mem */
 157                                         /* (physical address) */
/* 这个才是fb的真正缓冲地址,用户空间写的颜色值是写在这个地址开始的空间的 */
 158         __u32 smem_len;                 /* Length of frame buffer mem */
/* 长度 = 调色板 + 像素点*位宽
 159         __u32 type;                     /* see FB_TYPE_*                */
 160         __u32 type_aux;                 /* Interleave for interleaved Planes */
 161         __u32 visual;                   /* see FB_VISUAL_*              */
/* 使用的色彩,包括黑白,白黑,伪彩,真彩等,常使用的真彩即原始数据就是颜色,不需要在查表索引转换 */
 162         __u16 xpanstep;                 /* zero if no hardware panning  */
 163         __u16 ypanstep;                 /* zero if no hardware panning  */
 164         __u16 ywrapstep;                /* zero if no hardware ywrap    */
 165         __u32 line_length;              /* length of a line in bytes    */
/* 行像素点* 位宽 800*480,16BPP: line_length=800 * 2 */
 166         unsigned long mmio_start;       /* Start of Memory Mapped I/O   */
 167                                         /* (physical address) */
 168         __u32 mmio_len;                 /* Length of Memory Mapped I/O  */
 169         __u32 accel;                    /* Indicate to driver which     */
 170                                         /*  specific chip/card we have  */
 171         __u16 reserved[3];              /* Reserved for future compatibility */
 172 };

完成这三个结构体,一个framebuffer设备驱动框架就完成了,如此针对具体控制器的驱动流程如下:
1.申请fb_info,填充fb_var_screeninfo和fb_fix_screeninfo
2.完成LCD控制器初始化,即时序的配置
3.注册帧缓冲设备

虽然framebuffer是一个字符设备,但大多LCD控制器都集成在SOC上,在写驱动时仍作为一种platform_device设备进行注册。
以omapl138/am1808 LCD控制器驱动分析framebuffer注册流程,LCD屏使用群创7"屏。
 977 static struct platform_driver da8xx_fb_driver = {
 978         .probe = fb_probe,
 979         .remove = fb_remove,
 980         .suspend = fb_suspend,
 981         .resume = fb_resume,
 982         .driver = {
 983                    .name = DRIVER_NAME,
 984                    .owner = THIS_MODULE,
 985                    },
 986 };

标准的platform_drivers驱动结构,DRIVER_NAME与platform_device->name一致,主要分析probe过程。
744 static int __init fb_probe(struct platform_device *device)
 745 {
 746         struct da8xx_lcdc_platform_data *fb_pdata =
 747                                                 device->dev.platform_data;
 748         struct lcd_ctrl_config *lcd_cfg;
 749         struct da8xx_panel *lcdc_info;
 750         struct fb_info *da8xx_fb_info;
 751         struct clk *fb_clk = NULL;
 752         struct da8xx_fb_par *par;
 753         resource_size_t len;
 754         int ret, i;
 755 
 756         if (fb_pdata == NULL) {
 757                 dev_err(&device->dev, "Can not get platform data\n");
 758                 return -ENOENT;
 759         }
 760 
 761         lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0);
 762         if (!lcdc_regs) {
 763                 dev_err(&device->dev,
 764                         "Can not get memory resource for LCD controller\n");
 765                 return -ENOENT;
 766         }
 767 
 768         len = resource_size(lcdc_regs);
 769 
 770         lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name);
 771         if (!lcdc_regs)
 772                 return -EBUSY;
 773 
 774         da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len);
 775         if (!da8xx_fb_reg_base) {
 776                 ret = -EBUSY;
 777                 goto err_request_mem;
 778         }
 779 
 780         fb_clk = clk_get(&device->dev, NULL);
 781         if (IS_ERR(fb_clk)) {
 782                 dev_err(&device->dev, "Can not get device clock\n");
 783                 ret = -ENODEV;
 784                 goto err_ioremap;
 785         }
 786         ret = clk_enable(fb_clk);
 787         if (ret)
 788                 goto err_clk_put;

/* 以上部分都是SOC驱动基本的流程,获取私有数据,获取设备资源并映射,获取时钟并时能。*/
790         for (i = 0, lcdc_info = known_lcd_panels;
 791                 i < ARRAY_SIZE(known_lcd_panels);
 792                 i++, lcdc_info++) {
 793                 if (strcmp(fb_pdata->type, lcdc_info->name) == 0)
 794                         break;
 795         }
 796 
 797         if (i == ARRAY_SIZE(known_lcd_panels)) {
 798                 dev_err(&device->dev, "GLCD: No valid panel found\n");
 799                 ret = ENODEV;
 800                 goto err_clk_disable;
 801         } else
 802                 dev_info(&device->dev, "GLCD: Found %s panel\n",
 803                                         fb_pdata->type);
 804 
/* 根据platform_device->dev.platform_data->type 与驱动中的lcd_info->name是否有匹配,得到该驱动是否支持该LCD屏,
故要添加对屏的支持,修改这两个参数及时序即可 */
 805         lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data;
 806 
 807         da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par),
 808                                         &device->dev);
/* 分配一个fb_info结构体 */
 809         if (!da8xx_fb_info) {
 810                 dev_dbg(&device->dev, "Memory allocation failed for fb_info\n");
 811                 ret = -ENOMEM;
 812                 goto err_clk_disable;
 813         }
 814 
 815         par = da8xx_fb_info->par;
 816         par->lcdc_clk = fb_clk;
 817         par->pxl_clk = lcdc_info->pxl_clk;
 818 
 819         if (lcd_init(par, lcd_cfg, lcdc_info) < 0) {
 820                 dev_err(&device->dev, "lcd_init failed\n");
 821                 ret = -EFAULT;
 822                 goto err_release_fb;
 823         }
/* 硬件初始化,包括时钟分频,时序的配置等,当这里应该各种参数已经配置好,如果打开像素时钟,那么有数据被刷到LCD,现在往下继续完成fb_info的初始化及注册 */
 826         da8xx_fb_info->screen_base = dma_alloc_coherent(NULL,
 827                                         par->databuf_sz + PAGE_SIZE,
 828                                         (resource_size_t *)
 829                                         &da8xx_fb_info->fix.smem_start,
 830                                         GFP_KERNEL | GFP_DMA);
/* 申请fb_info所需的DMA通道,至于为什么用dma_alloc_coherent,为了保证数据的一致性,禁止到cache,
大小为 像素点大小+调色板大小 + PAGE_SIZE(页对齐) ,返回两个地址srceen_base是虚拟地址,da8xx_fb_info->fix.smem_start是设备总线地址 */

 831 
 832         if (!da8xx_fb_info->screen_base) {
 833                 dev_err(&device->dev,
 834                         "GLCD: kmalloc for frame buffer failed\n");
 835                 ret = -EINVAL;
 836                 goto err_release_fb;
 837         }
 838         memset(da8xx_fb_info->screen_base, 0, par->databuf_sz + PAGE_SIZE);
 839         /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */

 840         par->v_palette_base = da8xx_fb_info->screen_base +
 841                                 (PAGE_SIZE - par->palette_sz);
/* 调色板虚拟基地址 */
 842         par->p_palette_base = da8xx_fb_info->fix.smem_start +
 843                                 (PAGE_SIZE - par->palette_sz);
/* 调色板总线基地址 */ 
844         memset(par->v_palette_base, 0, PALETTE_SIZE);
 845         /* the rest of the frame buffer is pixel data */
 846         da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz;
/* framebuffer的帧数据虚拟地址 */
 847         da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz;
/* 总线地址(物理地址)
 848         da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz;
 849         da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8;
 850 
 851         par->irq = platform_get_irq(device, 0);
 852         if (par->irq < 0) {
 853                 ret = -ENOENT;
 854                 goto err_release_fb_mem;
 855         }
 856 
 857         ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par);
 858         if (ret)
 859                 goto err_release_fb_mem;
 855         }
 856 
 857         ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par);
 858         if (ret)
 859                 goto err_release_fb_mem;
 860 
 861         /* Initialize par */
 862         da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp;
 863 
 864         da8xx_fb_var.xres = lcdc_info->width;
 865         da8xx_fb_var.xres_virtual = lcdc_info->width;
 866 
 867         da8xx_fb_var.yres = lcdc_info->height;
 868         da8xx_fb_var.yres_virtual = lcdc_info->height;
 869 
 870         da8xx_fb_var.grayscale =
 871             lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0;
 872         da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp;
 873 
 874         da8xx_fb_var.hsync_len = lcdc_info->hsw;
 875         da8xx_fb_var.vsync_len = lcdc_info->vsw;
/* fb_var_screeninfo结构体的填充 */
877         /* Initialize fbinfo */
 878         da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT;
 879         da8xx_fb_info->fix = da8xx_fb_fix;
 880         da8xx_fb_info->var = da8xx_fb_var;
 881         da8xx_fb_info->fbops = &da8xx_fb_ops; /* fb_ops操作
 882         da8xx_fb_info->pseudo_palette = par->pseudo_palette;
 883         da8xx_fb_info->fix.visual = (da8xx_fb_info->var.bits_per_pixel <= 8) ?
 884                                 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
 885 
 886         ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0);
 887         if (ret)
 888                 goto err_free_irq;
 889         /* First palette_sz byte of the frame buffer is the palette */
 890         da8xx_fb_info->cmap.len = par->palette_sz;
/* fb_info 其他位的填充 */
 891 
 892         /* Flush the buffer to the screen. */
 893         lcd_blit(LOAD_DATA, par);
 894 #if 0
 895         {
 896                 int i;
 897                 for (i = 0; i < (800 * 480 * 2);) {
 898                         *((volatile unsigned short *)(0xC7A01000 + i)) = 0x0;
 899                         i+=2;
 900                 }
 901         }
 902 #endif
 903         /* initialize var_screeninfo */
 904         da8xx_fb_var.activate = FB_ACTIVATE_FORCE;
 905         fb_set_var(da8xx_fb_info, &da8xx_fb_var);
 906 
 907         dev_set_drvdata(&device->dev, da8xx_fb_info);
 908         /* Register the Frame Buffer  */
 909         if (register_framebuffer(da8xx_fb_info) < 0) {
 910                 dev_err(&device->dev,
 911                         "GLCD: Frame Buffer Registration Failed!\n");
 912                 ret = -EINVAL;
 913                 goto err_dealloc_cmap;
 914         }
/* 注册framebuffer */
916 #ifdef CONFIG_CPU_FREQ
 917         ret = lcd_da8xx_cpufreq_register(par);
 918         if (ret) {
 919                 dev_err(&device->dev, "failed to register cpufreq\n");
 920                 goto err_cpu_freq;
 921         }
 922 #endif
 923         /* enable raster engine */
 924         lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) |
 925                         LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
 926 
/* 对应LCD控制器,时能像素时钟 */
 927         return 0;

struct fb_ops结构体实现与硬件相关,用户需要实现其中几个函数:
 733 static struct fb_ops da8xx_fb_ops = {
 734         .owner = THIS_MODULE,
 735         .fb_check_var = fb_check_var, //调整显示参数
 736         .fb_setcolreg = fb_setcolreg,//设置颜色表
 737         .fb_ioctl = fb_ioctl,   //设置vsync.hsync等
 738         .fb_fillrect = cfb_fillrect, //在drivers/video/cfbfillrect.c
 739         .fb_copyarea = cfb_copyarea, //drivers/video/cfbcopyarea.c 
 740         .fb_imageblit = cfb_imageblit,//drivers/video/cfbimgblt.c
 741         .fb_cursor = dummy_cursor, //这个实现了一个空函数,避免在LCD上显示光标 
 742 };
 728 static int dummy_cursor(struct fb_info *info, struct fb_cursor *cursor)
 729 {
 730         return 0;
 731 }

至于帧缓冲的file_operation实现,在drivers/video/fbmem.c中实现
1423 static const struct file_operations fb_fops = {
1424         .owner =        THIS_MODULE,
1425         .read =         fb_read, 
1426         .write =        fb_write,
1427         .unlocked_ioctl = fb_ioctl,
1428 #ifdef CONFIG_COMPAT
1429         .compat_ioctl = fb_compat_ioctl,
1430 #endif
1431         .mmap =         fb_mmap,
1432         .open =         fb_open,
1433         .release =      fb_release,
1434 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1435         .get_unmapped_area = get_fb_unmapped_area,
1436 #endif
1437 #ifdef CONFIG_FB_DEFERRED_IO
1438         .fsync =        fb_deferred_io_fsync,
1439 #endif
1440 };
主要关心read,write,ioctl,mmap,open,release函数即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章