فهرست منبع

disp: msm: dp: update hpd handling for mst

On hpd low signaling, if MST is enabled, then the dp driver should
send the uevent to usermode and wait for the displays to be turned
off before destroying the connectors. In the current mst driver,
hpd low handling is a monolithic step. This change breaks this
into two separate callbacks into the mst driver so the display
driver can wait for usermode completion between these two steps.

When processing hpd low in sim mode, the mst driver uses the drm
framework's the port detection function. When hpd unplug is signaled
to the usermode, it checks the port status before disabling it. But
since the port hasn't been destroyed yet, the framework reports that
the port as still connected. This causes the driver to timeout while
waiting for the the displays to be disabled. This change adds a
wrapper which will report the port status as disconnected when hpd
low is signaled.

Change-Id: I1c59a4878018a300f258bd67c36d1cf4eaf5f67b
Signed-off-by: Rajkumar Subbiah <[email protected]>
Rajkumar Subbiah 5 سال پیش
والد
کامیت
65534be8da
3فایلهای تغییر یافته به همراه93 افزوده شده و 36 حذف شده
  1. 32 9
      msm/dp/dp_display.c
  2. 3 2
      msm/dp/dp_display.h
  3. 58 25
      msm/dp/dp_mst_drm.c

+ 32 - 9
msm/dp/dp_display.c

@@ -904,8 +904,11 @@ static void dp_display_process_mst_hpd_high(struct dp_display_private *dp,
 		info.mst_port_cnt = dp->debug->mst_port_cnt;
 		info.edid = dp->debug->get_edid(dp->debug);
 
+		if (dp->mst.cbs.set_mgr_state)
+			dp->mst.cbs.set_mgr_state(&dp->dp_display, true, &info);
+
 		if (dp->mst.cbs.hpd)
-			dp->mst.cbs.hpd(&dp->dp_display, true, &info);
+			dp->mst.cbs.hpd(&dp->dp_display, true);
 	}
 
 	DP_MST_DEBUG("mst_hpd_high. mst_active:%d\n", dp->mst.mst_active);
@@ -1131,16 +1134,34 @@ skip_notify:
 
 static void dp_display_process_mst_hpd_low(struct dp_display_private *dp)
 {
+	int rc = 0;
 	struct dp_mst_hpd_info info = {0};
 
 	if (dp->mst.mst_active) {
 		DP_MST_DEBUG("mst_hpd_low work\n");
 
-		if (dp->mst.cbs.hpd) {
+		/*
+		 * HPD unplug callflow:
+		 * 1. send hpd unplug event with status=disconnected
+		 * 2. send hpd unplug on base connector so usermode can disable
+		 * all external displays.
+		 * 3. unset mst state in the topology mgr so the branch device
+		 *  can be cleaned up.
+		 */
+		if (dp->mst.cbs.hpd)
+			dp->mst.cbs.hpd(&dp->dp_display, false);
+
+		dp_display_update_mst_state(dp, false);
+
+		if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) ||
+				dp_display_state_is(DP_STATE_ENABLED)))
+			rc = dp_display_send_hpd_notification(dp);
+
+		if (dp->mst.cbs.set_mgr_state) {
 			info.mst_protocol = dp->parser->has_mst_sideband;
-			dp->mst.cbs.hpd(&dp->dp_display, false, &info);
+			dp->mst.cbs.set_mgr_state(&dp->dp_display, false,
+					&info);
 		}
-		dp_display_update_mst_state(dp, false);
 	}
 
 	DP_MST_DEBUG("mst_hpd_low. mst_active:%d\n", dp->mst.mst_active);
@@ -1153,12 +1174,14 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp)
 	dp_display_state_remove(DP_STATE_CONNECTED);
 	dp->process_hpd_connect = false;
 	dp_audio_enable(dp, false);
-	dp_display_process_mst_hpd_low(dp);
 
-	if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) ||
-			dp_display_state_is(DP_STATE_ENABLED)) &&
-			!dp->mst.mst_active)
-		rc = dp_display_send_hpd_notification(dp);
+	if (dp->mst.mst_active) {
+		dp_display_process_mst_hpd_low(dp);
+	} else {
+		if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) ||
+				dp_display_state_is(DP_STATE_ENABLED)))
+			rc = dp_display_send_hpd_notification(dp);
+	}
 
 	mutex_lock(&dp->session_lock);
 	if (!dp->active_stream_cnt)

+ 3 - 2
msm/dp/dp_display.h

@@ -30,11 +30,12 @@ struct dp_mst_hpd_info {
 };
 
 struct dp_mst_drm_cbs {
-	void (*hpd)(void *display, bool hpd_status,
-			struct dp_mst_hpd_info *info);
+	void (*hpd)(void *display, bool hpd_status);
 	void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info);
 	void (*set_drv_state)(void *dp_display,
 			enum dp_drv_state mst_state);
+	int (*set_mgr_state)(void *dp_display, bool state,
+			struct dp_mst_hpd_info *info);
 };
 
 struct dp_mst_drm_install_info {

+ 58 - 25
msm/dp/dp_mst_drm.c

@@ -625,6 +625,23 @@ static void dp_mst_sim_handle_hpd_irq(void *dp_display,
 	}
 }
 
