Qualcomm MSM8937 dual DSI notes

Keywords: Android socket Google

Sketch

The dual dsi function is supported on MSM8937. I tune it in MSM8937-android 6.0. The following is a brief introduction of its implementation ideas and key code fragments.

There are two ways of dual screens supported by Qualcomm: one is to divide a picture equally on the left and right, and then brush it to the screen through two DSI hardware interfaces, which seems to be the way on MSM8952; the other is to brush a picture directly to the screen through two DSI hardware interfaces without segmentation, which is supported on MSM8937.

For the dual screen display function, the most attractive thing on the market is actually the dual screen display. If a picture is divided equally on the left and right, it feels like a double screen display, but it is still a double screen display. MSM8937 has supported a complete picture to two screens, which proves that from the perspective of hardware resources, its dual screen display can be realized - APP can achieve this function through Multimedia Routing Technology.

Qualcomm's idea is that there are two DSI interfaces on MSM8937, the main DSI interface is connected with MIPI screen, and the secondary DSI interface is connected with a DSI-2-HDMI conversion chip, so is the whole software structure from top to bottom. If both DSI interfaces need MIPI interface screens, some work needs to be done in the driver layer.

In the native MSM8937 dual dsi idea, the main screen must exist, at least in the software configuration; then when the HDMI driver registers, there will be a uevent event to report to the user space. When the HAL listening event listens to the event, it will do some operations, including setting the output resolution of HDMI and so on.

The string associated with uevent of HAL layer and Kernel layer is: "change@/devices/virtual/switch/hdmi". Meanwhile, the string associated with HAL layer and Kernel layer show/store is: "sys/devices/virtual/graphics/fb1/res_info". Of course, uevent and show()/store() are both part of the sysfs system.

HAL

On Android 6.0 and Android 7.0, SurfaceFlinger will create a thread listening to HDMI hot plug events in HAL layer by calling loadFbHalModule() and loadHwcModule(). The following is the code logic.

Int hwcsession:: open (const HW module, const char * name, HW device * * device) is registered as the open() function of struct HW module methods in HAL layer:

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},
  }
};    

When open ing, call Init:

int HWCSession::Open(const hw_module_t *module, const char *name, hw_device_t **device)
{
	int status = hwc_session->Init();
}  

A uevent listener thread will be created in Init:

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 termination
    length = 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;
}  

In HWCSession::HWCUeventThreadHandler(), first call uevent  init() to create a uevent listening socket, and then call uevent  next  event() to read uevent data circularly. Once the kernel sends a string related to "change@/devices/virtual/switch/hdmi" or "change@/devices/virtual/graphics/fb0", you can enter HotPlugHandler() for related work.

When the HDMI uevent insertion event is detected here, the system will run to the relevant part of the HDMI settings. The most important thing to note is that when the HDMI driver registers, it will read the EDID data of HDMI, which contains the resolution size and some other data supported by the current HDMI chip. In HAL layer, the program will go to the file node "sys/devices/virtual/graphics/fb1/res_info" through sysfs to read the data and set the resolution of MSM8937 output through the data. The two functions that need attention are:

  DisplayError HWHDMI::GetDisplayAttributes(uint32_t index, HWDisplayAttributes *display_attributes){};  
  DisplayError HWHDMI::SetDisplayAttributes(uint32_t index){};  

It should be noted that MSM8937 will swap the read resolution length and width, and then set them. If it is MIPI screen, the length and width here cannot be exchanged. Or, think of a way to get it to swap the length for the width.

For the above content, on msm8937-Android 6.0 and msm8937-Android 7.0, you can refer to hw_hdmi.cpp and hwc_session.cpp under the hardware / directory.

Kernel

In the kernel, MSM8937 has a layer of DBA, and its full spelling should be Display Bridge Abstract, which is the abstract layer of display bridge. The purpose of this is that in the kernel, some HDMI drivers are DSI-2-HDMI conversion chip drivers, while some chips are directly integrated with HDMI chips, so the conversion process is not needed.

In the Kernel, when the DSI controller probe is completed, the DSI DBA workqueue will be created. In this work, some DBA related initialization will be done. The most important thing is to set a default resolution for the current DBA. If there is no resolution data or resolution number in the EDI data returned by the subsequent bridging chip (here refers to the HDMI chip) If not, HAL will set the platform output according to the default resolution set here.

The following is a code snippet of the above procedure:

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);
}  

It's registered for delayed work.

static void mdss_dsi_dba_work(struct work_struct *work)
{
	pinfo->dba_data = mdss_dba_utils_init(&utils_init_data);
}  

The above work function initializes the DBA in a series of ways.

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);
}  

The above MSM? DBA? Register? Client () will be registered according to the configured chip? Name and instance? ID. If it finds that the values of these two fields in the configuration file are different from the values registered by the HDMI driver, it cannot continue here, and the initialization of the whole DBA is terminated.

In the HDMI edit set video resolution() function, a default resolution is set. In order to read and write the resolution of the current HDMI, the following two functions are provided in the 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);

When the DBA device is connected to MSM8937, the DBA chip driver will send an MSM DBA CB HPD connect event to inform the DBA layer that the bridging chip connection is successful. Then, the DBA abstract layer will send uevent event to HAL layer for processing.

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() is a callback function registered in MDSS? DBA? Utils? Init(). It will deal with various events reported by the bridge chip.

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);
}  

It's finally where uevent works.

Dual mipi screen of Dual dsi

In fact, it is very simple to implement MIPI + MIPI on the framework of MIPI + HDMI of MSM8937.

First of all, as analyzed above, MSM8937 is originally dual DSI, which has satisfied dual MIPI in hardware;
Thirdly, as long as a correct uevent event is given, the listening thread of HAL layer can work.

Then we can configure the parameters in the Kernel according to the requirements of HDMI - we can configure the parameters according to the code flow and the required parameters. Then at a certain time after startup, a fake uevent event is reported to HAL through the Kernel, which makes HAL layer mistakenly think that there is a bridge chip connected.

The parameters to be configured are as follows:

qcom,dba-panel;
qcom,bridge-name;
qcom,pluggable;
qcom,bridge-index

It should be noted that DSI-2-HDMI chips may have different reset modes than MIPI screens, so the processing places are different. In the default driver framework of MSM8937, there is no GPIO reset operation for DSI1 interface. You need to add MIPI screen reset code to the interface of DSI1, otherwise the screen will not light up.

Dual dsi dual screen display

As for this function, Google Android has provided document description and demo, and its APP has two methods for screen selection: media router and display manager. The details are as follows:

Android dual screen different display screen selection operation

(note that it may be necessary to climb over the wall)

2017-2-10

Published 26 original articles, won praise 22, visited 10000+
Private letter follow

Posted by Doom87 on Fri, 14 Feb 2020 01:13:22 -0800