基于展讯sc7731 - Android 5.1 代码分析浏览。将屏蔽细节,把握整体,并且不涉及其他设备和LCD的交互。

以下对sc7731 lcd大体流程进行简要说明。

第一,lcd 的两个阶段
1. 在uboot引导系统阶段,大约1~5秒左右,需要打印一个厂商log。这里对驱动要求非常简单,只要能打印log即可. (下面皆以lcd_ili9486e_mipi.c为例)
  驱动文件放置路径: u-boot64/drivers/video/sprdfb/lcd/

添加新屏时需要修改的文件分别为:
(1) u-boot64/drivers/video/sprdfb/lcd/Makefile 在该文件中添加编译该屏驱动的宏控

 obj-$(CONFIG_FB_LCD_ILI9806E_MIPI)  += lcd_ili9806e_mipi.o 

(2) u-boot64/drivers/video/sprdfb/sprdfb_panel.c 在该文件中的 static struct panel_cfg panel_cfg[] 表中,注册该LCD 驱动里面的句柄

 extern struct panel_spec lcd_ili9806e_mipi_spec;
static struct panel_cfg panel_cfg[] = {
#if defined(CONFIG_FB_LCD_ILI9806E_MIPI)
{
.lcd_id = 0x04,
.panel = &lcd_ili9806e_mipi_spec,
}
#endif
}

(3) special/u-boot64/include/configs/sp7731gea.h 在该文件中 #define 对应的宏控 --> 该处表示,该设备uboot 可以使用该LCD

 #define CONFIG_FB_LCD_ILI9806E_MIPI

2. 系统启动完毕后,lcd 将担负着人机对话的接口。这里的驱动代码以及处理机制,较之uboot目录里的要复杂许多,也精细许多。一切的故事将由此展开。
驱动文件放置路径: kernel/drivers/video/sprdfb/lcd/
添加新屏时需要修改的文件分别为:
(1) kernel/drivers/video/sprdfb/lcd/Makefile 在该文件中添加编译该屏驱动的宏控

 obj-$(CONFIG_FB_LCD_ILI9806E_MIPI) += lcd_ili9806e_mipi.o

(2) kernel/drivers/video/sprdfb/Kconfig 在该文件中把新屏链接到相关panel(该处修改时,注意语法层次缩进,否则会报错)

     config FB_LCD_ILI9806E_MIPI
boolean "support ili9806e mipi panel"
depends on FB_SC8825 || FB_SCX35 || FB_SCX30G || FB_SCX35L
default n

(3)special/kernel/arch/arm/configs/sp7731gea-dt_defconfig 在该文件中定义相关宏控("#" 表示注释,宏控定义必须顶最左边写)

 CONFIG_FB_LCD_ILI9806E_MIPI=y

说明: 如果一份代码下有多个工程甚至是多个工单的话,可能会产生不同的配置,这个时候就需要做一个所谓的差异化special 配置。展讯就这样做的。

第二,LCD 驱动框架流程
1. uboot 代码流程

      stdio_init()                         @u-boot64/common/stdio.c
drv_lcd_init() @u-boot64/common/lcd.c
lcd_init() @u-boot64/common/lcd.c
lcd_ctrl_init() @u-boot64/drivers/video/sprdfb/sprdfb_main.c
sprdfb_probe() @u-boot64/drivers/video/sprdfb/sprdfb_panel.c
sprdfb_panel_probe() @u-boot64/drivers/video/sprdfb/sprdfb_panel.c
adapt_panel_from_readid() @u-boot64/drivers/video/sprdfb/sprdfb_panel.c
struct panel_cfg panel_cfg[] @u-boot64/drivers/video/sprdfb/sprdfb_panel.c

说明: 在uboot里面,lcd 驱动会把自己的句柄注册到 sprdfb_panel 的 panel_cfg[]表中,然后uboot启动的时候,会通过以上流程去读取LCD ID,并传送给kernel。

2. kernel 代码结构(不涉及代码详细流程)
按照比较统一的观点是,lcd 在kernel里面的处理分为 framebuffer file ops 、framebuffer driver、lcd driver。这三部分依次形成调用关系。
但是按照个人观点,在以上三部分中,应该还有个panel,它介于framebuffer 和 具体lcd驱动代码之间,属于一个接口过渡层。

(1) framebuffer file ops
该部分代码位于7731_5.1/kernel/drivers/video/fbmem.c 文件中。
它的作用就是向VFS层提供文件操作接口,实现 struct file_operations 结构体。换句话说,就是向用户空间提供framebuffer 驱动操作接口。
当然,也是必须的,它肯定会调用framebuffer 驱动的一些接口。
至于怎么调,一方面是通过hook的方式,一方面也会使用 file_fb_info()这个接口

     static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