+static enum drm_connector_status dp_mst_sim_detect_port(
+			struct drm_connector *connector,
+			struct drm_dp_mst_topology_mgr *mgr,
+			struct drm_dp_mst_port *port)
+{
+	struct dp_mst_private *mst = container_of(mgr,
+			struct dp_mst_private, mst_mgr);
+	enum drm_connector_status status = connector_status_disconnected;
+
+	if (mst->mst_session_state)
+		status = drm_dp_mst_detect_port(connector, mgr, port);
+
+	DP_MST_DEBUG("mst sim port status: %d, session state: %d\n",
+		status, mst->mst_session_state);
+	return status;
+}
+
 static void _dp_mst_get_vcpi_info(
 		struct drm_dp_mst_topology_mgr *mgr,
 		int vcpi, int *start_slot, int *num_slots)
@@ -701,7 +718,7 @@ static const struct dp_drm_mst_fw_helper_ops drm_dp_sim_mst_fw_helper_ops = {
 	.update_payload_part1      = dp_mst_sim_update_payload_part1,
 	.check_act_status          = dp_mst_sim_no_action,
 	.update_payload_part2      = dp_mst_sim_update_payload_part2,
-	.detect_port               = drm_dp_mst_detect_port,
+	.detect_port               = dp_mst_sim_detect_port,
 	.get_edid                  = dp_mst_sim_get_edid,
 	.topology_mgr_set_mst      = dp_mst_sim_topology_mgr_set_mst,
 	.get_vcpi_info             = _dp_mst_get_vcpi_info,
@@ -2128,31 +2145,32 @@ static void dp_mst_hpd_event_notify(struct dp_mst_private *mst, bool hpd_status)
 
 	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
 
-	DP_MST_INFO_LOG("%s finished\n", __func__);
+	DP_MST_INFO_LOG("%s finished. hpd_status:%d\n", __func__, hpd_status);
 }
 
 /* DP Driver Callback OPs */
 
-static void dp_mst_display_hpd(void *dp_display, bool hpd_status,
+static int dp_mst_display_set_mgr_state(void *dp_display, bool state,
 		struct dp_mst_hpd_info *info)
 {
 	int rc;
 	struct dp_display *dp = dp_display;
 	struct dp_mst_private *mst = dp->dp_mst_prv_info;
 
-	mutex_lock(&mst->mst_lock);
-	mst->mst_session_state = hpd_status;
-	mutex_unlock(&mst->mst_lock);
-
-	if (!hpd_status) {
-		rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr,
-				hpd_status);
-		if (rc < 0)
-			goto fail;
+	/*
+	 * on hpd high, set_mgr_state is called before hotplug event is sent
+	 * to usermode and mst_session_state should be updated here.
+	 * on hpd_low, set_mgr_state is called after hotplug event is sent and
+	 * the session_state was already updated prior to that.
+	 */
+	if (state) {
+		mutex_lock(&mst->mst_lock);
+		mst->mst_session_state = state;
+		mutex_unlock(&mst->mst_lock);
 	}
 
 	if (info && !info->mst_protocol) {
-		if (hpd_status) {
+		if (state) {
 			mst->simulator.edid = (struct edid *)info->edid;
 			mst->simulator.port_cnt = info->mst_port_cnt;
 		}
@@ -2161,21 +2179,35 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status,
 		mst->mst_fw_cbs = &drm_dp_mst_fw_helper_ops;
 	}
 
-	if (hpd_status) {
-		rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr,
-				hpd_status);
-		if (rc < 0)
-			goto fail;
+	rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr, state);
+	if (rc < 0) {
+		DP_ERR("failed to set topology mgr state to %d. rc %d\n",
+				state, rc);
+		return rc;
 	}
 
-	dp_mst_hpd_event_notify(mst, hpd_status);
+	return 0;
+}
 
-	DP_MST_INFO_LOG("mst display hpd success. hpd:%d, rc:%d\n", hpd_status,
-			rc);
-	return;
-fail:
-	DRM_ERROR("mst display hpd failed. hpd: %d, rc: %d\n",
-			hpd_status, rc);
+static void dp_mst_display_hpd(void *dp_display, bool hpd_status)
+{
+	struct dp_display *dp = dp_display;
+	struct dp_mst_private *mst = dp->dp_mst_prv_info;
+
+	/*
+	 * on hpd high, set_mgr_state is called before hotplug event is sent
+	 * to usermode and mst_session_state was already updated there.
+	 * on hpd_low, hotplug event is sent before set_mgr_state and the
+	 * session state should be unset here for the connection status to be
+	 * updated accordingly.
+	 */
+	if (!hpd_status) {
+		mutex_lock(&mst->mst_lock);
+		mst->mst_session_state = hpd_status;
+		mutex_unlock(&mst->mst_lock);
+	}
+
+	dp_mst_hpd_event_notify(mst, hpd_status);
 }
 
 static void dp_mst_display_hpd_irq(void *dp_display,
@@ -2255,6 +2287,7 @@ static const struct dp_mst_drm_cbs dp_mst_display_cbs = {
 	.hpd = dp_mst_display_hpd,
 	.hpd_irq = dp_mst_display_hpd_irq,
 	.set_drv_state = dp_mst_set_state,
+	.set_mgr_state = dp_mst_display_set_mgr_state,
 };
 
 static const struct drm_dp_mst_topology_cbs dp_mst_drm_cbs = {