framebuffer 子系统分析
来源:互联网 发布:程序员入门必看的书籍 编辑:程序博客网 时间:2024/05/16 15:52
一、常见结构体分析
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;
- };
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]; /* 保留备用 */
- };
- #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
- };
- 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);
- #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);
- 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高度
- }
- 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;
- }
- 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;
- }
- }
- }
下面我们分析最复杂的函数
...
0 0
- framebuffer 子系统分析
- framebuffer 子系统分析
- framebuffer 子系统分析
- framebuffer 子系统解读
- framebuffer驱动分析
- s3c6410 framebuffer分析
- 【转】s3c6410 framebuffer分析
- s3c6410 framebuffer分析
- s3c6410 framebuffer分析
- s3c6410 framebuffer分析
- framebuffer驱动分析
- framebuffer驱动分析
- FrameBuffer驱动程序分析
- framebuffer驱动分析
- FrameBuffer驱动程序分析
- 基于imx FrameBuffer 分析
- Linux FrameBuffer分析
- framebuffer结构体分析
- Java中Native关键字的作用
- 第二册第一单元总结
- hdu 5115 Dire Wolf(区间dp)
- 单页面应用下的比如angular做聚合支付时候拦截后退的坑
- Hive 建表 加载数据 查询
- framebuffer 子系统分析
- [bzoj2169]连边
- CentOS 7下安装集群Hadoop-2.7.3
- Java传递参数
- noSQL-redis学习(五) -- redis事务
- TTL转485电路设计
- composer使用详解
- 积分图计算方法
- java.lang.IllegalThreadStateException异常分析