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函數即可

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