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;
823 struct mutex mm_lock;
824 struct fb_var_screeninfo var;
825 struct fb_fix_screeninfo fix;
826 struct fb_monspecs monspecs;
827 struct work_struct queue;
828 struct fb_pixmap pixmap;
829 struct fb_pixmap sprite;
830 struct fb_cmap cmap;
831 struct list_head modelist;
832 struct fb_videomode *mode;
833
834 #ifdef CONFIG_FB_BACKLIGHT
835
836
838 struct backlight_device *bl_dev;
839
840
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;
851 struct device *dev;
852 int class_flag;
854 struct fb_tile_ops *tileops;
855 #endif
856 char __iomem *screen_base;
857 unsigned long screen_size;
858 void *pseudo_palette;
859 #define FBINFO_STATE_RUNNING 0
860 #define FBINFO_STATE_SUSPENDED 1
861 u32 state;
862 void *fbcon_par;
863
864 void *par;
865
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;
239 __u32 yres;
240 __u32 xres_virtual;
241 __u32 yres_virtual;
242 __u32 xoffset;
243 __u32 yoffset;
244
245 __u32 bits_per_pixel;
247
248 struct fb_bitfield red;
249 struct fb_bitfield green;
250 struct fb_bitfield blue;
251 struct fb_bitfield transp;
252
253 __u32 nonstd;
254
255 __u32 activate;
256
257 __u32 height;
258 __u32 width;
259
260 __u32 accel_flags;
261
262
263 __u32 pixclock;
264 __u32 left_margin;
265 __u32 right_margin;
266 __u32 upper_margin;
267 __u32 lower_margin;
268 __u32 hsync_len;
269 __u32 vsync_len;
270 __u32 sync;
271 __u32 vmode;
272 __u32 rotate;
273 __u32 reserved[5];
274 };
以上是可變參數結構,另外還有struct fb_fix_screeninfo不可變參數:
154 struct fb_fix_screeninfo {
155 char id[16];
156 unsigned long smem_start;
157
158 __u32 smem_len;
160 __u32 type_aux;
161 __u32 visual;
162 __u16 xpanstep;
163 __u16 ypanstep;
164 __u16 ywrapstep;
165 __u32 line_length;
166 unsigned long mmio_start;
167
168 __u32 mmio_len;
169 __u32 accel;
170
171 __u16 reserved[3];
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;
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
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);
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 }
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);
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
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
846 da8xx_fb_info->screen_base = par->v_palette_base + par->palette_sz;
847 da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz;
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;
877
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;
890 da8xx_fb_info->cmap.len = par->palette_sz;
891
892
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
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
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 }
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
924 lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) |
925 LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
926
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函數即可