此篇笔记基于sc7731 - android 5.1,对lcd的framebuffer做一个简明笔记。
一共分为两大部分:第一部分,关于LCD的硬件方面的;第二部分,关于lcd核心处理(framebuffer)部分的。
第一部分,LCD硬件相关的
一、液晶
液晶是一种高分子有机材料。当给它加上直流电场后,原本有序的分子排列被打乱,一部分液晶变得不透明,颜色加深,便因此显示出字符和图形。
液晶的光电效应:干涉、散射、衍射、旋光、吸收等。
二、LCD种类
1. 构造: 使用两块玻璃板夹着一块液晶:一块玻璃板上设置薄膜晶体管(TFT);另一块玻璃板上放置彩色滤光片
----当改变TFT上的电信号和电压时,会改变液晶内部分子的转动流向,根据光学原理,从而控制每个像素点是否显示。
2. 种类
(1) STN : Super Twisted Nematic
在传统单色STN液晶显示器上加一彩色滤光片,并将单色显示矩阵中的每一像素分成三个子像素,分别通过彩色滤光片显示红、绿、蓝三基色,就可显示出彩色画面。
其功耗小、极度省电、可显示65536种色彩,一般功能机上或者低配智能机上不二选择。
(2) UFB: Ultra Fine Bright
三星手机的专用彩色显示屏技术。在设计上UFB采用了特别的光栅设计,可减少像素间距,以获得更佳的图像质量。
减小像素间距,以获得更佳的图像质量;可显示65536种色彩,能够达到128×160像素的分辨率;对比度是STN液晶显示屏的两倍;耗电量低于TFT。
(3) TFD: Thin Film Diode
是TFT和STN的折中,有着比STN更好的亮度和色彩饱和度,却又比TFT更省电。为LCD上每一个像素都配备了一颗单独的二极管来作为控制源。
高画质、超低功耗、小型化、动态影像的显示能力以及快速的反应时间。采用图像处理技术可以显示相当于26万色的图像。
(4) TFT: Thin Film Transistor
每个液晶像素点都是由集成在像素点后面的薄膜晶体管来驱动,具有高响应度、高亮度、高对比度等优点。
是各类笔记本电脑和台式机上的主流显示设备,中高端彩屏手机中分65536 色、16 万色,1600万色三种。
(5) OLED: Organic Light-Emitting Diode
OLED显示器很薄很轻,因为它不使用背光。
该类LCD广泛用于各种嵌入式设备。并且可实现高度可携带、折叠的显示技术。
三、LCD技术参数
从技术参数开始,驱动软件就必须要开始关注了。从这里开始,驱动软件要开始对lcd硬件相关部分进行描述和处理。怎么描述? 一个C语言的结构体足以。
(1) 可视面积
(2) 可视角度
(3) 点距
(4) 色彩度
(5) 对比值
(6) 亮度值
(7) 响应时间
四、LCD时序图
LCD 显示出一副图片,是从上到下、从左到右刷出来的。其显示过程,通过以下时序图进行描述。
外部引脚信号(通过示波器可量到的)
VSYNC: 帧同步信号,表示扫描1帧的开始,一帧也就是LCD显示的一个画面 --- 竖直方向。
HSYNC: 行同步信号,表示扫描1行的开始。 --- 水平方向。
VDEN: 数据使能信号。
VD[23:0]: LCD像素数据输出端口。
VCLK: 像素时钟信号。
寄存器参数
VSPW: 帧同步信号的脉宽,单位为1行(Line)的时间。 --- vsync_len
HSPW: 行同步信号的脉宽,单位为1VCLK的时间。 ---hsync_len
VFPD: 帧同步信号的前肩,单位为1行(Line)的时间。就是一帧图像结束后,在下一帧图像开始信号之前的无效行数。 ---lower_margin
VBPD: 帧同步信号的后肩,单位为1行(Line)的时间。就是一帧图像开始时,帧同步信号无效的行数。 ---upper_margin
HBPD: 行同步信号的后肩,单位为1VCLK的时间。就是水平同步信号开始位置到有效数据开始位置之间有多少个VCLK。 ---right_margin
HFPD: 表示一行的有效数据结束到下一个水平同步信号开始之间的DCLK的个数。---left_margin
LINEVAL: 帧显示尺寸-1,即屏行宽-1,对于800*480分配率的LCD屏,那么LINEVAL=480-1=479,也就是LCD屏显示一帧数据所需要的行的数目.
HOZVAL: 行显示尺寸-1,即屏列宽-1,对于800*480分配率的LCD屏,那么HOZVAL=800-1=799,请记住,是屏列宽,也就是LCD屏显示一行数据所需要的像素(pixel)的数目
说明:
当要显示一副图片时,LCD控制器要先发送一次VSYNC信号;然后会根据图片的组成行数,发送N个 HSYNC 和 VCLK 信号---显然,每一行开始,都会发送HSYNC信号;而每一行内部的每个像素点,都需要一个VCLK信号。
怎么理解VBPD & VFPD & HBPD & HFPD?
可以将LCD的扫描看成是一把电子枪从左上角到右下角的喷射。
当帧同步信号发出来后,它需要一个时间段,有效数据才能从枪里面喷出来。这个有效数据喷出的时间点和帧同步信号发出的时间点,中间有一个时间差值。 ---VBPD
同样的,当一副图片结束后,电子枪可能还需要一段时间才能停止工作,于是在这个有效数据结束的时间点,到电子枪完全停止工作的时间点,中间又是一个时间差值。(当然,电子枪未必会停止工作,它可能会继续发一个帧同步信号,开始刷新下一副图片) ---VFPD
那么水平同步信号也是一样的,每一行的最左端,它有一段时间是没有喷有效数据的;而每一行的最右端,也有一段时间是没有喷有效数据的。于是这里就形成了HBPD 和 HFPD。
如果用肉眼自行看手机屏幕的四边,会发现,手机屏幕的图片,似乎是镶嵌入LCD一样---有一个极细极细的黑边框。而这,就是因为上面四个寄存器参数设置的结果。
第二部分,LCD 驱动软件相关的
何谓帧缓冲?何谓缓冲?
简而言之,缓冲就是一个buffer,一个暂存数据的内存buffer区域;那么帧缓冲,就是一个用户暂存一帧图片的buffer。它不神秘,操作起来非常方便。
当用户需要显示一幅图片时,仅仅需要把该幅图片的具体内容放入该缓冲区即可。操作系统会负责将该幅图片搬运到lcd屏幕上。
framebuffer的概念来源于Linux。在linux中,framebuffer驱动设备被划分为字符设备,设备名: /dev/fbN (N取值范围0~31)
一、Framebuffer测试程序(关键代码)
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <fcntl.h> 4 #include <linux/fb.h> 5 #include <sys/mman.h> 6 7 int main() 8 { 9 int fbfd = 0; 10 struct fb_var_screeninfo vinfo; 11 struct fb_fix_screeninfo finfo; 12 long int screensize = 0; 13 char *fbp = 0; 14 int x = 0, y = 0; 15 long int location = 0; 16 17 // 以读写方式打开fb0 18 fbfd = open("/dev/fb0", O_RDWR); 19 if (!fbfd) { 20 printf("Error: cannot open framebuffer device.\n"); 21 exit(1); 22 } 23 printf("The framebuffer device was opened successfully.\n"); 24 25 // 获取硬件屏幕信息 26 if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) { 27 printf("Error reading fixed information.\n"); 28 exit(2); 29 } 30 31 // 获取用户可修改屏幕参数信息 32 if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) { 33 printf("Error reading variable information.\n"); 34 exit(3); 35 } 36 37 printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel ); 38 39 // 计算framebuffer空间大小,也就是可装下一幅图片的大小 40 screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; 41 42 // 内存映射,把一块内存区域从内核映射到用户空间,供用户直接操作 43 fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, 44 fbfd, 0); 45 if ((int)fbp == -1) { 46 printf("Error: failed to map framebuffer device to memory.\n"); 47 exit(4); 48 } 49 printf("The framebuffer device was mapped to memory successfully.\n"); 50 51 x = 100; y = 100; // Where we are going to put the pixel 52 53 // 绘制矩形, 200x200像素的 54 for ( y = 100; y < 300; y++ ) 55 for ( x = 100; x < 300; x++ ) { 56 57 location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + 58 (y+vinfo.yoffset) * finfo.line_length; 59 60 if ( vinfo.bits_per_pixel == 32 ) { 61 *(fbp + location) = 100; // Some blue 62 *(fbp + location + 1) = 15+(x-100)/2; // A little green 63 *(fbp + location + 2) = 200-(y-100)/5; // A lot of red 64 *(fbp + location + 3) = 0; // No transparency 65 } else { //assume 16bpp 66 int b = 10; 67 int g = (x-100)/6; // A little green 68 int r = 31-(y-100)/16; // A lot of red 69 unsigned short int t = r<<11 | g << 5 | b; 70 *((unsigned short int*)(fbp + location)) = t; 71 } 72 73 } 74 //解除映射 75 munmap(fbp, screensize); 76 //设备关闭 77 close(fbfd); 78 return 0; 79 }
程序来源: http://blog.chinaunix.net/uid-22666248-id-283476.html
二、Framebuffer驱动框架图
由图可知,framebuffer没有直接向用户空间提供文件操作接口,而是通过一个调用的关系,让fb_ops帮忙向用户空间提供了文件操作接口。
三、展讯framebuffer关键数据结构
1 struct fb_info { 2 3 struct fb_var_screeninfo var; /* Current var */ //LCD 用户可变参数 4 struct fb_fix_screeninfo fix; /* Current fix */ //LCD 固定参数 5 6 #ifdef CONFIG_FB_BACKLIGHT 7 /* assigned backlight device */ 8 /* set before framebuffer registration, 9 remove after unregister */ 10 struct backlight_device *bl_dev; //背光设备 11 #endif 12 13 14 struct fb_ops *fbops; //对底层硬件操作函数指针 15 16 char __iomem *screen_base; /* Virtual address */ 17 unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ //LCD I/O映射虚拟内存大小 18 19 u32 state; /* Hardware state i.e suspend */ //LCD 挂起或者恢复状态 20 21 };
说明:
仅仅保留关键字段。位于: include/linux/fb.h
1 struct fb_var_screeninfo { 2 __u32 xres; /* visible resolution */ //可见屏幕一行有多少个像素点 3 __u32 yres; //可见屏幕一列有多少个像素点 4 __u32 xres_virtual; /* virtual resolution */ //虚拟屏幕一行有多少个像素点 5 __u32 yres_virtual; 6 __u32 xoffset; /* offset from virtual to visible */ //虚拟到可见之间的行偏移 7 __u32 yoffset; /* resolution */ 8 9 __u32 bits_per_pixel; /* guess what */ //每个像素占的bit,除以8即是字节 10 __u32 grayscale; /* 0 = color, 1 = grayscale, */ //非0时,指定的灰度 11 /* >1 = FOURCC */ 12 struct fb_bitfield red; /* bitfield in fb mem if true color, */ //R 13 struct fb_bitfield green; /* else only length is significant */ //G 14 struct fb_bitfield blue; //B 15 struct fb_bitfield transp; /* transparency */ //透明度 16 17 __u32 nonstd; /* != 0 Non standard pixel format */ 18 19 __u32 activate; /* see FB_ACTIVATE_* */ 20 21 __u32 height; /* height of picture in mm */ 22 __u32 width; /* width of picture in mm */ 23 24 __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ 25 26 /* Timing: All values in pixclocks, except pixclock (of course) */ 27 __u32 pixclock; /* pixel clock in ps (pico seconds) */ //像素时钟 28 __u32 left_margin; /* time from sync to picture */ //行切换,从同步到绘图之间的延迟。也就是上面硬件部分介绍的 HFPD,以下参数意义类推 29 __u32 right_margin; /* time from picture to sync */ 30 __u32 upper_margin; /* time from sync to picture */ 31 __u32 lower_margin; 32 __u32 hsync_len; /* length of horizontal sync */ 33 __u32 vsync_len; /* length of vertical sync */ 34 __u32 sync; /* see FB_SYNC_* */ 35 __u32 vmode; /* see FB_VMODE_* */ 36 __u32 rotate; /* angle we rotate counter clockwise */ 37 __u32 colorspace; /* colorspace for FOURCC-based modes */ 38 __u32 reserved[4]; /* Reserved for future compatibility */ 39 };
说明:
该结构详细的记录了用户可修改的显示参数。位于include/uapi/linux/fb.h
1 struct fb_fix_screeninfo { 2 char id[16]; /* identification string eg "TT Builtin" */ 3 unsigned long smem_start; /* Start of frame buffer mem */ //fb缓存开始位置 4 /* (physical address) */ 5 __u32 smem_len; /* Length of frame buffer mem */ 6 __u32 type; /* see FB_TYPE_* */ //fb类型 7 __u32 type_aux; /* Interleave for interleaved Planes */ 8 __u32 visual; /* see FB_VISUAL_* */ 9 __u16 xpanstep; /* zero if no hardware panning */ 10 __u16 ypanstep; /* zero if no hardware panning */ 11 __u16 ywrapstep; /* zero if no hardware ywrap */ 12 __u32 line_length; /* length of a line in bytes */ 13 unsigned long mmio_start; /* Start of Memory Mapped I/O */ //内存I/O映射的开始位置 14 /* (physical address) */ 15 __u32 mmio_len; /* Length of Memory Mapped I/O */ //I/O映射的长度 16 __u32 accel; /* Indicate to driver which */ 17 /* specific chip/card we have */ 18 __u16 capabilities; /* see FB_CAP_* */ 19 __u16 reserved[2]; /* Reserved for future compatibility */ 20 };
说明 :
该结构详细的说明了用户不可更改的参数。位于include/uapi/linux/fb.h
1 struct fb_ops { 2 /* open/release and usage marking */ 3 struct module *owner; 4 int (*fb_open)(struct fb_info *info, int user); 5 int (*fb_release)(struct fb_info *info, int user); 6 7 /* For framebuffers with strange non linear layouts or that do not 8 * work with normal memory mapped access 9 */ 10 ssize_t (*fb_read)(struct fb_info *info, char __user *buf, 11 size_t count, loff_t *ppos); 12 ssize_t (*fb_write)(struct fb_info *info, const char __user *buf, 13 size_t count, loff_t *ppos); 14 15 /* checks var and eventually tweaks it to something supported, 16 * DO NOT MODIFY PAR */ 17 int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); 18 19 /* set the video mode according to info->var */ 20 int (*fb_set_par)(struct fb_info *info); 21 22 /* set color register */ 23 int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, 24 unsigned blue, unsigned transp, struct fb_info *info); 25 26 /* set color registers in batch */ 27 int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); 28 29 /* blank display */ 30 int (*fb_blank)(int blank, struct fb_info *info); 31 32 /* pan display */ 33 int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); 34 35 /* Draws a rectangle */ 36 void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); 37 /* Copy data from area to another */ 38 void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); 39 /* Draws a image to the display */ 40 void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); 41 42 /* Draws cursor */ 43 int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); 44 45 /* Rotates the display */ 46 void (*fb_rotate)(struct fb_info *info, int angle); 47 48 /* wait for blit idle, optional */ 49 int (*fb_sync)(struct fb_info *info); 50 51 /* perform fb specific ioctl (optional) */ 52 int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, 53 unsigned long arg); 54 55 /* Handle 32bit compat ioctl (optional) */ 56 int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, 57 unsigned long arg); 58 59 /* perform fb specific mmap */ 60 int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); 61 62 /* get capability given var */ 63 void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, 64 struct fb_var_screeninfo *var); 65 66 /* teardown any resources to do with this framebuffer */ 67 void (*fb_destroy)(struct fb_info *info); 68 69 /* called at KDB enter and leave time to prepare the console */ 70 int (*fb_debug_enter)(struct fb_info *info); 71 int (*fb_debug_leave)(struct fb_info *info); 72 };
说明:
该结构体详细说明了对底层硬件操作的方法。位于: include/linux/fb.h
以上几个结构体是,几乎所有的framebuffer都会有的。针对于展讯的实现方式,它还多了以下两个:
1 struct sprdfb_device { 2 struct fb_info *fb; 3 4 uint16_t enable; 5 uint16_t dev_id; /*main_lcd, sub_lcd*/ 6 7 uint32_t bpp; /*input bit per pixel*/ 8 9 uint16_t panel_ready; /*panel has been inited by uboot*/ 10 uint16_t panel_if_type; /*panel IF*/ 11 12 union{ 13 uint32_t mcu_timing[MCU_LCD_TIMING_KIND_MAX]; 14 uint32_t rgb_timing[RGB_LCD_TIMING_KIND_MAX]; 15 }panel_timing; 16 17 struct panel_spec *panel; 18 struct display_ctrl *ctrl; 19 20 #ifdef CONFIG_OF 21 struct device *of_dev; 22 #endif 23 24 uint32_t dpi_clock; 25 struct semaphore refresh_lock; 26 uint64_t frame_count; 27 28 #ifdef CONFIG_FB_ESD_SUPPORT 29 struct delayed_work ESD_work; 30 // struct semaphore ESD_lock; 31 uint32_t ESD_timeout_val; 32 bool ESD_work_start; 33 /*for debug only*/ 34 uint32_t check_esd_time; 35 uint32_t panel_reset_time; 36 uint32_t reset_dsi_time; 37 #ifdef FB_CHECK_ESD_BY_TE_SUPPORT 38 uint32_t esd_te_waiter; 39 wait_queue_head_t esd_te_queue; 40 uint32_t esd_te_done; 41 #endif 42 #endif 43 44 uint32_t logo_buffer_addr_v; 45 uint32_t logo_buffer_size; 46 47 #ifdef CONFIG_HAS_EARLYSUSPEND 48 struct early_suspend early_suspend; 49 #endif 50 51 void *priv1; 52 };
说明:
这个是展讯根据自己的平台特点,又封装了一层。如果用面向对象的观点看的话,它继承了 struct fb_info 结构体.
1 struct display_ctrl { 2 const char *name; 3 4 int32_t (*early_init) (struct sprdfb_device *dev); 5 int32_t (*init) (struct sprdfb_device *dev); 6 int32_t (*uninit) (struct sprdfb_device *dev); 7 8 int32_t (*refresh) (struct sprdfb_device *dev); 9 int32_t (*logo_proc) (struct sprdfb_device *dev); 10 11 int32_t (*suspend) (struct sprdfb_device *dev); 12 int32_t (*resume) (struct sprdfb_device *dev); 13 int32_t (*update_clk) (struct sprdfb_device *dev); 14 15 #ifdef CONFIG_FB_ESD_SUPPORT 16 int32_t (*ESD_check) (struct sprdfb_device *dev); 17 #endif 18 19 #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT 20 int32_t (*enable_overlay) (struct sprdfb_device *dev, struct overlay_info* info, int enable); 21 int32_t (*display_overlay) (struct sprdfb_device *dev, struct overlay_display* setting); 22 #endif 23 24 #ifdef CONFIG_FB_VSYNC_SUPPORT 25 int32_t (*wait_for_vsync) (struct sprdfb_device *dev); 26 #endif 27 28 #ifdef CONFIG_FB_MMAP_CACHED 29 void (*set_vma)(struct vm_area_struct *vma); 30 #endif 31 int32_t (*is_refresh_done) (struct sprdfb_device *dev); 32 33 };
说明:
这个是展讯的LCD主控器硬件操作,该结构体在uboot阶段也使用到了。
注意区别LCD主控制器和LCD显示屏。以上两个数据结构位于: drivers/video/sprdfb/sprdfb.h
四、考察fbmem.c里面实现的 struct file_operations fb_fops 是怎么最终操作到了最底层硬件的。
1. fb_read
1 static ssize_t 2 fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 3 { 4 struct fb_info *info = file_fb_info(file); 5 6 //... 7 if (info->fbops->fb_read) 8 return info->fbops->fb_read(info, buf, count, ppos); 9 //..... 10 } 11 12 static struct fb_info *file_fb_info(struct file *file) 13 { 14 struct inode *inode = file_inode(file); 15 int fbidx = iminor(inode); 16 struct fb_info *info = registered_fb[fbidx]; 17 18 if (info != file->private_data) 19 info = NULL; 20 return info; 21 }
registered_fb[fbidx] 是一个全局的数组,其原始定义为:
1 struct fb_info *registered_fb[FB_MAX];
它会在 [email protected] 里面使用到,具体的调用:
1 sprdfb_probe -> register_framebuffer -> do_register_framebuffer -> num_registered_fb[]
那么,fb_read 在读取的时候,通过 file_fb_info() 函数获取到num_registered_fb,于是顺利的获取到了展讯的framebuffer了。
而如果展讯framebuffer驱动提供了 fb_read操作函数,也即是if (info->fbops->fb_read) 判断为真的话,那就直接调用展讯提供的fb_read接口了。
而展讯对fbops的初始化如下:
1 static void setup_fb_info(struct sprdfb_device *dev) 2 { 3 struct fb_info *fb = dev->fb; 4 fb->fbops = &sprdfb_ops; 5 //.... 6 } 7 8 static struct fb_ops sprdfb_ops = { 9 .owner = THIS_MODULE, 10 .fb_check_var = sprdfb_check_var, 11 .fb_pan_display = sprdfb_pan_display, 12 .fb_fillrect = cfb_fillrect, 13 .fb_copyarea = cfb_copyarea, 14 .fb_imageblit = cfb_imageblit, 15 .fb_ioctl = sprdfb_ioctl, 16 #ifdef CONFIG_FB_MMAP_CACHED 17 .fb_mmap = sprdfb_mmap, 18 #endif 19 };
似乎很抱歉,并没有为file_operations提供读写函数。这没关系,那就file_operations 层自己实现一把即可。但是放心的是,ioctl/mmap这些,展讯都提供了。
(2016-1-3 over)
(本篇笔记的图片来源于 《Android 深度探索卷一 HAL与驱动开发》,部分内部也是参考了该书。也可以说,是对该书lcd部分学习后的一个笔记)