.llseek = default_llseek,
};

(2) framebuffer 驱动
该部分代码位于7731_5.1/kernel/drivers/video/sprdfb/sprdfb_main.c文件中,其中 sprdfb_probe() 探针函数是最关键的。
这是LCD 的一个核心,一切的实现都在这里开始。向上,给fb_fops提供调用,以便实现用户接口;向下,通过lcd panel,操作具体的硬件。
总结下展讯该探针函数大概的处理:

 static int sprdfb_probe(struct platform_device *pdev)
{
struct fb_info *fb = NULL; //分配帧缓冲使用的内存空间
fb = framebuffer_alloc(sizeof(struct sprdfb_device), &pdev->dev); //... //检查设备ID
if((SPRDFB_MAINLCD_ID != dev->dev_id) &&(SPRDFB_SUBLCD_ID != dev->dev_id)){
//...
} //LCD 硬件主控制操作函数 control ops
if(SPRDFB_MAINLCD_ID == dev->dev_id) {
dev->ctrl = &sprdfb_dispc_ctrl;
}else {
dev->ctrl = &sprdfb_lcdc_ctrl;
} //设置/获取 帧缓冲区mem各种参数 --LCD 硬件上固定的参数 ?
ret = setup_fb_mem(dev, pdev); //帧缓冲区显示参数的设置 ---用户可修改的参数 ?
setup_fb_info(dev); //注册帧缓冲区到系统
ret = register_framebuffer(fb); //利用帧缓冲的资源初始化平台驱动结构体
platform_set_drvdata(pdev, dev); //创建sysfs 文件系统
sprdfb_create_sysfs(dev); //对LCD控制器硬件的初始化
dev->ctrl->init(dev); //注册睡眠唤醒机制
register_early_suspend(&dev->early_suspend); //.... return ;
}

以上需要关注的是:
Control ops: 会牵涉到底层硬件操作接口的调用(想了想,硬件操作接口操作和硬件操作,还是选择了前者。硬件的的操作,是lcd 驱动去做的)
lcd 硬件固定参数: 这个是lcd的物理尺寸,无法更改的。
lcd 可修改参数: 这个可修改是在硬件物理尺寸的基础上来操作的。比如一张图片的大小,或者显示的范围(不一定准确,大概意思差不多).

(3) lcd panel过渡层
该部分代码位于7731_5.1/kernel/drivers/video/sprdfb/sprdfb_panel.c 文件中。
主要介于framebuffer 和 具体的lcd 具体驱动之间。可以简单的看做,是具体lcd 驱动的一个封装,然后把这些封装提供给framebuffer层使用。
我之所以叫它为过渡层,是因为这里纯粹就是一些函数接口的封装,不涉及kernel的模块机制。也就是不会modue_init到编译链接脚本。
以下是该过渡层提供给lcd驱动和framebuffer驱动的接口列表:

 //每一个LCD 驱动,都通过该接口加入到一个驱动链表里(这里是仅仅向系统加入一个驱动,不会去匹配硬件的,匹配硬件的操作是在framebuffer 驱动里面完成的)
//换句话说,该接口就是具体的LCD 驱动调用,比如在 ili9806e 的lcd 驱动文件 lcd_ili9806e_mipi.c 会调用该接口
int sprdfb_panel_register(struct panel_cfg *cfg); //移除一个lcd 驱动
//这里的移除,并非是把一个LCD驱动从链表移除,而是进行一种disable的操作
//该接口将被 sprdfb_remove @drivers/video/sprdfb/sprdfb_main.c 调用
void sprdfb_panel_remove(struct sprdfb_device *dev); //唤醒接口,与lcd 睡眠唤醒机制相关
//在 sprdfb_dispc_resume@drivers/video/sprdfb/Sprdfb_dispc.c 以及 sprdfb_lcdc_resume@drivers/video/sprdfb/Sprdfb_lcdc.c 里被调用
void sprdfb_panel_resume(struct sprdfb_device *dev, bool from_deep_sleep); //睡眠接口,与lcd 睡眠唤醒机制相关
//在sprdfb_dispc_suspend@drivers/video/sprdfb/Sprdfb_dispc.c 以及 sprdfb_lcdc_suspend@drivers/video/sprdfb/Sprdfb_lcdc.c 里被调用
void sprdfb_panel_suspend(struct sprdfb_device *dev); //检查ESD硬件接口, 在framebuffer 里使用, @drivers/video/sprdfb/sprdfb_main.c
uint32_t sprdfb_panel_ESD_check(struct sprdfb_device *dev); //改变fps,在sprdfb_dispc_chg_clk@drivers/video/sprdfb/Sprdfb_dispc.c 里使用
void sprdfb_panel_change_fps(struct sprdfb_device *dev, int fps_level); //以下4个接口与图形刷新有关系,在drivers/video/sprdfb/Sprdfb_dispc.c 和 drivers/video/sprdfb/Sprdfb_lcdc.c 都会使用到
void sprdfb_panel_after_refresh(struct sprdfb_device *dev);
void sprdfb_panel_before_refresh(struct sprdfb_device *dev);
void sprdfb_panel_invalidate(struct panel_spec *self);
void sprdfb_panel_invalidate_rect(struct panel_spec *self,uint16_t left, uint16_t top, uint16_t right, uint16_t bottom); //注册framebuffer驱动之前,会检查kernel里面的device ID 和 uboot里面传上来的device ID 是否相同。若不相同,则需要调用该接口,重新准备lcd panel 层
//sprdfb_probe@drivers/video/sprdfb/sprdfb_main.c 调用
bool sprdfb_panel_probe(struct sprdfb_device *dev); //static struct panel_spec *adapt_panel_from_readid(struct sprdfb_device *dev);
//匹配kernel中device ID 和 uboot里面传上来的device ID。如果匹配失败,则会导致前面 sprdfb_panel_probe() 接口被调用
//sprdfb_probe@drivers/video/sprdfb/sprdfb_main.c 调用
bool sprdfb_panel_get(struct sprdfb_device *dev); //static struct panel_spec *adapt_panel_from_uboot(uint16_t dev_id); //检查lcd panel 是否有效
int panel_ready(struct sprdfb_device *dev);

