diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index b929f53b0e..2c9e323b2f 100644 --- a/msm/dp/dp_display.c +++ b/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) { - info.mst_protocol = dp->parser->has_mst_sideband; - dp->mst.cbs.hpd(&dp->dp_display, false, &info); - } + /* + * 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.set_mgr_state(&dp->dp_display, false, + &info); + } } 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) diff --git a/msm/dp/dp_display.h b/msm/dp/dp_display.h index 0ea71cc80f..d73740b28c 100644 --- a/msm/dp/dp_display.h +++ b/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 { diff --git a/msm/dp/dp_mst_drm.c b/msm/dp/dp_mst_drm.c index cde75907db..77ece8fbcf 100644 --- a/msm/dp/dp_mst_drm.c +++ b/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; + } + + return 0; +} + +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); - - 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_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 = {