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