(4) lcd 驱动
这一层,就完全是同lcd硬件打交道了。那么,不同厂商不同型号的lcd,其驱动代码处理细节都是不同的。当然,处理流程肯定是一样的。
这些代码展讯都放在了 7731_5.1/kernel/drivers/video/sprdfb/lcd/ 的目录下。至于如何添加新屏,前面已经详细写过。
每个lcd的驱动,都会通过lcd panel层提供的sprdfb_panel_register()接口,把自己添加到一个panel_list_main 或者 panel_list_sub链表中去。至于细节,暂未分析。

第三,用户空间对framebuffer 驱动的调用
framebuffer驱动已经通过fb_ops 向用户空间提供了文件操作接口。
如果愿意,可以直接使用UNIX的文件编程接口 open/read/write/ioctl也行。当然,对于android这样如此复杂的系统,简单的这样操作,就只有实验价值而已。并且,这里的接口还涉及到c/c++与java的本地交互。实现一个操作库的可行性更高。
在Android里面,提供了一个操作framebuffer 驱动的库,名字叫 gralloc。其最终实现,当然还是会使用open等基础接口,不过其架构、效率、机制都非常的优秀。
在这里,有一个高大上的名字:HAL层。

1. gralloc 库
(1) 基础说明
该部分文件一般放在了 7731_5.1/hardware/libhardware/modules/gralloc/ 目录下。不过展讯在 vendor/sprd/open-source/libs/gralloc/ 目录下又搞了一份。
该库经过编译后,会生成一个动态的 gralloc.default.so 库文件,该库文件会放在 system/lib/hw/ 目录下,系统会通过特定的函数去读取该.so 并提出相应的信息。展讯的名字叫: gralloc.sc8830.so
以上.so文件放置的路径以及.so文件的名字,都可以通过 7731_5.1/hardware/libhardware/modules/gralloc/Android.mk (展讯: 7731_5.1/vendor/sprd/open-source/libs/gralloc/utgard/Android.mk)进行修改。

 #指定.so 放置的路径
LOCAL_MODULE_RELATIVE_PATH := hw #生成.so模块的名字
LOCAL_MODULE := gralloc.default

(2) gralloc 代码入口:
7731_5.1/hardware/libhardware/modules/gralloc/gralloc.cpp 是gralloc 库的核心文件。
入口: HAL_MODULE_INFO_SYM 是每个HAL模块都必须实现的一个宏,其中最重要的就是base成员。在这里定义了获取module、注册、注销、锁定缓冲区的操作接口。
模块ID: GRALLOC_HARDWARE_MODULE_ID 通过此ID来标示该模块.
关键性的函数接口: gralloc_device_open();

2. 调用 gralloc 模块
调用gralloc 库的地方比较多:

 7731_5./frameworks/native/libs/ui/GraphicBufferAllocator.cpp
7731_5./frameworks/native/libs/ui/GraphicBufferMapper.cpp
7731_5./frameworks/native/libs/ui/FramebufferNativeWindow.cpp
7731_5./frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
7731_5./frameworks/native/opengl/libagl/egl.cpp
7731_5./frameworks/native/opengl/libagl/texture.cpp
....

