简述

在 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_topen()函数:

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_nameinstance_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 笔记相关推荐

  1. Qualcomm平台Mipi DSI转RGB

    概述 Qualcomm平台显示接口基本上都只有Mipi,在较高端的平台可能还会支持HDMI等串行接口,但类似RGB接口的并行却不会支持.往往在设计一些产品时,客户指定的就是一些高通平台本身不支持的接口 ...

  2. Qualcomm MSM8937 ICN6211 MIPI-2-RGB 芯片驱动

    ( ICN6211 ILI9806E + 模拟SPI+模拟I2C ) /* Copyright (c) 2015-2016, The Linux Foundation. All rights rese ...

  3. lcm in qcom

    文章目录 lcm需要生产的相关文件 lcm in lk lcm in kernel 一些注意的事项 其他平台(sdm845) in kernel in uefi lcm需要生产的相关文件 根据fae提 ...

  4. 数据库 oracle function 替换身份证中间字符

    目标 使用sql写一个方法,实现传入身份证号,保留前四位和后四位,中间替换为* 实现 create or replace function fc(fid varchar2) return varcha ...

  5. Dual Contrastive Learning: Text Classification via Label-Aware Data Augmentation 阅读笔记

    Dual Contrastive Learning: Text Classification via Label-Aware Data Augmentation PyTorch实现:https://g ...

  6. TC358775XBG是一颗将MIPI DSI信号转换成single/ dual -link LVDS的芯片,最高分辨率支持到1920x1200

    TC358775XBG 功能:TC358775XBG是一颗将MIPI DSI信号转换成single/ dual -link LVDS的芯片,最高分辨率支持到1920x1200,其应用图如下: 产品特征 ...

  7. 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 ...

  8. 【Machine Learning 学习笔记】Stochastic Dual Coordinate Ascent for SVM 代码实现

    [Machine Learning 学习笔记]Stochastic Dual Coordinate Ascent for SVM 代码实现 通过本篇博客记录一下Stochastic Dual Coor ...

  9. Generalizable Medical Image Segmentation via Style Augmentation and Dual Normalization论文笔记

    论文摘要 近年来医疗图像分割算法训练出来的模型都是针对一个单独领域的,如利用基于MR(核磁共振成像)数据集训练出来的模型直接分割CT图像,那么效果可能会不尽如人意. 本文提出了一种dual-norma ...

  10. 台湾大学林轩田机器学习技法课程学习笔记2 -- Dual Support Vector Machine

    红色石头的个人网站:redstonewill.com 上节课我们主要介绍了线性支持向量机(Linear Support Vector Machine).Linear SVM的目标是找出最"胖 ...

最新文章

  1. vi编辑器中的各种(整行,多行,一个单词)复制
  2. fastdfs详细安装教程
  3. RHEL5中YUM命令解决RPM包依赖性
  4. 实用比较,帮你决策到底选择Vue还是Angular4、5
  5. c语言中有哪些函数关系,C语言中有哪些常用的函数
  6. pycharm怎么编写python代码_如何设置PyCharm中的Python代码模版(推荐)
  7. 吴恩达机器学习 逻辑回归 作业3(手写数字分类) Python实现 代码详细解释
  8. UI交互设计师在准备简历时应该注意什么?
  9. 有佳文档管理v2.03
  10. 【LOJ】#2187. 「SHOI2014」三叉神经树
  11. 【资源下载】netassist 下载 资源下载
  12. 如何将Win7、Win10笔记本,台式机系统C盘软件搬家? 只需3个步骤!!!
  13. 关于C语言的字符常量和符号常量
  14. XML学习之做过的实验——实验三
  15. 基于Element组件下动态生成多级表头以及数据
  16. 抖音:对比去年6月蓝V账号数量增长44.6倍 投稿量增长211倍
  17. 听觉能力类毕业论文文献有哪些?
  18. vue-map高德地图(1)输入坐标显示位置(组件封装)
  19. 2021中国大数据企业50强发布,华为云大数据连续六年入选
  20. js 元旦倒计时【非常nice】

热门文章

  1. 计算机快速录入,如何快速把书中的文字扫描并录入电脑
  2. 【Redis总结-基础入门】
  3. 积累资源,胜于一切项目
  4. mediatek无线usb网卡驱动 linux,Ralink雷凌USB无线网卡驱动5.1.7.0版 Ralink雷凌USB无线网卡驱动5.1.7.0版 无线网卡驱动 雷凌...
  5. 优雅的开发Swift和Objective C混编的Framework
  6. 小数分频器vhdl实现_小数分频VHDL代码
  7. Unity手游开发札记——布料系统原理浅析和在Unity手游中的应用
  8. Debian安装软件商店-Debian的GUI软件管理
  9. js实现的万年历显示
  10. 聊天室软件(服务器+图形界面客户端)