sc7731 Android 5.1 LCD驱动简明笔记之二

时间:2024-03-27 18:34:20

此篇笔记基于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 显示出一副图片,是从上到下、从左到右刷出来的。其显示过程,通过以下时序图进行描述。
sc7731 Android 5.1 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 驱动软件相关的
何谓帧缓冲?何谓缓冲?
简而言之,缓冲就是一个buffer,一个暂存数据的内存buffer区域;那么帧缓冲,就是一个用户暂存一帧图片的buffer。它不神秘,操作起来非常方便。
当用户需要显示一幅图片时,仅仅需要把该幅图片的具体内容放入该缓冲区即可。操作系统会负责将该幅图片搬运到lcd屏幕上。
framebuffer的概念来源于Linux。在linux中,framebuffer驱动设备被划分为字符设备,设备名: /dev/fbN (N取值范围0~31)

一、Framebuffer测试程序(关键代码)

 #include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h> int main()
{
int fbfd = ;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = ;
char *fbp = ;
int x = , y = ;
long int location = ; // 以读写方式打开fb0
fbfd = open("/dev/fb0", O_RDWR);
if (!fbfd) {
printf("Error: cannot open framebuffer device.\n");
exit();
}
printf("The framebuffer device was opened successfully.\n"); // 获取硬件屏幕信息
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
printf("Error reading fixed information.\n");
exit();
} // 获取用户可修改屏幕参数信息
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
printf("Error reading variable information.\n");
exit();
} printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel ); // 计算framebuffer空间大小,也就是可装下一幅图片的大小
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / ; // 内存映射,把一块内存区域从内核映射到用户空间,供用户直接操作
fbp = (char *)mmap(, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
fbfd, );
if ((int)fbp == -) {
printf("Error: failed to map framebuffer device to memory.\n");
exit();
}
printf("The framebuffer device was mapped to memory successfully.\n"); x = ; y = ; // Where we are going to put the pixel // 绘制矩形, 200x200像素的
for ( y = ; y < ; y++ )
for ( x = ; x < ; x++ ) { location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/) +
(y+vinfo.yoffset) * finfo.line_length; if ( vinfo.bits_per_pixel == ) {
*(fbp + location) = ; // Some blue
*(fbp + location + ) = +(x-)/; // A little green
*(fbp + location + ) = -(y-)/; // A lot of red
*(fbp + location + ) = ; // No transparency
} else { //assume 16bpp
int b = ;
int g = (x-)/; // A little green
int r = -(y-)/; // A lot of red
unsigned short int t = r<< | g << | b;
*((unsigned short int*)(fbp + location)) = t;
} }
//解除映射
munmap(fbp, screensize);
//设备关闭
close(fbfd);
return ;
}

程序来源: http://blog.chinaunix.net/uid-22666248-id-283476.html

二、Framebuffer驱动框架图

sc7731 Android 5.1 LCD驱动简明笔记之二

由图可知,framebuffer没有直接向用户空间提供文件操作接口,而是通过一个调用的关系,让file_operations结构体帮忙向用户空间提供了文件操作接口。

三、展讯framebuffer关键数据结构

1、struct fb_info{};

 struct fb_info {

     struct fb_var_screeninfo var;    /* Current var */ //LCD 用户可变参数
struct fb_fix_screeninfo fix; /* Current fix */ //LCD 固定参数 #ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev; //背光设备
#endif struct fb_ops *fbops; //对底层硬件操作函数指针 char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ //LCD I/O映射虚拟内存大小 u32 state; /* Hardware state i.e suspend */ //LCD 挂起或者恢复状态 };

说明:
仅仅保留关键字段。位于: include/linux/fb.h

2、struct fb_var_screeninfo{};

 struct fb_var_screeninfo {
__u32 xres; /* visible resolution */ //可见屏幕一行有多少个像素点
__u32 yres; //可见屏幕一列有多少个像素点
__u32 xres_virtual; /* virtual resolution */ //虚拟屏幕一行有多少个像素点
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */ //虚拟到可见之间的行偏移
__u32 yoffset; /* resolution */ __u32 bits_per_pixel; /* guess what */ //每个像素占的bit,除以8即是字节
__u32 grayscale; /* 0 = color, 1 = grayscale, */ //非0时,指定的灰度
/* >1 = FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */ //R
struct fb_bitfield green; /* else only length is significant */ //G
struct fb_bitfield blue; //B
struct fb_bitfield transp; /* transparency */ //透明度 __u32 nonstd; /* != 0 Non standard pixel format */ __u32 activate; /* see FB_ACTIVATE_* */ __u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */ //像素时钟
__u32 left_margin; /* time from sync to picture */ //行切换,从同步到绘图之间的延迟。也就是上面硬件部分介绍的 HFPD,以下参数意义类推
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[]; /* Reserved for future compatibility */
};

说明:
该结构详细的记录了用户可修改的显示参数。位于include/uapi/linux/fb.h

3、struct fb_fix_screeninfo{};

 struct fb_fix_screeninfo {
char id[]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */ //fb缓存开始位置
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */ //fb类型
__u32 type_aux; /* Interleave for interleaved Planes */
__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; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */ //内存I/O映射的开始位置
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */ //I/O映射的长度
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[]; /* Reserved for future compatibility */
};

说明 :
该结构详细的说明了用户不可更改的参数。位于include/uapi/linux/fb.h

4、struct fb_ops{};

 struct fb_ops {
/* open/release and usage marking */
struct module *owner;
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user); /* For framebuffers with strange non linear layouts or that do not
* work with normal memory mapped access
*/
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); /* checks var and eventually tweaks it to something supported,
* DO NOT MODIFY PAR */
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); /* set the video mode according to info->var */
int (*fb_set_par)(struct fb_info *info); /* set color register */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info); /* set color registers in batch */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); /* blank display */
int (*fb_blank)(int blank, struct fb_info *info); /* pan display */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); /* Draws a rectangle */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* Copy data from area to another */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* Draws a image to the display */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); /* Draws cursor */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); /* Rotates the display */
void (*fb_rotate)(struct fb_info *info, int angle); /* wait for blit idle, optional */
int (*fb_sync)(struct fb_info *info); /* perform fb specific ioctl (optional) */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg); /* Handle 32bit compat ioctl (optional) */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
unsigned long arg); /* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); /* get capability given var */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var); /* teardown any resources to do with this framebuffer */
void (*fb_destroy)(struct fb_info *info); /* called at KDB enter and leave time to prepare the console */
int (*fb_debug_enter)(struct fb_info *info);
int (*fb_debug_leave)(struct fb_info *info);
};

