介绍了Android SurfaceFlinger层次以下的图形合成和显示系统,主要基于高通MSM8k MDP4x平台。
做为Android Display专题。SurfaceFlinger的详细介绍参见链接文章。
Android GDI之SurfaceFlinger
SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构。SufaceFlinger主要功能是:
1)将Layers(Surfaces)内容的刷新到屏幕上
2)维持Layer的Zorder序列,并对Layer最终输出做出裁剪计算。
3)响应Client要求,创建Layer与客户端的Surface建立连接
4)接收Client要求,修改Layer属性(输出大小,Alpha等设定)
但是作为投递者的实际意义,我们首先需要知道的是如何投递,投掷物,投递路线,投递目的地。
1 SurfaceFlinger的基本组成框架
SurfaceFlinger管理对象为:
mClientsMap:管理客户端与服务端的连接。
ISurface,IsurfaceComposer:AIDL调用接口实例
mLayerMap:服务端的Surface的管理对象。
mCurrentState.layersSortedByZ:以Surface的Z-order序列排列的Layer数组。
graphicPlane缓冲区输出管理
OpenGL ES:图形计算,图像合成等图形库。
gralloc.xxx.so这是个跟平台相关的图形缓冲区管理器。
pmem Device:提供共享内存,在这里只是在gralloc.xxx.so可见,在上层被gralloc.xxx.so抽象了。
2 SurfaceFinger Client和服务端对象关系图
Client端与SurfaceFlinger连接图:
Client对象:一般的在客户端都是通过SurfaceComposerClient来跟SurfaceFlinger打交道。
3主要对象说明
3.1 DisplayHardware &FrameBuffer
首先SurfaceFlinger需要操作到屏幕,需要建立一个屏幕硬件缓冲区管理框架。Android在设计支持时,考虑多个屏幕的情况,引入了graphicPlane的概念。在SurfaceFlinger上有一个graphicPlane数组,每一个graphicPlane对象都对应一个DisplayHardware.在当前的Android(2.1)版本的设计中,系统支持一个graphicPlane,所以也就支持一个DisplayHardware。
SurfaceFlinger,Hardware硬件缓冲区的数据结构关系图。
3.2 Layer
method:setBuffer在SurfaceFlinger端建立显示缓冲区。这里的缓冲区是指的HW性质的,PMEM设备文件映射的内存。
1) layer的绘制
void Layer::onDraw(const Region& clip) const
{
int index = mFrontBufferIndex;
GLuint textureName = mTextures[index].name;
…
drawWithOpenGL(clip, mTextures[index]);
}
3.2 mCurrentState.layersSortedByZ
以Surface的Z-order序列排列的LayerBase数组,该数组是层显示遮挡的依据。在每个层计算自己的可见区域时,从Z-order顶层开始计算,是考虑到遮挡区域的裁减,自己之前层的可见区域就是自己的不可见区域。而绘制Layer时,则从Z-order底层开始绘制,这个考虑到透明层的叠加。
4 SurfaceFlinger的运行框架
我们从前面的章节
4.1 SurfaceFlinger::readyToRun()
(1)建立GraphicPanle
(2)建立FrameBufferHardware(确定输出目标)
初始化:OpenGL ES
建立兼容的mainSurface.利用eglCreateWindowSurface。
建立OpenGL ES进程上下文。
建立主Surface(OpenGL ES)。 DisplayHardware的Init()@DisplayHardware.cpp函数对OpenGL做了初始化,并创建立主Surface。为什么叫主Surface,因为所有的Layer在绘制时,都需要先绘制在这个主Surface上,最后系统才将主Surface的内容”投掷”到真正的屏幕上。
(3)主Surface的绑定
1)在DisplayHandware初始完毕后,hw.makeCurrent()将主Surface,OpenGL ES进程上下文绑定到SurfaceFlinger的上下文中,
2)之后所有的SurfaceFlinger进程中使用EGL的所有的操作目的地都是mSurface@DisplayHardware。
这样,在OpenGL绘制图形时,主Surface被记录在进程的上下文中,所以看不到显示的主Surfce相关参数的传递。下面是Layer-Draw,Hardware.flip的动作示意图:
4.2 ThreadLoop
(1)handleTransaction(…):主要计算每个Layer有无属性修改,如果有修改着内用需要重画。
(2)handlePageFlip()
computeVisibleRegions:根据Z-Order序列计算每个Layer的可见区域和被覆盖区域。裁剪输出范围计算-
在生成裁剪区域的时候,根据Z_order依次,每个Layer在计算自己在屏幕的可显示区域时,需要经历如下步骤:
1)以自己的W,H给出自己初始的可见区域
2)减去自己上面窗口所覆盖的区域
在绘制时,Layer将根据自己的可将区域做相应的区域数据Copy。
(3)handleRepaint()
composeSurfaces(需要刷新区域):
根据每个Layer的可见区域与需要刷新区域的交集区域从Z-Order序列从底部开始绘制到主Surface上。
(4)postFramebuffer()
(DisplayHardware)hw.flip(mInvalidRegion);
eglSwapBuffers(display,mSurface) :将mSruface投递到屏幕。
5总结
现在SurfaceFlinger干的事情利用下面的示意图表示出来:
更详细地,参考
Android GUI之SurfaceFlinger系列
Android display架构分析-SW架构分析(1-8)
SurfaceFlinger使用的各组件,参考
Learning about Android Graphics Subsystem by MIPS Engineer
*******************************************************************************
Copybit HAL Introduction
SurfaceFlinger layer的compositionType有三种:
HWC_FRAMEBUFFER的使用OpenGL ES来绘制;
HWC_OVERLAY的使用Overlay Engine来合成;
HWC_USE_COPYBIT的使用Copybit硬件加速绘制;
MSM8xxx平台Jellybean代码中没有发现使用HWC_USE_COPYBIT的layer,该平台下 Copybit 硬件加速主要有两种:
PPP :vpe模块的PPP,direct copy;
C2D :可能是2D GPU OpenVG之类的。
PPP驱动实现是做为Framebuffer设备的一个命令MSMFB_BLIT,C2D是使用c2d hal库;
MSM7627平台下hwcomposer还是使用copybit的。
可能早期系统没有Hardware Composer,又没有GPU的时候,layer draw就要使用Copybit去一层一层一Rect一Rect的拷贝了。
Copybit的代码在display/libcopybit下,硬件合成器使用Copybit做的封装代码在display/libhwcomposer/copybit和copybit_c2d中,前者对应PPP,后者对应C2D。
*******************************************************************************
AndroidGralloc流程分析for msm8960
主要介绍Gralloc/Framebuffer HAL设备,可以籍此考察显示Buffer(Ashmem、Pmem)的拥有者和传递。
平台中内存有ashmen、PMEM等多种内存类型,为了Video、Graphics、GPU内存访问的需要,android引入Gralloc模块实现内存的管理。Gralloc把FrameBuffer的分配也纳入了其中,并且新引入ION做为Gralloc的非FrameBuffer内存的分配器。ION对于内核态内存在用户进程之间的访问和硬件平台模块之间数据流转提供了高效的解决方案。
Android中 lcd 是一个帧缓冲设备,驱动程序通过处理器的 lcd控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在 lcd上显示出来。Android在 HAL 中提供了gralloc模块,封装了用户层对帧缓冲设备的所有操作接口,并通过 SurfaceFlinger服务向应用提供显示支持。在启动过程中系统会加载 gralloc模块,然后打开帧缓冲设备,获取设备的各种参数并完成 gralloc模块的初始化。当应用程序需要把内容显示到 lcd上时,需要通过 gralloc模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过 gralloc模块释放掉,然后解除对缓冲区的映射。
1、基础数据结构
gralloc模块通过 struct private_module_t来描述,该结构定义如下:
1. struct private_module_t {
2. gralloc_module_t base;
3.
4. private_handle_t* framebuffer; /* 指向图形缓冲区的句柄 */
5. uint32_t flags; /* 用来标志系统帧缓冲区是否支持双缓冲 */
6. uint32_t numBuffers; /* 表示系统帧缓冲的个数 */
7. uint32_t bufferMask; /* 记录系统帧缓冲的使用情况 */
8. pthread_mutex_t lock; /* 保护结构体private_module_t的并行访问 */
9. buffer_handle_t currentBuffer; /* 描述当前正在被渲染的图形缓冲区 */
10. int pmem_master; /* pmem设备节点的描述符 */
11. void* pmem_master_base; /* pmem的起始虚拟地址 */
12.
13. struct fb_var_screeninfo info; /* lcd的可变参数 */
14. struct fb_fix_screeninfo finfo; /* lcd的固定参数 */
15. float xdpi; /* x方向上每英寸的像素数量 */
16. float ydpi; /* y方向上每英寸的像素数量 */
17. float fps; /* lcd的刷新率 */
18.
19. int orientation; /* 显示方向 */
20.
21. enum {
22. PRIV_USAGE_LOCKED_FOR_POST = 0x80000000 /* flag to indicate we'll post this buffer */
23. };
24. };
该结构的成员记录了 gralloc模块的各种参数,主要为模块自己使用,应用程序操作的图形缓冲区的数据结构是struct private_handle_t,定义如下:
1. >#ifdef __cplusplus
2. struct private_handle_t : public native_handle {
3. #else
4. struct private_handle_t {
5. struct native_handle nativeHandle; /* 用来描述一个本地句柄值 */
6. #endif
7.
8. enum {
9. PRIV_FLAGS_FRAMEBUFFER = 0x00000001,
10. PRIV_FLAGS_USES_PMEM = 0x00000002,
11. PRIV_FLAGS_USES_MMEM = 0x00000004,
12. PRIV_FLAGS_NEEDS_FLUSH = 0x00000008,
13. };
14.
15. enum {
16. LOCK_STATE_WRITE = 1<<31,
17. LOCK_STATE_MAPPED = 1<<30,
18. LOCK_STATE_READ_MASK = 0x3FFFFFFF
19. };
20.
21. /* 指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
22. * 取决于private_handle_t描述的图形缓冲区是在帧缓冲区分配的,还是在内存中分配的 */
23. int fd;
24. /* 指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体 */
25. int magic;
26. /* 用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER
27. * 当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的 */
28. int flags;
29. int size; /* 描述一个图形缓冲区的大小 */
30. int offset; /* 描述一个图形缓冲区的偏移地址 */
31.
32. int phys; /* 图形缓冲区或帧缓冲的起始物理地址 */
33. int base; /* 图形缓冲区或帧缓冲的起始虚拟地址 */
34. int lockState;
35. int writeOwner;
36. int pid; /* 描述一个图形缓冲区的创建者的PID */
37.
38. #ifdef __cplusplus
39. static const int sNumInts = 9; /* 有9个整数变量 */
40. static const int sNumFds = 1; /* 有1个文件描述符 */
41. static const int sMagic = 0x3141592;
42.
43. private_handle_t(int fd, int size, int flags) :
44. fd(fd), magic(sMagic), flags(flags), size(size), offset(0),
45. phys(0), base(0), lockState(0), writeOwner(0), pid(getpid())
46. {
47. version = sizeof(native_handle);
48. numInts = sNumInts;
49. numFds = sNumFds;
50. }
51. ~private_handle_t() {
52. magic = 0;
53. }
54.
55. bool usesPhysicallyContiguousMemory() {
56. return (flags & PRIV_FLAGS_USES_PMEM) != 0;
57. }
58.
59. /* 用来验证一个native_handle_t指针是否指向了一个private_handle_t结构体 */
60. static int validate(const native_handle* h) {
61. const private_handle_t* hnd = (const private_handle_t*)h;
62. if (!h || h->version != sizeof(native_handle) ||
63. h->numInts != sNumInts || h->numFds != sNumFds ||
64. hnd->magic != sMagic)
65. {
66. LOGE("invalid gralloc handle (at %p)", h);
67. return -EINVAL;
68. }
69. return 0;
70. }
71.
72. static private_handle_t* dynamicCast(const native_handle* in) {
73. if (validate(in) == 0) {
74. return (private_handle_t*) in;
75. }
76. return NULL;
77. }
78. #endif
79. };
图形缓冲区的操作接口由结构struct gralloc_module_t 定义:
80. typedef struct gralloc_module_t {
81. struct hw_module_t common;
82.
83. /* 注册一个图形缓冲区,这个指定的图形缓冲区使用一个buffer_handle_t句柄来描述 */
84. int (*registerBuffer)(struct gralloc_module_t const* module,
85. buffer_handle_t handle);
86.
87. /* 注销一个图形缓冲区 */
88. int (*unregisterBuffer)(struct gralloc_module_t const* module,
89. buffer_handle_t handle);
90.
91. /* 用来锁定一个图形缓冲区并将缓冲区映射到用户进程
92. * 在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小
93. * 这是通过参数l、t、w和h来指定的,其中,参数l和t指定的是要访问的图形缓冲区的左上角位置
94. * 而参数w和h指定的是要访问的图形缓冲区的宽度和长度
95. * 锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中
96. * 另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定 */
97. int (*lock)(struct gralloc_module_t const* module,
98. buffer_handle_t handle, int usage,
99. int l, int t, int w, int h,
100. void** vaddr);
101.
102. int (*unlock)(struct gralloc_module_t const* module,
103. buffer_handle_t handle);
104.
105. int (*perform)(struct gralloc_module_t const* module,
106. int operation, ... );
107.
108. /* reserved for future use */
109. void* reserved_proc[7];
110.} gralloc_module_t;
gralloc设备则用结构 struct alloc_device_t来描述,其定义如下:
111.typedef struct alloc_device_t {
112. struct hw_device_t common;
113.
114. /* 申请图形缓冲区的内存空间 */
115. int (*alloc)(struct alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* handle, int* stride);
116.
117. /* 释放图形缓冲区的内存空间 */
118. int (*free)(struct alloc_device_t* dev,buffer_handle_t handle);
119.} alloc_device_t;
帧缓冲设备则采用结构 struct framebuffer_device_t描述:
120.typedef struct framebuffer_device_t {
121. struct hw_device_t common;
122.
123. const uint32_t flags; /* 用来记录系统帧缓冲区的标志 */
124.
125. const uint32_t width; /* lcd显示区域的像素点数 */
126. const uint32_t height;
127.
128. const int stride; /* 描述设备显示屏的一行有多少个像素点 */
129.
130. /* 描述系统帧缓冲区的像素格式,主要有HAL_PIXEL_FORMAT_RGBX_8888和HAL_PIXEL_FORMAT_RGB_565两种 */
131. const int format;
132.
133. const float xdpi;
134. const float ydpi;
135. const float fps; /* lcd刷新率 */
136. const int minSwapInterval; /* 交换两帧图像的最小间隔时间 */
137. const int maxSwapInterval; /* 交换两帧图像的最大间隔时间 */
138.
139. int reserved[8];
140.
141. /* 设置帧交换间隔 */
142. int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);
143.
144. /* 设置帧缓冲区的更新区域 */
145. int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);
146.
147. /* 用来将图形缓冲区buffer的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去 */
148. int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
149.
150. /* 用来通知fb设备device,图形缓冲区的组合工作已经完成 */
151. int (*compositionComplete)(struct framebuffer_device_t* dev);
152.
153. void* reserved_proc[8];
154. } framebuffer_device_t;
其中成员函数 post对应用程序来说是最重要的接口,它将完成数据写入显存的工作:
2、gralloc模块
HAL中通过 hw_get_module接口加载指定 id的模块,并获得一个 hw_module_t结构来打开设备,流程如下:
1. >#define HAL_LIBRARY_PATH1 "/system/lib/hw"
2. #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
3.
4. static const char *variant_keys[] = {
5. "ro.hardware", /* This goes first so that it can pick up a different file on the emulator. */
6. "ro.product.board",
7. "ro.board.platform",
8. "ro.arch"
9. };
10.
11. static const int HAL_VARIANT_KEYS_COUNT =
12. (sizeof(variant_keys)/sizeof(variant_keys[0]));
13.
14. int hw_get_module(const char *id, const struct hw_module_t **module)
15. {
16. int status;
17. int i;
18. const struct hw_module_t *hmi = NULL;
19. char prop[PATH_MAX];
20. char path[PATH_MAX];
21.
22. /*
23. * Here we rely on the fact that calling dlopen multiple times on
24. * the same .so will simply increment a refcount (and not load
25. * a new copy of the library).
26. * We also assume that dlopen() is thread-safe.
27. */
28.
29. /* Loop through the configuration variants looking for a module */
30. for (i=0 ; i
31. if (i < HAL_VARIANT_KEYS_COUNT) {
32. if (property_get(variant_keys[i], prop, NULL) == 0) { /* 读取variant_keys数组指定的属性值 */
33. continue;
34. }
35. snprintf(path, sizeof(path), "%s/%s.%s.so", /* 格式化模块名和路径,如:/system/lib/hw/gralloc.xxx.so */
36. HAL_LIBRARY_PATH1, id, prop);
37. if (access(path, R_OK) == 0) break;
38.
39. snprintf(path, sizeof(path), "%s/%s.%s.so",
40. HAL_LIBRARY_PATH2, id, prop);
41. if (access(path, R_OK) == 0) break;
42. } else {
43. snprintf(path, sizeof(path), "%s/%s.default.so",
44. HAL_LIBRARY_PATH1, id);
45. if (access(path, R_OK) == 0) break;
46. }
47. }
48.
49. status = -ENOENT;
50. if (i < HAL_VARIANT_KEYS_COUNT+1) {
51. /* load the module, if this fails, we're doomed, and we should not try to load a different variant. */
52. status = load(id, path, module); /* 加载模块 */
53. }
54.
55. return status;
56. }
可以看出,是使用id和系统平台的名字组合出so的文件名,去设定的目录动态加载该库文件然后解析特定符号,找到hw_module_t object。函数会在 /system/lib/hw或者 /vendor/lib/hw目录中去寻找gralloc.xxx.so文件,如果找到了就调用load接口完成加载。最终会调用 gralloc_device_open完成 gralloc 设备成员的初始化:
1. int gralloc_device_open(const hw_module_t* module, const char* name,
2. hw_device_t** device)
3. {
4. 98 int status = -EINVAL;
5. 99 if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
6. 100 const private_module_t* m = reinterpret_cast<const private_module_t*>(
7. 101 module);
8. 102 gpu_context_t *dev;
9. 103 IAllocController* alloc_ctrl = IAllocController::getInstance();
10.104 dev = new gpu_context_t(m, alloc_ctrl);
11.105 *device = &dev->common;
12.106 status = 0;
13. } else {
14. status = fb_device_open(module, name, device);
15. }
16.
17. return status;
18. }
可以认为Gralloc module中有两个设备gpu_alloc_device和fb_device,前者用于分配GPU0使用的内存和FB内存,GPU0内存管理使用ION allocator;后者用于获取分配Framebuffer Info并操作fb。
在android系统中,所有的图形缓冲区都是由SurfaceFlinger服务分配的,在系统帧缓冲区中分配的图形缓冲区只在 SurfaceFlinger服务中使用,而在内存中分配的图形缓冲区既可以在 SurfaceFlinger服务中使用,也可以在其它的应用程序中使用,当应用程序请求 SurfaceFlinger服务分配图形缓冲区时会发生两次映射:服务所在的进程首先会将申请到的缓冲区映射至服务的地址空间,然后应用程序使用这个图形缓冲时再将其映射至应用程序的地址空间。分配函数的实现如下:
1. static int gralloc_alloc_framebuffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle)
2. {
3. private_module_t* m = reinterpret_cast
4. dev->common.module);
5. pthread_mutex_lock(&m->lock);
6. int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle);
7. pthread_mutex_unlock(&m->lock);
8. return err;
9. }
10.
11. static int gralloc_alloc_buffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle)
12. {
13. 127 int err = 0;
14. 128 int flags = 0;
15. 129 size = roundUpToPageSize(size);
16. 130 alloc_data data;
17. 131 data.offset = 0;
18. 132 data.fd = -1;
19. 133 data.base = 0;
20. 134 data.size = size;
21. 135 if(format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED)
22. 136 data.align = 8192;
23. 137 else
24. 138 data.align = getpagesize();
25. 139 data.pHandle = (unsigned int) pHandle;
26. 140 err = mAllocCtrl->allocate(data, usage);
27. 141
28. 142 if (!err) {
29. 143 /* allocate memory for enhancement data */
30. 144 alloc_data eData;
31. 145 eData.fd = -1;
32. 146 eData.base = 0;
33. 147 eData.offset = 0;
34. 148 eData.size = ROUND_UP_PAGESIZE(sizeof(MetaData_t));
35. 149 eData.pHandle = data.pHandle;
36. 150 eData.align = getpagesize();
37. 151 int eDataUsage = GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP;
38. 152 int eDataErr = mAllocCtrl->allocate(eData, eDataUsage);
39. 153 ALOGE_IF(eDataErr, "gralloc failed for eData err=%s", strerror(-err));
40. 154
41. 155 if (usage & GRALLOC_USAGE_PRIVATE_UNSYNCHRONIZED) {
42. 156 flags |= private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED;
43. 157 }
44. 158
45. 159 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY) {
46. 160 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY;
47. 161 //The EXTERNAL_BLOCK flag is always an add-on
48. 162 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_BLOCK) {
49. 163 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_BLOCK;
50. 164 }
51. 165 if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_CC) {
52. 166 flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_CC;
53. 167 }
54. 168 }
55. 169
56. 170 flags |= data.allocType;
57. 171 int eBaseAddr = int(eData.base) + eData.offset;
58. 172 private_handle_t *hnd = new private_handle_t(data.fd, size, flags,
59. 173 bufferType, format, width, height, eData.fd, eData.offset,
60. 174 eBaseAddr);
61. 175
62. 176 hnd->offset = data.offset;
63. 177 hnd->base = int(data.base) + data.offset;
64. 178 *pHandle = hnd;
65. 179 }
66. 180
67. 181 ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
68. 182
69. 183 return err;
70. 184}
71.
72. /*****************************************************************************/
73.
74. static int gralloc_alloc(alloc_device_t* dev,int w, int h, int format, int usage,
75. buffer_handle_t* pHandle, int* pStride)
76. {
77. if (!pHandle || !pStride)
78. return -EINVAL;
79.
80. size_t size, stride;
81.
82. int align = 4;
83. int bpp = 0;
84. switch (format) { /* 一个像素点占用的字节数 */
85. case HAL_PIXEL_FORMAT_RGBA_8888:
86. case HAL_PIXEL_FORMAT_RGBX_8888:
87. case HAL_PIXEL_FORMAT_BGRA_8888:
88. bpp = 4;
89. break;
90. case HAL_PIXEL_FORMAT_RGB_888:
91. bpp = 3;
92. break;
93. case HAL_PIXEL_FORMAT_RGB_565:
94. case HAL_PIXEL_FORMAT_RGBA_5551:
95. case HAL_PIXEL_FORMAT_RGBA_4444:
96. bpp = 2;
97. break;
98. default:
99. return -EINVAL;
100. }
101. size_t bpr = (w*bpp + (align-1)) & ~(align-1);
102. size = bpr * h;
103. stride = bpr / bpp;
104.
105. int err;
106. if (usage & GRALLOC_USAGE_HW_FB) {
107. err = gralloc_alloc_framebuffer(dev, size, usage, pHandle); /* 在系统帧缓冲中分配图形缓冲区 */
108. } else {
109. err = gralloc_alloc_buffer(dev, size, usage, pHandle); /* 在内存中分配图形缓冲区 */
110. }
111.
112. if (err < 0) {
113. return err;
114. }
115.
116. *pStride = stride;
117. return 0;
118.}
3、gpu_alloc 模块
gpu0内存即非HW_FB内存使用ION分配器进行分配,此文不做详述。
4、fb模块
gralloc_device_open中会根据传递的参数分别初始化两个设备,定义如下:
1. >#define GRALLOC_HARDWARE_FB0 "fb0"
2. #define GRALLOC_HARDWARE_GPU0 "gpu0"
如果参数不是 "gpu0",那么是"fb%u"的形式,则会调用fb_device_open 初始化 fb 设备,主要流程和打开 gralloc基本一致,在函数中会通过调用 mapFrameBuffer->mapFrameBufferLocked获取帧缓存设备的参数并将其设备节点映射到用户空间,流程如下(大致如此,msm8960平台代码有所变化,msm平台上fb设备文件名是/dev/graphics/fb%u):
1. int mapFrameBufferLocked(struct private_module_t* module)
2. {
3. if (module->framebuffer) {
4. return 0;
5. }
6.
7. char const * const device_template[] = {
8. "/dev/graphics/fb%u",
9. "/dev/fb%u",
10. 0 };
11.
12. int fd = -1;
13. int i=0;
14. char name[64];
15.
16. while ((fd==-1) && device_template[i]) {
17. snprintf(name, 64, device_template[i], 0);
18. fd = open(name, O_RDWR, 0);
19. i++;
20. }
21. if (fd < 0)
22. return -errno;
23.
24. struct fb_fix_screeninfo finfo;
25. if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) /* 获取帧缓冲的固定参数 */
26. return -errno;
27.
28. struct fb_var_screeninfo info;
29. if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) /* 获取帧缓冲的可变参数 */
30. return -errno;
31.
32. info.reserved[0] = 0;
33. info.reserved[1] = 0;
34. info.reserved[2] = 0;
35. info.xoffset = 0;
36. info.yoffset = 0;
37. info.activate = FB_ACTIVATE_NOW;
38.
39. info.bits_per_pixel = 32;
40. info.red.offset = 16;
41. info.red.length = 8;
42. info.green.offset = 8;
43. info.green.length = 8;
44. info.blue.offset = 0;
45. info.blue.length = 8;
46. info.transp.offset = 24;
47. info.transp.length = 8;
48.
49. /*
50. * Request NUM_BUFFERS screens (at lest 2 for page flipping)
51. */
52. info.yres_virtual = info.yres * NUM_BUFFERS; /* 帧缓冲总长度 */
53.
54.
55. uint32_t flags = PAGE_FLIP; /* 支持缓冲交换 */
56. if (ioctl(fd, FBIOPAN_DISPLAY, &info) == -1) {
57. info.yres_virtual = info.yres;
58. flags &= ~PAGE_FLIP;
59. LOGW("FBIOPAN_DISPLAY failed, page flipping not supported");
60. }
61.
62. if (info.yres_virtual < info.yres * 2) {
63. /* we need at least 2 for page-flipping */
64. info.yres_virtual = info.yres;
65. flags &= ~PAGE_FLIP;
66. LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
67. info.yres_virtual, info.yres*2);
68. }
69.
70. if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
71. return -errno;
72.
73. int refreshRate = 1000000000000000LLU /
74. (
75. uint64_t( info.upper_margin + info.lower_margin + info.yres )
76. * ( info.left_margin + info.right_margin + info.xres )
77. * info.pixclock
78. ); /* 计算lcd刷新率 */
79.
80. if (refreshRate == 0) {
81. /* bleagh, bad info from the driver */
82. refreshRate = 60*1000; // 60 Hz
83. }
84.
85. if (int(info.width) <= 0 || int(info.height) <= 0) {
86. /* the driver doesn't return that information, default to 160 dpi */
87. info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
88. info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
89. }
90.
91. float xdpi = (info.xres * 25.4f) / info.width;
92. float ydpi = (info.yres * 25.4f) / info.height;
93. float fps = refreshRate / 1000.0f;
94.
95. LOGI( "using (fd=%d)\n"
96. "id = %s\n"
97. "xres = %d px\n"
98. "yres = %d px\n"
99. "xres_virtual = %d px\n"
100. "yres_virtual = %d px\n"
101. "bpp = %d\n"
102. "r = %2u:%u\n"
103. "g = %2u:%u\n"
104. "b = %2u:%u\n",
105. fd,
106. finfo.id,
107. info.xres,
108. info.yres,
109. info.xres_virtual,
110. info.yres_virtual,
111. info.bits_per_pixel,
112. info.red.offset, info.red.length,
113. info.green.offset, info.green.length,
114. info.blue.offset, info.blue.length
115. );
116.
117. LOGI( "width = %d mm (%f dpi)\n"
118. "height = %d mm (%f dpi)\n"
119. "refresh rate = %.2f Hz\n",
120. info.width, xdpi,
121. info.height, ydpi,
122. fps
123. );
124.
125. if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
126. return -errno;
127.
128. if (finfo.smem_len <= 0)
129. return -errno;
130.
131. module->flags = flags;
132. module->info = info;
133. module->finfo = finfo;
134. module->xdpi = xdpi;
135. module->ydpi = ydpi;
136. module->fps = fps;
137.
138. /*
139. * map the framebuffer
140. */
141.
142. int err;
143. size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual); /* 帧缓冲大小 */
144. module->framebuffer = new private_handle_t(dup(fd), fbSize,
145. private_handle_t::PRIV_FLAGS_USES_PMEM);
146.
147. module->numBuffers = info.yres_virtual / info.yres; /* 计算系统帧缓冲的个数 */
148. module->bufferMask = 0;
149.
150. void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); /* 将fb映射到用户空间 */
151. if (vaddr == MAP_FAILED) {
152. LOGE("Error mapping the framebuffer (%s)", strerror(errno));
153. return -errno;
154. }
155. module->framebuffer->base = intptr_t(vaddr); /* 帧缓冲的起始虚拟地址 */
156. memset(vaddr, 0, fbSize);
157. return 0;
158.}
关于fb设备的打开和HW_FB内存的分配,是在FrameBufferNativeWindow的构造代码中,可以看到打开fb0设备获取Framebuffer Info,然后使用gralloc为该FrameBufferNativeWindow分配两个HW_FB内存即Framebuffer,即每个Window,double buffer。代码如下:
1. 62/*
2. 63 * This implements the (main) framebuffer management. This class is used
3. 64 * mostly by SurfaceFlinger, but also by command line GL application.
4. 65 *
5. 66 * In fact this is an implementation of ANativeWindow on top of
6. 67 * the framebuffer.
7. 68 *
8. 69 * Currently it is pretty simple, it manages only two buffers (the front and
9. 70 * back buffer).
10. 71 *
11. 72 */
12. 73
13. 74FramebufferNativeWindow::FramebufferNativeWindow()
14. 75 : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
15. 76{
16. 77 hw_module_t const* module;
17. 78 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
18. 79 int stride;
19. 80 int err;
20. 81 int i;
21. 82 err = framebuffer_open(module, &fbDev);
22. 83 ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
23. 84
24. 85 err = gralloc_open(module, &grDev);
25. 86 ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
26. 87
27. 88 // bail out if we can't initialize the modules
28. 89 if (!fbDev || !grDev)
29. 90 return;
30. 91
31. 92 mUpdateOnDemand = (fbDev->setUpdateRect != 0);
32. 93
33. 94 // initialize the buffer FIFO
34. 95 if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
35. 96 fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){
36. 97 mNumBuffers = fbDev->numFramebuffers;
37. 98 } else {
38. 99 mNumBuffers = MIN_NUM_FRAME_BUFFERS;
39. 100 }
40. 101 mNumFreeBuffers = mNumBuffers;
41. 102 mBufferHead = mNumBuffers-1;
42. 103
43. 104 /*
44. 105 * This does not actually change the framebuffer format. It merely
45. 106 * fakes this format to surfaceflinger so that when it creates
46. 107 * framebuffer surfaces it will use this format. It's really a giant
47. 108 * HACK to allow interworking with buggy gralloc+GPU driver
48. 109 * implementations. You should *NEVER* need to set this for shipping
49. 110 * devices.
50. 111 */
51. 112#ifdef FRAMEBUFFER_FORCE_FORMAT
52. 113 *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
53. 114#endif
54. 115
55. 116 for (i = 0; i < mNumBuffers; i++)
56. 117 {
57. 118 buffers[i] = new NativeBuffer(
58. 119 fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
59. 120 }
60. 121
61. 122 for (i = 0; i < mNumBuffers; i++)
62. 123 {
63. 124 err = grDev->alloc(grDev,
64. 125 fbDev->width, fbDev->height, fbDev->format,
65. 126 GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);
66. 127
67. 128 ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
68. 129 i, fbDev->width, fbDev->height, strerror(-err));
69. 130
70. 131 if (err)
71. 132 {
72. 133 mNumBuffers = i;
73. 134 mNumFreeBuffers = i;
74. 135 mBufferHead = mNumBuffers-1;
75. 136 break;
76. 137 }
77. 138 }
78. 139
79. 140 const_cast
80. 141 const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
81. 142 const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
82. 143 const_cast<int&>(ANativeWindow::minSwapInterval) =
83. 144 fbDev->minSwapInterval;
84. 145 const_cast<int&>(ANativeWindow::maxSwapInterval) =
85. 146 fbDev->maxSwapInterval;
86. 147 } else {
87. 148 ALOGE("Couldn't get gralloc module");
88. 149 }
89. 150
90. 151 ANativeWindow::setSwapInterval = setSwapInterval;
91. 152 ANativeWindow::dequeueBuffer = dequeueBuffer;
92. 153 ANativeWindow::lockBuffer = lockBuffer;
93. 154 ANativeWindow::queueBuffer = queueBuffer;
94. 155 ANativeWindow::query = query;
95. 156 ANativeWindow::perform = perform;
96. 157 ANativeWindow::cancelBuffer = NULL;
97. 158}
创建FrameBufferNativeWindow仅发生在DisplayHardware的构造后初始化中,代码片段如下:
1. 150void DisplayHardware::init(uint32_t dpy)
2. 151{
3. 152 mNativeWindow = new FramebufferNativeWindow(); //******
4. 153 framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
5. 154 if (!fbDev) {
6. 155 ALOGE("Display subsystem failed to initialize. check logs. exiting...");
7. 156 exit(0);
8. 157 }
9. 158
10. 159 int format;
11. 160 ANativeWindow const * const window = mNativeWindow.get();
12. 161 window->query(window, NATIVE_WINDOW_FORMAT, &format);
13. 162 mDpiX = mNativeWindow->xdpi;
14. 163 mDpiY = mNativeWindow->ydpi;
15. 164 mRefreshRate = fbDev->fps;
16. ....
17. }
而DisplayHardware的真正构造仅在Surfacelinger启动后readyToRun中,其余都是使用拷贝构造默认的Bitwise Copy,当然这仅仅是针对一块屏的情况,当前大屏主流。相关代码片段如下:
1. 217status_t SurfaceFlinger::readyToRun()
2. 218{
3. 219 ALOGI( "SurfaceFlinger's main thread ready to run. "
4. 220 "Initializing graphics H/W...");
5. 221
6. 222 // we only support one display currently
7. 223 int dpy = 0;
8. 224
9. 225 {
10. 226 // initialize the main display
11. 227 GraphicPlane& plane(graphicPlane(dpy));
12. 228 DisplayHardware* const hw = new DisplayHardware(this, dpy); //*******
13. 229 plane.setDisplayHardware(hw);
14. 230 }
15. 231
16. 232 // create the shared control-block
17. 233 mServerHeap = new MemoryHeapBase(4096,
18. 234 MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
19. 235 ALOGE_IF(mServerHeap==0, "can't create shared memory dealer");
20. 236
21. 237 mServerCblk = static_cast
22. 238 ALOGE_IF(mServerCblk==0, "can't get to shared control block's address");
23. 239
24. 240 new(mServerCblk) surface_flinger_cblk_t;
25. 241
26. 242 // initialize primary screen
27. 243 // (other display should be initialized in the same manner, but
28. 244 // asynchronously, as they could come and go. None of this is supported
29. 245 // yet).
30. 246 const GraphicPlane& plane(graphicPlane(dpy));
31. 247 const DisplayHardware& hw = plane.displayHardware();
32. 248 const uint32_t w = hw.getWidth();
33. 249 const uint32_t h = hw.getHeight();
34. 250 const uint32_t f = hw.getFormat();
35. 251 hw.makeCurrent();
36. .....
37. }
fb模块最重要的工作就是将应用程序指定的内容写入显存中,是通过函数 fb_post完成的,流程如下(msm8960代码大致如此,不过使用的是FBIOPUT_VSCREENINFO IOCTL_CODE):
1. /* 将图形缓冲区buffer的内容渲染到帧缓冲区中去 */
2. static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
3. {
4. unsigned int phys;
5. void* virt;
6. int pitch;
7. int format;
8.
9. /* 首先验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的 */
10. if (private_handle_t::validate(buffer) < 0)
11. return -EINVAL;
12.
13. fb_context_t* ctx = (fb_context_t*)dev;
14.
15. private_handle_t const* hnd = reinterpret_cast
16. private_module_t* m = reinterpret_cast
17.
18. if (m->currentBuffer) { /* 当前正在渲染的图形缓冲区 */
19. m->base.unlock(&m->base, m->currentBuffer);
20. m->currentBuffer = 0;
21. }
22.
23. if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { /* 如果图形缓冲区是在系统帧缓冲中分配的 */
24. m->base.lock(&m->base, buffer, /* 锁定图像缓冲区 */
25. private_module_t::PRIV_USAGE_LOCKED_FOR_POST,
26. 0, 0, m->info.xres, m->info.yres, NULL);
27.
28. const size_t offset = hnd->base - m->framebuffer->base; /* 计算图形缓冲区与帧缓冲的偏移 */
29. /* 将作为参数的fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL
30. * 表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来
31. * 这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中 */
32. m->info.activate = FB_ACTIVATE_VBL;
33. m->info.yoffset = offset / m->finfo.line_length; /* 得到偏移的起始行 */
34.
35. if (ioctl(m->framebuffer->fd, FBIOPAN_DISPLAY, &m->info) == -1) { /* 刷新显示内容 */
36. LOGE("FBIOPAN_DISPLAY failed");
37. m->base.unlock(&m->base, buffer);
38. return -errno;
39. }
40.
41. if (UNLIKELY(mDebugFps)) {
42. debugShowFPS();
43. }
44.
45. m->currentBuffer = buffer; /* 设置当前图形缓冲区 */
46. return 0;
47. }
5、 Gralloc map/unmap、register/unregister
当GPU内存file descriptor从一个进程A传递到另一个进程B后,进程B做gralloc_register_buffer就是使用allocator此时是ION将该buffer在本进程映射一下,用于访问。这些功能做为gralloc的mapper功能。
关于内存file descriptor、binder传递该内存fd的具体机制参见ION的功能。
*******************************************************************************
SurfaceFlinger Layer Clip & Draw
/*
*收到VSYNC后 REFRESH显示
*/
413void SurfaceFlinger::onMessageReceived(int32_t what)
{
419 // if we're in a global transaction, don't do anything.
420 const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
421 uint32_t transactionFlags = peekTransactionFlags(mask);
422 if (CC_UNLIKELY(transactionFlags)) {
423 handleTransaction(transactionFlags);
424 }
425
426 // post surfaces (if needed)
427 handlePageFlip();
428
435 handleRefresh();
436
437 const DisplayHardware& hw(graphicPlane(0).displayHardware());
438
443 if (CC_UNLIKELY(mHwWorkListDirty)) {
444 // build the h/w work list
445 handleWorkList();
446 }
447
448 if (CC_LIKELY(hw.canDraw())) {
449 // repaint the framebuffer (if needed)
450 handleRepaint();
451 // inform the h/w that we're done compositing
452 hw.compositionComplete();
453 postFramebuffer();
454 } else {
455 // pretend we did the post
456 hw.compositionComplete();
457 }
}
511void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
512{
513 const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
514 const size_t count = currentLayers.size();
515
516 /*
517 * Traversal of the children
518 * (perform the transaction for each of them if needed)
519 */
520
/*
* 针对每个Layer,提交其所做的状态变化
*/
521 const bool layersNeedTransaction = transactionFlags & eTraversalNeeded;
522 if (layersNeedTransaction) {
523 for (size_t i=0 ; i
524 const sp
525 uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
526 if (!trFlags) continue;
527
528 const uint32_t flags = layer->doTransaction(0);
529 if (flags & Layer::eVisibleRegion)
530 mVisibleRegionsDirty = true;
531 }
532 }
533
534 /*
535 * Perform our own transaction if needed
536 */
537 /*
* 处理SurfaceFlinger全局状态变化
*/
538 if (transactionFlags & eTransactionNeeded) {
/*
*如果屏幕发生旋转,则设置mDirtyRegion为整个屏幕范围,更新mServerCblk
* 客户端可以访问到,通知HWC屏幕位向改变重新设置其参数。
*/
539 if (mCurrentState.orientation != mDrawingState.orientation) {
540 // the orientation has changed, recompute all visible regions
541 // and invalidate everything.
542
543 const int dpy = 0;
544 const int orientation = mCurrentState.orientation;
545 // Currently unused: const uint32_t flags = mCurrentState.orientationFlags;
546 GraphicPlane& plane(graphicPlane(dpy));
547 plane.setOrientation(orientation);
548 const Transform& planeTransform(plane.transform());
549
550 // update the shared control block
551 const DisplayHardware& hw(plane.displayHardware());
552 volatile display_cblk_t* dcblk = mServerCblk->displays + dpy;
553 dcblk->orientation = orientation;
554 dcblk->w = plane.getWidth();
555 dcblk->h = plane.getHeight();
556
557 mVisibleRegionsDirty = true;
558 mDirtyRegion.set(hw.bounds());
559
560 //set the new orientation to HWC
561 HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
562 hwc.eventControl(DisplayHardware::EVENT_ORIENTATION,
563 planeTransform.getOrientation());
564
565 }
566
/*
*如果有Layer增加,设置赃区域标志,此时mDirtyRegion还为空,
*每次Repaint后mDirtyRegion就清空了。
*此处的判断条件使用Layer个数比较,需要与下面mLayersRemoved结合看。
*如果Layer有减少,即使增加的个数小于减少的个数,
*那么mVisibleRegionsDirty一定会被设置。
*如果没有减少,增加Layer后数目一定会增多。可读性不好。
*/
567 if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
568 // layers have been added
569 mVisibleRegionsDirty = true;
570 }
571
/*
*有减少的Layer,那么其下Layer可能会暴露出来,需要Invalidate该Layer
*暴露出来的区域,所以需要记录这块区域。
*所有移除layer暴露出来的区域累积,记录在mDirtyRegionRemovedLayer中。
* Invalidate的效果是在lockPageFlip后,将mDirtyRegionRemovedLayer加到
* mDirtyRegion中。
*用户绘图后Post的赃区域在unlockPageFlip时做。
*/
572 // some layers might have been removed, so
573 // we need to update the regions they're exposing.
574 if (mLayersRemoved) {
575 mLayersRemoved = false;
576 mVisibleRegionsDirty = true;
577 const LayerVector& previousLayers(mDrawingState.layersSortedByZ);
578 const size_t count = previousLayers.size();
579 for (size_t i=0 ; i
580 const sp
581 if (currentLayers.indexOf( layer ) < 0) {
582 // this layer is not visible anymore
583 mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);
584 }
585 }
586 }
587 }
588
/*
* 复制CurrentState到DrawingState中,即提交,下面代码处理Repaint时使用DrawingState
*/
589 commitTransaction();
590}
735void SurfaceFlinger::handlePageFlip()
736{
737 ATRACE_CALL();
738 const DisplayHardware& hw = graphicPlane(0).displayHardware();
739 const Region screenRegion(hw.bounds());
740
/*
* 更新每个Layer的脏区域,获取每Layer这次重绘所需要的GraphicBuffer,
* 为每Layer生成纹理供GPU render使用。
*/
741 const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
742 const bool visibleRegions = lockPageFlip(currentLayers);
743
744 if (visibleRegions || mVisibleRegionsDirty) {
745 Region opaqueRegion;
/*
*计算更新mDirtyRegion,得到所有Opaqued Layers的总的Region。
*/
746 computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
747
748 /*
749 * rebuild the visible layer list;重建mVisibleLayersSortedByZ
750 */
751 const size_t count = currentLayers.size();
752 mVisibleLayersSortedByZ.clear();
753 mVisibleLayersSortedByZ.setCapacity(count);
754 for (size_t i=0 ; i
755 if (!currentLayers[i]->visibleRegionScreen.isEmpty())
756 mVisibleLayersSortedByZ.add(currentLayers[i]);
757 }
758
/*
* opaqueRegion区域外的区域绘制“虫洞”,记录该区域
*/
759 mWormholeRegion = screenRegion.subtract(opaqueRegion);
/*
*本轮处理中mVisibleRegionsDirty标志使用完毕,重置。
*/
760 mVisibleRegionsDirty = false;
761 invalidateHwcGeometry();
762 }
763
/*
* 主要是将每个Layer用户Post的区域并到赃区域上
*/
764 unlockPageFlip(currentLayers);
765
766 mDirtyRegion.orSelf(getAndClearInvalidateRegion());
767 mDirtyRegion.andSelf(screenRegion);
768}
775bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
776{
777 bool recomputeVisibleRegions = false;
778 size_t count = currentLayers.size();
779 sp
780 for (size_t i=0 ; i
781 const sp
782 layer->lockPageFlip(recomputeVisibleRegions);
783 }
784 return recomputeVisibleRegions;
785}
527void Layer::lockPageFlip(bool& recomputeVisibleRegions)
528{
529 ATRACE_CALL();
530
/*
* 本Layer有新Queued Buffer才需要更新纹理。
*/
531 if (mQueuedFrames > 0) {
532
533 // if we've already called updateTexImage() without going through
534 // a composition step, we have to skip this layer at this point
535 // because we cannot call updateTeximage() without a corresponding
536 // compositionComplete() call.
537 // we'll trigger an update in onPreComposition().
/*
*如果上次的重绘还没有显示,本轮又要显示了,直接返回。
*/
538 if (mRefreshPending) {
539 mPostedDirtyRegion.clear();
540 return;
541 }
542
543 // Capture the old state of the layer for comparisons later
544 const bool oldOpacity = isOpaque();
545 sp
546
/*
*因为有mRefreshPending时导致直接return,所有需要“引发”下个Frame的显示;
* signalLayerUpdate()即是requestNextVsync(),因为setRefreshRate(0)时,
*不接收VSYNC,所以需要显式要求下一个VSYNC发过来,“引发”下帧显示
*/
547 // signal another event if we have more frames pending
548 if (android_atomic_dec(&mQueuedFrames) > 1) {
549 mFlinger->signalLayerUpdate();
550 }
551
/*
*内部类用于检验Queued过来的Buffer是否符合该Layer的显示要求,
*不符合则reject,不予显示。
*如Camera或Video图像buffer的大小,格式等要符合。
*/
552 struct Reject : public SurfaceTexture::BufferRejecter {
553 Layer::State& front;
554 Layer::State& current;
555 bool& recomputeVisibleRegions;
556 Reject(Layer::State& front, Layer::State& current,
557 bool& recomputeVisibleRegions)
558 : front(front), current(current),
559 recomputeVisibleRegions(recomputeVisibleRegions) {
560 }
561
562 virtual bool reject(const sp
563 const BufferQueue::BufferItem& item) {
564 if (buf == NULL) {
565 return false;
566 }
567
568 uint32_t bufWidth = buf->getWidth();
569 uint32_t bufHeight = buf->getHeight();
570
571 // check that we received a buffer of the right size
572 // (Take the buffer's orientation into account)
573 if (item.mTransform & Transform::ROT_90) {
574 swap(bufWidth, bufHeight);
575 }
576
577
578 bool isFixedSize = item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
579 if (front.active != front.requested) {
580
581 if (isFixedSize ||
582 (bufWidth == front.requested.w &&
583 bufHeight == front.requested.h))
584 {
585 // Here we pretend the transaction happened by updating the
586 // current and drawing states. Drawing state is only accessed
587 // in this thread, no need to have it locked
588 front.active = front.requested;
589
590 // We also need to update the current state so that
591 // we don't end-up overwriting the drawing state with
592 // this stale current state during the next transaction
593 //
594 // NOTE: We don't need to hold the transaction lock here
595 // because State::active is only accessed from this thread.
596 current.active = front.active;
597
598 // recompute visible region
599 recomputeVisibleRegions = true;
600 }
601
622
623 if (!isFixedSize) {
624 if (front.active.w != bufWidth ||
625 front.active.h != bufHeight) {
626 // reject this buffer
627 return true;
628 }
629 }
630 return false;
631 }
632 };
633
634
635 Reject r(mDrawingState, currentState(), recomputeVisibleRegions);
636
/*
*使用该Layer的mActiveBuffer生成SurfaceTexture,用于OpenGL/3D GPU render。
*图像格式不符合时,Reject::reject()被回调。
*/
637 if (mSurfaceTexture->updateTexImage(&r) < NO_ERROR) {
638 // something happened!
639 recomputeVisibleRegions = true;
640 return;
641 }
/*************
*updateTexImage会释放上轮该Layer使用的GraphicBuffer;
*也即本轮使用的GraphicBuffer持续到下次需要重绘时释放。
*记得其申请是在lockPageFlip中记录在mActiveBuffer。
*/
642
/*
*记录或更新当前使用的即mActiveBuffer字段
*注意该buffer直到该Layer下轮重绘Repaint时才Release,
*期间SurfaceTexture对该Buffer是不可用的。
*/
643 // update the active buffer
644 mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
645 if (mActiveBuffer == NULL) {
646 // this can only happen if the very first buffer was rejected.
647 return;
648 }
649
/*
*设置mRefreshPending标志了,如果本轮还没有Paint而下次又来了,直接返回。
*/
650 mRefreshPending = true;
651 mFrameLatencyNeeded = true;
652 if (oldActiveBuffer == NULL) {
653 // the first time we receive a buffer, we need to trigger a
654 // geometry invalidation.
655 mFlinger->invalidateHwcGeometry();
656 }
657
/*
*如果Crop & Transform & Scale改变,重设HWC参数
*/
658 Rect crop(mSurfaceTexture->getCurrentCrop());
659 const uint32_t transform(mSurfaceTexture->getCurrentTransform());
660 const uint32_t scalingMode(mSurfaceTexture->getCurrentScalingMode());
661 if ((crop != mCurrentCrop) ||
662 (transform != mCurrentTransform) ||
663 (scalingMode != mCurrentScalingMode))
664 {
665 mCurrentCrop = crop;
666 mCurrentTransform = transform;
667 mCurrentScalingMode = scalingMode;
668 mFlinger->invalidateHwcGeometry();
669 }
670
/*
*比较GraphicBuffer的维度是否有改变,用于更新HWC的维度参数,
*从而使HWC知道该准备多大的buffer空间,和图像参数用于合成。
*/
671 if (oldActiveBuffer != NULL) {
672 uint32_t bufWidth = mActiveBuffer->getWidth();
673 uint32_t bufHeight = mActiveBuffer->getHeight();
674 if (bufWidth != uint32_t(oldActiveBuffer->width) ||
675 bufHeight != uint32_t(oldActiveBuffer->height)) {
676 mFlinger->invalidateHwcGeometry();
677 }
678 }
679
680 mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
681 if (oldOpacity != isOpaque()) {
682 recomputeVisibleRegions = true;
683 }
684
/*
* FIXME?每个layer的dirty是在后面调用的computeVisibleRegions()中计算出来的,
*可以在彼时设置给Layer,记录脏区域是个很好的优化。
*但是Region mPostedDirtyRegion是class Layer而不是class LayerBase的成员,
*慢慢FIX! 此dirty非computeVisibleRegions中的dirty
*/
685 // FIXME: mPostedDirtyRegion = dirty & bounds
686 const Layer::State& front(drawingState());
687 mPostedDirtyRegion.set(front.active.w, front.active.h);
688
689 glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
690 glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
691 }
692}
Layer Clip精髓所在----
592void SurfaceFlinger::computeVisibleRegions(
593 const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
594{
595 ATRACE_CALL();
596
597 const GraphicPlane& plane(graphicPlane(0));
598 const Transform& planeTransform(plane.transform());
599 const DisplayHardware& hw(plane.displayHardware());
600 const Region screenRegion(hw.bounds());
601
602 Region aboveOpaqueLayers;
603 Region aboveCoveredLayers;
604 Region dirty;
605
606 bool secureFrameBuffer = false;
607
608 size_t i = currentLayers.size();
/*
* Clip不就是计算遮挡吗?z-order从顶向底,合乎逻辑。
*/
609 while (i--) {
610 const sp
611 layer->validateVisibility(planeTransform);
612
613 // start with the whole surface at its current location
614 const Layer::State& s(layer->drawingState());
615
616 /*
617 * opaqueRegion: area of a surface that is fully opaque.
618 */
619 Region opaqueRegion;
620
621 /*
622 * visibleRegion: area of a surface that is visible on screen
623 * and not fully transparent. This is essentially the layer's
624 * footprint minus the opaque regions above it.
625 * Areas covered by a translucent surface are considered visible.
626 */
627 Region visibleRegion;
628
629 /*
630 * coveredRegion: area of a surface that is covered by all
631 * visible regions above it (which includes the translucent areas).
632 */
633 Region coveredRegion;
634
635
636 // handle hidden surfaces by setting the visible region to empty
637 if (CC_LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
//该Layer是否半透
638 const bool translucent = !layer->isOpaque();
//该Layer可见范围
639 const Rect bounds(layer->visibleBounds());
640 visibleRegion.set(bounds);
641 visibleRegion.andSelf(screenRegion);
642 if (!visibleRegion.isEmpty()) {
/*
*如果本layer具有全透明区域(全透明子窗口),如Video或Camera,
*本Layer该区域一定是不可见的,visibleRegion应该减去全透区域,
* translucent的判断条件并不表示该Layer为半透,而是有全透区域时,
*该Layer的Opaque属性应该设置为false,表并非Full Opaque。
* setTransparentRegion/setTransparentRegionWindow
* => setTransparentRegionHint设置透明的。
*那半透明子窗口如何呢?因为Layer的地位相当于该应用的Parent most window,
*所以半透子窗口下的区域也一定是本Layer的子窗口,而不可能是别的Layer,
*从而该半透子窗口在本Layer范围内部就做Alpha混叠了,对于本Layer来说是
* Opaque的,所以不需要半透部分区域。半透属性是针对整个Layer的。
*/
643 // Remove the transparent area from the visible region
644 if (translucent) {
645 visibleRegion.subtractSelf(layer->transparentRegionScreen);
646 }
647
648 // compute the opaque region
649 const int32_t layerOrientation = layer->getOrientation();
/*
*如果该Layer是Opaque的,那么其整个可见区域一定是遮挡下面的层的。
*记录,累积到aboveOpaqueLayers, 供计算下面层的遮挡之用。
*/
650 if (s.alpha==255 && !translucent &&
651 ((layerOrientation & Transform::ROT_INVALID) == false)) {
652 // the opaque region is the layer's footprint
653 opaqueRegion = visibleRegion;
654 }
655 }
656 }
657
/*
* coveredRegion本层被覆盖的区域,包括被上面层半透覆盖的区域,覆盖并非遮挡。
*本层可见区域总对下面层构成覆盖,累积到aboveCoveredLayers
*/
658 // Clip the covered region to the visible region
659 coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
660
661 // Update aboveCoveredLayers for next (lower) layer
662 aboveCoveredLayers.orSelf(visibleRegion);
663
/*
*减去本层被上面层遮挡的区域
*/
664 // subtract the opaque region covered by the layers above us
665 visibleRegion.subtractSelf(aboveOpaqueLayers);
666
/*
*计算本层的脏区域,分内容是否为脏(size是否变化)两种情形。
*如果是用内容脏,即size变化,那么认为整个区域都是脏的;
*如果是移除上层的Layer暴露出本Layer区域,则计算可见的最小的脏区域;
*此时赃区域是屏幕坐标系统?
*这么有用的个dirty为什么不保存给各个Layer?
*此时的dirty还不包括用户Posted的真正意义上的脏区域!
*为什么不直接地处理为visbleRegion.andSelf(mDirtyRegionRemovedLayer)?
*因为mDirtyRegionRemovedLayer仅是个区域,并没有记录层的透明属性。
*/
667 // compute this layer's dirty region
668 if (layer->contentDirty) {
669 // we need to invalidate the whole region
670 dirty = visibleRegion;
671 // as well, as the old visible region
672 dirty.orSelf(layer->visibleRegionScreen);
673 layer->contentDirty = false;
674 } else {
675 /* compute the exposed region:
676 * the exposed region consists of two components:
677 * 1) what's VISIBLE now and was COVERED before
678 * 2) what's EXPOSED now less what was EXPOSED before
679 *
680 * note that (1) is conservative, we start with the whole
681 * visible region but only keep what used to be covered by
682 * something -- which mean it may have been exposed.
683 *
684 * (2) handles areas that were not covered by anything but got
685 * exposed because of a resize.
686 */
687 const Region newExposed = visibleRegion - coveredRegion;
688 const Region oldVisibleRegion = layer->visibleRegionScreen;
689 const Region oldCoveredRegion = layer->coveredRegionScreen;
690 const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
691 dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
692 }
/*
*被遮挡区域不需要重绘,从脏区域里除去
*/
693 dirty.subtractSelf(aboveOpaqueLayers);
694
/*
*累积总的脏区域
*/
695 // accumulate to the screen dirty region
696 dirtyRegion.orSelf(dirty);
697
/*
*累积遮挡区域
*/
698 // Update aboveOpaqueLayers for next (lower) layer
699 aboveOpaqueLayers.orSelf(opaqueRegion);
700
/*
*为每个Layer记录其mVisibleRegion和mCoveredRegion
*/
701 // Store the visible region is screen space
702 layer->setVisibleRegion(visibleRegion);
703 layer->setCoveredRegion(coveredRegion);
704
705 // If a secure layer is partially visible, lock-down the screen!
706 if (layer->isSecure() && !visibleRegion.isEmpty()) {
707 secureFrameBuffer = true;
708 }
709 }
710
/*
* 把mDirtyRegionRemovedLayer并到mDirtyRegion中去。
* 有移除Layers暴露出来的区域需要其下的Layers重绘,其实这个在dirty计算时已处理
* 关键的是移除的Layer是最底层Layer的时候则直接露出屏底色,所以要此处要或上。
*/
711 // invalidate the areas where a layer was removed
712 dirtyRegion.orSelf(mDirtyRegionRemovedLayer);
713 mDirtyRegionRemovedLayer.clear();
714
/*
* 抓屏相关?
*/
715 mSecureFrameBuffer = secureFrameBuffer;
/*
* 传出遮挡区域,Opaque Region为所有Opaque Layer Area之和
*/
716 opaqueRegion = aboveOpaqueLayers;
717}
787void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
788{
789 const GraphicPlane& plane(graphicPlane(0));
790 const Transform& planeTransform(plane.transform());
791 const size_t count = currentLayers.size();
792 sp
793 for (size_t i=0 ; i
794 const sp
795 layer->unlockPageFlip(planeTransform, mDirtyRegion);
796 }
797}
694void Layer::unlockPageFlip(
695 const Transform& planeTransform, Region& outDirtyRegion)
696{
697 ATRACE_CALL();
698
699 Region postedRegion(mPostedDirtyRegion);
700 if (!postedRegion.isEmpty()) {
701 mPostedDirtyRegion.clear();
702 if (!visibleRegionScreen.isEmpty()) {
703 // The dirty region is given in the layer's coordinate space
704 // transform the dirty region by the surface's transformation
705 // and the global transformation.
706 const Layer::State& s(drawingState());
707 const Transform tr(planeTransform * s.transform);
708 postedRegion = tr.transform(postedRegion);
709
/*
* computeVisibleRegions处理的赃区域是size变化和上层遮挡移除的情况,需要重绘;
*而用户修改提交的区域也需要重绘,还未加到赃区域中,此时并进来。
*/
710 // At this point, the dirty region is in screen space.
711 // Make sure it's constrained by the visible region (which
712 // is in screen space as well).
713 postedRegion.andSelf(visibleRegionScreen);
714 outDirtyRegion.orSelf(postedRegion);
715 }
716 }
717}
handleRefresh()没什么作用了。
handleWorkList()是为HWComposer分配缓冲工作集,用于硬件合成,暂不考察。
DisplayHardware.canDraw()用于判断当前是否处于draw过程中,屏是否已经关闭等,得出需要Repaint的判断。
835void SurfaceFlinger::handleRepaint()
836{
837 ATRACE_CALL();
838
839 // compute the invalid region
840 mSwapRegion.orSelf(mDirtyRegion);
841
842 if (CC_UNLIKELY(mDebugRegion)) {
843 debugFlashRegions();
844 }
845
846 // set the frame buffer
847 const DisplayHardware& hw(graphicPlane(0).displayHardware());
848 glMatrixMode(GL_MODELVIEW);
849 glLoadIdentity();
850
/*
* 针对DisplayHardware是支持RECT更新还是RECTS更新,做相应处理。
*/
851 uint32_t flags = hw.getFlags();
852 if (flags & DisplayHardware::SWAP_RECTANGLE) {
853 // we can redraw only what's dirty, but since SWAP_RECTANGLE only
854 // takes a rectangle, we must make sure to update that whole
855 // rectangle in that case
856 mDirtyRegion.set(mSwapRegion.bounds());
857 } else {
858 if (flags & DisplayHardware::PARTIAL_UPDATES) {
859 // We need to redraw the rectangle that will be updated
860 // (pushed to the framebuffer).
861 // This is needed because PARTIAL_UPDATES only takes one
862 // rectangle instead of a region (see DisplayHardware::flip())
863 mDirtyRegion.set(mSwapRegion.bounds());
864 } else {
865 // we need to redraw everything (the whole screen)
866 mDirtyRegion.set(hw.bounds());
867 mSwapRegion = mDirtyRegion;
868 }
869 }
870
/*
* 此处先不考虑HWC,HWC另见后续MIMO display – Overlay & HWComposer
* 只考虑使用 OpenGL / 3D GPU render的情况,即各layer->draw。
*/
871 setupHardwareComposer();
872 composeSurfaces(mDirtyRegion);
873
874 // update the swap region and clear the dirty region
875 mSwapRegion.orSelf(mDirtyRegion);
876 mDirtyRegion.clear();
877}
912void SurfaceFlinger::composeSurfaces(const Region& dirty)
913{
914 const DisplayHardware& hw(graphicPlane(0).displayHardware());
915 HWComposer& hwc(hw.getHwComposer());
916 hwc_layer_t* const cur(hwc.getLayers());
917
/*
* 不考虑HWC或者除HWC_OVERLAY有HWC_FB或者Layer数目超出HWC管道数时,
* 使用GPU render。
*/
918 const size_t fbLayerCount = hwc.getLayerCount(HWC_FRAMEBUFFER);
919 if (!cur || fbLayerCount) {
920 // Never touch the framebuffer if we don't have any framebuffer layers
921
/*
*有HWC_FB有HWC_OVERLAY共存的情形,如Camera或Video
*暂时认为0不表,详见后续 MIMO display – Overlay & HWComposer
*/
922 if (hwc.getLayerCount(HWC_OVERLAY)) {
923 // when using overlays, we assume a fully transparent framebuffer
924 // NOTE: we could reduce how much we need to clear, for instance
925 // remove where there are opaque FB layers. however, on some
926 // GPUs doing a "clean slate" glClear might be more efficient.
927 // We'll revisit later if needed.
928 const Region region(hw.bounds());
/*
*为HWC先清原来的画面
*/
929#ifdef QCOMHW
930 if (0 != qdutils::CBUtils::qcomuiClearRegion(region,
931 hw.getEGLDisplay()))
932#endif
933 {
934 glClearColor(0, 0, 0, 0);
935 glClear(GL_COLOR_BUFFER_BIT);
936 }
937 } else {
/*
*看来FB的时候不需要清屏;如果需要画虫洞,则为该区域清屏以显示虫洞
*/
938 // screen is already cleared here
939 if (!mWormholeRegion.isEmpty()) {
940 // can happen with SurfaceView
941#ifdef QCOMHW
942 if (0 != qdutils::CBUtils::qcomuiClearRegion(mWormholeRegion,
943 hw.getEGLDisplay()))
944#endif
945 drawWormhole();
946 }
947 }
948
/*
*真正开画
*/
949 /*
950 * and then, render the layers targeted at the framebuffer
951 */
952
953 const Vector< sp
954 const size_t count = layers.size();
955
/*
*显然的,从底层向顶层画
*/
956 for (size_t i=0 ; i
957 const sp
/*
*各Layer的赃区域
*/
958 const Region clip(dirty.intersect(layer->visibleRegionScreen));
959 if (!clip.isEmpty()) {
960 if (cur && (cur[i].compositionType == HWC_OVERLAY)) {
961 if (i && (cur[i].hints & HWC_HINT_CLEAR_FB)
962 && layer->isOpaque()) {
963 // never clear the very first layer since we're
964 // guaranteed the FB is already cleared
965#ifdef QCOMHW
966 if (0 != qdutils::CBUtils::qcomuiClearRegion(clip,
967 hw.getEGLDisplay()))
968#endif
969 layer->clearWithOpenGL(clip);
970 }
971 continue;
972 }
/*
*如果是HWC_OVERLAY的,不需要Layer自己画
*/
973#ifdef QCOMHW
974 if (cur && (cur[i].compositionType != HWC_FRAMEBUFFER))
975 continue;
976#endif
977
/*
* HWC_FB Layer,自己开画
*/
978 // render the layer
979 layer->draw(clip);
980 }
981 }
982 } else if (cur && !mWormholeRegion.isEmpty()) {
983 const Region region(mWormholeRegion.intersect(mDirtyRegion));
984 if (!region.isEmpty()) {
985#ifdef QCOMHW
986 if (0 != qdutils::CBUtils::qcomuiClearRegion(region,
987 hw.getEGLDisplay()))
988#endif
989 drawWormhole();
990 }
991 }
992}
344void LayerBase::draw(const Region& clip) const
345{
346 //Dont draw External-only layers
347 if (isLayerExternalOnly(getLayer())) {
348 return;
349 }
350 onDraw(clip);
351}
/*
* Bind Texture and Draw using OpenGL for this layer on theBACK Framebuffer.
*/
321void Layer::onDraw(const Region& clip) const
322{
323 ATRACE_CALL();
324
325 if (CC_UNLIKELY(mActiveBuffer == 0)) {
326 // the texture has not been created yet, this Layer has
327 // in fact never been drawn into. This happens frequently with
328 // SurfaceView because the WindowManager can't know when the client
329 // has drawn the first time.
330
331 // If there is nothing under us, we paint the screen in black, otherwise
332 // we just skip this update.
333
334 // figure out if there is something below us
335 Region under;
336 const SurfaceFlinger::LayerVector& drawingLayers(
337 mFlinger->mDrawingState.layersSortedByZ);
338 const size_t count = drawingLayers.size();
339 for (size_t i=0 ; i
340 const sp
341 if (layer.get() == static_cast
342 break;
343 under.orSelf(layer->visibleRegionScreen);
344 }
345 // if not everything below us is covered, we plug the holes!
346 Region holes(clip.subtract(under));
347 if (!holes.isEmpty()) {
348 clearWithOpenGL(holes, 0, 0, 0, 1);
349 }
350 return;
351 }
352#ifdef QCOMHW
353 if (!qdutils::isGPUSupportedFormat(mActiveBuffer->format)) {
354 clearWithOpenGL(clip, 0, 0, 0, 1);
355 return;
356 }
357#endif
358 if (!isProtected()) {
359 // TODO: we could be more subtle with isFixedSize()
360 const bool useFiltering = getFiltering() || needsFiltering() || isFixedSize();
361
362 // Query the texture matrix given our current filtering mode.
363 float textureMatrix[16];
364 mSurfaceTexture->setFilteringEnabled(useFiltering);
365 mSurfaceTexture->getTransformMatrix(textureMatrix);
366
367 // Set things up for texturing.
368 glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName);
369 GLenum filter = GL_NEAREST;
370 if (useFiltering) {
371 filter = GL_LINEAR;
372 }
373 glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, filter);
374 glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, filter);
375 glMatrixMode(GL_TEXTURE);
376 glLoadMatrixf(textureMatrix);
377 glMatrixMode(GL_MODELVIEW);
378 glDisable(GL_TEXTURE_2D);
379 glEnable(GL_TEXTURE_EXTERNAL_OES);
380 } else {
381 glBindTexture(GL_TEXTURE_2D, mFlinger->getProtectedTexName());
382 glMatrixMode(GL_TEXTURE);
383 glLoadIdentity();
384 glMatrixMode(GL_MODELVIEW);
385 glDisable(GL_TEXTURE_EXTERNAL_OES);
386 glEnable(GL_TEXTURE_2D);
387 }
388
389 drawWithOpenGL(clip);
390
391 glDisable(GL_TEXTURE_EXTERNAL_OES);
392 glDisable(GL_TEXTURE_2D);
393}
/*
* 提交FrameBuffer
* 对于使用GPU的情况,composeSurfaces已经将所有Surface都合成到BACK Framebuffer上了;
* 对于HWComposer的情况,此时还没有启动硬件合成,是在DisplayHardware::flip =>
* HWComposer::commit中合成到BACK Framebuffer上并交换Framebuffer的。
* 为了统一,应该把HWC合成的功能也放到composeSurfaces类似的composeLayers里面去,
* 不用Surface字样是因为Surface是GPU使用的,MDP HWComposer并不具有
* 操作Surface/Texture的能力,而只能操作支持格式的GraphicBuffer。
*/
463void SurfaceFlinger::postFramebuffer()
464{
465 ATRACE_CALL();
466 // mSwapRegion can be empty here is some cases, for instance if a hidden
467 // or fully transparent window is updating.
468 // in that case, we need to flip anyways to not risk a deadlock with
469 // h/w composer.
470
471 const DisplayHardware& hw(graphicPlane(0).displayHardware());
472 const nsecs_t now = systemTime();
473 mDebugInSwapBuffers = now;
474 hw.flip(mSwapRegion);
475
476 size_t numLayers = mVisibleLayersSortedByZ.size();
477 for (size_t i = 0; i < numLayers; i++) {
478 mVisibleLayersSortedByZ[i]->onLayerDisplayed();
479 }
480
481
482 mLastSwapBufferTime = systemTime() - now;
483 mDebugInSwapBuffers = 0;
484 mSwapRegion.clear();
485}
435void DisplayHardware::flip(const Region& dirty) const
436{
437 checkGLErrors();
438
439 EGLDisplay dpy = mDisplay;
440 EGLSurface surface = mSurface;
441
442#ifdef EGL_ANDROID_swap_rectangle
443 if (mFlags & SWAP_RECTANGLE) {
444 const Region newDirty(dirty.intersect(bounds()));
445 const Rect b(newDirty.getBounds());
446 eglSetSwapRectangleANDROID(dpy, surface,
447 b.left, b.top, b.width(), b.height());
448 }
449#endif
450
451 if (mFlags & PARTIAL_UPDATES) {
452 mNativeWindow->setUpdateRectangle(dirty.getBounds());
453 }
454
455 mPageFlipCount++;
456
/*
* mHwc->commit中也会调用eglSwapBuffers,因为不管用什么方式,
* EGL是FrameBufferNativeWindow的管理者,实现buffer swap,避免竞争。
*/
457 if (mHwc->initCheck() == NO_ERROR) {
458 mHwc->commit();
459 } else {
460 eglSwapBuffers(dpy, surface);
461 }
462 checkEGLErrors("eglSwapBuffers");
463
464 // for debugging
465 //glClearColor(1,0,0,0);
466 //glClear(GL_COLOR_BUFFER_BIT);
467}
没有GPU的eglSwapBuffers实现,就看看软的吧。
484EGLBoolean egl_window_surface_v2_t::swapBuffers()
485{
486 if (!buffer) {
487 return setError(EGL_BAD_ACCESS, EGL_FALSE);
488 }
489
490 /*
491 * Handle eglSetSwapRectangleANDROID()
492 * We copyback from the front buffer
493 */
494 if (!dirtyRegion.isEmpty()) {
495 dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
496 if (previousBuffer) {
497 // This was const Region copyBack, but that causes an
498 // internal compile error on simulator builds
/*
* front buffer和back buffer的赃区域的差值拷贝回back buffer中;
*因为front buffer的赃区域(即其重绘的区域)并未更新到back buffer中,
*所以需要考回来;但是back buffer的赃区域已经重绘了,是不能覆盖掉的,
*所以两个赃区域相减。
*这仅是double buffer的时候的实现,triple buffer两个dirtyRegion就无法保证了。
*/
499 /*const*/ Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
500 if (!copyBack.isEmpty()) {
501 void* prevBits;
502 if (lock(previousBuffer,
503 GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
504 // copy from previousBuffer to buffer
505 copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
506 unlock(previousBuffer);
507 }
508 }
509 }
510 oldDirtyRegion = dirtyRegion;
511 }
512 /*
* 释放front framebuffer
*/
513 if (previousBuffer) {
514 previousBuffer->common.decRef(&previousBuffer->common);
515 previousBuffer = 0;
516 }
517
/*
* 解锁back framebuffer,表用户用完,要queue了。queueBuffer()
*/
518 unlock(buffer);
519 previousBuffer = buffer;
520 nativeWindow->queueBuffer(nativeWindow, buffer);
521 buffer = 0;
522
/*
* back framebuffer已经提交了,需要再dequeue下一个framebuffer来做back framebuffer
* 并且锁住,供使用;
*/
523 // dequeue a new buffer
524 if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) {
525
526 // TODO: lockBuffer should rather be executed when the very first
527 // direct rendering occurs.
528 nativeWindow->lockBuffer(nativeWindow, buffer);
529
530 // reallocate the depth-buffer if needed
531 if ((width != buffer->width) || (height != buffer->height)) {
532 // TODO: we probably should reset the swap rect here
533 // if the window size has changed
534 width = buffer->width;
535 height = buffer->height;
536 if (depth.data) {
537 free(depth.data);
538 depth.width = width;
539 depth.height = height;
540 depth.stride = buffer->stride;
541 depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
542 if (depth.data == 0) {
543 setError(EGL_BAD_ALLOC, EGL_FALSE);
544 return EGL_FALSE;
545 }
546 }
547 }
548
549 // keep a reference on the buffer
550 buffer->common.incRef(&buffer->common);
551
/*
* lock/unlock
*/
552 // finally pin the buffer down
553 if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
554 GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
555 ALOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
556 buffer, buffer->width, buffer->height);
557 return setError(EGL_BAD_ACCESS, EGL_FALSE);
558 // FIXME: we should make sure we're not accessing the buffer anymore
559 }
560 } else {
561 return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
562 }
563
564 return EGL_TRUE;
565}
*****************************************************************************
Android Overlay on QC MDP4平台要点简记
File Orgnization
目录/hardware/qcom/display/liboverlay/
Android.mk
mdpRotator.cpp Overlay Rotator Wrpper
mdpWrapper.h MDP Normal and Overlay FrameBuffer IOCTL Wrapper
mdssRotator.cpp Overlay MDSS Rotator Wrapper
overlay.cpp Overlay Top level implementation file
overlay.h Overlay Top level declaration file
overlayCtrl.cpp OverlayCtrl implementation file
overlayCtrlData.h OverlayCtrl and OverlayData declaration file including OverlayData implementation
overlayImpl.h Overlay implementation which operates overlay pipes pair(LayerMixer)
overlayMdp.cpp Overlay implementation on MDP, used by OverlayCtrlData
overlayMdp.h Overlay on MDP
overlayMem.h Overlay VG pipe input kernel memory file descriptor, maybe graphic buffer or rotator output buffer
overlayRotator.cpp Overlay Rotator top level implementation
overlayRotator.h Overlay Rotator top level declaration
overlayState.h Overlay state machine
overlayUtils.cpp Overlay Utils
overlayUtils.h Overlay Utils
pipes/ Overlay Pipes, that is Overlay channel. It is a VG and RGB pipe pair on MDP.
Platform architecture
MDP中每个VG和RGB pipe pair作为一个LayerMixer的输入,由LayerMixer完成Overlay功能,作为一个Overlay channel。
注意,RGB和VG并不固定配对做为某个LayerMixer的输入。如MDP4.2的LayerMixer0可以合成一个Border或BaseLayer和4个layer,即可以多达5个pipe输入到LayerMixer0,从而用作Hardware Composer。
当使用Overlay功能时:
RGB pipe的输入是普通的Framebuffer,是Surfaceflinger的合成输出;
VG的输入是video或graphics或camera图像等,是内核空间内存buffer,其owner一般是Video、Graphics或V4L2等。当其前端是Rotator时,Rotator的输入是这些buffer,Rotator的输出Overlay rotator frame buffer作为VG的输入。
每个Overlay Channel结构如下图所示
关于Overlay Buffer(FrameBuffer RotatorBuffer OverlayBuffer)这些名称并不特别明确,只要明白Overlay Channel数据流路上的各输入输出Buffer的位置和作用即可。
下面以Layermixer1(对应/dev/graphics/fb0)为参考详述各buffer:
只UI显示时,
Framebuffer是fb0的framebuffer,是从启动时预留出的bootmem中的分配出来的。LayerMixer1处于BLT模式,Layermixer1和DMA_P(Primary display driver)分离,可以由软件完全控制。该Framebuffer做为DMA_P的输入,经MIPI_DSI输出到主屏上。
启用Overlay时,
上述Framebuffer做为RGB1 pipe的输入,而视频或图像的内核buffer做为VG pipe的输入,二者经Layermixer1合成;此时LayerMixer1工作在非BLT模式,LayerMixer1和DMA_P attach在一起,LayerMixer1输出控制参数直接提供给DMA_P使用。此时LayerMixer1仍有两种工作模式,FrameBuffer模式和DIRECT_OUT模式,前者时LayerMixer1和DMA_P之间使用一个overlaydouble buffer做缓冲,输出给DMA_P;DIRECT_OUT模式下不使用该ovl double buffer,LayerMixer1直接输出给DMA_P。
一般VG和RGB的输入都可以是double buffer,ping-pang;LayerMixer的输出也是double buffer。DMA_P/S/E做为display driver传输前端buffer作为后端接口控制器的输入。
下面两图是QC MDP UI mirror和Video mirror时的两结构图,并没有明确画出LayerMix1的Overlay流程路径,个别buffer的owner可能也有所差错,buffer也并不全,仅是大致描述Overlay及其部分buffer。
MDP和DSI和后端显示控制器和接口的连接结构如下图。
Layer architecture
Overlay -> OverlayImpl |
OverlayCtrlData |
OverlayMDPCtrlData |
MDPWrapper |
FrameBuffer |
KeyPoint
Ctrl用来设置overlay channel的参数,Data用来提交buffer到Overlay channel queue。其实使用overlay本质上就是设置好pin路由,设置好通道工作参数,然后不停的提交数据让Overlay Enginee工作。MDP的Overlay Channel并没有别的特殊的编程接口,都是使用控制、状态和数据寄存器来访问。其实MDP提供了远比Android Overlay实现强得多的Overlay功能。
Some flow
Ctrl::commit() -> MDPCtrl::set() -> mdp_wrapper::setOverlay() -> ioctl(fd, MSMFB_OVERLAY_SET, &ov)
-> msm_fb -> mdp4_overlay设置Overlay工作参数。
Data::queueBuffer -> MDPData::play -> mdp_wrapper::play() -> ioctl(fd, MSMFB_OVERLAY_PLAY, &od)
-> msm_fb -> mdp4_overlay进行Overlay合成。注意queueBuffer第一参数fd是memFd,是内核空间的buffer,并不在用户空间和内核空间拷贝buffer数据。作用与framebuffer类似的是提交内核空间的该buffer到Overlay Enginee Queue。
有了这些平台相关知识,msm_fb和mdp4_overlay驱动的功能也就容易理解了。
*****************************************************************************
Overlay & HWC on MDP -- MIMO Display软硬整合
概述
Android显示系统SurfaceFlinger使用Overlay和HWC(Hardware composer)完成Surface Layer的硬件合成。Overlay和HWC表现为两个HAL,为芯片方案制造商留了实现余地。
因为Overlay也时常被称为hardware composition,为了避免混淆,本文中Overlay专指Android Display Overlay HAL,对应liboverlay.so;HWC专指SurfaceFlinger使用的硬件合成器HAL,对应hwcomposer.msm8xxxx.so。
Qualcomm MSM8k系列平台的MDP4.x(Mobile Display Platform)提供了硬件Overlay的功能,Android Overlay HAL与这个Overlay硬件相对应;当然,为了与上段照应,需要说明的是,MDP并不存在一个实现HWC的专门硬件,HWC也是在Overlay上实现的;体现在Android软件中,HWC HAL是建立在Overlay HAL基础上的,是使用Overlay HAL来实现的。
因此,再次,准确地说,提供硬件合成功能的是模块是Overlay,是MDP Overlay硬件的体现;而HWC则是SurfaceFlinger使用Overlay HAL的一个桥梁。
Overlay并非仅仅由SurfaceFlinger得到Surface后就不再经过SurfaceFlinger而使用硬件直接输出;从函数调用上看似乎是这样的,但是实际情形却非如此。硬件合成仅是合成的一种手段,合成的结果输出destination还是必须受控,因而也必须纳入SurfaceFlinger的输出管理范畴。
Overlay硬件平台
MSM8k上目前有MDP4.0, MDP4.1, MDP4.2三个硬件版本,主要是合成能力和管道数目的区别。通常,一个RGB pipe和一个VG pipe组成一个pipe pair,前者是对应UI RGB图像数据,后者对应Camera或Video的RGB或YUV数据,两个pipe输入到一个LayerMixer0用于合成;额外的,LayerMixer可能还有BF(Border Fill) pipe,用于视频按比例显示时屏幕多余边框填充,这个多用于Ext TV或HDMI输出的时候。MDP4有7个pipe,3个LayerMixer,其中LayerMix0可以配置有多达两个RGB pipe,两个VG pipe,一个BF pipe输入,完成5 Layer合成。
上述pipe是LayerMixer的输入元素,LayerMixer的输出对应到LCD,TV,HDMI等,当然不是直接对应,而是由DMA channel和上述模块的控制器相连。三个LayerMixer对应的三个输出使用一般是约定的,当然,软件层面上MDP driver中对每个管道的目标LayerMixer也做了固定的配置。三个输出一般标为Primary, Seconday, Extra,对应的DMA通道为DMA_P, DMA_S, DMA_E。
更直观地,参见Android Overlay on QC MDP4平台要点简记
由于LayerMixer0提供了多达5个Layer的合成能力,所以当没有Camera或Video优先使用它的时候,它被充分利用来做Layer的Hardware composition。提前且概要地,这儿必须清楚说明的是,LayerMixer0使用的两种情景:
当有Camera或Video的时候,SurfaceView对应的Layer为HWC_OVERLAY,这时该Layer对应一个VG pipe,而其余HWC_FRAMEBUFFER Layer经3D GPU render到Framebuffer后,该Framebuffer输入一个pipe(RGB1--base layer?),和VG pipe经LayerMixer0合成输出。
当没有Camera或Video的时候,如果UI Layer即HWC_FRAMEBUFFER Layer小于等于3个且都满足图像格式条件,那么这些Layer的CompositionType属性会被修改为HWC_OVERLAY,为每个Layer分配pipe,经LayerMixer0合成经DMA_P输出,这就是HWC。由于BF pipe能力条件的限制,不使用其做HWC,而RGB1做预留的base layer Framebuffer使用,所以标榜的4-layer mdp composition support实际只能接受SurfceFlinger的3个Layer做合成,也就是SurfaceFlinger的属性debug.mdpcomp.maxlayer=3。当超过3个Layer的时候,由于管道数量的限制,不能够再使用LayerMixer0,就使用GPU render,也即每个Layer->draw。当然GPU render也算是一个Hardware Composition,CompositionType方式的其中之一就是GPU。
Overlay HAL结构
MIMO输入输出代码结构设计,简单地讲,分为流路控制和数据流动两部分;Overlay及构建在其上的hwcomposer都是采用这种流路控制和数据流动的结构。
Overlay有多种应用场景,具体设计上描述为Overlay State。就是说如果允许管道任意灵活组合使用的话,可以有很多种花样的应用,但是这儿预先设计好这么些应用场景,应用场景一定,需要的管道类型和数目随之确定,管道连接的LayerMixer及其输出Video驱动控制器也确定。
Overlay State具体实现使用模板类OverlayImpl
hwcomposer也设计了几个封装类与Overlay应用场景对应,屏蔽了场景功能的具体实现。hwcomposer中定义的应用场景比Overlay提供的场景还要少,仅是有限几个。大致结构图如下:
由于overlay场景太多,因此只示意了四个,OV_BYPASS_x_LAYER可以是1,2,3个,分别对应1个,2个或3个HWC_FRAMEBUFFER Layer和base layer Framebuffer合成的情形。
hwc_prepare的设计逻辑过于固化和简单,成了一种基于if-else判断的优先配置方式,宽阔的城堡顶部建了个鸟笼似的小阁楼;应该设计成Policy或RouteState的形式更具备可扩展性。
Overlay进行流路控制的接口是commit,然后调用其具体实现类和MDP封装类的相关接口,一个调用栈(适当向内核驱动做了扩展)如下:
mdp4_overlay_set()
msmfb_overlay_set()
ioctl(fb, MSMFB_OVERLAY_SET)
overlay::mdp_wrapper::setOverlay()
MdpCtrl::set()
Ctrl::commit()
pipe::commit() -> GernericPipe
OverlayImplBase::commit() -> OverlayImpl<>::commit()
Overlay::commit(overlay::utils::eDest)
Overlay提交数据的接口是queueBuffer,然后同样调用其具体实现类和MDP封装类的相关接口,一个调用栈(适当向内核驱动做了扩展)如下:
mdp4_overlay_play()
msmfb_overlay_play()
ioctl(fd, MSMFB_OVERLAY_PLAY, &od) msmfb_overlay_data.id is mdp_overlay.id is the kernel pipe index in kernel db returned by MdpCtrl.commit()
overlay::mdp_wrapper::play()
MdpData::play(int fd, uint32_t offset)
Data::play()
GenericPipe
OverlayImplBase::queueBuffer() -> OverlayImpl<>::(int fd, uint32_t offset, utils::eDest dest)
Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
Camera或Video Overlay
Camera Overlay一般有两种实现模式,一种模式是V4L2不提供Overlay接口,在用户态把V4L2 buffer handle传递给Framebuffer,由Framebuffer Overlay Implementation完成合成;另一种模式是V4L2提供Overlay接口,可以把Video buffer在V4L2内核驱动中提交给Framebuffer Overlay内核驱动完成合成。QC MSM8k Overlay是采用的第一种模式实现,但是V4L2和Framebuffer驱动也提供了第二种模式的实现;第二种模式可参见Freescale处理器上Android Overlay的实现。
Camera的取景器PreviewDisplay Window是个SurfaceView,所在的Layer(HWC_OVERLAY)在UI Layer底下,但是UI layer会开个全透的区域露出这个HWC_OVERLAY Layer。
下面描述其合成过程。
3D GPU 绘制:所有的HWC_FRAMEBUFFER layers会首先经OpenGL 3D GPU绘制到back Framebuffer上;
OV_PIPE0配置:然后在setupHardwareComposer=> HWComposer::prepare=>hwc_prepare=>VideoOverlay::prepare中使用HWC_OVERLAY Layer参数设置OV_PIPE0分配的类型为Video/Graphic的MDP pipe;
OV_PIPE0提交:DisplayHardware::flip=>HWComposer::commit=>hwc_set=>VideoOverlay::draw提交HWC_OVERLAY Layer的图像数据到OV_PIPE0的内核VG pipe的工作队列中(单容量队列即可);
OV_PIPE3配置:DisplayHardware::flip => HWComposer::commit=> hwc_set=>eglSwapBuffers => queueBuffer=> fb_post=>update_framebuffer中使用base layer back framebuffer参数配置OV_PIPE3即RGB1 pipe(注意base layer的特殊地位);
OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1 pipe时,对应stage 0 for base layer(内核驱动)会启动MDP LayerMixer0进行Layers合成;
DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。
BufferFlip:queueBuffer后,该framebuffer即为front framebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为back framebuffer。
此时Hardware Composer使用的是VideoOverlay@hwcomposer.msm8960.so对应的逻辑。VG pipe设置和提交的栈示例如下:
/system/lib/liboverlay.so (overlay::Overlay::commit(overlay::utils::eDest)+71)
/system/lib/liboverlay.so configPrimVid(hwc_context_t *ctx, hwc_layer_t *layer)
/system/lib/liboverlay.so VideoOverlay::configure(hwc_context_t *ctx, hwc_layer_t *, hwc_layer_t *)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::VideoOverlay::prepare(hwc_context_t *, hwc_layer_list_t *)+xxx)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::setup(hwc_context_t*, hwc_layer_list*)+163) for each pipe.
/system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::configure(hwc_composer_device*, hwc_layer_list*)+59)
/system/lib/hw/hwcomposer.msm8960.so hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
/system/lib/libsurfaceflinger.so (android::HWComposer::prepare() const+9)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::setupHardwareComposer()+127)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::handleRepaint()+147)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+91)
/system/lib/liboverlay.so Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
/system/lib/hw/hwcomposer.msm8960.so (qhwc::VideoOverlay::draw(hwc_context_t*, hwc_layer_list*)+xx) for each pipe.
/system/lib/hw/hwcomposer.msm8960.so static int hwc_set(hwc_composer_device_t *dev,hwc_display_t dpy,hwc_surface_t sur, hwc_layer_list_t* list)
/system/lib/libsurfaceflinger.so (android::HWComposer::commit() const+11)
/system/lib/libsurfaceflinger.so (android::DisplayHardware::flip(android::Region const&) const+59)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::postFramebuffer()+61)
/system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+103)
Base layer对应的RGB pipe设置和提交的栈示例从略。Base layer提交到RGB1时真正启动LayerMixer0的4-stage layer composition。
FB Layer Hardware Composition
当开启硬件(debug.sf.hw=1)合成且合成方式选择MDP时,属性debug.mdpcomp.maxlayer决定了可以使用的通道的个数,当前设置为3,因为RGB1 pipe预留为Camera或Video时的base layer使用,在全Framebuffer layer MDP合成情景下,RGB1对应的base layer是一个处于最顶端的全透明层。相关系统设置和属性如下:
在系统根目录文件 /systembuild.prop中,
debug.sf.hw=1
debug.egl.hw=1
debug.composition.type=mdp
debug.enable.wl_log=1
debug.mdpcomp.maxlayer=3
debug.mdpcomp.logs=0
其中CompositionType有 4种,debug.composition.type=dyn/c2d/mdp/cpu/gpu,dyn是根据c2d和mdp的硬件存在选择使用二者之一。
gpu/c2d/mdp可以释放cpu/gpu,减轻cpu/gpu的负担,但并不一定能使显示效果流畅。
SurfaceFlinger启动时会取debug.mdpcomp.maxlayer属性值记录在全局变量sMaxLayers中。相关代码如下:
782 sMaxLayers = 0;
783 if(property_get("debug.mdpcomp.maxlayer", property, NULL) > 0) {
784 if(atoi(property) != 0)
785 sMaxLayers = atoi(property);
786 }
当没有Camera/Video/HDMI/WFD等Overlay应用情景时,所有Layer都是HWC_FRAMEBUFFER的,MDP的LayerMixer0是闲置的,这时可以优化利用其来做Framebuffer Layer合成。由于管道数目限制的原因,只能合成小于等于sMaxLayers个Layers。多于3个的时候是否可以MDP合成其中的3个?可能需要考虑Layer buffer维度、格式、缩放、Z序等因素。当多于3个的时候,是遍历layer::draw使用GPU来绘制纹理到back framebuffer上的。
下面着重看少于等于3个Layer,MDP合成的情况。
首先,所有的Layer的compositiontype都是HWC_FRAMEBUFFER的;
然后SurfaceFlinger在setHardwareComposer时发现hwc_prepare没有别的优先的Overlay情景,最后的一个if分支就是使用MDP来做Layer合成;SurfaceFlinger会检查Layer的属性看是否满足使用MDP的条件,然后设置满足条件的Layer的属性[compositionType, flags] = (HWC_OVERLAY, MDPCOMP);这样SurfaceFlinger::composeSurfaces时,就不再通过layer::draw使用GPU来绘制;
PIPE配置:SurfaceFlinger为HWC创建工作集,为每个Layer分配并使用MDPComp::prepare配置每个pipe,如果有缩放需求,则会分配VG pipe,因为RGB pipe不支持缩放;有一个Layer则Overlay状态为BYPASS_1_LAYER,表示有1个Layer Bypass,不需要OpenGL绘制,同理2个Layer为BYPASS_2_LAYER,3个为BYPASS_3_LAYER;
PIPE提交:在DisplayHardware::flip中提交每个Layer的数据到管道,即MDPComp::draw() => Overlay::queueBuffer()。注意Layer图像数据是在PMEM/ASHMEM内存中而不是Framebuffer内存中,但仍物理连续或IOMMU映射连续,LayerMixerdraw可访问。MDPComp::draw完即提交数据到对应管道的单容量队列后,清layer->flags &= ~HWC_MDPCOMP标志;
在eglSwapBuffers时,真正做back & front Buffer的切换(准确地说双缓冲是切换,三缓冲是轮用)
OV_PIPE3配置:DisplayHardware::flip => HWComposer::commit=> hwc_set=>eglSwapBuffers => queueBuffer=> fb_post=>update_framebuffer中使用base layer back framebuffer参数配置OV_PIPE3即RGB1 pipe;
OV_PIPE3提交:当update_framebuffer真正提交图像数据到RGB1 pipe时,对应stage 0 for base layer(内核驱动)会启动MDP LayerMixer0进行Layers合成;此时base layer是个最顶层的全透明Layer,不妨碍底层Layer的显示;
DMA传输:然后fb_post通过FBIOPUT_VSCREENINFO或PAN_DISPLAY启动DMA_P送合成好的图像经MIPI-DSI输出显示。
BufferFlip:queueBuffer后,该framebuffer即为front framebuffer,然后eglSwapBuffers会dequeueBuffer下一个Framebuffer做为back framebuffer。
HWC功能时,是MDPComp@hwcomposer.msm8960.so对应的逻辑,一个Layer对应的 pipe设置和提交的栈示例如下:
#00 pc 0000b0d0 /system/lib/liboverlay.so (overlay::Overlay::commit(overlay::utils::eDest)+71)
#01 pc 00005d16 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::prepare(hwc_context_t*, hwc_layer*, qhwc::MDPComp::mdp_pipe_info&)+577)
#02 pc 00006190 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::setup(hwc_context_t*, hwc_layer_list*)+163) for each pipe.
#03 pc 0000652c /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::configure(hwc_composer_device*, hwc_layer_list*)+59)
#04 pc 000037ea /system/lib/hw/hwcomposer.msm8960.so hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list)
#05 pc 0001ebba /system/lib/libsurfaceflinger.so (android::HWComposer::prepare() const+9)
#06 pc 00020df4 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::setupHardwareComposer()+127)
#07 pc 000212f4 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::handleRepaint()+147)
#08 pc 000222e8 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+91)
/system/lib/liboverlay.so Overlay::queueBuffer(int fd, uint32_t offset, utils::eDest dest)
#00 pc 00006592 /system/lib/hw/hwcomposer.msm8960.so (qhwc::MDPComp::draw(hwc_context_t*, hwc_layer_list*)+85) for each pipe.
#01 pc 00003ae4 /system/lib/hw/hwcomposer.msm8960.so static int hwc_set(hwc_composer_device_t *dev,hwc_display_t dpy,hwc_surface_t sur, hwc_layer_list_t* list)
#02 pc 0001ed56 /system/lib/libsurfaceflinger.so (android::HWComposer::commit() const+11)
#03 pc 0001e57c /system/lib/libsurfaceflinger.so (android::DisplayHardware::flip(android::Region const&) const+59)
#04 pc 000209c6 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::postFramebuffer()+61)
#05 pc 00022474 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int)+103)
Base layer对应的RGB pipe设置和提交的栈示例从略。重复地,Base layer提交到RGB1时真正启动LayerMixer0的4-stage layer composition。
HDMI / WFD
有机会待续。
LayerMixer0合成后图像经DMA_P输出时,在BLT模式和DIRECTOUT模式下LayerMixer0与DMA之间没有buffer;即使FB模式,也是一个Overlay buffer而未必是做为Framebuffer内存,当然这个Overlay buffer做为DMA_P输入传输给MIPI-DSI/LCDC。
*****************************************************************************
FrameBuffer driver including MDP Overlay
Framebuffer设备的sysfs
330static int msm_fb_create_sysfs(struct platform_device *pdev)
331{
332 int rc;
333 struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
334
335 rc = sysfs_create_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group);
336 if (rc)
337 MSM_FB_ERR("%s: sysfs group creation failed, rc=%d\n", __func__,
338 rc);
339 return rc;
340}
root@android:/sys/class/graphics/fb0 # ls -al
-rw-r--r-- root root 4096 1970-06-27 09:37 bits_per_pixel
-rw-r--r-- root root 4096 1970-06-27 09:37 blank
-rw-r--r-- root root 4096 1970-06-27 09:37 console
-rw-r--r-- root root 4096 1970-06-27 09:37 cursor
-r--r--r-- root root 4096 1970-06-27 09:37 dev
-rw-r--r-- root root 4096 1970-06-27 09:37 mode
-rw-r--r-- root root 4096 1970-06-27 09:37 modes
-r--r--r-- root root 4096 1970-06-27 09:37 msm_fb_type
-r--r--r-- root root 4096 1970-06-27 09:37 name
-rw-r--r-- root root 4096 1970-06-27 09:37 pan
drwxr-xr-x root root 1970-06-27 08:28 power
-rw-r--r-- root root 4096 1970-06-27 09:37 rotate
-rw-r--r-- root root 4096 1970-06-27 09:37 state
-r--r--r-- root root 4096 1970-06-27 09:37 stride
lrwxrwxrwx root root 1970-06-27 09:37 subsystem -> ../../../../class/graphics
-rw-r--r-- root root 4096 1970-06-27 08:28 uevent
-rw-r--r-- root root 4096 1970-06-27 09:37 virtual_size
-r--r--r-- root root 4096 1970-06-27 08:28 vsync_event
root@android:/sys/class/graphics/fb0 # cat msm_fb_type
mipi dsi cmd panel
root@android:/sys/class/graphics/fb0 # cat bits_per_pixel
32
130|root@android:/sys/class/graphics/fb0 # cat dev
29:0
root@android:/sys/class/graphics/fb0 # cat modes
U:480x854p-0
root@android:/sys/class/graphics/fb0 # cat name
msmfb42_90501
root@android:/sys/class/graphics/fb0 # cat stride
1920
root@android:/sys/class/graphics/fb0 # cat virtual_size
480,2566
cont_splash_done field
Add support for "Continuous Splash Screen" feature.
The image displayed on the screen by the android bootloaderdriver should continue till the android animation shows up.
Delay the display initialization for MDP, display dependent clocksand panel power on functions.
bootloader显示的image在linux内核启动过程中保持显示在屏幕上,知道开机动画显示,即linux内核启动过程中不要出现黑屏。
Early suspend & Early resume
Early suspend是有wakelock还占有,系统还不能整体suspend,但是可以关闭屏幕、背光、输入等;在Early suspended状态时,重新打开屏幕、背光和输入,是为对应的early resume。
fb_register中相关设置如下:
1551 mfd->early_suspend.suspend = msmfb_early_suspend;
1552 mfd->early_suspend.resume = msmfb_early_resume;
1553 mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
1554 register_early_suspend(&mfd->early_suspend);
数据结构定义如下:
23/* The early_suspend structure defines suspend and resume hooks to be called
24 * when the user visible sleep state of the system changes, and a level to
25 * control the order. They can be used to turn off the screen and input
26 * devices that are not used for wakeup.
27 * Suspend handlers are called in low to high level order, resume handlers are
28 * called in the opposite order. If, when calling register_early_suspend,
29 * the suspend handlers have already been called without a matching call to the
30 * resume handlers, the suspend handler will be called directly from
31 * register_early_suspend. This direct call can violate the normal level order.
32 */
33enum {
34 EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
35 EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
36 EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
37};
38struct early_suspend {
39#ifdef CONFIG_HAS_EARLYSUSPEND
40 struct list_head link;
41 int level;
42 void (*suspend)(struct early_suspend *h);
43 void (*resume)(struct early_suspend *h);
44#endif
45};
backlight
msm_fb_set_backlight以后是使用led_trigger调用真正led_classdev "wled"的brightnes_set去设置背光。
用户态ioctl通过msm_fb_set_backlight调用到msm_fb_panel_data::set_backlight,
"lcd_backlight".brightness_set -> msm_fb_panel_data::set_backlight -> "bkl_trigger".led_trigger -> "wled".brightness_set。然后找真正操作硬件IC部分。
驱动中设置背光则是绕过"lcd_backlight"设备直接通过backlight_worker工作执行到msm_fb_panel_data::set_backlight,然后-> "bkl_trigger".led_trigger -> "wled".brightness_set。
可以认为"lcd_backlight"是背光抽象设备,通过led_trigger的led组映射到不同的led_classdev设备
以三星DSI CMD屏为例:
In mipi_samsung.c
294static void mipi_samsung_set_backlight(struct msm_fb_data_type *mfd)
295{
296 if (!cci_fb_UpdateDone){
297 printk("Taylor: No BL before LCM on\n");
298 return;
299 }
300
301 pr_debug("Taylor: %s : Set BL:%d\n",__func__, mfd->bl_level);
302 if ((mipi_samsung_pdata->enable_wled_bl_ctrl)
303 && (wled_trigger_initialized)) {
304 led_trigger_event(bkl_led_trigger, mfd->bl_level);
305 return;
306 }
307}
kernel/drivers/leds/leds-pm8xxx.c
#define PM8XXX_LEDS_DEV_NAME "pm8xxx-led"
2283static struct platform_driver pm8xxx_led_driver = {
2284 .probe = pm8xxx_led_probe,
2285 .remove = __devexit_p(pm8xxx_led_remove),
2286 .driver = {
2287 .name = PM8XXX_LEDS_DEV_NAME,
2288 .owner = THIS_MODULE,
2289 },
2290};
pm8xxx_led_probe会对pm8038_led_info数组中的每个led使用设置led_classdev字段,并且初始化work item,然后使用led_classdev_register向系统注册每个led设备。
2197 INIT_WORK(&led_dat->work, pm8xxx_led_work);
2198 INIT_WORK(&led_dat->modework, pm8xxx_mode_work);
2199 INIT_WORK(&led_dat->testwork, pm8xxx_test_work);
每个led的brightness_set字段设置为pm8xxx_led_set。
1790static void pm8xxx_led_set(struct led_classdev *led_cdev,
1791 enum led_brightness value)
1792{
1793 struct pm8xxx_led_data *led;
1794
1795 led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
1796
1797 if (value < LED_OFF || value > led->cdev.max_brightness) {
1798 dev_err(led->cdev.dev, "Invalid brightness value exceeds");
1799 return;
1800 }
1801
1802 led->cdev.brightness = value;
1803 schedule_work(&led->work);
1804}
1730static void pm8xxx_led_work(struct work_struct *work)
1731{
1732 int rc;
1733
1734 struct pm8xxx_led_data *led = container_of(work,
1735 struct pm8xxx_led_data, work);
1736
1737 if (led->pwm_dev == NULL) {
1738 __pm8xxx_led_work(led, led->cdev.brightness);
1739 } else {
1740 rc = pm8xxx_led_pwm_work(led);
1741 if (rc)
1742 pr_err("could not configure PWM mode for LED:%d\n",
1743 led->id);
1744 }
1745}
对PM8XXX_ID_WLED,是使用__pm8xxx_led_work
1692static void __pm8xxx_led_work(struct pm8xxx_led_data *led,
1693 enum led_brightness level)
1694{
1695 int rc;
1696
1697 mutex_lock(&led->lock);
1698
1699 switch (led->id) {
1700 case PM8XXX_ID_LED_KB_LIGHT:
1701 led_kp_set(led, level);
1702 break;
1703 case PM8XXX_ID_LED_0:
1704 case PM8XXX_ID_LED_1:
1705 case PM8XXX_ID_LED_2:
1706 led_lc_set(led, level);
1707 break;
1708 case PM8XXX_ID_FLASH_LED_0:
1709 case PM8XXX_ID_FLASH_LED_1:
1710 led_flash_set(led, level);
1711 break;
1712 case PM8XXX_ID_WLED:
1713 rc = led_wled_set(led, level);
1714 if (rc < 0)
1715 pr_err("wled brightness set failed %d\n", rc);
1716 break;
1717 case PM8XXX_ID_RGB_LED_RED:
1718 case PM8XXX_ID_RGB_LED_GREEN:
1719 case PM8XXX_ID_RGB_LED_BLUE:
1720 led_rgb_set(led, level);
1721 break;
1722 default:
1723 dev_err(led->cdev.dev, "unknown led id %d", led->id);
1724 break;
1725 }
1726
1727 mutex_unlock(&led->lock);
1728}
led_wled_set写电源管理芯片pm8xxx的控制寄存器,控制wled。
Framebuffer fb_info::node
registered_fb它是一个数组,它的类型就是struct fb_info,它用于保存我们调用register_framebuffer传进来的struct fb_info。
num_registered_fb代表注册帧缓冲设备的个数。
1522 for (i = 0; i < FB_MAX; i++)
1523 if (!registered_fb[i])
1524 break;
1525 fb_info->node = i;
相当于找到一个空的次设备号。
Framebuffer像素格式
主屏RGBA8888
In mipi_dsi_probe()
481 /*
482 * get/set panel specific fb info
483 */
484 mfd->panel_info = pdata->panel_info;
485 pinfo = &mfd->panel_info;
486
487 if (mfd->panel_info.type == MIPI_VIDEO_PANEL)
488 mfd->dest = DISPLAY_LCDC;
489 else
490 mfd->dest = DISPLAY_LCD;
491
492 if (mdp_rev == MDP_REV_303 &&
493 mipi_dsi_pdata->get_lane_config) {
494 if (mipi_dsi_pdata->get_lane_config() != 2) {
495 pr_info("Changing to DSI Single Mode Configuration\n");
496#ifdef CONFIG_FB_MSM_MDP303
497 update_lane_config(pinfo);
498#endif
499 }
500 }
501
502 if (mfd->index == 0)
503 mfd->fb_imgType = MSMFB_DEFAULT_TYPE; // configed as RGBA8888 for fb0
504 else
505 mfd->fb_imgType = MDP_RGB_565;
msmfb_update_notify/ msmfb_no_update_notify
用于CABL功能时,统计直方图使用;
更新屏幕前complete(&msmfb_update_notify)从而在准确时间点启动直方图统计;2*HZ后,msmfb_no_update_notify_timer超时,在timer超时回调中complete(&mfd->msmfb_no_update_notify)结束直方图统计。
sw_refresher
使用定时器去触发mdp_refresh_screen,添加work进行dma_fnc调用。
针对某些接口的VIDEO模式屏且控制器没有hw_refresh支持时,可以使用sw_refresher。
FB_ACTIVATE_VBL标志涵义
fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL,表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来。这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中。
Framebuffer初始化
设备资源申请是在MACHINE_DESC中实现的。示例如下:
3463MACHINE_START(MSM8930_CDP, "QCT MSM8930 CDP")
3464 .map_io = msm8930_map_io,
3465 .reserve = msm8930_reserve,
3466 .init_irq = msm8930_init_irq,
3467 .handle_irq = gic_handle_irq,
3468 .timer = &msm_timer,
3469 .init_machine = msm8930_cdp_init,
3470 .init_early = msm8930_allocate_memory_regions,
3471 .init_very_early = msm8930_early_memory,
3472 .restart = msm_restart,
3473MACHINE_END
machine_desc{.init_very_early, .init_early, .init_machine, .restart}, module_init driver的初始化顺序参考Machine_desc & boot & Kernel_init & initcall & module_init
函数msm8930_cdp_init做Machine级别的设备的注册,部分代码如下:
static void __init msm8930_cdp_init(void) @ kernel/arch/arm/mach-msm/board-8930.c
{
…
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm8930_init_gpu();
msm8930_init_mmc();
msm8930_init_cam();
msm8930_init_fb();
}
msm8930_cdp_init中与display相关的是msm8930_init_fb()函数,这个函数注册了几个id为0的设备。各主要设备名如下,
“msm_fb” msm framebuffer设备,注意不是linux framebuffer设备,但是有对应关系;
“wfd” wifi显示设备;
“mipi_dsi_cmd_samsung_fwvga” mipi-dsi接口cmd模式LCD屏设备;
“hdmi_msm” HDMI显示器设备;
“mdp” mobile display station显示引擎设备;
“mipi-dsi” MIPI-DSI显示器驱动设备(id例外用了1,可能有的平台上有两个MIPI-DSI,另一个id为0);
1168void __init msm8930_init_fb(void) @ kernel/arch/arm/mach-msm/board-8930-display.c
1169{
1170 platform_device_register(&msm_fb_device);
1171
1172#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
1173 platform_device_register(&wfd_panel_device);
1174 platform_device_register(&wfd_device);
1175#endif
1176
1178#ifdef CONFIG_FB_MSM_MIPI_NOVATEK_CMD_QHD_PT
1179 platform_device_register(&mipi_dsi_novatek_panel_device);
1180#endif
1181
1184#ifdef CONFIG_FB_MSM_MIPI_SA77_CMD_FWVGA_PANEL
1185 platform_device_register(&mipi_dsi_cmd_chimei_fwvga_panel_device);
1186 platform_device_register(&mipi_dsi_cmd_samsung_fwvga_panel_device);
1187#endif
1189
1190#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
1191 platform_device_register(&hdmi_msm_device);
1192#endif
1193
1194 platform_device_register(&mipi_dsi_toshiba_panel_device);
1195
1196 msm_fb_register_device("mdp", &mdp_pdata);
1197 msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata);
1198#ifdef CONFIG_MSM_BUS_SCALING
1199#ifdef CONFIG_FB_MSM_DTV
1200 msm_fb_register_device("dtv", &dtv_pdata);
1201#endif
1202#endif
1203}
因为注册这些设备的意图主要是资源申请和初步初始化设备,所以各设备注册顺序并无关紧要。其初始化顺序还与后来的驱动实际注册顺序有关。
首先注册paltform_devicemsm_fb_device,设备定义如下
71static struct resource msm_fb_resources[] = {
72 {
73 .flags = IORESOURCE_DMA,
74 }
75};
135static struct msm_fb_platform_data msm_fb_pdata = {
136 .detect_client = msm_fb_detect_panel,
137};
138
139static struct platform_device msm_fb_device = {
140 .name = "msm_fb",
141 .id = 0,
142 .num_resources = ARRAY_SIZE(msm_fb_resources),
143 .resource = msm_fb_resources,
144 .dev.platform_data = &msm_fb_pdata,
145};
然后注册panel设备,设备定义如下
845static struct mipi_dsi_panel_platform_data samsung_pdata = {
846 .enable_wled_bl_ctrl = 0x1,
847};
848
849static struct platform_device mipi_dsi_cmd_samsung_fwvga_panel_device = {
850 .name = "dsi_cmd_samsung_fwvga",
851 .id = 0,
852 .dev = {
853 .platform_data = &samsung_pdata,
854 }
855};
然后关键的注册mdp和mipi-dsi controller,设备定义如下
1749void __init msm_fb_register_device(char *name, void *data)
1750{
1751 if (!strncmp(name, "mdp", 3))
1752 msm_register_device(&msm_mdp_device, data);
1753 else if (!strncmp(name, "lcdc", 4))
1754 msm_register_device(&msm_lcdc_device, data);
1755 else if (!strncmp(name, "mipi_dsi", 8))
1756 msm_register_device(&msm_mipi_dsi_device, data);
1757#ifdef CONFIG_FB_MSM_TVOUT
1758 else if (!strncmp(name, "tvenc", 5))
1759 msm_register_device(&msm_tvenc_device, data);
1760 else if (!strncmp(name, "tvout_device", 12))
1761 msm_register_device(&msm_tvout_device, data);
1762#endif
1763#ifdef CONFIG_MSM_BUS_SCALING
1764 else if (!strncmp(name, "dtv", 3))
1765 msm_register_device(&msm_dtv_device, data);
1766#endif
1767 else
1768 printk(KERN_ERR "%s: unknown device! %s\n", __func__, name);
1769}
mdp和mipi-dsi设备及寄存器映射和中断需求如下
1484#define MIPI_DSI_HW_BASE 0x04700000
1485#define ROTATOR_HW_BASE 0x04E00000
1486#define TVENC_HW_BASE 0x04F00000
1487#define MDP_HW_BASE 0x05100000
1488
1489static struct resource msm_mipi_dsi_resources[] = {
1490 {
1491 .name = "mipi_dsi",
1492 .start = MIPI_DSI_HW_BASE,
1493 .end = MIPI_DSI_HW_BASE + 0x000F0000 - 1,
1494 .flags = IORESOURCE_MEM,
1495 },
1496 {
1497 .start = DSI_IRQ,
1498 .end = DSI_IRQ,
1499 .flags = IORESOURCE_IRQ,
1500 },
1501};
1502
1503static struct platform_device msm_mipi_dsi_device = {
1504 .name = "mipi_dsi",
1505 .id = 1,
1506 .num_resources = ARRAY_SIZE(msm_mipi_dsi_resources),
1507 .resource = msm_mipi_dsi_resources,
1508};
1509
1510static struct resource msm_mdp_resources[] = {
1511 {
1512 .name = "mdp",
1513 .start = MDP_HW_BASE,
1514 .end = MDP_HW_BASE + 0x000F0000 - 1,
1515 .flags = IORESOURCE_MEM,
1516 },
1517 {
1518 .start = INT_MDP,
1519 .end = INT_MDP,
1520 .flags = IORESOURCE_IRQ,
1521 },
1522};
1523
1524static struct platform_device msm_mdp_device = {
1525 .name = "mdp",
1526 .id = 0,
1527 .num_resources = ARRAY_SIZE(msm_mdp_resources),
1528 .resource = msm_mdp_resources,
1529};
以上设备注册时,其驱动并未加载,因为machine_desc.init_machine设备注册的arch_initcall是在.initcall3.init中,而module_init driver注册是在.initcall6.init中。等其驱动加载时,在对应的probe函数中,判断设备id,初步初始化设备,保存其资源分配。
此时,以下id为0的设备panel, dtv, hdmi, mdp, msm_fb, id为1的mipi-dsi设备已经注册进系统了。
dsi_cmd_chimei_fwvga.0
dsi_cmd_samsung_fwvga.0
dsi_cmd_samsung_fwvga.1281
dtv.0
dtv.458753
hdmi_msm.0
hdmi_msm.1
mdp.0
mdp.458753
mdp.591105
mdp.655361
mipi_dsi.1
mipi_dsi.591105
mipi_toshiba.0
msm_fb.0
msm_fb.458753
msm_fb.591105
msm_fb.655361
下面描述各驱动注册,各驱动都是module_init的。
msm_fb驱动注册如下
module_init(msm_fb_init);
3898int __init msm_fb_init(void) @ kernel/drivers/video/msm/msm_fb.c
3899{
3900 int rc = -ENODEV;
3901
3902 if (msm_fb_register_driver())
3903 return rc;
….
}
3705static int msm_fb_register_driver(void)
3706{
3707 return platform_driver_register(&msm_fb_driver);
3708}
msm_fb_driver驱动定义如下
734static struct platform_driver msm_fb_driver = {
735 .probe = msm_fb_probe,
736 .remove = msm_fb_remove,
737#ifndef CONFIG_HAS_EARLYSUSPEND
738 .suspend = msm_fb_suspend,
739 .resume = msm_fb_resume,
740#endif
741 .shutdown = NULL,
742 .driver = {
743 /* Driver name must match the device name added in platform.c. */
744 .name = "msm_fb",
745 .pm = &msm_fb_dev_pm_ops,
746 },
747};
platform_device “msm_fb”的resource[0]是一块DMA内存,是framebuffer内存,但是在资源定义中并没有设置size,而在msm_fb_probe中从其使用可以看到该DMA内存已经分配。其size是在machine的init_early中从bootmem中分配的,比machine级别设备注册要早!
.init_early = msm8930_allocate_memory_regions,
msm8930_allocate_memory_regions(void) @ kernel/arch/arm/mach-msm/board-8930.c
1006static void __init msm8930_allocate_memory_regions(void)
1007{
1008 msm8930_allocate_fb_region();
1009}
msm8930_allocate_fb_region() @ kernel/arch/arm/mach-msm/board-8930-display.c
1205void __init msm8930_allocate_fb_region(void)
1206{
1207 void *addr;
1208 unsigned long size;
1209
1210 size = MSM_FB_SIZE;
1211 addr = alloc_bootmem_align(size, 0x1000);
1212 msm_fb_resources[0].start = __pa(addr);
1213 msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1;
1214 pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", size, addr, __pa(addr));
1216}
MSM_FB_SIZE宏定义如下,TRIPLE_BUFFER已是主流
32#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
33#define MSM_FB_PRIM_BUF_SIZE \
34 (roundup((1920 * 1088 * 4), 4096) * 3) /* 4 bpp x 3 pages */
35#else
36#define MSM_FB_PRIM_BUF_SIZE \
37 (roundup((1920 * 1088 * 4), 4096) * 2) /* 4 bpp x 2 pages */
38#endif
39/* Note: must be multiple of 4096 */
40#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE, 4096)
当”msm_fb”注册时,msm_fb_probe在设备和驱动match后被调用。对于msm_fb_device id=0,只做fbram保存和ION client创建;这时probe到的设备对应/sys/bus/platform/devices /msm_fb.0。
msm_ion_client_create(-1, pdev->name);
”mipi-dsi” driver定义和注册如下 (in kernel/drivers/video/msm/mipi_dsi.c)
55static struct platform_driver mipi_dsi_driver = {
56 .probe = mipi_dsi_probe,
57 .remove = mipi_dsi_remove,
58 .shutdown = NULL,
59 .driver = {
60 .name = "mipi_dsi",
61 },
62};
603static int mipi_dsi_register_driver(void)
604{
605 return platform_driver_register(&mipi_dsi_driver);
606}
607
608static int __init mipi_dsi_driver_init(void)
609{
610 int ret;
611
612 mipi_dsi_init();
613
614 ret = mipi_dsi_register_driver();
615
616 device_initialize(&dsi_dev);
617
618 if (ret) {
619 pr_err("mipi_dsi_register_driver() failed!\n");
620 return ret;
621 }
622
623 return ret;
624}
625
626module_init(mipi_dsi_driver_init);
“mdp” driver定义和注册如下(in kernel/drivers/video/msm/mdp.c)
2094static struct platform_driver mdp_driver = {
2095 .probe = mdp_probe,
2096 .remove = mdp_remove,
2097#ifndef CONFIG_HAS_EARLYSUSPEND
2098 .suspend = mdp_suspend,
2099 .resume = NULL,
2100#endif
2101 .shutdown = NULL,
2102 .driver = {
2103 /*
2104 * Driver name must match the device name added in
2105 * platform.c.
2106 */
2107 .name = "mdp",
2108 .pm = &mdp_dev_pm_ops,
2109 },
2110};
3001static int mdp_register_driver(void)
3002{
3003#ifdef CONFIG_HAS_EARLYSUSPEND
3004 early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1;
3005 early_suspend.suspend = mdp_early_suspend;
3006 early_suspend.resume = mdp_early_resume;
3007 register_early_suspend(&early_suspend);
3008#endif
3009
3010 return platform_driver_register(&mdp_driver);
3011}
3012
3013static int __init mdp_driver_init(void)
3014{
3015 int ret;
3016
3017 mdp_drv_init();
3018
3019 ret = mdp_register_driver();
3020 if (ret) {
3021 printk(KERN_ERR "mdp_register_driver() failed!\n");
3022 return ret;
3023 }
3024
3025#if defined(CONFIG_DEBUG_FS)
3026 mdp_debugfs_init();
3027#endif
3028
3029 return 0;
3030
3031}
3032
3033module_init(mdp_driver_init);
当真正从屏驱动中添加一块显示设备时,为了让上级设备(“mipi-dsi” “mdp” “msm_fb” “fb”)使用下级设备,高通实现为下级设备创建了每个上级设备的实例,通过从下到上的设备probe链一级一级向上注册。这时保证从下到上的设备注册顺序就是至关重要的了,probe链做注册来保证这一点。
当然为了达到上级设备使用和管理下级设备的目标,另一种方法是下级设备向上级设备做register,这时要保证下级设备向上级设备注册时,上级设备的用于管理的相关数据结构已经准备好。
下面描述由屏驱动添加屏到注册linux framebuffer设备的流程。
在各自的屏设备注册文件中,会去探测屏,这种探测不是做真正扫描,仅仅是使用设备名字验证一下,以SAMSUNG MIPI DSI CMD屏为例,驱动会使用相应规则ID注册一块屏。
static int __init mipi_cmd_samsung_fwvga_pt_init(void) @ kernel/drivers/video/msm/mipi_samsung_cmd_fwvga_pt.c
{
37 int ret;
38
39 if (msm_fb_detect_client("mipi_cmd_samsung_fwvga"))
40 return 0;
……
88 ret = mipi_samsung_device_register(&pinfo, MIPI_DSI_PRIM, MIPI_DSI_PANEL_QHD_PT);
90 if (ret)
91 pr_err("%s: failed to register device!\n", __func__);
92
93 return ret;
94}
95
96module_init(mipi_cmd_samsung_fwvga_pt_init);
探测函数int msm_fb_detect_client(const char *name)首先使用主屏和外屏名字匹配,匹配不成则使用msm_fd_device.dev.platform .detect_client = msm_fb_detect_panel去匹配。上面欲同时注册SAMSUNG和CHIMEI两块屏设备,当然不能同时detect_panel成功,使用GPIO管脚配置做了二次匹配。实际不同批次的手机可能用到两块屏中的一种。
然后mipi_samsung_device_register()(对CHIMEI则是mipi_chimei_device_register)注册屏驱动和屏设备。驱动定义和注册具体如下,
358static struct platform_driver this_driver = {
359 .probe = mipi_samsung_lcd_probe,
360 .driver = {
361 .name = "dsi_cmd_samsung_fwvga",
362 },
363};
364
365static struct msm_fb_panel_data samsung_panel_data = {
366 .on = mipi_samsung_lcd_on,
367 .off = mipi_samsung_lcd_off,
368 .set_backlight = mipi_samsung_set_backlight,
369};
373int mipi_samsung_device_register(struct msm_panel_info *pinfo, u32 channel, u32 panel)
375{
376 struct platform_device *pdev = NULL;
377 int ret;
378
379 if ((channel >= 3) || ch_used[channel])
380 return -ENODEV;
381
382 ch_used[channel] = TRUE;
383
384 ret = mipi_samsung_lcd_init();
385 if (ret) {
386 pr_err("mipi_samsung_lcd_init() failed with ret %u\n", ret);
387 return ret;
388 }
389
390 pdev = platform_device_alloc("dsi_cmd_samsung_fwvga", (panel << 8)|channel);
391 if (!pdev)
392 return -ENOMEM;
393
394 samsung_panel_data.panel_info = *pinfo;
395
396 ret = platform_device_add_data(pdev, &samsung_panel_data,
397 sizeof(samsung_panel_data));
398 if (ret) {
399 printk(KERN_ERR
400 "%s: platform_device_add_data failed!\n", __func__);
401 goto err_device_put;
402 }
403
404 ret = platform_device_add(pdev);
405 if (ret) {
406 printk(KERN_ERR
407 "%s: platform_device_register failed!\n", __func__);
408 goto err_device_put;
409 }
410
411 return 0;
412
413err_device_put:
414 platform_device_put(pdev);
415 return ret;
416}
417
418static int mipi_samsung_lcd_init(void)
419{
420
421 led_trigger_register_simple("bkl_trigger", &bkl_led_trigger);
422 pr_info("%s: SUCCESS (WLED TRIGGER)\n", __func__);
423 wled_trigger_initialized = 1;
424
425 mipi_dsi_buf_alloc(&samsung_tx_buf, DSI_BUF_SIZE);
426 mipi_dsi_buf_alloc(&samsung_rx_buf, DSI_BUF_SIZE);
427
428 return platform_driver_register(&this_driver);
429}
在mipi_samsung_lcd_init()中注册platform_driver屏驱动,然后分配注册platform_device屏设备;platform_data设置为msm_fb_panel_data向上传递参数并用于上级设备调用控制屏开关和背光。
屏设备注册后,platform_device和platform_driver match,驱动的probe函数被调用,把参数一级一级向上带,用于设置上级设备参数,向上一级一级注册设备(“mipi-dsi”, “mdp”, “msm_fb”, “framebuffer”)。
一个类似的调用栈如下
------------[ cut here ]------------
WARNING: at /home/CORPUSERS/xp010548/myworkdroid/7x25a/LINUX/kernel/drivers/video/msm/msm_fb.c:1221 msm_fb_probe+0xf4/0xcbc()
msm_fb_probe
Modules linked in:
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
---[ end trace 65f8ea860415c051 ]---
下面考察设备probe链。
311static int __devinit mipi_samsung_lcd_probe(struct platform_device *pdev)
312{
…..
338 current_pdev = msm_fb_add_device(pdev);
339
340 if (current_pdev) {
341 mfd = platform_get_drvdata(current_pdev);
342 if (!mfd)
343 return -ENODEV;
344 if (mfd->key != MFD_KEY)
345 return -EINVAL;
346
347 mipi = &mfd->panel_info.mipi;
348
349 if (phy_settings != NULL)
350 mipi->dsi_phy_db = phy_settings;
351
352 if (dlane_swap)
353 mipi->dlane_swap = dlane_swap;
354 }
355 return 0;
356}
msm_fb_add_device做如下事情:
调用struct platform_device *msm_fb_device_alloc(struct msm_fb_panel_data *pdata, u32 type, u32 id)函数分配platform_device “mipi-dsi.type_devid”并设置其platform_data为msm_fb_panel_data。
以额外msm_fb_data_type结构分配framebuffer。
注册”mipi-dsi”设备platform_device_add(this_dev)。
“mipi-dsi”设备和驱动进行attach, match后,“mipi-dsi” driver的probe被调用到。
static int mipi_dsi_probe(struct platform_device *pdev) @ kernel/drivers/video/msm/mipi_dsi.c做如下事情(这次的设备ID是真正实例的了,不是1了):
分配”mdp”设备实例并设置其数据,设置”mipi-dsi”设备操作函数,用于向下操作一级一级设备;
继续设置linux framebuffer的参数;
根据屏分辨率设置mipi-dsi工作频率;
注册该”mdp”设备实例;
“mdp”设备和驱动进行attach, match后,“mdp” driver的probe被调用到。
static int mdp_probe(struct platform_device *pdev) @ kernel/drivers/video/msm/mdp.c
分配”msm_fb”实例并设置其数据;
配置”mdp”工作模式;
注册”msm_fb”实例;
“msm_fb”设备和驱动进行attach, match后,“msm_fb” driver的probe被调用到。
static int msm_fb_probe(struct platform_device *pdev) @ kernel/drivers/video/msm/msm_fb.c
调用msm_fb_register设置linux framebuffer并register_framebuffer。
至此,系统中相关设备如下:
dsi_cmd_chimei_fwvga.0
dsi_cmd_samsung_fwvga.0
dsi_cmd_samsung_fwvga.1281 ->mipi_dsi.591105 -> mdp.591105 -> msm_fb.591105 -> fbx
dtv.0
dtv.458753 ->mdp.458753 -> msm_fb.458753 -> fbx
hdmi_msm.0
hdmi_msm.1 ->mdp.655361 -> msm_fb.655361 -> fbx (hdmi)
mdp.0
mipi_dsi.1
mipi_toshiba.0
msm_fb.0
*******************************************************************************
MDP图像合成和显示过程—Framebuffer驱动
Overlay设置和提交过程
msmfb_overlay_set(struct fb_info *info, void __user *p)
è mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req)
申请pipe,并设置pipe的clock和bandwidth。
int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
è int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req)
计算pipe输入缓冲地址、参数配置等;将request暂存,如对DSI CMD屏,是使用void mdp4_dsi_cmd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe),而DSI VIDEO屏是使用void mdp4_dsi_video_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)。
base_pipe设置和提交,Layer Mix, DMA
当送FBIOPUT_VSCREENINFO或FBIOPAN_DISPLAY命令给FB设备时,最终会调用msm_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
msm_fb_pan_display
è mdp_set_dma_pan_info(info, dirtyPtr, (var->activate == FB_ACTIVATE_VBL)); //设置DMA区域
è mdp_dma_pan_update(info); //启动DMA,包括Layer Mixer合成和DMA输出
void mdp_dma_pan_update(struct fb_info *info)
è mfd->dma_fnc(mfd);
对MIPI DSI CMD屏,该DMA函数是mdp4_dsi_cmd_overlay。
void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd)
{
……
pipe = vctrl->base_pipe;
……
if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) { // it is certain for base_pipe
// it is setup in mdp4_overlay_update_dsi_cmd when dsi cmd panel is on.
mdp4_mipi_vsync_enable(mfd, pipe, 0);
mdp4_overlay_setup_pipe_addr(mfd, pipe);
mdp4_dsi_cmd_pipe_queue(0, pipe); // arm the base pipe with framebuffer
}
mdp4_overlay_mdp_perf_upd(mfd, 1); // level up the mdp performance
mutex_lock(&mfd->dma->ov_mutex);
mdp4_dsi_cmd_pipe_commit(cndx, 0); // fire the overlay layer mixer and dma
mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
}
int mdp4_dsi_cmd_pipe_commit(int cndx, int wait)
{
int i, undx;
int mixer = 0;
struct vsycn_ctrl *vctrl;
struct vsync_update *vp;
struct mdp4_overlay_pipe *pipe;
struct mdp4_overlay_pipe *real_pipe;
unsigned long flags;
int need_dmap_wait = 0;
int need_ov_wait = 0;
int cnt = 0;
/**
*static struct vsycn_ctrl { … } vsync_ctrl_db [MAX_CONTROLLER]
*在不同的显示器文件中有不同的定义,注意其是static的。
*不同显示应用时关联不同的LayerMixer,所以同一种应用中,
*使用具有同一mixer_num的pipe,mixer_num由应用分配的主pipe即该应用对应的
* vsync_ctrl_db的base_pipe指定。所有pipe的LayerMixer0是驱动中固定指定好的。
*/
vctrl = &vsync_ctrl_db[0];
mutex_lock(&vctrl->update_lock);
undx = vctrl->update_ndx;
vp = &vctrl->vlist[undx];
pipe = vctrl->base_pipe;
mixer = pipe->mixer_num; // the Layer mixer used, LayerMixer0 here.
if (vp->update_cnt == 0) {
mutex_unlock(&vctrl->update_lock);
return cnt;
}
//vctrl->update_ndx++;
//vctrl->update_ndx &= 0x01;
vp->update_cnt = 0; /* reset */
if (vctrl->blt_free) {
vctrl->blt_free--;
if (vctrl->blt_free == 0)
mdp4_free_writeback_buf(vctrl->mfd, mixer);
}
mutex_unlock(&vctrl->update_lock);
/* free previous committed iommu back to pool */
mdp4_overlay_iommu_unmap_freelist(mixer);
spin_lock_irqsave(&vctrl->spin_lock, flags);
if (pipe->ov_blt_addr) {
/* Blt */
if (vctrl->blt_wait)
need_dmap_wait = 1;
if (vctrl->ov_koff != vctrl->ov_done) {
INIT_COMPLETION(vctrl->ov_comp);
need_ov_wait = 1;
}
} else {
/* direct out */
if (vctrl->dmap_koff != vctrl->dmap_done) {
INIT_COMPLETION(vctrl->dmap_comp);
pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n",
__func__, vctrl->ov_koff, vctrl->ov_done,
vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id());
need_dmap_wait = 1;
}
}
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
/* setup completion for dmap wait in DIRECTOUT mode or overlay wait in BLT mode */
if (need_dmap_wait) {
pr_debug("%s: wait4dmap\n", __func__);
mdp4_dsi_cmd_wait4dmap(0);
}
if (need_ov_wait) {
pr_debug("%s: wait4ov\n", __func__);
mdp4_dsi_cmd_wait4ov(0);
}
if (pipe->ov_blt_addr) {
if (vctrl->blt_end) {
vctrl->blt_end = 0;
pipe->ov_blt_addr = 0;
pipe->dma_blt_addr = 0;
pr_info("%s: reset ov_blt_addr and dma_blt_addr\n", __func__);
}
}
/* if blt has some change, reconfig overlay proccessor and dmap*/
if (vctrl->blt_change) {
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_dmap_xy(pipe);
vctrl->blt_change = 0;
}
pipe = vp->plist;
/*
*All pipes used here is targeted to LayerMixer0.
* These pipes are allocated with MIXER0 indeed,
* and queued in vctrl->vlist[undx]. >plist[pipe->pipe_ndx - 1] again.
* Obvious with target to MIXER0.
*/
for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
if (pipe->pipe_used) {
/* pipes in vp->plist maybe point to same physical pipe */
cnt++;
real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
if (real_pipe && real_pipe->pipe_used) {
/* pipe not unset */
mdp4_overlay_vsync_commit(pipe);
}
/* free previous iommu to freelist
* which will be freed at next
* pipe_commit
*/
mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
pipe->pipe_used = 0; /* clear */
}
}
/* tx dcs command if had any */
mipi_dsi_cmdlist_commit(1);
/* mixer is MIXER0 here. Setup mixer’s each stage with pipe */
mdp4_mixer_stage_commit(mixer);
pipe = vctrl->base_pipe;
spin_lock_irqsave(&vctrl->spin_lock, flags);
if (pipe->ov_blt_addr) {
mdp4_dsi_cmd_blt_ov_update(pipe);
pipe->ov_cnt++;
vctrl->ov_koff++;
INIT_COMPLETION(vctrl->ov_comp);
vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
} else {
INIT_COMPLETION(vctrl->dmap_comp);
vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
vctrl->dmap_koff++;
}
pr_debug("%s: kickoff, pid=%d\n", __func__, current->pid);
/* kickoff overlay engine */
mdp4_stat.kickoff_ov0++;
outpdw(MDP_BASE + 0x0004, 0);
mb();
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
mdp4_stat.overlay_commit[pipe->mixer_num]++;
/* wait for vsync */
if (wait) {
long long tick;
mdp4_dsi_cmd_wait4vsync(cndx, &tick);
}
return cnt;
}
void mdp4_overlay_vsync_commit(struct mdp4_overlay_pipe *pipe)
{
if (pipe->pipe_type == OVERLAY_TYPE_VIDEO)
mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */
else
mdp4_overlay_rgb_setup(pipe); /* rgb pipe */
pr_debug("%s: pipe=%x ndx=%d num=%d used=%d\n", __func__,
(int) pipe, pipe->pipe_ndx, pipe->pipe_num, pipe->pipe_used);
/* figure out the flush value to fill register */
mdp4_overlay_reg_flush(pipe, 1);
/* stage setup but not commit */
mdp4_mixer_stage_up(pipe, 0);
}
void mdp4_mixer_stage_commit(int mixer)
{
struct mdp4_overlay_pipe *pipe;
int i, num;
u32 data, stage;
int off;
unsigned long flags;
data = 0;
for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) {
pipe = ctrl->stage[mixer][i];
if (pipe == NULL)
continue;
pr_debug("%s: mixer=%d ndx=%d stage=%d\n", __func__,
mixer, pipe->pipe_ndx, i);
stage = pipe->mixer_stage;
if (mixer >= MDP4_MIXER1)
stage += 8;
stage <<= (4 * pipe->pipe_num);
data |= stage;
}
/*
* stage_commit may be called from overlay_unset
* for command panel, mdp clocks may be off at this time.
* so mdp clock enabled is necessary
*/
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
mdp_clk_ctrl(1);
mdp4_mixer_blend_setup(mixer);
off = 0;
if (data != ctrl->mixer_cfg[mixer]) {
ctrl->mixer_cfg[mixer] = data;
if (mixer >= MDP4_MIXER2) {
/* MDP_LAYERMIXER2_IN_CFG */
off = 0x100f0;
} else {
/* mixer 0 or 1 */
num = mixer + 1;
num &= 0x01;
data |= ctrl->mixer_cfg[num];
off = 0x10100;
}
pr_debug("%s: mixer=%d data=%x flush=%x pid=%d\n", __func__,
mixer, data, ctrl->flush[mixer], current->pid);
}
local_irq_save(flags);
if (off)
outpdw(MDP_BASE + off, data);
if (ctrl->flush[mixer]) {
outpdw(MDP_BASE + 0x18000, ctrl->flush[mixer]);
ctrl->flush[mixer] = 0;
}
local_irq_restore(flags);
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
mdp_clk_ctrl(0);
}
*****************************************************************************
[END]