diff options
-rw-r--r-- | msm/dp/dp_debug.h | 34 | ||||
-rw-r--r-- | msm/dp/dp_display.c | 139 |
2 files changed, 154 insertions, 19 deletions
diff --git a/msm/dp/dp_debug.h b/msm/dp/dp_debug.h index 931152ad..aa239be3 100644 --- a/msm/dp/dp_debug.h +++ b/msm/dp/dp_debug.h @@ -13,13 +13,33 @@ #include "dp_aux.h" #include "dp_display.h" -#define DP_WARN(fmt, ...) DRM_WARN("[msm-dp-warn] "fmt, ##__VA_ARGS__) -#define DP_ERR(fmt, ...) DRM_DEV_ERROR(NULL, "[msm-dp-error]" fmt, \ - ##__VA_ARGS__) -#define DP_INFO(fmt, ...) DRM_DEV_INFO(NULL, "[msm-dp-info] "fmt, \ - ##__VA_ARGS__) -#define DP_DEBUG(fmt, ...) DRM_DEV_DEBUG_DP(NULL, "[msm-dp-debug] "fmt, \ - ##__VA_ARGS__) +#define DP_DEBUG(fmt, ...) \ + do { \ + if (unlikely(drm_debug & DRM_UT_KMS)) \ + DRM_DEBUG("[msm-dp-debug][%-4d]"fmt, current->pid, \ + ##__VA_ARGS__); \ + else \ + pr_debug("[drm:%s][msm-dp-debug][%-4d]"fmt, __func__,\ + current->pid, ##__VA_ARGS__); \ + } while (0) + +#define DP_INFO(fmt, ...) \ + do { \ + if (unlikely(drm_debug & DRM_UT_KMS)) \ + DRM_INFO("[msm-dp-info][%-4d]"fmt, current->pid, \ + ##__VA_ARGS__); \ + else \ + pr_info("[drm:%s][msm-dp-info][%-4d]"fmt, __func__, \ + current->pid, ##__VA_ARGS__); \ + } while (0) + +#define DP_WARN(fmt, ...) \ + pr_warn("[drm:%s][msm-dp-warn][%-4d]"fmt, __func__, \ + current->pid, ##__VA_ARGS__) + +#define DP_ERR(fmt, ...) \ + pr_err("[drm:%s][msm-dp-err][%-4d]"fmt, __func__, \ + current->pid, ##__VA_ARGS__) /** * struct dp_debug diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 67d771cd..177d8fac 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -690,6 +690,24 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) bool hpd = !!dp_display_state_is(DP_STATE_CONNECTED); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, hpd); + + /* + * Send the notification only if there is any change. This check is + * necessary since it is possible that the connect_work may or may not + * skip sending the notification in order to respond to a pending + * attention message. Attention work thread will always attempt to + * send the notification after successfully handling the attention + * message. This check here will avoid any unintended duplicate + * notifications. + */ + if (dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) && hpd) { + DP_DEBUG("connection notified already, skip notification\n"); + goto skip_wait; + } else if (dp_display_state_is(DP_STATE_DISCONNECT_NOTIFIED) && !hpd) { + DP_DEBUG("disonnect notified already, skip notification\n"); + goto skip_wait; + } + dp->aux->state |= DP_STATE_NOTIFICATION_SENT; if (!dp->mst.mst_active) @@ -823,6 +841,25 @@ static void dp_display_host_ready(struct dp_display_private *dp) return; } + /* + * Reset the aborted state for AUX and CTRL modules. This will + * allow these modules to execute normally in response to the + * cable connection event. + * + * One corner case still exists. While the execution flow ensures + * that cable disconnection flushes all pending work items on the DP + * workqueue, and waits for the user module to clean up the DP + * connection session, it is possible that the system delays can + * lead to timeouts in the connect path. As a result, the actual + * connection callback from user modules can come in late and can + * race against a subsequent connection event here which would have + * reset the aborted flags. There is no clear solution for this since + * the connect/disconnect notifications do not currently have any + * sessions IDs. + */ + dp->aux->abort(dp->aux, false); + dp->ctrl->abort(dp->ctrl, false); + dp->aux->init(dp->aux, dp->parser->aux_cfg); dp->panel->init(dp->panel); @@ -891,7 +928,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); - dp_display_host_init(dp); dp_display_host_ready(dp); dp->link->psm_config(dp->link, &dp->panel->link_info, false); @@ -929,9 +965,36 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) end: mutex_unlock(&dp->session_lock); + /* + * Delay the HPD connect notification to see if sink generates any + * IRQ HPDs immediately after the HPD high. + */ + usleep_range(10000, 10100); + + /* + * If an IRQ HPD is pending, then do not send a connect notification. + * Once this work returns, the IRQ HPD would be processed and any + * required actions (such as link maintenance) would be done which + * will subsequently send the HPD notification. To keep things simple, + * do this only for SST use-cases. MST use cases require additional + * care in order to handle the side-band communications as well. + * + * One of the main motivations for this is DP LL 1.4 CTS use case + * where it is possible that we could get a test request right after + * a connection, and the strict timing requriements of the test can + * only be met if we do not wait for the e2e connection to be set up. + */ + if (!dp->mst.mst_active && + (work_busy(&dp->attention_work) == WORK_BUSY_PENDING)) { + SDE_EVT32_EXTERNAL(dp->state, 99); + DP_DEBUG("Attention pending, skip HPD notification\n"); + goto skip_notify; + } + if (!rc && !dp_display_state_is(DP_STATE_ABORTED)) dp_display_send_hpd_notification(dp); +skip_notify: SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc); return rc; } @@ -1121,7 +1184,6 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) dp_display_clean(dp); dp_display_host_unready(dp); - dp_display_host_deinit(dp); mutex_unlock(&dp->session_lock); @@ -1172,6 +1234,10 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) dp_display_disconnect_sync(dp); + mutex_lock(&dp->session_lock); + dp_display_host_deinit(dp); + mutex_unlock(&dp->session_lock); + if (!dp->debug->sim_mode && !dp->parser->no_aux_switch && !dp->parser->gpio_aux_switch) dp->aux->aux_switch(dp->aux, false, ORIENTATION_NONE); @@ -1246,6 +1312,10 @@ static void dp_display_attention_work(struct work_struct *work) if (dp_display_is_sink_count_zero(dp)) { dp_display_handle_disconnect(dp); } else { + /* + * connect work should take care of sending + * the HPD notification. + */ if (!dp->mst.mst_active) queue_work(dp->wq, &dp->connect_work); } @@ -1258,6 +1328,10 @@ static void dp_display_attention_work(struct work_struct *work) dp_display_handle_disconnect(dp); dp->panel->video_test = true; + /* + * connect work should take care of sending + * the HPD notification. + */ queue_work(dp->wq, &dp->connect_work); goto mst_attention; @@ -1268,7 +1342,6 @@ static void dp_display_attention_work(struct work_struct *work) mutex_lock(&dp->session_lock); dp_audio_enable(dp, false); - mutex_unlock(&dp->session_lock); if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { SDE_EVT32_EXTERNAL(dp->state, @@ -1287,13 +1360,22 @@ static void dp_display_attention_work(struct work_struct *work) dp->ctrl->link_maintenance(dp->ctrl); } - mutex_lock(&dp->session_lock); dp_audio_enable(dp, true); mutex_unlock(&dp->session_lock); if (dp->link->sink_request & (DP_TEST_LINK_PHY_TEST_PATTERN | - DP_TEST_LINK_TRAINING)) + DP_TEST_LINK_TRAINING)) { goto mst_attention; + } else { + /* + * It is possible that the connect_work skipped sending + * the HPD notification if the attention message was + * already pending. Send the notification here to + * account for that. This is not needed if this + * attention work was handling a test request + */ + dp_display_send_hpd_notification(dp); + } } cp_irq: @@ -1698,8 +1780,22 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); - if (dp_display_state_is(DP_STATE_ABORTED | DP_STATE_ENABLED)) { - dp_display_state_show("[not initialized]"); + /* + * If the physical connection to the sink is already lost by the time + * we try to set up the connection, we can just skip all the steps + * here safely. + */ + if (dp_display_state_is(DP_STATE_ABORTED)) { + dp_display_state_log("[aborted]"); + goto end; + } + + /* + * If DP_STATE_ENABLED, there is nothing left to do. + * However, this should not happen ideally. So, log this. + */ + if (dp_display_state_is(DP_STATE_ENABLED)) { + dp_display_state_show("[already enabled]"); goto end; } @@ -1820,15 +1916,25 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); + /* + * If DP_STATE_READY is not set, we should not do any HW + * programming. + */ if (!dp_display_state_is(DP_STATE_READY)) { dp_display_state_show("[host not ready]"); goto end; } - if (dp_display_state_is(DP_STATE_ABORTED)) { - dp_display_state_show("[aborted]"); - goto end; - } + /* + * It is possible that by the time we get call back to establish + * the DP pipeline e2e, the physical DP connection to the sink is + * already lost. In such cases, the DP_STATE_ABORTED would be set. + * However, it is necessary to NOT abort the display setup here so as + * to ensure that the rest of the system is in a stable state prior to + * handling the disconnect notification. + */ + if (dp_display_state_is(DP_STATE_ABORTED)) + dp_display_state_log("[aborted, but continue on]"); rc = dp_display_stream_enable(dp, panel); if (rc) @@ -1865,13 +1971,22 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); + /* + * If DP_STATE_READY is not set, we should not do any HW + * programming. + */ if (!dp_display_state_is(DP_STATE_ENABLED)) { dp_display_state_show("[not enabled]"); goto end; } + /* + * If the physical connection to the sink is already lost by the time + * we try to set up the connection, we can just skip all the steps + * here safely. + */ if (dp_display_state_is(DP_STATE_ABORTED)) { - dp_display_state_show("[aborted]"); + dp_display_state_log("[aborted]"); goto end; } |