但是都会通过一个统一的接口来调用: hw_get_module()@hardware/libhardware/hardware.c
所有的HAL调用,应该都是使用该接口。而对于各模块的区别,就是使用模块ID来区别的,比如: GRALLOC_HARDWARE_MODULE_ID

(over)
2015-12-31

sc7731 Android 5.1 LCD驱动简明笔记之一的更多相关文章

  1. sc7731 Android 5.1 LCD驱动简明笔记之三

    此篇笔记基于sc7731 - android 5.1,对lcd的gralloc库做一个简明笔记. 第一部分 调用gralloc.sc8830.so所谓的Gralloc模块,它就是一个模块,一个操作ke ...

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

    此篇笔记基于sc7731 - android 5.1,对lcd的framebuffer做一个简明笔记. 一共分为两大部分:第一部分,关于LCD的硬件方面的:第二部分,关于lcd核心处理(framebu ...

  3. LCD驱动学习笔记

    通过这几天的学习发现驱动的框架感觉都差不多,一般分为以下几个步骤: 分配一个结构体 struct x *x = amlloc(); 设置结构体的参数 硬件寄存器 file_operations 注册 ...

  4. 【转】Android LCD(四):LCD驱动调试篇

    关键词:android LCD TFTSN75LVDS83B  TTL-LVDS LCD电压背光电压 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台 ...

  5. FL2440驱动添加(3)LCD驱动添加学习笔记

    FL2440 LCD内置控制器,320*240 TFT型LCD. 自我理解总结的两种添加驱动模式: 非platform方式添加驱动: 加载驱动: 1,硬件初始化,申请内存,并作地址映射 2,分配设备号 ...

  6. android系统平台显示驱动开发简要:LCD驱动调试篇『四』

    平台信息: 内核:linux3.4.39系统:android4.4 平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新浪博 ...

  7. AM335x(TQ335x)学习笔记——LCD驱动移植

    TI的LCD控制器驱动是非常完善的,共通的地方已经由驱动封装好了,与按键一样,我们可以通过DTS配置完成LCD的显示.下面,我们来讨论下使用DTS方式配置内核完成LCD驱动的思路. (1)初步分析 由 ...

  8. 高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

  9. Android 开发之 ---- 底层驱动开发(一) 【转】

    转自:http://blog.csdn.net/jmq_0000/article/details/7372783 版权声明:本文为博主原创文章,未经博主允许不得转载. 驱动概述 说到 Android ...

随机推荐

  1. MySQL与Oracle 差异比较之七用户权限

    用户权限 编号 类别 ORACLE MYSQL 注释 1 创建用户 Create user user_name identified by user_password default tablespa ...

  2. HDU5805 NanoApe Loves Sequence (BestCoder Round #86 B)前后缀预处理

    分析:维护空隙的差,然后预处理前缀最大,后缀最大,扫一遍 #include <cstdio> #include <cstring> #include <cmath> ...

  3. ORA-12516: TNS: 监听程序无法找到匹配协议栈的可用句柄解决方法

    1.查看当前连接进程数SQL>select count(*) from v$process;2.查看连接数上限SQL>select value from v$parameter where ...

  4. ChromePHP - Chrome浏览器下的PHP debug工具

    一款 Chrome 下用来配合调试 PHP 的工具,看官方介绍应该和 FirePHP 有异曲同工的.喜欢用Chrome 的PHPer 可以尝试一下. 官方网站:http://www.chromephp ...

  5. IOS播放音乐和音效

    1.播放音效 1.1 首先获取到音效文件路径 NSString *path = [[NSBundle mainBundle] pathForResource:soundFileName ofType: ...

  6. ansibleplaybook的使用

    1.简单格式要求 [root@ansibleserver ansible]# cat nagios.yml --- - hosts: nagiosserver tasks: - name: ensur ...

  7. Linux网卡启动报错(this device is not active)

    重启网络服务 service network restart   报如下错误:   shutting down interface eth0: error:device "eth0" ...

  8. python实现不可修改的常量

    因为种种原因,Python并未提供如C/C++/Java一样的const修饰符,换言之,python中没有常量,至少截止2015年年末,还没有这个打算.Python程序一般通过约定俗成的变量名全大写的 ...

  9. mongdb创建自增主键(primary key)的相关讨论 - Jason.Zhi

    根据mongodb官方文档介绍,如果在插入(insert)操作时,没有指定主键id,那么它会自动给插入行自动附上一个主键id.看起来不错,但是详细看看,就会发现这个id值有点复杂. 如下图: mong ...

  10. Matlab 图像画在坐标轴下

    >> x=linspace(,*pi,); >> y=sin(x); >> figure;plot(x,y,'r-') >> set(gca,'xaxi ...