Qualcomm MSM8937 dual DSI 笔记
简述
在 MSM8937 上有支持了 dual dsi 功能,本人在 msm8937-android6.0 上将其调通,以下简记其实现的思路以及关键代码片段。
高通支持的双屏有两种方式:其一为将一副图片左右均分,然后通过两个 DSI 硬件接口刷到屏幕上去,似乎在 MSM8952 上就是这种方式;其二为将一副图片不做分割操作,直接通过两个 DSI 硬件接口刷到屏幕上去,在 MSM8937 上则是支持的这种方式。
对于双屏显示功能,市面上最吸引人的其实是双屏异显。如果将一副图片左右均分,给人感觉上是双屏异显了,但它其实还是双屏同显。而 MSM8937 已经支持了一幅图片完整的刷到了两个屏幕上,证明从硬件资源上来看,其双屏异显是可以实现的—APP通过多媒体路由技术即可实现该功能。
高通的思路是,MSM8937 上两个 DSI 接口,其中主 DSI 接口接 MIPI 屏,副 DSI 接口挂一个 DSI-2-HDMI 的转换芯片,从上到下整个软件结构也是这样实现的。如果两个 DSI 接口都要挂 MIPI 接口的屏,则在驱动层需要做一些工作。
在原生的 MSM8937 dual dsi 思路中,主屏是必须要有的,起码在软件配置上要存在的;然后 HDMI 驱动注册的时候,会有一个 uevent 事件上报到用户空间,当 HAL 层的监听事件监听到该事件以后,就会做一些操作,包括设置 HDMI 的输出分辨率等等。
HAL 层和 Kernel 层 uevent 关联的字符串为: “change@/devices/virtual/switch/hdmi”。同时,HAL 层和 Kernel 层 show/store 关联的字符串为: “sys/devices/virtual/graphics/fb1/res_info”。当然, uevent 和 show()/store() 都属于 sysfs 系统的一部分。
HAL
在 Android 6.0 和 Android 7.0 上,SurfaceFlinger 通过对 loadFbHalModule()
和 loadHwcModule()
的调用,会在 HAL 层创建出一个监听 HDMI 热插拔事件的线程出来。以下为代码逻辑。
int HWCSession::Open(const hw_module_t *module, const char *name, hw_device_t **device)
被注册为 HAL 层 struct hw_module_methods_t
的 open()
函数:
static sdm::HWCSession::HWCModuleMethods g_hwc_module_methods;hwc_module_t HAL_MODULE_INFO_SYM = {.common = {.tag = HARDWARE_MODULE_TAG,.version_major = 2,.version_minor = 0,.id = HWC_HARDWARE_MODULE_ID,.name = "QTI Hardware Composer Module",.author = "CodeAurora Forum",.methods = &g_hwc_module_methods,.dso = 0,.reserved = {0},}
};
在 open 的时候,调用 Init:
int HWCSession::Open(const hw_module_t *module, const char *name, hw_device_t **device)
{int status = hwc_session->Init();
}
在 Init 中会创建一个 uevent 监听线程:
int HWCSession::Init() {if (pthread_create(&uevent_thread_, NULL, &HWCUeventThread, this) < 0) { }
} void* HWCSession::HWCUeventThread(void *context) {return reinterpret_cast<HWCSession *>(context)->HWCUeventThreadHandler();
} void* HWCSession::HWCUeventThreadHandler() {static char uevent_data[PAGE_SIZE];int length = 0;prctl(PR_SET_NAME, uevent_thread_name_, 0, 0, 0);setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);if (!uevent_init()) {DLOGE("Failed to init uevent");pthread_exit(0);return NULL;}while (!uevent_thread_exit_) {// keep last 2 zeroes to ensure double 0 terminationlength = uevent_next_event(uevent_data, INT32(sizeof(uevent_data)) - 2);if (strcasestr(HWC_UEVENT_SWITCH_HDMI, uevent_data)) {DLOGI("Uevent HDMI = %s", uevent_data);int connected = GetEventValue(uevent_data, length, "SWITCH_STATE=");if (connected >= 0) {DLOGI("HDMI = %s", connected ? "connected" : "disconnected");if (HotPlugHandler(connected) == -1) {DLOGE("Failed handling Hotplug = %s", connected ? "connected" : "disconnected");}}} else if (strcasestr(HWC_UEVENT_GRAPHICS_FB0, uevent_data)) {DLOGI("Uevent FB0 = %s", uevent_data);int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");if (panel_reset == 0) {if (hwc_procs_) {reset_panel_ = true;hwc_procs_->invalidate(hwc_procs_);} else {DLOGW("Ignore resetpanel - hwc_proc not registered");}}}}pthread_exit(0);return NULL;
}
在 HWCSession::HWCUeventThreadHandler()
中,首先调用 uevent_init()
创建 uevent 监听套接字,然后再调用 uevent_next_event()
循环读取 uevent 数据。一旦 kernel 发送了 “change@/devices/virtual/switch/hdmi” 或者 “change@/devices/virtual/graphics/fb0” 相关的字符串,这里就可以进入 HotPlugHandler()
进行相关的工作了。
当这里检测到了 HDMI uevent 插入事件以后,系统就会跑到 HDMI 设置相关的部分去了。其中最需要注意的是,HDMI 驱动在注册的时候,会读取到 HDMI 的 EDID 数据,该数据中包含了当前 HDMI 芯片支持的分辨率大小和一些其他数据。在 HAL 层,程序会通过 sysfs 去文件节点“sys/devices/virtual/graphics/fb1/res_info” 读取该数据,并通过该数据,来设置 MSM8937 输出的分辨率。需要关注的两个函数是:
DisplayError HWHDMI::GetDisplayAttributes(uint32_t index, HWDisplayAttributes *display_attributes){}; DisplayError HWHDMI::SetDisplayAttributes(uint32_t index){};
需要注意的是,MSM8937 会将读取到的分辨率长和宽进行交换,然后进行设置。如果是 MIPI 屏的话,这里的长宽就不能交换。或者说,想办法让其把长宽交换回来。
以上内容,在 msm8937-Android 6.0 和 msm8937-Android 7.0 上可以参考 hardware/ 目录下的 hw_hdmi.cpp 和 hwc_session.cpp 两个文件。
Kernel
在 Kernel 中,MSM8937 有一个 DBA 的层,其全拼应该是 Display Bridge Abstract,显示桥抽象层。这样做的目的是因为,在 kernel 中,一部分 HDMI 驱动为 DSI-2-HDMI 转换芯片驱动,而有的芯片上,是直接集成了 HDMI 芯片,不需要转换这个过程了。
在 Kernel 中,在 DSI 控制器 probe 完毕的时候,会创建 dsi-dba 的 workqueue,在该 work 中,会去做一些 DBA 相关的初始化,最重要的就是,给当前的 DBA 设置一个默认的分辨率,如果后续桥接芯片(这里指的就是 HDMI 芯片吧)中返回的 EDID 数据中没有分辨率数据或者分辨率数据不符合要求,则 HAL 将根据这里设置的默认分辨率进行平台输出设置。
以下为上述过程的代码片段:
static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
{ctrl_pdata->workq = create_workqueue("mdss_dsi_dba");INIT_DELAYED_WORK(&ctrl_pdata->dba_work, mdss_dsi_dba_work);
}
上面注册了延时工作。
static void mdss_dsi_dba_work(struct work_struct *work)
{pinfo->dba_data = mdss_dba_utils_init(&utils_init_data);
}
上面的 work 函数中会对 DBA 进行一系列的初始化。
void *mdss_dba_utils_init(struct mdss_dba_utils_init_data *uid)
{/* initialize DBA registration data */info.instance_id = uid->instance_id;info.cb = mdss_dba_utils_dba_cb;info.cb_data = udata;/* register client with DBA and get device's ops*/udata->dba_data = msm_dba_register_client(&info, &udata->ops);/* create sysfs nodes for other modules to intract with utils */ret = mdss_dba_utils_sysfs_create(uid->kobj);/* register with edid module for parsing edid buffer */udata->edid_data = hdmi_edid_init(&edid_init_data);/* Initialize to default resolution */hdmi_edid_set_video_resolution(uid->pinfo->edid_data,DEFAULT_VIDEO_RESOLUTION, true);
}
上面的 msm_dba_register_client()
会根据配置的 chip_name
和 instance_id
进行注册。如果其发现在配置文件中的这两个字段的值,同 HDMI 驱动注册的值不相同,这里就不能继续下去了,整个 DBA 的初始化也就终止了。
在 hdmi_edid_set_video_resolution()
函数中则是设置一个默认的分辨率。为了读写当前 HDMI 的分辨率,kernel 中提供了以下两个函数:
static ssize_t hdmi_edid_sysfs_rda_res_info(struct device *dev,struct device_attribute *attr, char *buf);
static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,struct device_attribute *attr, const char *buf, size_t count);
static DEVICE_ATTR(res_info, S_IRUGO | S_IWUSR, hdmi_edid_sysfs_rda_res_info,hdmi_edid_sysfs_wta_res_info);
而在 DBA 设备连接到 MSM8937 上的时候,DBA 芯片驱动会发出一个 MSM_DBA_CB_HPD_CONNECT
的事件,告知 DBA 层有桥接芯片连接成功,接着,DBA 抽象层就发送 uevent 事件到 HAL 层进行处理。
static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
{switch (event) {case MSM_DBA_CB_HPD_CONNECT:if (pluggable) {mdss_dba_utils_notify_display(udata, 1); }}
}
mdss_dba_utils_dba_cb()
是在 mdss_dba_utils_init()
里面注册的回调函数。它会根据桥接芯片上报的各种事件,进行处理。
static void mdss_dba_utils_notify_display(struct mdss_dba_utils_data *udata, int val)
{switch_set_state(&udata->sdev_display, val);
} void switch_set_state(struct switch_dev *sdev, int state)
{kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
}
这里最终就到了 uevent 处理的地方了。
Dual dsi 之 Dual mipi 屏
在 MSM8937 的 MIPI + HDMI 的框架上实现 MIPI + MIPI,其实已经非常简单了。
首先如上所分析,MSM8937 本来就是双 DSI,这在硬件上已经满足了双 MIPI;
再次,只要给一个正确的 uevent 事件,HAL 层的监听线程就能工作了。
则我们可以按照 HDMI 的要求,在 Kernel 进行配置各项参数—可以根据代码流程,需要什么参数,就配置什么参数。 然后在开机启动后的一定时间,通过 Kernel 向 HAL 报一个假的 uevent 事件,让 HAL 层误以为有桥接芯片连接上了。
需要配置的参数如下:
qcom,dba-panel;
qcom,bridge-name;
qcom,pluggable;
qcom,bridge-index
需要注意的是,DSI-2-HDMI芯片,可能 reset 的方式同 MIPI 屏不同,则处理的地方也就不同。在 MSM8937 默认的驱动框架中,是没有给 DSI1接口做 GPIO reset操作的,需要给 DSI1 的接口加上 MIPI 屏 reset 的代码,否则屏是不会亮的。
Dual dsi 之双屏异显
关于这个功能,google-android 有给出文档说明和 demo,其 APP 有两种方法进行选屏操作:一是 media router;二是 display manager。详细内容为如下连接:
Android 双屏异显选屏操作
(注意,可能需要翻墙)
2017-2-10
Qualcomm MSM8937 dual DSI 笔记相关推荐
- Qualcomm平台Mipi DSI转RGB
概述 Qualcomm平台显示接口基本上都只有Mipi,在较高端的平台可能还会支持HDMI等串行接口,但类似RGB接口的并行却不会支持.往往在设计一些产品时,客户指定的就是一些高通平台本身不支持的接口 ...
- Qualcomm MSM8937 ICN6211 MIPI-2-RGB 芯片驱动
( ICN6211 ILI9806E + 模拟SPI+模拟I2C ) /* Copyright (c) 2015-2016, The Linux Foundation. All rights rese ...
- lcm in qcom
文章目录 lcm需要生产的相关文件 lcm in lk lcm in kernel 一些注意的事项 其他平台(sdm845) in kernel in uefi lcm需要生产的相关文件 根据fae提 ...
- 数据库 oracle function 替换身份证中间字符
目标 使用sql写一个方法,实现传入身份证号,保留前四位和后四位,中间替换为* 实现 create or replace function fc(fid varchar2) return varcha ...
- Dual Contrastive Learning: Text Classification via Label-Aware Data Augmentation 阅读笔记
Dual Contrastive Learning: Text Classification via Label-Aware Data Augmentation PyTorch实现:https://g ...
- TC358775XBG是一颗将MIPI DSI信号转换成single/ dual -link LVDS的芯片,最高分辨率支持到1920x1200
TC358775XBG 功能:TC358775XBG是一颗将MIPI DSI信号转换成single/ dual -link LVDS的芯片,最高分辨率支持到1920x1200,其应用图如下: 产品特征 ...
- Dual Graph Attention Networks for Deep Latent Representation of Multifaceted Social...》论文学习笔记
Dual Graph Attention Networks for Deep Latent Representation of Multifaceted Social Effects in Recom ...
- 【Machine Learning 学习笔记】Stochastic Dual Coordinate Ascent for SVM 代码实现
[Machine Learning 学习笔记]Stochastic Dual Coordinate Ascent for SVM 代码实现 通过本篇博客记录一下Stochastic Dual Coor ...
- Generalizable Medical Image Segmentation via Style Augmentation and Dual Normalization论文笔记
论文摘要 近年来医疗图像分割算法训练出来的模型都是针对一个单独领域的,如利用基于MR(核磁共振成像)数据集训练出来的模型直接分割CT图像,那么效果可能会不尽如人意. 本文提出了一种dual-norma ...
- 台湾大学林轩田机器学习技法课程学习笔记2 -- Dual Support Vector Machine
红色石头的个人网站:redstonewill.com 上节课我们主要介绍了线性支持向量机(Linear Support Vector Machine).Linear SVM的目标是找出最"胖 ...
最新文章
- vi编辑器中的各种(整行,多行,一个单词)复制
- fastdfs详细安装教程
- RHEL5中YUM命令解决RPM包依赖性
- 实用比较,帮你决策到底选择Vue还是Angular4、5
- c语言中有哪些函数关系,C语言中有哪些常用的函数
- pycharm怎么编写python代码_如何设置PyCharm中的Python代码模版(推荐)
- 吴恩达机器学习 逻辑回归 作业3(手写数字分类) Python实现 代码详细解释
- UI交互设计师在准备简历时应该注意什么?
- 有佳文档管理v2.03
- 【LOJ】#2187. 「SHOI2014」三叉神经树
- 【资源下载】netassist 下载 资源下载
- 如何将Win7、Win10笔记本,台式机系统C盘软件搬家? 只需3个步骤!!!
- 关于C语言的字符常量和符号常量
- XML学习之做过的实验——实验三
- 基于Element组件下动态生成多级表头以及数据
- 抖音:对比去年6月蓝V账号数量增长44.6倍 投稿量增长211倍
- 听觉能力类毕业论文文献有哪些?
- vue-map高德地图(1)输入坐标显示位置(组件封装)
- 2021中国大数据企业50强发布,华为云大数据连续六年入选
- js 元旦倒计时【非常nice】
热门文章
- 计算机快速录入,如何快速把书中的文字扫描并录入电脑
- 【Redis总结-基础入门】
- 积累资源,胜于一切项目
- mediatek无线usb网卡驱动 linux,Ralink雷凌USB无线网卡驱动5.1.7.0版 Ralink雷凌USB无线网卡驱动5.1.7.0版 无线网卡驱动 雷凌...
- 优雅的开发Swift和Objective C混编的Framework
- 小数分频器vhdl实现_小数分频VHDL代码
- Unity手游开发札记——布料系统原理浅析和在Unity手游中的应用
- Debian安装软件商店-Debian的GUI软件管理
- js实现的万年历显示
- 聊天室软件(服务器+图形界面客户端)