转自:https://shiminblog.github.io/dual-dsi-msm8937/

简述

在 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 双屏异显选屏操作

(注意,可能需要翻墙)

Dual display on msm8937相关推荐

  1. Dual Display分析---设备树

    本文硬件基于I.MX8MQ EVK. 今天遇到一个问题,如何让LCD和HDMI双屏显示.由于官方文档支持的双屏同显是基于双HDMI的,所以来分析一下双屏显示的设备树. 首先从datasheet里可以得 ...

  2. android 读build.prop,Android build.prop简介

    build.prop位于手机的/system/build.prop中 build.prop记录一些系统设置,是一个属性文件,相当于Windows系统的注册表. build.prop生成: Make系统 ...

  3. Android系统移植与调试之-------build.prop文件详细赏析

    小知识:什么是build.prop?   /system/build.prop 是一个属性文件,在Android系统中.prop文件很重要,记录了系统的设置和改变,类似於/etc中的文件.这个文件是如 ...

  4. android build.prop的参数

    一.什么是build.prop? /system/build.prop 是一个属性文件,在Android系统中.prop文件很重要,记录了系统的设置和改变,类似於/etc中的文件.这个文件是如何生成的 ...

  5. 小知识:什么是build.prop?

    /system/build.prop 是一个属性文件,在Android系统中.prop文件很重要,记录了系统的设置和改变,类似於/etc中的文件.这个文件是如何生成的呢? build/tools/bu ...

  6. build.prop文件详细赏析

    小知识:什么是build.prop?   /system/build.prop 是一个属性文件,在Android系统中.prop文件很重要,记录了系统的设置和改变,类似於/etc中的文件.这个文件是如 ...

  7. html只显示一句话_您所说的话:如何最大化多显示器设置

    html只显示一句话 Earlier this week we asked you to share your multi-monitor workspace tips and tricks. Now ...

  8. DisplayPort 端口

    现在很多显卡都提供了对 DisplayPort 端口的支持,我开始还不重视,但是发现这个东西大有来头,隐隐有超越hdmi的架势. 如果想了解更多,我们来看看视频传输接口的发展史. 显示器作为计算机的必 ...

  9. VBE_INFO(获取VBE信息)

    File:      VBE_INFO.txt Name:      获取VBE信息 Author:    zyl910 Blog:      http://blog.csdn.net/zyl910/ ...

  10. linux控制usb接口供电,Linux里控制USB口的供电

    原文链接http://scateu.me/2016/05/30/linux-toggle-usb-power.html 保存一下 方法一 偶然间获得一个USB LED灯. 正好家里有OpenWrt的路 ...

最新文章

  1. EOS与以太坊有哪些区别?
  2. NS_ASSUME_NONNULL_BEGIN 延伸
  3. 助力南京银行打造国内首个分布式核心业务系统
  4. Ajax.net实现loading登陆的效果
  5. 数据结构与算法—单源最短路径dijkstra算法
  6. java安全 ——JAAS(Java 认证和授权服务)开发指南
  7. 全网首发:FreeSwitch服务器转发引起的硬解失败原因分析
  8. EA6900刷梅林教程超详细
  9. An illegal reflective access operation has occurred警告
  10. three.js判断两个向量(角度)夹角误差是否小于某个值
  11. element-ui el-table 表格渲染错位以及高度计算错误问题
  12. 利用批处理代码快速恢复桌面图标小箭头,更可能解决win7出现黑色方块问题
  13. 《Vue3+TS》开发一个自己的起始页(二)chrome插件化
  14. CodeWarrior 使用教程第六课:定制
  15. Unity实用小工具或脚本——可折叠伸缩的多级列表二(带搜索功能)
  16. 天美生物在美上市背后:财务报表存在重大缺陷,胡永卫持股34%
  17. 团体程序设计天梯赛-练习集——L3-008 喊山
  18. 在Word中为标题样式添加自动编号功能
  19. sanic教程-Sanic 应用
  20. 【项目管理案例】第十期:如何做好项目采购管理

热门文章

  1. 一些收藏默认网站后缀
  2. 数据抽取的常见理论方法
  3. 在网页中加入透明flash代码
  4. 添加透明FLASH和FLASH大全
  5. linux整站下载工具
  6. 泰坦尼克号-决策树模型
  7. allshare cast安卓版下载_PanDownload 安卓手机版,解决百度网盘下载速度慢
  8. matlab dlt标定,实验三 Matlab图像处理基本操作及摄像机标定(DLT)
  9. 2021暑假牛客多校第一场A题(博弈论+暴力打表)
  10. wps怎么做时间线_wps中的word文档如何制作时间轴