一、常见结构体分析
1、fb_info
struct fb_info { int node; int flags; struct mutex lock; /* 调用open/release/ioctl时的锁 */ struct mutex mm_lock; /* fb_mmap和smem_*的锁 */ struct fb_var_screeninfo var; /* 当前LCD的可变参数 */ struct fb_fix_screeninfo fix; /* 当前LCD的固定参数 */ struct fb_monspecs monspecs; /* 当前LCD标准 */ struct work_struct queue; /* 帧缓冲工作队列 */ struct fb_pixmap pixmap; /* 图像硬件mapper */ struct fb_pixmap sprite; /* 光标硬件mapper */ struct fb_cmap cmap; /* 当前LCD的颜色表 */ struct list_head modelist; /* mode list */ struct fb_videomode *mode; /* 当前的video模式 */ #ifdef CONFIG_FB_BACKLIGHT /* 对应的背光设备,在framebuffer之前注册及其之后注销 */ struct backlight_device *bl_dev; /* 背光等级调整 */ struct mutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS]; #endif #ifdef CONFIG_FB_DEFERRED_IO struct delayed_work deferred_work; struct fb_deferred_io *fbdefio; #endif struct fb_ops *fbops; /* 帧缓冲底层操作函数接口 */ struct device *device; /* 父设备 */ struct device *dev; /* fb设备*/ int class_flag; /* 私有sysfs标志 */ #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */ #endif char __iomem *screen_base; /* 虚拟基地址即framebuffer起始虚拟地址 */ unsigned long screen_size; /* 显存大小 */ void *pseudo_palette; /* 伪16色颜色表 */ #define FBINFO_STATE_RUNNING 0 #define FBINFO_STATE_SUSPENDED 1 u32 state; /* Hardware state i.e suspend */ void *fbcon_par; /* fbcon use-only private area */ /* 私有数据 */ void *par; /* we need the PCI or similiar aperture base/size not smem_start/size as smem_start may just be an object allocated inside the aperture so may not actually overlap */ struct apertures_struct { unsigned int count; struct aperture { resource_size_t base; resource_size_t size; } ranges[0]; } *apertures; };帧缓冲设备中最关键的数据结构就是fb_info,其中记录了设备的全部信息,包括设备的设置参数、状态以及操作函数指针,每一个帧缓冲设备必须对应一个fb_info结构体。
2、fb_var_screeninfo 和 fb_fix_screeninfo
struct fb_var_screeninfo { __u32 xres; /* 可视分辨率 */ __u32 yres; __u32 xres_virtual; /* 虚拟分辨率 */ __u32 yres_virtual; __u32 xoffset; /* 虚拟到可视之间的偏移 */ __u32 yoffset; __u32 bits_per_pixel; /* 每个像素点占用的位数 */ __u32 grayscale; /* 非0时指灰度值 */ struct fb_bitfield red; /* fb内存的RGB位域 */ struct fb_bitfield green; struct fb_bitfield blue; struct fb_bitfield transp; /* 透明度 */ __u32 nonstd; /* 非0时代指非标准像素格式 */ __u32 activate; /* see FB_ACTIVATE_* */ __u32 height; /* 高度 mm */ __u32 width; /* 宽度 mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* 时序: 除pixel以外,其他的都以像素时钟为单位 */ __u32 pixclock; /* 像素时钟(皮秒) */ __u32 left_margin; /* 行前肩:从行同步信号到有效数据开始的时钟数 */ __u32 right_margin; /* 行后肩:从有效数据末尾到行同步信号的时钟数 */ __u32 upper_margin; /* 帧前肩:从帧同步信号到有效行开始的行数 */ __u32 lower_margin; /* 帧后肩:从有效行末尾到帧同步信号的行数 */ __u32 hsync_len; /* 行同步信号占用的时钟数 */ __u32 vsync_len; /* 帧同步信号占用的行数 */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* 旋转角度 */ __u32 reserved[5]; /* 保留备用 */ };
struct fb_fix_screeninfo { char id[16]; /* 标识符 */ unsigned long smem_start; /* framebuffer的物理起始地址 */ __u32 smem_len; /* framebuffer的长度 */ __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* 隔行扫描参数 */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* 一行的字节数 */ unsigned long mmio_start; /* 内存映射I/O的物理起始地址 */ __u32 mmio_len; /* 内存映射I/O的长度 */ __u32 accel; __u16 reserved[3]; /* 保留备用 */ };这两个结构体分别记录了显示器可以修改和不可修改的信息,这些数据成员需要在驱动程序中初始化。其中fix.visual代表显示器使用的色彩模式,Linux中支持的色彩模式如下:
#define FB_VISUAL_MONO01 0 /* 黑白. 1=Black 0=White */ #define FB_VISUAL_MONO10 1 /* 黑白. 1=White 0=Black */ #define FB_VISUAL_TRUECOLOR 2 /* 真彩色,由红绿蓝三基色构成*/ #define FB_VISUAL_PSEUDOCOLOR 3 /* 伪彩色,采用索引颜色 */ #define FB_VISUAL_DIRECTCOLOR 4 /* 查表显示 */ #define FB_VISUAL_STATIC_PSEUDOCOLOR 5 /* 只读的伪彩色 */
fb_bitfield结构体描述每一个像素在显示缓冲区中的组织方式。
struct fb_bitfield { __u32 offset; /* 位域偏移 */ __u32 length; /* 位域长度 */ __u32 msb_right; /* != 0 : 最高有效位在右边 */ };
3、fb_ops
struct fb_ops { /* 打开/释放 */ struct module *owner; int (*fb_open)(struct fb_info *info, int user); int (*fb_release)(struct fb_info *info, int user); /* 下面两个函数是非线性布局的/常规内存映射无法工作的缓冲设备需要的接口 */ ssize_t (*fb_read)(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos); ssize_t (*fb_write)(struct fb_info *info, 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); /* 设置颜色寄存器 */ int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info); /* 批量设置颜色寄存器 */ int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); /* 清屏 */ int (*fb_blank)(int blank, struct fb_info *info); /* pan display */ int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); /* 绘制一个矩形 */ void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); /* 拷贝数据 */ void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); /* 绘制一幅图像 */ void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); /* 绘制光标 */ int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); /* 旋转显示 */ void (*fb_rotate)(struct fb_info *info, int angle); /* 等待blit空闲(可选) */ int (*fb_sync)(struct fb_info *info); /* ioctl函数(可选) */ int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg); /* 32位的compat ioctl(可选) */ int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg); /* fb特定的mmap */ int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); /* 根据var得到显示器的功能信息 */ void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, struct fb_var_screeninfo *var); /* 销毁fb */ void (*fb_destroy)(struct fb_info *info); };
fb_ops是驱动编写人员应该实现的底层函数接口。
4、tccfb_info
struct tccfb_info { struct fb_info *fb; // fbi结构体 struct device *dev; //struct tccfb_mach_info *mach_info; /* raw memory addresses */ dma_addr_t map_dma; /* physical */ u_char * map_cpu; /* virtual */ u_int map_size; /* addresses of pieces placed in raw buffer */ u_char * screen_cpu; /* virtual address of buffer */ dma_addr_t screen_dma; /* physical address of buffer */ u_int imgch; struct tccfb_platform_data *pdata; #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; struct early_suspend earlier_suspend; #endif };二、framebuffer注册流程分析
static struct platform_driver tccfb_driver = { // 底层函数接口 .probe = tccfb_probe, .remove = tccfb_remove, .suspend = tccfb_suspend, .shutdown = tccfb_shutdown, .resume = tccfb_resume, .driver = { .name = "tccxxx-lcd", .owner = THIS_MODULE, }, }; static int __init tccfb_init(void) { printk(KERN_INFO " %s (built %s %s)\n", __func__, __DATE__, __TIME__); fb_power_state = 1; tca_fb_init(); // 初始化控制器 #ifdef TCC_VIDEO_DISPLAY_BY_VSYNC_INT // 初始化锁 spin_lock_init(&vsync_lock) ; spin_lock_init(&vsync_lockDisp ) ; #endif return platform_driver_register(&tccfb_driver); } static void __exit tccfb_cleanup(void) { tca_fb_cleanup(); // 注销控制器 platform_driver_unregister(&tccfb_driver); } module_init(tccfb_init); module_exit(tccfb_cleanup);其中
int tca_fb_init(void) // 该函数主要完成LCD控制器时钟和DDI_CACHE的初始化 { struct lcd_panel *lcd_info; pmap_t pmap_fb_video; printk(KERN_INFO " tcc92xx %s (built %s %s)\n", __func__, __DATE__, __TIME__); pmap_get_info("fb_video", &pmap_fb_video); #ifdef TCC_LCDC1_USE // 获取控制器地址 pLCDC = (volatile PLCDC)tcc_p2v(HwLCDC1_BASE); #ifdef CONFIG_ARCH_TCC92XX pLCDC_FB_CH = (volatile PLCDC_CHANNEL)tcc_p2v(pLCDC->LI2C); #else pLCDC_FB_CH = (volatile PLCDC_CHANNEL)tcc_p2v(HwLCDC1_CH_BASE(2)); #endif// Fb_Lcdc_num = 1; #else pLCDC = (volatile PLCDC)tcc_p2v(HwLCDC0_BASE); #ifdef CONFIG_ARCH_TCC92XX pLCDC_FB_CH = (volatile PLCDC_CHANNEL)tcc_p2v(HwLCDC0_CH_BASE(0)); #else pLCDC_FB_CH = (volatile PLCDC_CHANNEL)tcc_p2v(HwLCDC0_CH_BASE(2)); #endif// Fb_Lcdc_num = 0; #endif// TCC_LCDC1_USE tca92xxfb_clock_init(); // 初始化时钟 tca92xxfb_clock_set(PWR_CMD_ON); // 打开时钟 tcc_ddi_cache_setting(); // 设置DDI_CACHE TCC_OUTPUT_LCDC_Init(); // 初始化控制器 init_waitqueue_head(&lcdc_struct.waitq); lcdc_struct.state = 1; #ifdef CONFIG_FB_M2M_COPY lcd_info = tccfb_get_panel(); if(pmap_fb_video.size == 0) { Gmap_cpu = dma_alloc_writecombine(0, lcd_info->xres * lcd_info->yres * 4 , &Gmap_dma, GFP_KERNEL); } else { unsigned int fb_size = lcd_info->xres * lcd_info->yres * 4; Gmap_dma = pmap_fb_video.base + (fb_size * 2); Gmap_dma = PAGE_ALIGN(Gmap_dma); Gmap_cpu = ioremap_nocache(Gmap_dma, fb_size); } #endif// printk(" %s LCDC:%d end \n", __func__, Fb_Lcdc_num); return 0; } EXPORT_SYMBOL(tca_fb_init); void tca_fb_cleanup(void) // 该函数也是完成对时钟的设置 { dprintk(" %s LCDC:%d \n", __func__, Fb_Lcdc_num); tca92xxfb_clock_delete(); } EXPORT_SYMBOL(tca_fb_cleanup);在板级初始化程序中有对TCC_FB设备的初始化
#if defined(CONFIG_FB_TCC) /*---------------------------------------------------------------------- * Device : LCD Frame Buffer resource * Description: *----------------------------------------------------------------------*/ static u64 tcc_device_lcd_dmamask = 0xffffffffUL; struct platform_device tcc_lcd_device = { .name = "tccxxx-lcd", .id = -1, .dev = { .dma_mask = &tcc_device_lcd_dmamask, // .coherent_dma_mask = 0xffffffffUL } }; #endif
然后在下面的函数中注册设备
int __init m801_88_init_panel(void) { int ret; if (!machine_is_m801_88()) return 0; printk("%s LCD panel type %d\n", __func__, tcc_panel_id); switch (tcc_panel_id) { #ifdef CONFIG_LCD_AT070TN93 case PANEL_ID_AT070TN93: platform_device_register(&at070tn93_lcd); // 注册lcd panel设备 break; #endif default: pr_err("Not supported LCD panel type %d\n", tcc_panel_id); return -EINVAL; } ret = platform_device_register(&tcc_lcd_device); // 注册fb设备 if (ret) return ret; platform_device_register(&m801_88_backlight); // 注册背光控制设备 ret = platform_driver_register(&m801_88_backlight_driver); // 注册背光控制驱动 if (ret) return ret; return 0; } device_initcall(m801_88_init_panel);1、另外讲下lcd panel的注册流程
static struct lcd_platform_data lcd_pdata = { // panel的几个控制引脚 .power_on = GPIO_LCD_ON, .display_on = GPIO_LCD_DISPLAY, .bl_on = GPIO_LCD_BL, .reset = GPIO_LCD_RESET, }; #ifdef CONFIG_LCD_AT070TN93 static struct platform_device at070tn93_lcd = { .name = "at070tn93_lcd", .dev = { .platform_data = &lcd_pdata, }, }; #endif//CONFIG_LCD_AT070TN93
static struct lcd_panel at070tn93_panel = { // panel的参数 .name = "AT070TN93", .manufacturer = "INNOLUX", .id = PANEL_ID_AT070TN93, .xres = 1024, .yres = 600, .width = 136, .height = 3, .bpp = 18, .clk_freq = 500000, .clk_div = 1, .bus_width = 18, .lpw = 320, .lpc = 1024, .lswc = 24, .lewc = 160, .vdb = 0, .vdf = 10, .fpw1 = 3, .flc1 = 600, .fswc1 = 0, .fewc1 = 22, .fpw2 = 3, .flc2 = 600, .fswc2 = 0, .fewc2 = 22, .sync_invert = IV_INVERT_EN | IH_INVERT_EN, .init = at070tn93_panel_init, .set_power = at070tn93_set_power, .set_backlight_level = at070tn93_set_backlight_level, }; static int at070tn93_probe(struct platform_device *pdev) { printk("%s : %d\n", __func__, 0); mutex_init(&panel_lock); lcd_pwr_state = 1; at070tn93_panel.dev = &pdev->dev; tccfb_register_panel(&at070tn93_panel); // 注册panel return 0; } static int at070tn93_remove(struct platform_device *pdev) { return 0; } static struct platform_driver at070tn93_lcd = { .probe = at070tn93_probe, .remove = at070tn93_remove, .driver = { .name = "at070tn93_lcd", .owner = THIS_MODULE, }, }; static __init int at070tn93_init(void) { return platform_driver_register(&at070tn93_lcd); // 注册panel驱动 } static __exit void at070tn93_exit(void) { platform_driver_unregister(&at070tn93_lcd); } subsys_initcall(at070tn93_init); module_exit(at070tn93_exit);
int tccfb_register_panel(struct lcd_panel *panel) { dprintk(" %s name:%s \n", __func__, panel->name); lcd_panel = panel; // 赋值给全局变量 return 1; } EXPORT_SYMBOL(tccfb_register_panel);
2、另外讲下背光控制设备的注册流程
static void m801_88_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { struct lcd_panel *lcd_panel = tccfb_get_panel(); // 获取全局的panel if (lcd_panel->set_backlight_level) lcd_panel->set_backlight_level(lcd_panel, value); // 调用panel的函数实现背光控制 } static struct led_classdev m801_88_backlight_led = { .name = "lcd-backlight", .brightness = DEFAULT_BACKLIGHT_BRIGHTNESS, .brightness_set = m801_88_brightness_set, }; #define M801_88_GPIO_LCD_RESET TCC_GPC(29) #define M801_88_GPIO_LCD_ON TCC_GPG(2) #define M801_88_GPIO_LCD_BL TCC_GPA(4) static int m801_88_backlight_probe(struct platform_device *pdev) { tcc_gpio_config(M801_88_GPIO_LCD_ON, GPIO_FN(0)); //gpio_d 21 //初始化IO tcc_gpio_config(M801_88_GPIO_LCD_BL, GPIO_FN(0)); //gpio a 4 tcc_gpio_config(M801_88_GPIO_LCD_RESET, GPIO_FN(0)); //gpio c 28 gpio_request(M801_88_GPIO_LCD_ON, "lcd_on"); gpio_request(M801_88_GPIO_LCD_BL, "lcd_bl"); gpio_request(M801_88_GPIO_LCD_RESET, "lcd_reset"); return led_classdev_register(&pdev->dev, &m801_88_backlight_led); // 注册led_classdev } static int m801_88_backlight_remove(struct platform_device *pdev) { led_classdev_unregister(&m801_88_backlight_led); return 0; } static struct platform_driver m801_88_backlight_driver = { // 驱动 .probe = m801_88_backlight_probe, .remove = m801_88_backlight_remove, .driver = { .name = "m801_88-backlight", .owner = THIS_MODULE, }, }; static struct platform_device m801_88_backlight = { // 设备 .name = "m801_88-backlight", };回到主题
当fb的设备和驱动匹配在一起之后便执行tccfb_probe函数
static int __init tccfb_probe(struct platform_device *pdev) { struct tccfb_info *info; struct fb_info *fbinfo; int ret; int plane = 0; unsigned int screen_width, screen_height; if (!lcd_panel) { // 是否已经注册panel pr_err("tccfb: no LCD panel data\n"); return -EINVAL; } pr_info("LCD panel is %s %s %d x %d\n", lcd_panel->manufacturer, lcd_panel->name, lcd_panel->xres, lcd_panel->yres); screen_width = lcd_panel->xres; // 设置分辨率 screen_height = lcd_panel->yres; #if defined(CONFIG_TCC_HDMI_UI_SIZE_1280_720) // 是否定义HDMI开机显示UI if(tcc_display_data.resolution == 1) { screen_width = 720; screen_height = 576; } else if(tcc_display_data.resolution == 2) { screen_width = 800; screen_height = 480; } #endif printk("%s, screen_width=%d, screen_height=%d\n", __func__, screen_width, screen_height); pmap_get_info("fb_video", &pmap_fb_video); #if defined(CONFIG_TCC_EXCLUSIVE_UI_LAYER) pmap_get_info("exclusive_viqe", &pmap_eui_viqe); #endif for (plane = 0; plane < CONFIG_FB_TCC_DEVS; plane++) { fbinfo = framebuffer_alloc(sizeof(struct tccfb_info), &pdev->dev); // 申请fbi 下面开始填充结构体 info = fbinfo->par; info->fb = fbinfo; //platform_set_drvdata(pdev, fbinfo); strcpy(fbinfo->fix.id, tccfb_driver_name); fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; fbinfo->fix.type_aux = 0; fbinfo->fix.xpanstep = 0; fbinfo->fix.ypanstep = 1; fbinfo->fix.ywrapstep = 0; fbinfo->fix.accel = FB_ACCEL_NONE; fbinfo->var.nonstd = 0; // 标准像素格式 fbinfo->var.activate = FB_ACTIVATE_NOW; fbinfo->var.accel_flags = 0; fbinfo->var.vmode = FB_VMODE_NONINTERLACED; fbinfo->fbops = &tccfb_ops; // 注册操作函数 fbinfo->flags = FBINFO_FLAG_DEFAULT; fbinfo->var.xres = screen_width; fbinfo->var.xres_virtual = fbinfo->var.xres; fbinfo->var.yres = screen_height; #ifdef TCC_FB_DOUBLE fbinfo->var.yres_virtual = fbinfo->var.yres * 2; #else fbinfo->var.yres_virtual = fbinfo->var.yres; #endif// fbinfo->var.bits_per_pixel = default_scn_depth[plane]; // 设置每个像素占用的位数 都是16位 fbinfo->fix.line_length = fbinfo->var.xres * fbinfo->var.bits_per_pixel/8; // 计算一行的大小 tccfb_check_var(&fbinfo->var, fbinfo); // 检测参数 // the memory size that LCD should occupy fbinfo->fix.smem_len = fbinfo->var.xres * fbinfo->var.yres_virtual * SCREEN_DEPTH_MAX / 8; // 计算缓冲区大小 info->imgch = plane; // 通道 /* Initialize video memory */ ret = tccfb_map_video_memory(info, plane); // 申请缓冲区内存空间 if (ret) { dprintk( KERN_ERR "Failed to allocate video RAM: %d\n", ret); ret = -ENOMEM; } ret = register_framebuffer(fbinfo); // 注册fbi if (ret < 0) { dprintk(KERN_ERR "Failed to register framebuffer device: %d\n", ret); goto free_video_memory; } // fbinfo->var.reserved[0] = 0x54445055; tccfb_set_par(fbinfo); // 设置参数 if (plane == 0) // 判断是否顶层 if (fb_prepare_logo(fbinfo, FB_ROTATE_UR)) { // 准备logo /* Start display and show logo on boot */ fb_set_cmap(&fbinfo->cmap, fbinfo); // 设置颜色表 dprintk("fb_show_logo\n"); fb_show_logo(fbinfo, FB_ROTATE_UR); // 显示logo } printk(KERN_INFO "fb%d: %s frame buffer device\n", fbinfo->node, fbinfo->fix.id); } tcc_lcd_interrupt_reg(TRUE); // 设置中断寄存器 #ifdef CONFIG_HAS_EARLYSUSPEND // 电源相关的函数 info->early_suspend.suspend = tcc_fb_early_suspend; info->early_suspend.resume = tcc_fb_late_resume; info->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING; register_early_suspend(&info->early_suspend); info->earlier_suspend.suspend = tcc_fb_earlier_suspend; info->earlier_suspend.resume = tcc_fb_later_resume; info->earlier_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; register_early_suspend(&info->earlier_suspend); #endif return 0; free_video_memory: tccfb_unmap_video_memory(info); dprintk("TCC92xx fb init failed.\n"); return ret; }
操作函数接口如下
static struct fb_ops tccfb_ops = { .owner = THIS_MODULE, .fb_check_var = tccfb_check_var, .fb_set_par = tccfb_set_par, .fb_blank = tccfb_blank, .fb_setcolreg = tccfb_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_ioctl = tccfb_ioctl, .fb_pan_display = tccfb_pan_display, };
检测参数的函数如下
static int tccfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { /* 判断bpp是否越界 */ if (var->bits_per_pixel > 32) var->bits_per_pixel = 32; else if (var->bits_per_pixel < 16) var->bits_per_pixel = 16; /* 设置r/g/b的位置和偏移 */ if (var->bits_per_pixel == 16) { var->red.offset = 11; var->green.offset = 5; var->blue.offset = 0; var->red.length = 5; var->green.length = 6; var->blue.length = 5; var->transp.length = 0; } else if (var->bits_per_pixel == 32) { var->red.offset = 16; var->green.offset = 8; var->blue.offset = 0; var->transp.offset = 24; var->red.length = 8; var->green.length = 8; var->blue.length = 8; var->transp.length = 8; } else { var->red.length = var->bits_per_pixel; var->red.offset = 0; var->green.length = var->bits_per_pixel; var->green.offset = 0; var->blue.length = var->bits_per_pixel; var->blue.offset = 0; var->transp.length = 0; } if (var->yres_virtual < var->yoffset + var->yres) // 判断yres_virtual是否越界 var->yres_virtual = var->yoffset + var->yres; return 0; }
申请缓冲区内存空间函数如下
static int __init tccfb_map_video_memory(struct tccfb_info *fbi, int plane) { fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE); // 内存对齐 if(FB_VIDEO_MEM_SIZE == 0) { fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, &fbi->map_dma, GFP_KERNEL); printk("map_video_memory (fbi=%p) kenel memoy, dma:0x%x map_size:%08x\n", fbi,fbi->map_dma, fbi->map_size); } else { fbi->map_dma = FB_VIDEO_MEM_BASE; // dma起始地址 fbi->map_cpu = ioremap_nocache(fbi->map_dma, fbi->map_size); // 申请内存空间,得到起始虚拟地址 printk("map_video_memory (fbi=%p) used map memoy,map dma:0x%x size:%08x\n", fbi, fbi->map_dma ,fbi->map_size); } fbi->map_size = fbi->fb->fix.smem_len; if (fbi->map_cpu) { // 申请是否成功 /* prevent initial garbage on screen */ dprintk("map_video_memory: clear %p:%08x\n", fbi->map_cpu, fbi->map_size); memset(fbi->map_cpu, 0x00, fbi->map_size); // 初始化空间 fbi->screen_dma = fbi->map_dma; // 赋值物理起始地址 fbi->fb->screen_base = fbi->map_cpu; // 赋值虚拟起始地址 fbi->fb->fix.smem_start = fbi->screen_dma; // 赋值物理起始地址 // Set the LCD frame buffer start address switch (plane) // 不同panel { case 2: // IMG2 fb_mem_vaddr[plane] = fbi->map_cpu; fb_mem_size [plane] = fbi->map_size; break; case 1: // IMG1 fb_mem_vaddr[plane] = fbi->map_cpu; fb_mem_size [plane] = fbi->map_size; break; case 0: // IMG0 fb_mem_vaddr[plane] = fbi->map_cpu; fb_mem_size [plane] = fbi->map_size; break; } dprintk("map_video_memory: dma=%08x cpu=%p size=%08x\n", fbi->map_dma, fbi->map_cpu, fbi->fb->fix.smem_len); } return fbi->map_cpu ? 0 : -ENOMEM; }设置参数函数如下
static int tccfb_set_par(struct fb_info *info) { struct tccfb_info *fbi = info->par; struct fb_var_screeninfo *var = &info->var; sprintk("- tccfb_set_par pwr:%d output:%d \n",fb_power_state, Output_SelectMode); if (var->bits_per_pixel == 16) // 设置显示器色彩模式 fbi->fb->fix.visual = FB_VISUAL_TRUECOLOR; else if (var->bits_per_pixel == 32) fbi->fb->fix.visual = FB_VISUAL_TRUECOLOR; else fbi->fb->fix.visual = FB_VISUAL_PSEUDOCOLOR; fbi->fb->fix.line_length = (var->xres*var->bits_per_pixel)/8; #ifndef CONFIG_TCC_OUTPUT_STARTER /* activate this new configuration */ if( fb_power_state && Output_SelectMode != TCC_OUTPUT_COMPONENT ) tccfb_activate_var(fbi, var); #endif return 0; }
static void tccfb_activate_var(struct tccfb_info *fbi, struct fb_var_screeninfo *var) { unsigned int imgch = 0; sprintk("%s node:0x%x TCC_DEVS:%d \n", __func__, fbi->fb->node, CONFIG_FB_TCC_DEVS); if((0 <= fbi->fb->node) && (fbi->fb->node < CONFIG_FB_TCC_DEVS)) imgch = fbi->fb->node; else return; tca_fb_activate_var(fbi, var); }
void tca_fb_activate_var(struct tccfb_info *fbi, struct fb_var_screeninfo *var) { unsigned int imgch, bpp_value,tmp_value, base_addr; unsigned int regl, lcd_width, lcd_height, img_width, img_height; #define IMG_AOPT 2 unsigned int alpha_type = 0, alpha_blending_en = 0; unsigned int chromaR, chromaG, chromaB, chroma_en; imgch = fbi->fb->node; // 通道 if(fbi->fb->var.bits_per_pixel == 32) { chroma_en = 0; alpha_type = 1; alpha_blending_en = 1; bpp_value = 0x000C; //RGB888 } else if(fbi->fb->var.bits_per_pixel == 16) // 判断像素点深度并设置相关属性 { chroma_en = 1; alpha_type = 0; alpha_blending_en = 0; bpp_value = 0x000A; //RGB565 } else { printk("%s:fb%d Not Supported BPP!\n", __FUNCTION__, fbi->fb->node); return; } chromaR = chromaG = chromaB = 0; sprintk("%s: fb%d Supported BPP!\n", __FUNCTION__, fbi->fb->node); // 计算缓冲区空间末尾地址 base_addr = fbi->map_dma + fbi->fb->var.xres * var->yoffset * (fbi->fb->var.bits_per_pixel/8); if(fbi->fb->var.yoffset > fbi->fb->var.yres) { base_addr = PAGE_ALIGN(base_addr); } sprintk("%s: fb%d Baddr:0x%x Naddr:0x%x!\n", __FUNCTION__, fbi->fb->node, base_addr, pLCDC_FB_CH->LIBA0); regl = pLCDC->LDS; // get LCD size 获取寄存器值 lcd_width = (regl & 0xFFFF); lcd_height = ((regl>>16) & 0xFFFF); img_width = fbi->fb->var.xres; img_height = fbi->fb->var.yres; if(img_width > lcd_width) img_width = lcd_width; if(img_height > lcd_height) img_height = lcd_height; /* 写新的寄存器值 */ switch(imgch) { case 0: // default framebuffer tmp_value = pLCDC_FB_CH->LIC; pLCDC_FB_CH->LIC = (tmp_value & 0xFFFFFFE0) | (bpp_value & 0x0000001F); pLCDC_FB_CH->LIO = fbi->fb->var.xres * (fbi->fb->var.bits_per_pixel/8); pLCDC_FB_CH->LIS = (img_height<< 16) | (img_width); //Size pLCDC_FB_CH->LIP = 0; // position #ifdef CONFIG_FB_M2M_COPY if(!tccxxx_overlay_use()) { pLCDC_FB_CH->LIBA0 = Gmap_dma; } else #endif//CONFIG_FB_M2M_COPY { pLCDC_FB_CH->LIBA0 = base_addr; } #ifdef CONFIG_ARCH_TCC92XX pLCDC_FB_CH->LIC |= (HwLIC_IEN); // image enable #else pLCDC_FB_CH->LIC |= (HwLIC_IEN | HwLCT_RU); // image enable #endif// CONFIG_ARCH_TCC92XX // overlay 加己 BITCSET (pLCDC->LI2C, 0x1<< 30, (alpha_blending_en) << 30); // alpha enable BITCSET (pLCDC->LI2C, 0x3<< 25, (IMG_AOPT) << 25); // alpha opt BITCSET (pLCDC->LI2C, 0x1<< 24, (alpha_type) << 24); // alpha select BITCSET (pLCDC->LI2C, 0x1<< 29, (chroma_en) << 29); // chroma-keying BITCSET (pLCDC->LI2KR, 0xff << 0, (chromaR) << 0); // key BITCSET (pLCDC->LI2KR, 0xff << 16, (0xF8) << 16); // keymask BITCSET (pLCDC->LI2KG, 0xff << 0, (chromaG) << 0); // key BITCSET (pLCDC->LI2KG, 0xff << 16, (0xFC) << 16); // keymask BITCSET (pLCDC->LI2KB, 0xff << 0, (chromaB) << 0); // key BITCSET (pLCDC->LI2KB, 0xff << 16, (0xF8) << 16); // keymask tmp_value = pLCDC->LI2C; pLCDC->LI2C = (tmp_value & 0xFFFFFFE0) | (bpp_value & 0x0000001F); pLCDC->LI2O = fbi->fb->var.xres * (fbi->fb->var.bits_per_pixel/8); #ifdef CONFIG_FB_TCC_USE_VSYNC_INTERRUPT tca_fb_vsync_activate(var, fbi); // 设置场同步中断 #else msleep(16); #endif break; case 1: tmp_value = pLCDC->LI1C; pLCDC->LI1C = (tmp_value & 0xFFFFFFE0) | (bpp_value & 0x0000001F); pLCDC->LI1O = fbi->fb->var.xres * (fbi->fb->var.bits_per_pixel/8); pLCDC->LI1S = (img_height<< 16) | (img_width); //Size pLCDC->LI1BA0 = base_addr; #ifdef CONFIG_ARCH_TCC92XX pLCDC->LI1C |= (HwLIC_IEN); #else pLCDC->LI1C |= (HwLIC_IEN | HwLCT_RU); #endif// break; case 2: tmp_value = pLCDC->LI0C; pLCDC->LI0C = (tmp_value & 0xFFFFFFE0) | (bpp_value & 0x0000001F); pLCDC->LI0O = fbi->fb->var.xres * (fbi->fb->var.bits_per_pixel/8); pLCDC->LI0S = (img_height<< 16) | (img_width); //Size pLCDC->LI0BA0 = base_addr; #ifdef CONFIG_ARCH_TCC92XX pLCDC->LI0C |= (HwLIC_IEN); #else pLCDC->LI0C |= (HwLIC_IEN | HwLCT_RU); #endif// break; } return; } EXPORT_SYMBOL(tca_fb_activate_var);
3、另外讲下开机logo的显示过程
int fb_prepare_logo(struct fb_info *info, int rotate) { int depth = fb_get_color_depth(&info->var, &info->fix); // 获取颜色深度 unsigned int yres; memset(&fb_logo, 0, sizeof(struct logo_data)); if (info->flags & FBINFO_MISC_TILEBLITTING || info->flags & FBINFO_MODULE) return 0; if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { // 判断色彩模式 我们是真彩色的 depth = info->var.blue.length; if (info->var.red.length < depth) depth = info->var.red.length; if (info->var.green.length < depth) depth = info->var.green.length; } if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) { /* assume console colormap */ depth = 4; } /* Return if no suitable logo was found */ fb_logo.logo = fb_find_logo(depth); // 获取logo if (!fb_logo.logo) { return 0; } if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD) // 判断是否旋转 yres = info->var.yres; else yres = info->var.xres; if (fb_logo.logo->height > yres) { fb_logo.logo = NULL; return 0; } /* 判断颜色模式 */ if (fb_logo.logo->type == LINUX_LOGO_CLUT224) fb_logo.depth = 8; else if (fb_logo.logo->type == LINUX_LOGO_VGA16) fb_logo.depth = 4; else fb_logo.depth = 1; if (fb_logo.depth > 4 && depth > 4) { switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: fb_logo.needs_truepalette = 1; // 真彩色 break; case FB_VISUAL_DIRECTCOLOR: fb_logo.needs_directpalette = 1; fb_logo.needs_cmapreset = 1; break; case FB_VISUAL_PSEUDOCOLOR: fb_logo.needs_cmapreset = 1; break; } } return fb_prepare_extra_logos(info, fb_logo.logo->height, yres); // 获取logo高度 }获取logo的函数
const struct linux_logo * __init_refok fb_find_logo(int depth) { const struct linux_logo *logo = NULL; if (nologo) return NULL; ... if (depth >= 8) { #ifdef CONFIG_LOGO_LINUX_CLUT224 /* Generic Linux logo */ logo = &logo_linux_clut224; #endif } return logo; } EXPORT_SYMBOL_GPL(fb_find_logo);
const struct linux_logo logo_linux_clut224 __initconst = { .type = LINUX_LOGO_CLUT224, .width = 1024, .height = 768, .clutsize = 221, .clut = logo_linux_clut224_clut, .data = logo_linux_clut224_data };设置颜色表
int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info) { int i, start, rc = 0; u16 *red, *green, *blue, *transp; u_int hred, hgreen, hblue, htransp = 0xffff; red = cmap->red; green = cmap->green; blue = cmap->blue; transp = cmap->transp; start = cmap->start; if (start < 0 || (!info->fbops->fb_setcolreg && !info->fbops->fb_setcmap)) return -EINVAL; if (info->fbops->fb_setcmap) { rc = info->fbops->fb_setcmap(cmap, info); } else { for (i = 0; i < cmap->len; i++) { hred = *red++; hgreen = *green++; hblue = *blue++; if (transp) htransp = *transp++; if (info->fbops->fb_setcolreg(start++, hred, hgreen, hblue, htransp, info)) break; } } if (rc == 0) fb_copy_cmap(cmap, &info->cmap); return rc; }显示logo
int fb_show_logo(struct fb_info *info, int rotate) { int y; y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, // 显示logo num_online_cpus()); y = fb_show_extra_logos(info, y, rotate); return y; }
static int fb_show_logo_line(struct fb_info *info, int rotate, const struct linux_logo *logo, int y, unsigned int n) { u32 *palette = NULL, *saved_pseudo_palette = NULL; unsigned char *logo_new = NULL, *logo_rotate = NULL; struct fb_image image; /* Return if the frame buffer is not mapped or suspended */ if (logo == NULL || info->state != FBINFO_STATE_RUNNING || info->flags & FBINFO_MODULE) return 0; image.depth = 8; // 设置深度 224色 image.data = logo->data; // 数据 if (fb_logo.needs_cmapreset) fb_set_logocmap(info, logo); if (fb_logo.needs_truepalette || fb_logo.needs_directpalette) { palette = kmalloc(256 * 4, GFP_KERNEL); if (palette == NULL) return 0; if (fb_logo.needs_truepalette) fb_set_logo_truepalette(info, logo, palette); // 设置调色板 else fb_set_logo_directpalette(info, logo, palette); saved_pseudo_palette = info->pseudo_palette; info->pseudo_palette = palette; } if (fb_logo.depth <= 4) { logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL); if (logo_new == NULL) { kfree(palette); if (saved_pseudo_palette) info->pseudo_palette = saved_pseudo_palette; return 0; } image.data = logo_new; fb_set_logo(info, logo, logo_new, fb_logo.depth); } image.dx = 0; // 显示logo的起始坐标 image.dy = y; image.width = logo->width; // logo的宽度和高度 image.height = logo->height; if (rotate) { logo_rotate = kmalloc(logo->width * logo->height, GFP_KERNEL); if (logo_rotate) fb_rotate_logo(info, logo_rotate, &image, rotate); } fb_do_show_logo(info, &image, rotate, n); kfree(palette); if (saved_pseudo_palette != NULL) info->pseudo_palette = saved_pseudo_palette; kfree(logo_new); kfree(logo_rotate); return logo->height; }开始显示
static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, int rotate, unsigned int num) { unsigned int x; if (rotate == FB_ROTATE_UR) { for (x = 0; x < num && image->dx + image->width <= info->var.xres; x++) { info->fbops->fb_imageblit(info, image); // 调用图像绘制函数显示logo image->dx += image->width + 8; } } else if (rotate == FB_ROTATE_UD) { for (x = 0; x < num && image->dx >= 0; x++) { info->fbops->fb_imageblit(info, image); image->dx -= image->width + 8; } } else if (rotate == FB_ROTATE_CW) { for (x = 0; x < num && image->dy + image->height <= info->var.yres; x++) { info->fbops->fb_imageblit(info, image); image->dy += image->height + 8; } } else if (rotate == FB_ROTATE_CCW) { for (x = 0; x < num && image->dy >= 0; x++) { info->fbops->fb_imageblit(info, image); image->dy -= image->height + 8; } } }图像绘制函数
void cfb_imageblit(struct fb_info *p, const struct fb_image *image) { u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel; u32 width = image->width; u32 dx = image->dx, dy = image->dy; u8 __iomem *dst1; if (p->state != FBINFO_STATE_RUNNING) return; bitstart = (dy * p->fix.line_length * 8) + (dx * bpp); // 计算起始点 start_index = bitstart & (32 - 1); pitch_index = (p->fix.line_length & (bpl - 1)) * 8; bitstart /= 8; bitstart &= ~(bpl - 1); dst1 = p->screen_base + bitstart; // 计算起始点的虚拟地址 if (p->fbops->fb_sync) p->fbops->fb_sync(p); if (image->depth == 1) { if (p->fix.visual == FB_VISUAL_TRUECOLOR || p->fix.visual == FB_VISUAL_DIRECTCOLOR) { fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color]; bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color]; } else { fgcolor = image->fg_color; bgcolor = image->bg_color; } if (32 % bpp == 0 && !start_index && !pitch_index && ((width & (32/bpp-1)) == 0) && bpp >= 8 && bpp <= 32) fast_imageblit(image, p, dst1, fgcolor, bgcolor); else slow_imageblit(image, p, dst1, fgcolor, bgcolor, start_index, pitch_index); } else color_imageblit(image, p, dst1, start_index, pitch_index); }
static inline void color_imageblit(const struct fb_image *image, struct fb_info *p, u8 __iomem *dst1, u32 start_index, u32 pitch_index) { /* Draw the penguin */ u32 __iomem *dst, *dst2; u32 color = 0, val, shift; int i, n, bpp = p->var.bits_per_pixel; u32 null_bits = 32 - bpp; u32 *palette = (u32 *) p->pseudo_palette; const u8 *src = image->data; u32 bswapmask = fb_compute_bswapmask(p); dst2 = (u32 __iomem *) dst1; // 起始虚拟地址 for (i = image->height; i--; ) { // 行数 n = image->width; // 列数 dst = (u32 __iomem *) dst1; // 当前虚拟地址 shift = 0; val = 0; if (start_index) { u32 start_mask = ~fb_shifted_pixels_mask_u32(p, start_index, bswapmask); val = FB_READL(dst) & start_mask; shift = start_index; } while (n--) { if (p->fix.visual == FB_VISUAL_TRUECOLOR || // 真彩色 p->fix.visual == FB_VISUAL_DIRECTCOLOR ) color = palette[*src]; else color = *src; color <<= FB_LEFT_POS(p, bpp); val |= FB_SHIFT_HIGH(p, color, shift ^ bswapmask); // 合成像素 if (shift >= null_bits) { FB_WRITEL(val, dst++); // 写到buffer就从LCD上显示出来了 val = (shift == null_bits) ? 0 : FB_SHIFT_LOW(p, color, 32 - shift); } shift += bpp; shift &= (32 - 1); src++; } if (shift) { u32 end_mask = fb_shifted_pixels_mask_u32(p, shift, bswapmask); FB_WRITEL((FB_READL(dst) & end_mask) | val, dst); } dst1 += p->fix.line_length; // 换行 if (pitch_index) { dst2 += p->fix.line_length; dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1)); start_index += pitch_index; start_index &= 32 - 1; } } }回到主题
下面我们分析最复杂的函数
...