android lcd工作原理,Android display架构分析(二)
Display接口介绍
在Android平台下,应用程序面对的显示部分的接口就是HAL,参考copybit.c,具体接口如下介绍:
open_copybit
初始化相关变量,并调用open("/dev/graphics/fb0", O_RDWR,
0);打开fb设备。
set_parameter_copybit
设置各种操作参数,如rotate、alpha、dither等。
stretch_copybit
Copy一块数据(Rectangle)到显存,然后并命令msm_fb进行显示。
close_copybit
调用close(ctx->mFD);关闭fb设备。
Note:另外,应用程序在使用上面接口之前,需要调用mapFrameBuffer接口(EGLDisplaySurface.cpp),其功能如下:
1、初始化显示相关参数,并设置到底层。
2、映射出显存的虚拟地址。
2、Kernel
display接口
Kernel部分显示的接口全部都在fbmem.c中,这里详细介绍一下:
fb_open
打开Linux下fb设备。
fb_read/fb_write
读写显存中的数据
fb_ioctl
对显示设备的命令操作。如get或set一些显示参数、通知底层进行刷屏等。
在典型应用中,画屏的一般步骤如下:
1.
打开/dev/fb设备文件。
2.
用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
3.
将屏幕缓冲区映射到用户空间。
4.
映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。
典型程序段如下:
#include
int main()
{
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
fbfd = open("/dev/fb0", O_RDWR);
ioctl(fbfd, FBIOGET_FSCREENINFO,
&finfo); ioctl(fbfd, FBIOGET_VSCREENINFO,
&vinfo);
screensize = vinfo.xres * vinfo.yres *
vinfo.bits_per_pixel / 8;
fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED,
fbfd, 0);
...
}
在不同应用程序中,上层的调用会有所不同,比如Andriod下会选择应用程序跳过Linux
fb操作层,直接操作显卡驱动层,称之为BLT accelerator。
下面看一下Android平台下画屏的操作流程。
1、通过mapFrameBuffer直接把用户空间的数据映射到显存中。
2、调用HAL中的stretch函数直接命令MSM设备提取显存数据然后送入MDP
PPP进行处理并经MDDI接口送到外围LCD组件。
具体的函数调用流程如下:
copybit_open();//打开BlitEngine,同时也打开fb设备
mapFrameBuffer();//设置显示参数,同时得到显存虚拟地址
copybit->stretch(copybit,
&dst, &src,
&sdrect, &sdrect,
&it);//通知底层去刷屏
接下的流程是:
stretch_copybit->
msm_copybit->
fb_ioctl()->msm_fb_ioctl(MSMFB_BLIT)->
msmfb_blit->
mdp_blit->
mdp_ppp_blit->
mdp_start_ppp->MDP&MDDI HW
operation
Note:
本部分介绍的完全是用户空间显示部分的架构,与kernel并没有直接的联系,主要是JNI以下到HAL以上的部分。
1、Surface
manager(surface
flinger)简介
Surface
manager是用户空间中framework下libraries中负责显示相关的一个模块。如下:
当系统同时执行多个应用程序时,Surface Manager会负责管理显示与存取操作间的互动,另外也负责将2D绘图与3D绘图进行显示上的合成。
surface manager 可以准备一块
surface(可以看作一个layer),把
surface 的 fd (一块内存) 传给一个
app,让 app 可以在上面作画。 典型应用如下:
Android中的图形系统采用Client/Server架构,如下:
Client端:应用程序相关部分。代码分为两部分,一部分是由Java提供的供应用使用的api,另一部分则是由c++写成的底层实现。
Server端:即SurfaceFlinger,负责合成并送入buffer显示。其主要由c++代码编写而成。
Client和Server之前通过Binder的IPC方式进行通信,总体结构图如下:
如上图所示,Surface的client部分其实是提供给各应用程序进行画图操作的一个桥梁,该桥梁通过binder通向server端的Surfaceflinger,Surfaceflinger负责合成各个surface,然后把buffer传送到framebuffer端进行底层显示。其中每个surface对应2个buffer,一个front
buffer, 一个back buffer,更新时,数据更新在back
buffer上,需要显示时,则将back buffer和front
buffer互换。
下一部分我们重点研究一下Surfaceflinger。
根据前面的介绍,surfaceflinger作为一个server
process,上层的应用程序(作为client)通过Binder方式与其进行通信。Surfaceflinger作为一个thread,这里把它分为3个部分,如下:
1、 Thread本身处理部分,包括初始化以及thread
loop。
2、 Binder部分,负责接收上层应用的各个设置和命令,并反馈状态标志给上层。
3、与底层的交互,负责调用底层接口(HAL)。
结构图如下:
注释:
a、 Binder接收到应用程序的命令(如创建surface、设置参数等),传递给flinger。
b、 Flinger完成对应命令后将相关结果状态反馈给上层。
c、在处理上层命令过程中,根据需要设置event(主要和显示有关),通知Thread
Loop进行处理。
d、 Flinger根据上层命令通知底层进行处理(主要是设置一些参数,Layer、position等)
e、 Thread
Loop中进行surface的合成并通知底层进行显示(Post
buffer)。
f、 DisplayHardware层根据flinger命令调用HAL进行HW的操作。
下面来具体分析一些SurfaceFlinger中重要的处理函数以及surface、Layer的属性
1)、readToRun
SurfaceFlinger thread的初始化函数,主要任务是分配内存和设置底层接口(EGL&HAL)。
status_t
SurfaceFlinger::readyToRun()
{
…
…
mServerHeap = new
MemoryDealer(4096, MemoryDealer::READ_ONLY);//为IPC分配共享内存
…
mSurfaceHeapManager = new
SurfaceHeapManager(this, 8 <<
20);//为flinger分配heap,大小为8M,存放具体的显示数据
{
// initialize the main
display
GraphicPlane&
plane(graphicPlane(dpy));
DisplayHardware* const hw = new DisplayHardware(this,
dpy);
plane.setDisplayHardware(hw);//保存显示接口
}
//获取显示相关参数
const GraphicPlane&
plane(graphicPlane(dpy));
const DisplayHardware& hw =
plane.displayHardware();
const uint32_t w = hw.getWidth();
const uint32_t h = hw.getHeight();
const uint32_t f = hw.getFormat();
…
…
// Initialize OpenGL|ES
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
…
…
}
2)、ThreadLoop
Surfaceflinger的loop函数,主要是等待其他接口发送的event,进行显示数据的合成以及显示。
bool
SurfaceFlinger::threadLoop()
{
waitForEvent();//等待其他接口的signal event
…
…
// post surfaces (if needed)
handlePageFlip();//处理翻页机制
const DisplayHardware&
hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw()))
{
// repaint the framebuffer (if needed)
handleRepaint();//合并所有layer并填充到buffer中去
…
…
postFramebuffer();//互换front
buffer和back
buffer,调用EGL接口进行显示
}
…
…
}
3)、createSurface
提供给应用程序的主要接口,该接口可以创建一个surface,底层会根据参数创建layer以及分配内存,surface相关参数会反馈给上层
sp
SurfaceFlinger::createSurface(ClientID clientId, int
pid,
ISurfaceFlingerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
…
…
int32_t id = c->generateId(pid);
if (uint32_t(id) >= NUM_LAYERS_MAX) //NUM_LAYERS_MAX=31
{
LOGE("createSurface() failed, generateId = %d", id);
return
}
…
layer = createNormalSurfaceLocked(c, d, id, w,
h, format, flags);//创建layer,根据参数(宽高格式)分配内存(共2个buffer:front/back
buffer)
if (layer)
{
setTransactionFlags(eTransactionNeeded);
surfaceHandle = layer->getSurface();//创建surface
if (surfaceHandle != 0)
surfaceHandle->getSurfaceData(params);//创建的surface参数反馈给应用层
}
}4)、setClientState
处理上层的各个命令,并根据flag设置event通知Threadloop进行处理
status_t
SurfaceFlinger::setClientState(
ClientID cid,
int32_t count,
const layer_state_t* states)
{
Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
cid <<= 16;
for (int i=0 ; i
{
const layer_state_t& s =
states[i];
LayerBaseClient* layer = getLayerUser_l(s.surface |
cid);
if (layer)
{
const uint32_t what = s.what;
//
检测应用层是否设置各个标志,如果有则通知底层完成对应操作,并通知ThreadLoop做对应的处理
if
(what & eDestroyed) //删除该层Layer
{
if (removeLayer_l(layer) == NO_ERROR)
{
flags |= eTransactionNeeded;
continue;
}
}
if (what & ePositionChanged) //显示位置变化
{
if (layer->setPosition(s.x, s.y))
flags |= eTraversalNeeded;
}
if (what & eLayerChanged) //Layer改变
{
if (layer->setLayer(s.z))
{
mCurrentState.layersSortedByZ.reorder(
layer,
&Layer::compareCurrentStateZ);
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
if (what & eSizeChanged)
{
if (layer->setSize(s.w, s.h))//设置宽高变化
flags |= eTraversalNeeded;
}
if (what & eAlphaChanged) {//设置Alpha效果
if
(layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
flags
|= eTraversalNeeded;
}
if (what & eMatrixChanged) {//矩阵参数变化
if (layer->setMatrix(s.matrix))
flags |= eTraversalNeeded;
}
if (what & eTransparentRegionChanged) {//显示区域变化
if
(layer->setTransparentRegionHint(s.transparentRegion))
flags |= eTraversalNeeded;
}
if (what & eVisibilityChanged) {//是否显示
if (layer->setFlags(s.flags,
s.mask))
flags |= eTraversalNeeded;
}
}
}
if (flags)
{
setTransactionFlags(flags);//通过signal通知ThreadLoop
}
return NO_ERROR;
}
5)、composeSurfaces
该接口在Threadloop中被调用,负责将所有存在的surface进行合并,OpenGl模块负责这个部分。
6)、postFramebuffer
该接口在Threadloop中被调用,负责将合成好的数据(存于back
buffer中)推入在front
buffer中,然后调用HAL接口命令底层显示。
7)、从3中可知,上层每创建一个surface的时候,底层都会同时创建一个layer,下面看一下surface及layer的相关属性。
Note:code中相关结构体太大,就不全部罗列出来了
A、Surface相关属性(详细参考文件surface.h)
a1:SurfaceID:根据此ID把相关surface和layer对应起来
a2:SurfaceInfo
包括宽高格式等信息
a3:2个buffer指针、buffer索引等信息
B、Layer相关属性(详细参考文件layer.h/layerbase.h/layerbitmap.h)
包括Layer的ID、宽高、位置、layer、alpha指、前后buffer地址及索引、layer的状态信息(如eFlipRequested、eBusy、eLocked等)
参考上面linux下fb设备的软件架构,可以知道,要加入一个新的MDDI
接口的LCM,Driver的工作就是要提供自己的mddi_xxxx.c(在这次porting的过程中,为了节省时间,我们直接修改了mddi_toshiba.c),并且完成和这个lcd相关的HWr的初始化。主要的工作包括:
A、初始化和LCD /
LCD背光相关的IO以及电源;
B、编写初始化函数
。主要是初始化LCD控制器,这个一般LCD厂商会提供;然后分配显存,这个高通release过来的code已经包含这个动作了,最后是初始化一个fb_info的结构体,在这里主要是把LCD的一些信息登记进来。
C、把LCD的设备以及驱动注册到系统中去。(这里因为是替换现有的驱动,所以相关修改的部分不多。)
上述B、C部分代码请参考kernel\drivers\video\msm\mddi_toshiba.c。
更改一些GPIO的配置以及一些电源的电平配置;然后通过实际测量,确保一下信号正常:
A、供给LCD以及MDDI Bridge的电源;
B、MDDI Bridge以及LCD reset信号;
C、控制背光IC的GPIO工作正常(背光不打开,无法调试LCD)。
1.2.2Porting
LCD初始化序列
LCD
init的code以及外围MDDI Bridge的初始化code,都可以之前Boston Windows Mobile系统的code base中获得;把这部分code移植到mddi_Toshiba.c中,并更改相应的图像格式、分辨率等配置,编译通过。LCD初始化部分就算基本完成。
1.2.3LCD初始化过程的调试
由于硬件在之前Boston
load是可以工作的,可以认为硬件连接等没有问题,所以只需关注软件部分就行。
Display部分软件调试过程如下:
A、 开机后,量一下GPIO是否为code中配置预期的状态(可确保code中的
GPIO接口工作正常);
B、 量一下各个电源是否都处于Code中定义的电平值。这些都OK后,背光
是会亮的(背光的控制比较简单,一个GPIO即可);
C、 这个时候如果LCD以及MDDI Bridge有被正常初始化的话,屏幕上是会
看出来的。反之,如果屏幕没有显示,需要用JTAG跟一下mddi_Toshiba.c中的初始化函数是否在开机的时候有被调用过。
目前版本中,是根据外围MDDI Bridge中读到的的厂商号来决定加载哪个驱动模块的。在本次调试中,bootloader中可以正确读到厂商号,所以bootloader中对于LCD的初始化是有做的,所以屏幕看到的状态就是LCD初始化后的样子(花屏) 但Kernel起来后,并没有其他显示,用JTAG跟了后发现,Kernel中MODULE INIT中读不到正确的厂商号,所以说后面的driver没有被加载。接着发现如果在bootloader中如果不做MDDI Bridge的初始化,的话后面的MODULE INIT就可正常运行,该问题目前还没有澄清(现在暂时先把bootloader中的init
disable掉)。
初始化正常后,屏幕会显示UI的相关画面,但明显颜色、位置都不对。
这个可能是数据类型配置不对导致的,即MDP输出的类型、MDDI配置的类型以LCD接收的类型不匹配导致,也有可能是RGB的顺序不对导致(可配置成BGR)。经过调试后,把MDP端输出的格式配置成RGB565,同时外围MDDI
Bridge以及LCD的input格式也配置成RGB565,这时显示色彩正常了。
如果位置或者方向不对,比如说上下或是左右颠倒,可以更改LCD的配置中的扫描方向即可。
后续发现一个问题,播放video的时候颜色都是黑白的。
这个问题很容易让人误解,按照正常的理解,video
decode出来的数据为YCbCr,Y为亮度信号,CbCr为色差信号,如果只有Y信号的话颜色应该就是黑白的。所以有2个怀疑点,一个是decode出来的数据有误,另一个是MDDI
Bridge误把输入的YcbCr信号当作RGB信号进行出来,这个也是有可能的。但很快第二个怀疑点被排除了(因为单更改MDDI
input格式后还是不能解决问题)。
后来又详细的看了显示部分的代码,并用JTAG追踪video播放的时候用的显示接口,发现目前所有的显示接口输出的格式都是RGB格式,也就是说在通过MDP之前YcbCr已经被转化过;而MDP里的转换功能并没有使用,MDP只是被当作一个DMA完成数据的直接传输,文档中叫做Bypasse。
YcbCr到RGB的转换是由Android的lib来完成。发了个SR给高通,高通的回复也确认了,在6.3.50中,Android上层缺少这个lib(copybit.default.so),6.3.60之后的版本经解决了这个问题。
android lcd工作原理,Android display架构分析(二)相关推荐
- android camera 工作原理,Android Camera原理之openCamera模块(一)
我们平时开发,知道怎么调度api,怎么调起camera,怎么调用camera的实例来操作camera就可以了,但是这些调度的背后都做了什么事情,我们可能不太清楚,本文打算从openCamera这个调用 ...
- Android display架构分析-SW架构分析(1-8)
参考: Android display架构分析二-SW架构分析 Android display架构分析三-Kernel Space Display架构介绍 Android display架构分析四-m ...
- 高通Android display架构分析
目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...
- 【Camera基础(一)】Camera摄像头工作原理及整机架构
Camera摄像头工作原理及整机架构 一.摄像头的基本工作原理 二.编码 三.编码的目的 四.传输 五.Android系统的camera架构 一.摄像头的基本工作原理 如图所示,一个景象的反射光被镜头 ...
- Android Q 10.1 KeyMaster源码分析(二) - 各家方案的实现
写在之前 这两篇文章是我2021年3月初看KeyMaster的笔记,本来打算等分析完KeyMaster和KeyStore以后再一起做成一系列贴出来,后来KeyStore的分析中断了,这一系列的文章就变 ...
- Xen 工作原理与体系架构
Xen 工作原理与体系架构 XEN工作原理 Xen通过hypervisor软件层来访问物理硬件,实现在一台单独的计算机上运行多个各自独立彼此隔离的子操作系统.hypervisor扮演着类似交通警察的角 ...
- Android ListView工作原理完全解析,带你从源码的角度彻底理解
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44996879 在Android所有常用的原生控件当中,用法最复杂的应该就是List ...
- android lint工作机制,Android架构
MVC mvc model view controller 模式视图控制器 M: 业务逻辑处理 V:处理数据显示的部分 C:Activity处理用户交互的问题,中间桥梁的作用,解耦的作用. 特点: 耦 ...
- android电容触摸屏的驱动及其上层工作原理,android 电容屏(一):电容屏基本原理篇...
关键词:android 电容屏 tp ITO 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 平台:S5PV310(samsung exynos ...
- Android中AMS工作原理,Android AMS启动详解
启动 在Android系统启动流程中中我们提到过,AMS是在system_service中启动的, //frameworks/base/services/java/corri/android/serv ...
最新文章
- MSSQL差异备份取系统权限
- Linux 磁盘管理 二(Raid、LVM、Quota)
- OpenCV学习——轮廓检测
- P6466-分散层叠算法(Fractional Cascading)【模板】
- WIN10系统 截图或者某些程序时屏幕会自动放大怎么办
- [Oracle]如何查看一个数据文件是否是自动扩展
- java封装数组_Java封装数组之动态数组实现方法详解
- 单片机定时器中断原理
- 课时4:改进我们的小游戏
- 微服务_SpringCloud微服务架构实战:高并发微服务架构设计
- linux命令界面输入不了密码,如何在 Linux 中不输入密码运行 sudo 命令
- 程序员书单_java专项进阶篇
- 早先离开这个公司的人,都比吾聪明
- 《信号与系统学习笔记》—采样(二)
- python真好玩 pdf 下载_Python真好玩:教孩子学编程_PDF电子书
- 人人都能玩航拍 手把手教你装4轴
- 情人节程序员用HTML网页表白【我永远属于你】 HTML5七夕情人节表白网页源码 HTML+CSS+JavaScript
- 计算机端口连接失败,电脑Telnet命令提示在端口23连接失败的解决办法
- STM32F1系列与STM32F4系列的GPIO
- 微信群控的服务器怎么用,微信群控开发SDK使用教程--手机客户端返回聊天消息的原始内容给服务端...