1.名词解释

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的特效。

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内存。

更多相关文章

  1. 2.1 五种数据存储与共享方式的比较、
  2. Android应用开发揭秘(笔记) 第六章 Android数据存储
  3. 我的android 第12天 - 嵌入式关系型SQLite数据库存储数据
  4. android 数据双向绑定学习笔记
  5. Android用GSon处理Json数据
  6. android 3d游戏研究(二)(边学边写,多谢高手指正,鞠躬) :数据库
  7. 基于FFmpeg的音频编码(PCM数据编码成AAC android)
  8. Android利用JDBC连接服务器数据库
  9. Android 本地数据存储 API(二)

随机推荐

  1. python 基础语法1
  2. mfs changelog日志解析
  3. crmeb 多商户系统 首页精品推荐 推荐单品
  4. crmeb 多商户系统 首页精品推荐 推荐单品
  5. 2021年北京高新技术企业认定条件及时间
  6. 使用频率最高的21条Linux命令,让你多出一
  7. 《都挺好》弹幕比剧还精彩?394452条弹幕数
  8. 北京高新技术企业认定最新条件及相关要求
  9. 淘宝爬虫实战(附代码和数据集)——今天你防
  10. 测试 python的魔术方法大全!记得收藏!!!