说明:
该结构体详细说明了对底层硬件操作的方法。位于: include/linux/fb.h

以上几个结构体是,几乎所有的framebuffer都会有的。针对于展讯的实现方式,它还多了以下两个:

5、struct sprdfb_device{};

  struct sprdfb_device {
struct fb_info *fb; uint16_t enable;
uint16_t dev_id; /*main_lcd, sub_lcd*/ uint32_t bpp; /*input bit per pixel*/ uint16_t panel_ready; /*panel has been inited by uboot*/
uint16_t panel_if_type; /*panel IF*/ union{
uint32_t mcu_timing[MCU_LCD_TIMING_KIND_MAX];
uint32_t rgb_timing[RGB_LCD_TIMING_KIND_MAX];
}panel_timing; struct panel_spec *panel;
struct display_ctrl *ctrl; #ifdef CONFIG_OF
struct device *of_dev;
#endif uint32_t dpi_clock;
struct semaphore refresh_lock;
uint64_t frame_count; #ifdef CONFIG_FB_ESD_SUPPORT
struct delayed_work ESD_work;
// struct semaphore ESD_lock;
uint32_t ESD_timeout_val;
bool ESD_work_start;
/*for debug only*/
uint32_t check_esd_time;
uint32_t panel_reset_time;
uint32_t reset_dsi_time;
#ifdef FB_CHECK_ESD_BY_TE_SUPPORT
uint32_t esd_te_waiter;
wait_queue_head_t esd_te_queue;
uint32_t esd_te_done;
#endif
#endif uint32_t logo_buffer_addr_v;
uint32_t logo_buffer_size; #ifdef CONFIG_HAS_EARLYSUSPEND
struct early_suspend early_suspend;
#endif void *priv1;
};

说明:
这个是展讯根据自己的平台特点,又封装了一层。如果用面向对象的观点看的话,它继承了 struct fb_info 结构体.

6、struct display_ctrl{};

 struct display_ctrl {
const char *name; int32_t (*early_init) (struct sprdfb_device *dev);
int32_t (*init) (struct sprdfb_device *dev);
int32_t (*uninit) (struct sprdfb_device *dev); int32_t (*refresh) (struct sprdfb_device *dev);
int32_t (*logo_proc) (struct sprdfb_device *dev); int32_t (*suspend) (struct sprdfb_device *dev);
int32_t (*resume) (struct sprdfb_device *dev);
int32_t (*update_clk) (struct sprdfb_device *dev); #ifdef CONFIG_FB_ESD_SUPPORT
int32_t (*ESD_check) (struct sprdfb_device *dev);
#endif #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT
int32_t (*enable_overlay) (struct sprdfb_device *dev, struct overlay_info* info, int enable);
int32_t (*display_overlay) (struct sprdfb_device *dev, struct overlay_display* setting);
#endif #ifdef CONFIG_FB_VSYNC_SUPPORT
int32_t (*wait_for_vsync) (struct sprdfb_device *dev);
#endif #ifdef CONFIG_FB_MMAP_CACHED
void (*set_vma)(struct vm_area_struct *vma);
#endif
int32_t (*is_refresh_done) (struct sprdfb_device *dev); };

说明:
这个是展讯的LCD主控器硬件操作,该结构体在uboot阶段也使用到了。
注意区别LCD主控制器和LCD显示屏。以上两个数据结构位于: drivers/video/sprdfb/sprdfb.h

 四、考察fbmem.c里面实现的 struct file_operations fb_fops 是怎么最终操作到了最底层硬件的。

1. fb_read

  static ssize_t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct fb_info *info = file_fb_info(file); //...
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
//.....
} static struct fb_info *file_fb_info(struct file *file)
{
struct inode *inode = file_inode(file);
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx]; if (info != file->private_data)
info = NULL;
return info;
}

registered_fb[fbidx] 是一个全局的数组,其原始定义为:

 struct fb_info *registered_fb[FB_MAX];

它会在 register_framebuffer@fbmem.c 里面使用到,具体的调用:

 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的初始化如下:

 static void setup_fb_info(struct sprdfb_device *dev)
{
struct fb_info *fb = dev->fb;
fb->fbops = &sprdfb_ops;
//....
} static struct fb_ops sprdfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = sprdfb_check_var,
.fb_pan_display = sprdfb_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_ioctl = sprdfb_ioctl,
#ifdef CONFIG_FB_MMAP_CACHED
.fb_mmap = sprdfb_mmap,
#endif
};

似乎很抱歉,并没有为file_operations提供读写函数。这没关系,那就file_operations 层自己实现一把即可。但是放心的是,ioctl/mmap这些,展讯都提供了。

(over)

2016-1-3

(本篇笔记的图片来源于 《Android 深度探索卷一 HAL与驱动开发》,部分内部也是参考了该书。也可以说,是对该书lcd部分学习后的一个笔记)