1.名词解释 java
GPU:Graphic Processing Unit (图形处理器)
OpenGL:Open Graphic Library 定义了一个跨编程语言、跨平台的编程接口的规格,不一样厂商会有不一样的实现方法,它主要用于三维图象(二维的亦可)绘制。
SurfaceFlinger:Android中负责Surface之间叠加、混合操做的动态库
Skia:Android中的2D图形库
libagl:Android中经过软件方法实现的一套OpenGL动态库
libhgl:为区别libagl,自定义的一种叫法。特指GPU厂商提供的硬件实现的OpenGL
composition:特指SurfaceFlinger对各个Surface之间的叠加、混合操做
render:特指使用OpenGL动态库进行3D渲染
copybit:Android使用2D引擎来加速图形操做(主要是Surface之间的composition操做)的一种技术,对应着一个或几个动态库。
pmem:Android特有驱动,从linux内核中reserve物理连续内存,能够为2d、3d引擎、vpu等设备分配物理连续内存。
Android在启动后,会在运行时根据配置文件加载OpenGL(libagl & libhgl)的实现,若是有libhgl实现,默认使用libhgl实现,不然使用libagl实现。
OpenGL在Android中两个做用:
1. 用于Surface的composition操做。
SurfaceFlinger 会调用到OpenGL中,经过libagl或者libhgl作Surface的组合、叠加操做。
2. 用于图形图像的渲染
Android framework会对OpenGL实现进行java层次的简单封装,在java应用程序中对OpenGL的调用最终会调用到libagl或者libhgl中去。
不少第三方游戏、3D图库、某些launcher会使用OpenGL实现比较炫丽UI的特效。 node
Copybit在Android中的做用
Copybit在Android中主要用于Surface的composition操做。
Skia在Android中的做用
Skia是Android的2D图形库,用于绘制文字、几何图形、图像等。
Skia的设备后端:Raster、OpenGL、PDF
双缓冲:
在计算机上的动画与实际的动画有些不一样:实际的动画都是先画好了,播放的时候直接拿出来显示就行。计算机动画则是画一张,就拿出来一张,再画下一张,再拿 出来。若是所须要绘制的图形很简单,那么这样也没什么问题。但一旦图形比较复杂,绘制须要的时间较长,问题就会变得突出。
让咱们把计算机想象成一个画图比较快的人,假如他直接在屏幕上画图,而图形比较复杂,则有可能在他只画了某幅图的一半的时候就被观众看到。然后面虽然他把 画补全了,但观众的眼睛却又没有反应过来,还停留在原来那个残缺的画面上。也就是说,有时候观众看到完整的图象,有时却又只看到残缺的图象,这样就形成了 屏幕的闪烁。
如何解决这一问题呢?咱们设想有两块画板,画图的人在旁边画,画好之后把他手里的画板与挂在屏幕上的画板相交换。这样以来,观众就不会看到残缺的画了。这 一技术被应用到计算机图形中,称为双缓冲技术。即:在存储器(颇有多是显存)中开辟两块区域,一块做为发送到显示器的数据,一块做为绘画的区域,在适当 的时候交换它们。因为交换两块内存区域实际上只须要交换两个指针,这一方法效率很是高,因此被普遍的采用。
(以上这段是网上摘的)
------------------------------------------------------------------------------------------
LCD调光方式有:
一线脉冲计数调光
PWM调光
调试LCD须要注意的:
SCLK
SDA
CS
RESET
上面这几路线是发命令和参数用的,因此首先要调通,这几路OK后屏应该亮或花屏
PCLK
HSYNC
VSYNC
DE
这几路是刷数据用的,屏亮后显示不正常就调这几路的参数
320x480的,参数为:
PCLK 10MHz
HSYNC 30KHz(行刷新)
VSYNC 60Hz(列刷新,即一屏)
SCLK 70KHz
DE 30KHz(Data Enable RGB写数据时用到)
CS SPI写命令或参数时用,往屏上刷数据时用不到
常见的问题:
---
lk亮屏了,可是白屏,查下极性,看是否是反了
极性: lcdc侧的Pclk,Hsync,Vsync,DE是高有效,那么panel driver的
这些信号也应该是高有效,在往寄存器写参数时要核对下
---
要注意这些东西,HFP,HBP,VFP,VBP 即一般所讲的前沿,后沿屏幕图像左右抖动,不用说,确定是HFP/HBP不对。
前沿后沿就是给hsync,vsync往屏幕刷数据提供时间,能够看你的LCD IC SPEC了解。
另外关于前沿后沿(前消隐/后消隐)的概念, 感受下面这一段是比较靠谱的:
正常的TFT一行的显示周期是 前消隐+实际点输出+后消隐
HBP、HFP 表明先后消隐所须要的周期
若是前消隐设置小了,后消隐设置大了,LCD控制器的实际输出就会被当作消隐而不会实际显示出来,看到的效果就是图像左移,反之图像右移.若是先后消隐都 设置小了,理论上第二行的图像可能会被当作第一行的图像显示,形成屏幕歪斜不一样步,可是有些TFT中内部的时序电路会自动补上缺乏的时钟,因此也不必定会 看到不一样步的画面.场的消隐同理.
至于为何要消隐是为了兼容CRT显示器的显示原理,CRT显示器每个扫描行完成后,电子枪须要回扫,这段时间不能显示,因此这段时间的视频信号须要暂停一下,就是消隐.
调试步骤(这段是网上摘的):
1)调试lcd背光,背光主要分为PMIC自带的和单独的DCDC,若是为PMIC自带的背光,通常平台厂商已经作好,
直接调用接口便可,若是为单独的DCDC驱动,则须要用GPIO控制DCDC的EN端
2)确认lcd的模拟电,io电是否正常
3)根据lcd的分辨率,RGB/CPU/MIPI等不一样的接口,配置控制寄存器接口
4)根据lcd spec配置PCLK的频率,配置PCLK,VSYNC,HSYNC,DE等控制线的极性
5)使用示波器测试全部clk的波形,确认频率,极性是否符合要求
6)使用示波器测试data线,看是否有数据输出,bpp的设置是否正确
7)若是lcd须要初始化,配置spi的接口,通常分为cpu自带的spi控制器,和gpio模拟的spi。
8)根据lcd spec中的初始化代码进行lcd的初始化
9)用示波器测量lcd的spi clk及数据线,确认是否正常输出
10)正常状况下,此时lcd应该能够点亮。若是没有点亮,按照上述步骤1到9,逐项进行检查测试,重点检查第5项,clk的极性
11)若是lcd点亮,可是花屏。则须要先确认数据格式是否正确,而后确认fb里的数据是否正常,有如下几种方法确认fb里的数据
i)cat /dev/graphics/fb0 > /sdcard/fb0,而后将/sdcard/fb0 >到另外一台相同分辨率及相同格式的手机上,看图片显示是否正常
ii)使用irfanview软件显示cat /dev/graphics/fb0出来的raw数据,注意要正确设置分辨率及格式,不然显示花屏
iii)若是adb链接正常,可使用豌豆莢等软件,查看fb中的数据是否正常
经过以上三种途径,若是确认fb中的数据正常显示,则极可能为lcd初始化代码的问题,或者clk极性的问题,若是fb数据不正常,则可能为lcd控制寄存器配置不正常致使。
LCD屏的调试注意事项
1. Pix clock是否在规定的范围内。
2. Pclk是否极性正确。上升沿仍是降低沿。
3. 变频引发的闪屏问题。能够经过锁定频率来试验是不是变频引发。
要确保每一步都正确,那确定能够显示出图像来
------------------------------------------------------------------------------------------
framebuffer
咱们如今用的是Adreno系列,Adreno是什么?就至关于PC机上的显卡。显卡,也叫GPU,它的好坏会对手机的多媒体播放和3D游戏性能有着直接的影响。framebuffer即帧缓冲区,这个帧缓冲区就位于显卡的内部,帧缓冲区是显卡上固化的存储器,其中存放的是当前屏幕的内容。你要作高通平台,那么还会遇到MDP,
MDP(Mobile Display Processor), MDP是什么? 比方说你用的data format是RGB666,那么这里的data format输出是多少位是由MDP决定的,MDP1.1/MDP1.2只能输出16或18bpp,而MDP1.3能够输出24bpp。还有图形的旋转等效果,上面说的Copybit进行composition的动做我想底层就是由MDP支持的。咱们也能够选择是GPU来composition仍是MDP 来composition。 那么GPU和MDP是什么关系?GPU是显卡,GPU处理要显示的数据,即把矢量转换为bit,MDP负责把数据转成接口信号送给相关的panel。在MDP里有DMA,DMA会把framebuffer里的内容放到panel上。后面分析代码会看到,咱们把数据写到MDP就无论了,它内部应该是把数据再调整,而后再写到各个接口(MIPI/LCDC)链接的panel上。这些东西都是SoC,因此你在板子上是看不到的。
显卡对CPU来讲是外设,外设会经过I/O接口链接到I/O总线上,I/O总线再链接到CPU。 I/O接口包含多个I/O端口。每一个链接到I/O总线上的设备都有本身的I/O地址集,一般称为I/O端口(LKD part13 原话)。为I/O编程提供统一的方法,但又不牺牲性能,因此设计者们把一组I/O端口(即一地址集)组织成一组专用的寄存器。这也就是咱们一般所说的I/O端口即寄存器的由来。因此每一个外设都是经过读写其寄存器来控制的。
I/O接口是处于一组I/O端口和对应的设备控制器之间的一种硬件电路。它起翻译器的做用,即把I/O端口中的值转换成设备所须要的命令和数据。还能够经过一条IRQ线把这种电路链接到可编程中断控制器上,以使它表明相应的设备发出中断请求。
硬件组织上了解后咱们看软件上怎么处理。咱们要作的工做是,检测哪些I/O端口已经分配给I/O设备。 一般来说,I/O设备驱动程序为了探测硬件设备,须要盲目地向某一I/O端口写入数据,可是,若是其余硬件设备已经使用了这个端口,那么系统就会崩溃。因此,linux的设计者们便想出用"资源"来记录分配给每一个硬件设备的I/O端口。
资源(resource)被互斥地分配给设备驱动程序,一个资源表示I/O端口地址的一个范围。
我为何会说I/O呢,由于帧缓冲区就是I/O内存,和I/O有关,抛砖引玉。关于I/O端口和I/O内存的区别但是有学问的,你知道吗?不知道是哪位大侠创做或整理的文章,粉好,你要是知道这二者的区别,那就不用看了,你要是不知道,那不妨看一看吧:http://blog.csdn.net/insoonior/article/details/8011192#t0
说到这里咱们须要看代码了:
static struct resource msm_fb_resources[] = {
{
.flags = IORESOURCE_DMA,
}
};
static struct platform_device msm_fb_device = {
.name = "msm_fb",
.id = 0,
.num_resources = ARRAY_SIZE(msm_fb_resources),
.resource = msm_fb_resources,
.dev = {
.platform_data = &msm_fb_pdata,
}
};
void __init msm_msm7627a_allocate_memory_regions(void)
{
addr = alloc_bootmem_align(fb_size, 0x1000); //这就是framebuffer的物理地址,在framebuffer的驱动中会用到
msm_fb_resources[0].start = __pa(addr);
msm_fb_resources[0].end = msm_fb_resources[0].start + fb_size - 1;
}
系统启动并初始化时会经过如下调用,把device 的 resource 加入到resource树中:
# TO tag FROM line in file/text
1 1 platform_add_devices 1455 arch/arm/mach-msm/board-qrd7627a.c
2 1 platform_device_register 114 drivers/base/platform.c
3 1 platform_device_add 337 drivers/base/platform.c
4 1 insert_resource 275 drivers/base/platform.c
5 1 insert_resource_conflict 667 conflict = insert_resource_conflict(parent, new);
6 1 __insert_resource 651 conflict = __insert_resource(parent, new);
有了以上的资源后就清楚了:
static int msm_fb_probe(struct platform_device *pdev)
{
fbram_size =
pdev->resource[0].end - pdev->resource[0].start + 1;
fbram_phys = (char *)pdev->resource[0].start;
fbram = __va(fbram_phys);
}
static int msm_fb_register(struct msm_fb_data_type *mfd)
{
struct fb_fix_screeninfo *fix;
struct fb_var_screeninfo *var; //主要是根据panel的信息初始化这两个数据结构
fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram;
fbram += fbram_offset;
fbram_phys += fbram_offset;
fbram_size -= fbram_offset;
fbi->screen_base = fbram;
fbi->fix.smem_start = (unsigned long)fbram_phys;
}
smem_start在后面分析mdp_lcdc_update时会用到.
------------------------------------------------------------------------------------------
咱们看下数据到framebuffer后是怎么显示到屏幕上的。先从HAL层分析数据的走向,再往上的逻辑待定。
android/hardware/msm7k/libgralloc-qsd8k/ 这是display subsys的HAL层
// 上层调用这句ioctl把数据推给driver,放到framebuffer里
ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info)
// 上层经过这句能够获知屏幕的大小
ioctl(fd, FBIOGET_VSCREENINFO, &info)
ioctl一路调用下来会到
drivers/video/fbmem.c
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
case FBIOPUT_VSCREENINFO:
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
if (!lock_fb_info(info))
return -ENODEV;
console_lock();
info->flags |= FBINFO_MISC_USEREVENT;
ret = fb_set_var(info, &var); //struct fb_info info, struct fb_var_screeninfo var
info->flags &= ~FBINFO_MISC_USEREVENT;
console_unlock();
unlock_fb_info(info);
if (!ret && copy_to_user(argp, &var, sizeof(var)))
ret = -EFAULT;
break;
int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
fb_pan_display(info, &info->var);
int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
if ((err = info->fbops->fb_pan_display(var, info)))
return err;
drivers/video/msm/msm_fb.c
static int msm_fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */ /* 若是未上电,给panel上电 */
mdp_set_dma_pan_info(info, NULL, TRUE);
if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
pr_err("%s: can't turn on display!\n", __func__);
return -EINVAL;
}
}
mdp_dma_pan_update(info); /* 这句是把数据经过MDP更新到panel */
drivers/video/msm/mdp_dma.c
void mdp_dma_pan_update(struct fb_info *info)
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
mfd->dma_fnc(mfd);
drivers/video/msm/mdp.c
static int mdp_probe(struct platform_device *pdev)
case LCDC_PANEL:
mfd->dma_fnc = mdp_lcdc_update;
drivers/video/msm/mdp_dma_lcdc.c
void mdp_lcdc_update(struct msm_fb_data_type *mfd)
struct fb_info *fbi = mfd->fbi;
uint8 *buf;
int bpp;
bpp = fbi->var.bits_per_pixel / 8;
buf = (uint8 *) fbi->fix.smem_start;
buf += calc_fb_offset(mfd, fbi, bpp);
MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf); /* 最终会调到这句,上层抛过来的一堆数据存在buf指针指向的区域,而后把这个地址写到MDP BASE,而后MDP再写到panel上 */
drivers/video/msm/mdp.h
#define MDP_OUTP(addr, data) outpdw((addr), (data))
drivers/video/msm/msm_fb_def.h
#define outpdw(port, val) writel(val, port)
------------------------------------------------------------------------------------------ framebuffer还有好多方方面面没提到的,网上一搜一大把,就不提了。我只写些本身认为须要整理的东西,算是作个整理吧。 初始化过程:fbmem-->msm_fb-->mdp-->lcdc-->panel 初始化好以后,panel device的注册过程:panel-->lcdc-->mdp-->msm_fb-->fbmem on/off: msm_fb-->mdp-->lcdc-->panel msm_fb的pdata是mdp mdp的pdata是lcdc lcdc的pdata是panel panel driver probe里初始化了一个lcdc_dev,在lcdc driver probe里初始化了一个mdp dev,在mdp driver probe里初始化了一个msm_fb dev。最后调用msm_fb_register-->register_framebuffer最终和fbmem关联上。 遗留问题: addr = alloc_bootmem_align(fb_size, 0x1000)分配的内存怎么与帧缓冲区这个IO内存对应上的? 在手机的GPU上,可能framebuffer并未在GPU里,就是一块从物理内存上分配获得的。 gpu确实有块缓存,adreno 205配置了256KB图形缓存(GMEM)专用于颜色缓冲,Z缓冲和模板缓冲等。显然这个缓存不是framebuffer,不然过小了。 因此这个问题也不攻自破了,在手机设备上也许根本不存在帧缓冲区这个IO内存。