disp: msm: dp: improve interop experience for fast hotplug scenarios

Current implementation waits for 10ms prior to sending the connection
notification to user mode. This delay is to check for any potential
IRQ HPD event from the sink which may require a link maintenance.
However, this delay may not be sufficient for certain use cases.
Increase this delay to 150ms and modify the implementation to exit
the wait whenever an IRQ HPD is received. This ensures that we can
process the IRQ HPD in a timely manner as per the specification. To
further improve debug ability, add the support to configure this delay
though debugfs:

   echo [delay_ms] > /sys/kernel/debug/drm_dp/connect_notification_delay_ms

Certain cables are unable to handle back-to-back HPD notifications and
may end up skipping some events. To improve interoperability, delay the
handling of disconnect notification. Sinks would typically issue an HPD
high following an HPD low only after they sense that the mainlink has
been torn down. Delaying the handling of HPD low would in turn delay the
issuing of the subsequent HPD high from the sink. Here again, make this
delay configurable through debugfs to improve debug ability of these
interop issues:

   echo [delay_ms] > /sys/kernel/debug/drm_dp/disconnect_delay_ms

Change-Id: Ie29198af4dcda6d392798a3a93ebb3ddaa6746c8
Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
This commit is contained in:
Aravind Venkateswaran
2020-09-25 21:24:52 -07:00
parent 683bf52ce0
commit 7454e06259
3 changed files with 82 additions and 3 deletions

View File

@@ -11,6 +11,7 @@
#include <linux/of_irq.h>
#include <linux/soc/qcom/fsa4480-i2c.h>
#include <linux/usb/phy.h>
#include <linux/jiffies.h>
#include "sde_connector.h"
@@ -158,6 +159,7 @@ struct dp_display_private {
struct device_node *aux_switch_node;
struct dentry *root;
struct completion notification_comp;
struct completion attention_comp;
struct dp_hpd *hpd;
struct dp_parser *parser;
@@ -1026,6 +1028,8 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
static int dp_display_process_hpd_high(struct dp_display_private *dp)
{
int rc = -EINVAL;
unsigned long wait_timeout_ms;
unsigned long t;
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
mutex_lock(&dp->session_lock);
@@ -1101,7 +1105,14 @@ end:
* Delay the HPD connect notification to see if sink generates any
* IRQ HPDs immediately after the HPD high.
*/
usleep_range(10000, 10100);
reinit_completion(&dp->attention_comp);
wait_timeout_ms = min_t(unsigned long,
dp->debug->connect_notification_delay_ms,
(unsigned long) MAX_CONNECT_NOTIFICATION_DELAY_MS);
t = wait_for_completion_timeout(&dp->attention_comp,
msecs_to_jiffies(wait_timeout_ms));
DP_DEBUG("wait_timeout=%lu ms, time_waited=%u ms\n", wait_timeout_ms,
jiffies_to_msecs(t));
/*
* If an IRQ HPD is pending, then do not send a connect notification.
@@ -1118,7 +1129,7 @@ end:
*/
if (!dp->mst.mst_active &&
(work_busy(&dp->attention_work) == WORK_BUSY_PENDING)) {
SDE_EVT32_EXTERNAL(dp->state, 99);
SDE_EVT32_EXTERNAL(dp->state, 99, jiffies_to_msecs(t));
DP_DEBUG("Attention pending, skip HPD notification\n");
goto skip_notify;
}
@@ -1127,7 +1138,8 @@ end:
dp_display_send_hpd_notification(dp);
skip_notify:
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state,
wait_timeout_ms, rc);
return rc;
}
@@ -1327,6 +1339,9 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp)
static void dp_display_disconnect_sync(struct dp_display_private *dp)
{
int disconnect_delay_ms;
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
/* cancel any pending request */
dp_display_state_add(DP_STATE_ABORTED);
@@ -1338,7 +1353,22 @@ static void dp_display_disconnect_sync(struct dp_display_private *dp)
cancel_work_sync(&dp->attention_work);
flush_workqueue(dp->wq);
/*
* Delay the teardown of the mainlink for better interop experience.
* It is possible that certain sinks can issue an HPD high immediately
* following an HPD low as soon as they detect the mainlink being
* turned off. This can sometimes result in the HPD low pulse getting
* lost with certain cable. This issue is commonly seen when running
* DP LL CTS test 4.2.1.3.
*/
disconnect_delay_ms = min_t(u32, dp->debug->disconnect_delay_ms,
(u32) MAX_DISCONNECT_DELAY_MS);
DP_DEBUG("disconnect delay = %d ms\n", disconnect_delay_ms);
msleep(disconnect_delay_ms);
dp_display_handle_disconnect(dp);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state,
disconnect_delay_ms);
}
static int dp_display_usbpd_disconnect_cb(struct device *dev)
@@ -1559,6 +1589,7 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
} else if ((dp->hpd->hpd_irq && dp_display_state_is(DP_STATE_READY)) ||
dp->debug->mst_hpd_sim) {
queue_work(dp->wq, &dp->attention_work);
complete_all(&dp->attention_comp);
} else if (dp->process_hpd_connect ||
!dp_display_state_is(DP_STATE_CONNECTED)) {
dp_display_state_remove(DP_STATE_ABORTED);
@@ -3371,6 +3402,7 @@ static int dp_display_probe(struct platform_device *pdev)
}
init_completion(&dp->notification_comp);
init_completion(&dp->attention_comp);
dp->pdev = pdev;
dp->name = "drm_dp";