summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--msm/dp/dp_debug.h34
-rw-r--r--msm/dp/dp_display.c139
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;
}