Currently MST on a port can get enabled/disabled from the hotplug work and get disabled from the short pulse work in a racy way. Fix this by relying on the MST state checking in the hotplug work and just schedule a hotplug work from the short pulse handler if some problem happened during the MST interrupt handling.
This removes the explicit MST disabling in case of an AUX failure, but if AUX fails, then probably the detection will also fail during the scheduled hotplug work and it's not guaranteed that we'll see intermittent errors anyway.
While at it also simplify the error checking of the MST interrupt handler.
Signed-off-by: Imre Deak imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 33 +++---------------------- 1 file changed, 4 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 55fda074c0ad..befbcacddaa1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5604,7 +5604,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) } }
- return need_retrain; + return need_retrain ? -EINVAL : 0; }
static bool @@ -7255,35 +7255,10 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) }
if (intel_dp->is_mst) { - switch (intel_dp_check_mst_status(intel_dp)) { - case -EINVAL: - /* - * If we were in MST mode, and device is not - * there, get out of MST mode - */ - drm_dbg_kms(&i915->drm, - "MST device may have disappeared %d vs %d\n", - intel_dp->is_mst, - intel_dp->mst_mgr.mst_state); - intel_dp->is_mst = false; - drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, - intel_dp->is_mst); - - return IRQ_NONE; - case 1: - return IRQ_NONE; - default: - break; - } - } - - if (!intel_dp->is_mst) { - bool handled; - - handled = intel_dp_short_pulse(intel_dp); - - if (!handled) + if (intel_dp_check_mst_status(intel_dp) < 0) return IRQ_NONE; + } else if (!intel_dp_short_pulse(intel_dp)) { + return IRQ_NONE; }
return IRQ_HANDLED;
Make the locking look symmetric with the unlocking.
Signed-off-by: Imre Deak imre.deak@intel.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 1bdf3cfeeebb..5bc72e800b85 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1183,7 +1183,7 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, ret = wait_event_timeout(mgr->tx_waitq, check_txmsg_state(mgr, txmsg), (4 * HZ)); - mutex_lock(&mstb->mgr->qlock); + mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { ret = -EIO;
On Thu, 2020-06-04 at 00:10 +0300, Imre Deak wrote:
Make the locking look symmetric with the unlocking.
Reviewed-by: José Roberto de Souza jose.souza@intel.com
Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 1bdf3cfeeebb..5bc72e800b85 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1183,7 +1183,7 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, ret = wait_event_timeout(mgr->tx_waitq, check_txmsg_state(mgr, txmsg), (4 * HZ));
- mutex_lock(&mstb->mgr->qlock);
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { ret = -EIO;
Some TypeC -> native DP adapters, at least the Club CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP alt mode specification adapters should forward short pulses with a duration greater than 250 usec. According to the DP specificatin DP sources should detect short pulses in the 500 usec -> 2 ms range. Based on this filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 ms if the source doesn't respond, so use a 50 ms poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP specification.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
Signed-off-by: Imre Deak imre.deak@intel.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 18 +++++++++++++++--- drivers/gpu/drm/i915/display/intel_dp_mst.c | 15 +++++++++++++++ include/drm/drm_dp_mst_helper.h | 1 + 3 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..d1bf340a95a8 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,23 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; + unsigned long wait_expires = jiffies + msecs_to_jiffies(4000); int ret;
- ret = wait_event_timeout(mgr->tx_waitq, - check_txmsg_state(mgr, txmsg), - (4 * HZ)); + for (;;) { + ret = wait_event_timeout(mgr->tx_waitq, + check_txmsg_state(mgr, txmsg), + mgr->cbs->update_hpd_irq_state ? + msecs_to_jiffies(50) : + wait_expires); + + if (ret || !mgr->cbs->update_hpd_irq_state || + time_after(jiffies, wait_expires)) + break; + + mgr->cbs->update_hpd_irq_state(mgr); + } + mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..1ff7d0096262 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -765,8 +765,23 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_update_hpd_irq_state(struct drm_dp_mst_topology_mgr *mgr) +{ + struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + spin_lock_irq(&i915->irq_lock); + i915->hotplug.short_port_mask |= BIT(dig_port->base.port); + spin_unlock_irq(&i915->irq_lock); + + queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work); +} + static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector, + .update_hpd_irq_state = intel_dp_mst_update_hpd_irq_state, };
static struct intel_dp_mst_encoder * diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..c902f4380200 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,7 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path); + void (*update_hpd_irq_state)(struct drm_dp_mst_topology_mgr *mgr); };
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
Some TypeC -> native DP adapters, at least the Club CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP alt mode specification adapters should forward short pulses with a duration greater than 250 usec. According to the DP specificatin DP sources should detect short pulses in the 500 usec -> 2 ms range. Based on this filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 ms if the source doesn't respond, so use a 50 ms poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP specification.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2: - Fix the wait event timeout for the no-poll case.
Signed-off-by: Imre Deak imre.deak@intel.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 19 ++++++++++++++++--- drivers/gpu/drm/i915/display/intel_dp_mst.c | 15 +++++++++++++++ include/drm/drm_dp_mst_helper.h | 1 + 3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..4e987a513df8 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,24 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; + unsigned long wait_timeout = msecs_to_jiffies(4000); + unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq, - check_txmsg_state(mgr, txmsg), - (4 * HZ)); + for (;;) { + ret = wait_event_timeout(mgr->tx_waitq, + check_txmsg_state(mgr, txmsg), + mgr->cbs->update_hpd_irq_state ? + msecs_to_jiffies(50) : + wait_timeout); + + if (ret || !mgr->cbs->update_hpd_irq_state || + time_after(jiffies, wait_expires)) + break; + + mgr->cbs->update_hpd_irq_state(mgr); + } + mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..1ff7d0096262 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -765,8 +765,23 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_update_hpd_irq_state(struct drm_dp_mst_topology_mgr *mgr) +{ + struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + spin_lock_irq(&i915->irq_lock); + i915->hotplug.short_port_mask |= BIT(dig_port->base.port); + spin_unlock_irq(&i915->irq_lock); + + queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work); +} + static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector, + .update_hpd_irq_state = intel_dp_mst_update_hpd_irq_state, };
static struct intel_dp_mst_encoder * diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..c902f4380200 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,7 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path); + void (*update_hpd_irq_state)(struct drm_dp_mst_topology_mgr *mgr); };
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
On Thu, Jun 04, 2020 at 01:18:59AM +0300, Imre Deak wrote:
Some TypeC -> native DP adapters, at least the Club CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP alt mode specification adapters should forward short pulses with a duration greater than 250 usec. According to the DP specificatin DP sources should detect short pulses in the 500 usec -> 2 ms range.
IIRC it was 250 usec -> 2 ms as well in the DP spec.
500 usec -> 1 ms is the duration of the short hpd the signalling side should use.
Based on this filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 ms if the source doesn't respond, so use a 50 ms poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP specification.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2:
- Fix the wait event timeout for the no-poll case.
Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 19 ++++++++++++++++--- drivers/gpu/drm/i915/display/intel_dp_mst.c | 15 +++++++++++++++ include/drm/drm_dp_mst_helper.h | 1 + 3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..4e987a513df8 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,24 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
- unsigned long wait_timeout = msecs_to_jiffies(4000);
- unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
(4 * HZ));
- for (;;) {
ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
mgr->cbs->update_hpd_irq_state ?
msecs_to_jiffies(50) :
wait_timeout);
if (ret || !mgr->cbs->update_hpd_irq_state ||
time_after(jiffies, wait_expires))
break;
First I thought this was changing the behaviour when the callback isn't provided, but then I noticed the ?: stuff for the timeout.
I think this stuff deserves a comment to explain why we would ever do such a thing instead of simply waiting like we did before.
mgr->cbs->update_hpd_irq_state(mgr);
- }
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..1ff7d0096262 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -765,8 +765,23 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_update_hpd_irq_state(struct drm_dp_mst_topology_mgr *mgr) +{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- spin_lock_irq(&i915->irq_lock);
- i915->hotplug.short_port_mask |= BIT(dig_port->base.port);
- spin_unlock_irq(&i915->irq_lock);
- queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work);
I might suggest putting this code right next to intel_hpd_irq_handler() so that people can actually see it when working on the hotplug code.
+}
static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector,
- .update_hpd_irq_state = intel_dp_mst_update_hpd_irq_state,
};
static struct intel_dp_mst_encoder * diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..c902f4380200 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,7 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
- void (*update_hpd_irq_state)(struct drm_dp_mst_topology_mgr *mgr);
I guess a bit of docs for this might be nice. Maybe s/update/poll/ might make the intention more clear? Not sure.
};
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Thu, Jun 04, 2020 at 06:12:27PM +0300, Ville Syrjälä wrote:
On Thu, Jun 04, 2020 at 01:18:59AM +0300, Imre Deak wrote:
Some TypeC -> native DP adapters, at least the Club CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP alt mode specification adapters should forward short pulses with a duration greater than 250 usec. According to the DP specificatin DP sources should detect short pulses in the 500 usec -> 2 ms range.
IIRC it was 250 usec -> 2 ms as well in the DP spec.
500 usec -> 1 ms is the duration of the short hpd the signalling side should use.
Ah, correct (and this is what makes actually sense). For reference it's described under "5.1.4 Source Device Behavior upon HPD Pulse Detection"
Based on this filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 ms if the source doesn't respond, so use a 50 ms poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP specification.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2:
- Fix the wait event timeout for the no-poll case.
Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 19 ++++++++++++++++--- drivers/gpu/drm/i915/display/intel_dp_mst.c | 15 +++++++++++++++ include/drm/drm_dp_mst_helper.h | 1 + 3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..4e987a513df8 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,24 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
- unsigned long wait_timeout = msecs_to_jiffies(4000);
- unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
(4 * HZ));
- for (;;) {
ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
mgr->cbs->update_hpd_irq_state ?
msecs_to_jiffies(50) :
wait_timeout);
if (ret || !mgr->cbs->update_hpd_irq_state ||
time_after(jiffies, wait_expires))
break;
First I thought this was changing the behaviour when the callback isn't provided, but then I noticed the ?: stuff for the timeout.
I think this stuff deserves a comment to explain why we would ever do such a thing instead of simply waiting like we did before.
Ok, will add a compact form of the commit log explanation.
mgr->cbs->update_hpd_irq_state(mgr);
- }
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..1ff7d0096262 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -765,8 +765,23 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_update_hpd_irq_state(struct drm_dp_mst_topology_mgr *mgr) +{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- spin_lock_irq(&i915->irq_lock);
- i915->hotplug.short_port_mask |= BIT(dig_port->base.port);
- spin_unlock_irq(&i915->irq_lock);
- queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work);
I might suggest putting this code right next to intel_hpd_irq_handler() so that people can actually see it when working on the hotplug code.
Ok.
+}
static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector,
- .update_hpd_irq_state = intel_dp_mst_update_hpd_irq_state,
};
static struct intel_dp_mst_encoder * diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..c902f4380200 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,7 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
- void (*update_hpd_irq_state)(struct drm_dp_mst_topology_mgr *mgr);
I guess a bit of docs for this might be nice. Maybe s/update/poll/ might make the intention more clear? Not sure.
Ok.
};
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
-- Ville Syrjälä Intel
Some TypeC -> native DP adapters, at least the Club 3D CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP Standard 2.0 section 5.1.4: - DP sinks should generate short pulses in the 500 usec -> 1 msec range - DP sources should detect short pulses in the 250 usec -> 2 msec range
According to the DP Alt Mode on TypeC Standard section 3.9.2, adapters should detect and forward short pulses according to how sources should detect them as specified in the DP Standard (250 usec -> 2 msec).
Based on the above filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 msec if the source doesn't respond, so use a 50 msec poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP Standard.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2: - Fix the wait event timeout for the no-poll case. v3 (Ville): - Fix the short pulse duration limits in the commit log prescribed by the DP Standard. - Add code comment explaining why/how polling is used. - Factor out a helper to schedule the port's hpd irq handler and move it to the rest of hotplug handlers. - Document the new MST callback. - s/update_hpd_irq_state/poll_hpd_irq/
Cc: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Imre Deak imre.deak@intel.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 32 ++++++++++++++++++-- drivers/gpu/drm/i915/display/intel_dp_mst.c | 10 ++++++ drivers/gpu/drm/i915/display/intel_hotplug.c | 18 +++++++++++ drivers/gpu/drm/i915/display/intel_hotplug.h | 2 ++ include/drm/drm_dp_mst_helper.h | 9 ++++++ 5 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..2a309fb2c4cc 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,37 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; + unsigned long wait_timeout = msecs_to_jiffies(4000); + unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq, - check_txmsg_state(mgr, txmsg), - (4 * HZ)); + for (;;) { + /* + * If the driver provides a way for this, change to + * poll-waiting for the MST reply interrupt if we didn't receive + * it for 50 msec. This would cater for cases where the HPD + * pulse signal got lost somewhere, even though the sink raised + * the corresponding MST interrupt correctly. One example is the + * Club 3D CAC-1557 TypeC -> DP adapter which for some reason + * filters out short pulses with a duration less than ~540 usec. + * + * The poll period is 50 msec to avoid missing an interrupt + * after the sink has cleared it (after a 110msec timeout + * since it raised the interrupt). + */ + ret = wait_event_timeout(mgr->tx_waitq, + check_txmsg_state(mgr, txmsg), + mgr->cbs->poll_hpd_irq ? + msecs_to_jiffies(50) : + wait_timeout); + + if (ret || !mgr->cbs->poll_hpd_irq || + time_after(jiffies, wait_expires)) + break; + + mgr->cbs->poll_hpd_irq(mgr); + } + mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..9be52643205d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -33,6 +33,7 @@ #include "intel_connector.h" #include "intel_ddi.h" #include "intel_display_types.h" +#include "intel_hotplug.h" #include "intel_dp.h" #include "intel_dp_mst.h" #include "intel_dpio_phy.h" @@ -765,8 +766,17 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr) +{ + struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); + + intel_hpd_trigger_irq(dp_to_dig_port(intel_dp)); +} + static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector, + .poll_hpd_irq = intel_dp_mst_poll_hpd_irq, };
static struct intel_dp_mst_encoder * diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c index 4f6f560e093e..664f88354101 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug.c @@ -347,6 +347,24 @@ static void i915_digport_work_func(struct work_struct *work) } }
+/** + * intel_hpd_trigger_irq - trigger an hpd irq event for a port + * @dig_port: digital port + * + * Trigger an HPD interrupt event for the given port, emulating a short pulse + * generated by the sink, and schedule the dig port work to handle it. + */ +void intel_hpd_trigger_irq(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + spin_lock_irq(&i915->irq_lock); + i915->hotplug.short_port_mask |= BIT(dig_port->base.port); + spin_unlock_irq(&i915->irq_lock); + + queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work); +} + /* * Handle hotplug events outside the interrupt handler proper. */ diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.h b/drivers/gpu/drm/i915/display/intel_hotplug.h index 777b0743257e..a704d7c94d16 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.h +++ b/drivers/gpu/drm/i915/display/intel_hotplug.h @@ -10,6 +10,7 @@
struct drm_i915_private; struct intel_connector; +struct intel_digital_port; struct intel_encoder; enum port;
@@ -18,6 +19,7 @@ enum intel_hotplug_state intel_encoder_hotplug(struct intel_encoder *encoder, struct intel_connector *connector); void intel_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 pin_mask, u32 long_mask); +void intel_hpd_trigger_irq(struct intel_digital_port *dig_port); void intel_hpd_init(struct drm_i915_private *dev_priv); void intel_hpd_init_work(struct drm_i915_private *dev_priv); void intel_hpd_cancel_work(struct drm_i915_private *dev_priv); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..b230ff6f7081 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,15 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path); + /* + * Checks for any pending MST interrupts, passing them to MST core for + * processing, the same way an HPD IRQ pulse handler would do this. + * If provided MST core calls this callback from a poll-waiting loop + * when waiting for MST down message replies. The driver is expected + * to guard against a race between this callback and the driver's HPD + * IRQ pulse handler. + */ + void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr); };
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
On Thu, Jun 04, 2020 at 09:45:00PM +0300, Imre Deak wrote:
Some TypeC -> native DP adapters, at least the Club 3D CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP Standard 2.0 section 5.1.4:
- DP sinks should generate short pulses in the 500 usec -> 1 msec range
- DP sources should detect short pulses in the 250 usec -> 2 msec range
According to the DP Alt Mode on TypeC Standard section 3.9.2, adapters should detect and forward short pulses according to how sources should detect them as specified in the DP Standard (250 usec -> 2 msec).
Based on the above filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 msec if the source doesn't respond, so use a 50 msec poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP Standard.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2:
- Fix the wait event timeout for the no-poll case.
v3 (Ville):
- Fix the short pulse duration limits in the commit log prescribed by the DP Standard.
- Add code comment explaining why/how polling is used.
- Factor out a helper to schedule the port's hpd irq handler and move it to the rest of hotplug handlers.
- Document the new MST callback.
- s/update_hpd_irq_state/poll_hpd_irq/
Cc: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Imre Deak imre.deak@intel.com
lgtm Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 32 ++++++++++++++++++-- drivers/gpu/drm/i915/display/intel_dp_mst.c | 10 ++++++ drivers/gpu/drm/i915/display/intel_hotplug.c | 18 +++++++++++ drivers/gpu/drm/i915/display/intel_hotplug.h | 2 ++ include/drm/drm_dp_mst_helper.h | 9 ++++++ 5 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..2a309fb2c4cc 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,37 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
- unsigned long wait_timeout = msecs_to_jiffies(4000);
- unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
(4 * HZ));
- for (;;) {
/*
* If the driver provides a way for this, change to
* poll-waiting for the MST reply interrupt if we didn't receive
* it for 50 msec. This would cater for cases where the HPD
* pulse signal got lost somewhere, even though the sink raised
* the corresponding MST interrupt correctly. One example is the
* Club 3D CAC-1557 TypeC -> DP adapter which for some reason
* filters out short pulses with a duration less than ~540 usec.
*
* The poll period is 50 msec to avoid missing an interrupt
* after the sink has cleared it (after a 110msec timeout
* since it raised the interrupt).
*/
ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
mgr->cbs->poll_hpd_irq ?
msecs_to_jiffies(50) :
wait_timeout);
if (ret || !mgr->cbs->poll_hpd_irq ||
time_after(jiffies, wait_expires))
break;
mgr->cbs->poll_hpd_irq(mgr);
- }
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..9be52643205d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -33,6 +33,7 @@ #include "intel_connector.h" #include "intel_ddi.h" #include "intel_display_types.h" +#include "intel_hotplug.h" #include "intel_dp.h" #include "intel_dp_mst.h" #include "intel_dpio_phy.h" @@ -765,8 +766,17 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr) +{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
+}
static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
};
static struct intel_dp_mst_encoder * diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c index 4f6f560e093e..664f88354101 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug.c @@ -347,6 +347,24 @@ static void i915_digport_work_func(struct work_struct *work) } }
+/**
- intel_hpd_trigger_irq - trigger an hpd irq event for a port
- @dig_port: digital port
- Trigger an HPD interrupt event for the given port, emulating a short pulse
- generated by the sink, and schedule the dig port work to handle it.
- */
+void intel_hpd_trigger_irq(struct intel_digital_port *dig_port) +{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- spin_lock_irq(&i915->irq_lock);
- i915->hotplug.short_port_mask |= BIT(dig_port->base.port);
- spin_unlock_irq(&i915->irq_lock);
- queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work);
+}
/*
- Handle hotplug events outside the interrupt handler proper.
*/ diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.h b/drivers/gpu/drm/i915/display/intel_hotplug.h index 777b0743257e..a704d7c94d16 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.h +++ b/drivers/gpu/drm/i915/display/intel_hotplug.h @@ -10,6 +10,7 @@
struct drm_i915_private; struct intel_connector; +struct intel_digital_port; struct intel_encoder; enum port;
@@ -18,6 +19,7 @@ enum intel_hotplug_state intel_encoder_hotplug(struct intel_encoder *encoder, struct intel_connector *connector); void intel_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 pin_mask, u32 long_mask); +void intel_hpd_trigger_irq(struct intel_digital_port *dig_port); void intel_hpd_init(struct drm_i915_private *dev_priv); void intel_hpd_init_work(struct drm_i915_private *dev_priv); void intel_hpd_cancel_work(struct drm_i915_private *dev_priv); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..b230ff6f7081 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,15 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
- /*
* Checks for any pending MST interrupts, passing them to MST core for
* processing, the same way an HPD IRQ pulse handler would do this.
* If provided MST core calls this callback from a poll-waiting loop
* when waiting for MST down message replies. The driver is expected
* to guard against a race between this callback and the driver's HPD
* IRQ pulse handler.
*/
- void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr);
};
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
2.23.1
Hi Dave, Lyude,
are you ok to merge this patchset via the drm-intel-next-queued tree?
--Imre
On Thu, Jun 04, 2020 at 09:45:00PM +0300, Imre Deak wrote:
Some TypeC -> native DP adapters, at least the Club 3D CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP Standard 2.0 section 5.1.4:
- DP sinks should generate short pulses in the 500 usec -> 1 msec range
- DP sources should detect short pulses in the 250 usec -> 2 msec range
According to the DP Alt Mode on TypeC Standard section 3.9.2, adapters should detect and forward short pulses according to how sources should detect them as specified in the DP Standard (250 usec -> 2 msec).
Based on the above filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 msec if the source doesn't respond, so use a 50 msec poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP Standard.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2:
- Fix the wait event timeout for the no-poll case.
v3 (Ville):
- Fix the short pulse duration limits in the commit log prescribed by the DP Standard.
- Add code comment explaining why/how polling is used.
- Factor out a helper to schedule the port's hpd irq handler and move it to the rest of hotplug handlers.
- Document the new MST callback.
- s/update_hpd_irq_state/poll_hpd_irq/
Cc: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 32 ++++++++++++++++++-- drivers/gpu/drm/i915/display/intel_dp_mst.c | 10 ++++++ drivers/gpu/drm/i915/display/intel_hotplug.c | 18 +++++++++++ drivers/gpu/drm/i915/display/intel_hotplug.h | 2 ++ include/drm/drm_dp_mst_helper.h | 9 ++++++ 5 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..2a309fb2c4cc 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,37 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
- unsigned long wait_timeout = msecs_to_jiffies(4000);
- unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
(4 * HZ));
- for (;;) {
/*
* If the driver provides a way for this, change to
* poll-waiting for the MST reply interrupt if we didn't receive
* it for 50 msec. This would cater for cases where the HPD
* pulse signal got lost somewhere, even though the sink raised
* the corresponding MST interrupt correctly. One example is the
* Club 3D CAC-1557 TypeC -> DP adapter which for some reason
* filters out short pulses with a duration less than ~540 usec.
*
* The poll period is 50 msec to avoid missing an interrupt
* after the sink has cleared it (after a 110msec timeout
* since it raised the interrupt).
*/
ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
mgr->cbs->poll_hpd_irq ?
msecs_to_jiffies(50) :
wait_timeout);
if (ret || !mgr->cbs->poll_hpd_irq ||
time_after(jiffies, wait_expires))
break;
mgr->cbs->poll_hpd_irq(mgr);
- }
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..9be52643205d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -33,6 +33,7 @@ #include "intel_connector.h" #include "intel_ddi.h" #include "intel_display_types.h" +#include "intel_hotplug.h" #include "intel_dp.h" #include "intel_dp_mst.h" #include "intel_dpio_phy.h" @@ -765,8 +766,17 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr) +{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
+}
static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
};
static struct intel_dp_mst_encoder * diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c index 4f6f560e093e..664f88354101 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug.c @@ -347,6 +347,24 @@ static void i915_digport_work_func(struct work_struct *work) } }
+/**
- intel_hpd_trigger_irq - trigger an hpd irq event for a port
- @dig_port: digital port
- Trigger an HPD interrupt event for the given port, emulating a short pulse
- generated by the sink, and schedule the dig port work to handle it.
- */
+void intel_hpd_trigger_irq(struct intel_digital_port *dig_port) +{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- spin_lock_irq(&i915->irq_lock);
- i915->hotplug.short_port_mask |= BIT(dig_port->base.port);
- spin_unlock_irq(&i915->irq_lock);
- queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work);
+}
/*
- Handle hotplug events outside the interrupt handler proper.
*/ diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.h b/drivers/gpu/drm/i915/display/intel_hotplug.h index 777b0743257e..a704d7c94d16 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.h +++ b/drivers/gpu/drm/i915/display/intel_hotplug.h @@ -10,6 +10,7 @@
struct drm_i915_private; struct intel_connector; +struct intel_digital_port; struct intel_encoder; enum port;
@@ -18,6 +19,7 @@ enum intel_hotplug_state intel_encoder_hotplug(struct intel_encoder *encoder, struct intel_connector *connector); void intel_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 pin_mask, u32 long_mask); +void intel_hpd_trigger_irq(struct intel_digital_port *dig_port); void intel_hpd_init(struct drm_i915_private *dev_priv); void intel_hpd_init_work(struct drm_i915_private *dev_priv); void intel_hpd_cancel_work(struct drm_i915_private *dev_priv); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..b230ff6f7081 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,15 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
- /*
* Checks for any pending MST interrupts, passing them to MST core for
* processing, the same way an HPD IRQ pulse handler would do this.
* If provided MST core calls this callback from a poll-waiting loop
* when waiting for MST down message replies. The driver is expected
* to guard against a race between this callback and the driver's HPD
* IRQ pulse handler.
*/
- void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr);
};
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Hi! Awesome patch series!
Reviewed-by: Lyude Paul lyude@redhat.com
Also re merging via drm-intel-next-queued - I think that should be fine, fwiw merging via drm-misc-next might be another option (I've definitely done this in the past for series that touched MST and drivers, but I don't have a hard preference either way).
On Tue, 2020-06-09 at 15:15 +0300, Imre Deak wrote:
Hi Dave, Lyude,
are you ok to merge this patchset via the drm-intel-next-queued tree?
--Imre
On Thu, Jun 04, 2020 at 09:45:00PM +0300, Imre Deak wrote:
Some TypeC -> native DP adapters, at least the Club 3D CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP Standard 2.0 section 5.1.4:
- DP sinks should generate short pulses in the 500 usec -> 1 msec range
- DP sources should detect short pulses in the 250 usec -> 2 msec range
According to the DP Alt Mode on TypeC Standard section 3.9.2, adapters should detect and forward short pulses according to how sources should detect them as specified in the DP Standard (250 usec -> 2 msec).
Based on the above filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 msec if the source doesn't respond, so use a 50 msec poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP Standard.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2:
- Fix the wait event timeout for the no-poll case.
v3 (Ville):
- Fix the short pulse duration limits in the commit log prescribed by the DP Standard.
- Add code comment explaining why/how polling is used.
- Factor out a helper to schedule the port's hpd irq handler and move it to the rest of hotplug handlers.
- Document the new MST callback.
- s/update_hpd_irq_state/poll_hpd_irq/
Cc: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 32 ++++++++++++++++++-- drivers/gpu/drm/i915/display/intel_dp_mst.c | 10 ++++++ drivers/gpu/drm/i915/display/intel_hotplug.c | 18 +++++++++++ drivers/gpu/drm/i915/display/intel_hotplug.h | 2 ++ include/drm/drm_dp_mst_helper.h | 9 ++++++ 5 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..2a309fb2c4cc 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,37 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
- unsigned long wait_timeout = msecs_to_jiffies(4000);
- unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
(4 * HZ));
- for (;;) {
/*
* If the driver provides a way for this, change to
* poll-waiting for the MST reply interrupt if we didn't receive
* it for 50 msec. This would cater for cases where the HPD
* pulse signal got lost somewhere, even though the sink raised
* the corresponding MST interrupt correctly. One example is the
* Club 3D CAC-1557 TypeC -> DP adapter which for some reason
* filters out short pulses with a duration less than ~540 usec.
*
* The poll period is 50 msec to avoid missing an interrupt
* after the sink has cleared it (after a 110msec timeout
* since it raised the interrupt).
*/
ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
mgr->cbs->poll_hpd_irq ?
msecs_to_jiffies(50) :
wait_timeout);
if (ret || !mgr->cbs->poll_hpd_irq ||
time_after(jiffies, wait_expires))
break;
mgr->cbs->poll_hpd_irq(mgr);
- }
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..9be52643205d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -33,6 +33,7 @@ #include "intel_connector.h" #include "intel_ddi.h" #include "intel_display_types.h" +#include "intel_hotplug.h" #include "intel_dp.h" #include "intel_dp_mst.h" #include "intel_dpio_phy.h" @@ -765,8 +766,17 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr) +{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
+}
static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
};
static struct intel_dp_mst_encoder * diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c index 4f6f560e093e..664f88354101 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug.c @@ -347,6 +347,24 @@ static void i915_digport_work_func(struct work_struct *work) } }
+/**
- intel_hpd_trigger_irq - trigger an hpd irq event for a port
- @dig_port: digital port
- Trigger an HPD interrupt event for the given port, emulating a short
pulse
- generated by the sink, and schedule the dig port work to handle it.
- */
+void intel_hpd_trigger_irq(struct intel_digital_port *dig_port) +{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- spin_lock_irq(&i915->irq_lock);
- i915->hotplug.short_port_mask |= BIT(dig_port->base.port);
- spin_unlock_irq(&i915->irq_lock);
- queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work);
+}
/*
- Handle hotplug events outside the interrupt handler proper.
*/ diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.h b/drivers/gpu/drm/i915/display/intel_hotplug.h index 777b0743257e..a704d7c94d16 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.h +++ b/drivers/gpu/drm/i915/display/intel_hotplug.h @@ -10,6 +10,7 @@
struct drm_i915_private; struct intel_connector; +struct intel_digital_port; struct intel_encoder; enum port;
@@ -18,6 +19,7 @@ enum intel_hotplug_state intel_encoder_hotplug(struct intel_encoder *encoder, struct intel_connector *connector); void intel_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 pin_mask, u32 long_mask); +void intel_hpd_trigger_irq(struct intel_digital_port *dig_port); void intel_hpd_init(struct drm_i915_private *dev_priv); void intel_hpd_init_work(struct drm_i915_private *dev_priv); void intel_hpd_cancel_work(struct drm_i915_private *dev_priv); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..b230ff6f7081 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,15 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
- /*
* Checks for any pending MST interrupts, passing them to MST core for
* processing, the same way an HPD IRQ pulse handler would do this.
* If provided MST core calls this callback from a poll-waiting loop
* when waiting for MST down message replies. The driver is expected
* to guard against a race between this callback and the driver's HPD
* IRQ pulse handler.
*/
- void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr);
};
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Tue, Jun 09, 2020 at 11:58:18AM -0400, Lyude Paul wrote:
Hi! Awesome patch series!
Reviewed-by: Lyude Paul lyude@redhat.com
Thanks.
Also re merging via drm-intel-next-queued - I think that should be fine, fwiw merging via drm-misc-next might be another option (I've definitely done this in the past for series that touched MST and drivers, but I don't have a hard preference either way).
Ok, if no objections I'll merge 2/3 via drm-misc-next, that seems to make more sense.
Could you also take a look at https://patchwork.freedesktop.org/series/78100/
I should've CC'd you.
On Tue, 2020-06-09 at 15:15 +0300, Imre Deak wrote:
Hi Dave, Lyude,
are you ok to merge this patchset via the drm-intel-next-queued tree?
--Imre
On Thu, Jun 04, 2020 at 09:45:00PM +0300, Imre Deak wrote:
Some TypeC -> native DP adapters, at least the Club 3D CAC-1557 adapter, incorrectly filter out HPD short pulses with a duration less than ~540 usec, leading to MST probe failures.
According to the DP Standard 2.0 section 5.1.4:
- DP sinks should generate short pulses in the 500 usec -> 1 msec range
- DP sources should detect short pulses in the 250 usec -> 2 msec range
According to the DP Alt Mode on TypeC Standard section 3.9.2, adapters should detect and forward short pulses according to how sources should detect them as specified in the DP Standard (250 usec -> 2 msec).
Based on the above filtering out short pulses with a duration less than 540 usec is incorrect.
To make such adapters work add support for a driver polling on MST inerrupt flags, and wire this up in the i915 driver. The sink can clear an interrupt it raised after 110 msec if the source doesn't respond, so use a 50 msec poll period to avoid missing an interrupt. Polling of the MST interrupt flags is explicitly allowed by the DP Standard.
This fixes MST probe failures I saw using this adapter and a DELL U2515H monitor.
v2:
- Fix the wait event timeout for the no-poll case.
v3 (Ville):
- Fix the short pulse duration limits in the commit log prescribed by the DP Standard.
- Add code comment explaining why/how polling is used.
- Factor out a helper to schedule the port's hpd irq handler and move it to the rest of hotplug handlers.
- Document the new MST callback.
- s/update_hpd_irq_state/poll_hpd_irq/
Cc: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/drm_dp_mst_topology.c | 32 ++++++++++++++++++-- drivers/gpu/drm/i915/display/intel_dp_mst.c | 10 ++++++ drivers/gpu/drm/i915/display/intel_hotplug.c | 18 +++++++++++ drivers/gpu/drm/i915/display/intel_hotplug.h | 2 ++ include/drm/drm_dp_mst_helper.h | 9 ++++++ 5 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5bc72e800b85..2a309fb2c4cc 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1178,11 +1178,37 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
- unsigned long wait_timeout = msecs_to_jiffies(4000);
- unsigned long wait_expires = jiffies + wait_timeout; int ret;
- ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
(4 * HZ));
- for (;;) {
/*
* If the driver provides a way for this, change to
* poll-waiting for the MST reply interrupt if we didn't receive
* it for 50 msec. This would cater for cases where the HPD
* pulse signal got lost somewhere, even though the sink raised
* the corresponding MST interrupt correctly. One example is the
* Club 3D CAC-1557 TypeC -> DP adapter which for some reason
* filters out short pulses with a duration less than ~540 usec.
*
* The poll period is 50 msec to avoid missing an interrupt
* after the sink has cleared it (after a 110msec timeout
* since it raised the interrupt).
*/
ret = wait_event_timeout(mgr->tx_waitq,
check_txmsg_state(mgr, txmsg),
mgr->cbs->poll_hpd_irq ?
msecs_to_jiffies(50) :
wait_timeout);
if (ret || !mgr->cbs->poll_hpd_irq ||
time_after(jiffies, wait_expires))
break;
mgr->cbs->poll_hpd_irq(mgr);
- }
- mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..9be52643205d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -33,6 +33,7 @@ #include "intel_connector.h" #include "intel_ddi.h" #include "intel_display_types.h" +#include "intel_hotplug.h" #include "intel_dp.h" #include "intel_dp_mst.h" #include "intel_dpio_phy.h" @@ -765,8 +766,17 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo return NULL; }
+static void +intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr) +{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
+}
static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
};
static struct intel_dp_mst_encoder * diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c index 4f6f560e093e..664f88354101 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug.c @@ -347,6 +347,24 @@ static void i915_digport_work_func(struct work_struct *work) } }
+/**
- intel_hpd_trigger_irq - trigger an hpd irq event for a port
- @dig_port: digital port
- Trigger an HPD interrupt event for the given port, emulating a short
pulse
- generated by the sink, and schedule the dig port work to handle it.
- */
+void intel_hpd_trigger_irq(struct intel_digital_port *dig_port) +{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- spin_lock_irq(&i915->irq_lock);
- i915->hotplug.short_port_mask |= BIT(dig_port->base.port);
- spin_unlock_irq(&i915->irq_lock);
- queue_work(i915->hotplug.dp_wq, &i915->hotplug.dig_port_work);
+}
/*
- Handle hotplug events outside the interrupt handler proper.
*/ diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.h b/drivers/gpu/drm/i915/display/intel_hotplug.h index 777b0743257e..a704d7c94d16 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.h +++ b/drivers/gpu/drm/i915/display/intel_hotplug.h @@ -10,6 +10,7 @@
struct drm_i915_private; struct intel_connector; +struct intel_digital_port; struct intel_encoder; enum port;
@@ -18,6 +19,7 @@ enum intel_hotplug_state intel_encoder_hotplug(struct intel_encoder *encoder, struct intel_connector *connector); void intel_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 pin_mask, u32 long_mask); +void intel_hpd_trigger_irq(struct intel_digital_port *dig_port); void intel_hpd_init(struct drm_i915_private *dev_priv); void intel_hpd_init_work(struct drm_i915_private *dev_priv); void intel_hpd_cancel_work(struct drm_i915_private *dev_priv); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9e1ffcd7cb68..b230ff6f7081 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -475,6 +475,15 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
- /*
* Checks for any pending MST interrupts, passing them to MST core for
* processing, the same way an HPD IRQ pulse handler would do this.
* If provided MST core calls this callback from a poll-waiting loop
* when waiting for MST down message replies. The driver is expected
* to guard against a race between this callback and the driver's HPD
* IRQ pulse handler.
*/
- void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr);
};
#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Thu, 2020-06-04 at 00:10 +0300, Imre Deak wrote:
Currently MST on a port can get enabled/disabled from the hotplug work and get disabled from the short pulse work in a racy way. Fix this by relying on the MST state checking in the hotplug work and just schedule a hotplug work from the short pulse handler if some problem happened during the MST interrupt handling.
Nice
This removes the explicit MST disabling in case of an AUX failure, but if AUX fails, then probably the detection will also fail during the scheduled hotplug work and it's not guaranteed that we'll see intermittent errors anyway.
While at it also simplify the error checking of the MST interrupt handler.
Reviewed-by: José Roberto de Souza jose.souza@intel.com
Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/display/intel_dp.c | 33 +++---------------------- 1 file changed, 4 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 55fda074c0ad..befbcacddaa1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5604,7 +5604,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) } }
- return need_retrain;
- return need_retrain ? -EINVAL : 0;
}
static bool @@ -7255,35 +7255,10 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) }
if (intel_dp->is_mst) {
switch (intel_dp_check_mst_status(intel_dp)) {
case -EINVAL:
/*
* If we were in MST mode, and device is not
* there, get out of MST mode
*/
drm_dbg_kms(&i915->drm,
"MST device may have disappeared %d vs %d\n",
intel_dp->is_mst,
intel_dp->mst_mgr.mst_state);
intel_dp->is_mst = false;
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
intel_dp->is_mst);
return IRQ_NONE;
case 1:
return IRQ_NONE;
default:
break;
}
- }
- if (!intel_dp->is_mst) {
bool handled;
handled = intel_dp_short_pulse(intel_dp);
if (!handled)
if (intel_dp_check_mst_status(intel_dp) < 0) return IRQ_NONE;
} else if (!intel_dp_short_pulse(intel_dp)) {
return IRQ_NONE;
}
return IRQ_HANDLED;
On Thu, Jun 04, 2020 at 12:10:38AM +0300, Imre Deak wrote:
Currently MST on a port can get enabled/disabled from the hotplug work and get disabled from the short pulse work in a racy way. Fix this by relying on the MST state checking in the hotplug work and just schedule a hotplug work from the short pulse handler if some problem happened during the MST interrupt handling.
This removes the explicit MST disabling in case of an AUX failure, but if AUX fails, then probably the detection will also fail during the scheduled hotplug work and it's not guaranteed that we'll see intermittent errors anyway.
While at it also simplify the error checking of the MST interrupt handler.
Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/display/intel_dp.c | 33 +++---------------------- 1 file changed, 4 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 55fda074c0ad..befbcacddaa1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5604,7 +5604,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) } }
- return need_retrain;
- return need_retrain ? -EINVAL : 0;
}
static bool @@ -7255,35 +7255,10 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) }
if (intel_dp->is_mst) {
switch (intel_dp_check_mst_status(intel_dp)) {
case -EINVAL:
/*
* If we were in MST mode, and device is not
* there, get out of MST mode
*/
drm_dbg_kms(&i915->drm,
"MST device may have disappeared %d vs %d\n",
intel_dp->is_mst,
intel_dp->mst_mgr.mst_state);
intel_dp->is_mst = false;
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
intel_dp->is_mst);
return IRQ_NONE;
case 1:
return IRQ_NONE;
default:
break;
}
- }
- if (!intel_dp->is_mst) {
bool handled;
handled = intel_dp_short_pulse(intel_dp);
if (!handled)
if (intel_dp_check_mst_status(intel_dp) < 0) return IRQ_NONE;
Since we no longer need the tristate return, can you follow up with a conversion to bool return? I'd vote to make it match the semantics of intel_dp_short_pulse() so we get one step closer to unifying the hpd_irq handling across the board.
} else if (!intel_dp_short_pulse(intel_dp)) {
return IRQ_NONE;
}
return IRQ_HANDLED;
-- 2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Thu, Jun 04, 2020 at 05:55:30PM +0300, Ville Syrjälä wrote:
On Thu, Jun 04, 2020 at 12:10:38AM +0300, Imre Deak wrote:
Currently MST on a port can get enabled/disabled from the hotplug work and get disabled from the short pulse work in a racy way. Fix this by relying on the MST state checking in the hotplug work and just schedule a hotplug work from the short pulse handler if some problem happened during the MST interrupt handling.
This removes the explicit MST disabling in case of an AUX failure, but if AUX fails, then probably the detection will also fail during the scheduled hotplug work and it's not guaranteed that we'll see intermittent errors anyway.
While at it also simplify the error checking of the MST interrupt handler.
Signed-off-by: Imre Deak imre.deak@intel.com
drivers/gpu/drm/i915/display/intel_dp.c | 33 +++---------------------- 1 file changed, 4 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 55fda074c0ad..befbcacddaa1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5604,7 +5604,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) } }
- return need_retrain;
- return need_retrain ? -EINVAL : 0;
}
static bool @@ -7255,35 +7255,10 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) }
if (intel_dp->is_mst) {
switch (intel_dp_check_mst_status(intel_dp)) {
case -EINVAL:
/*
* If we were in MST mode, and device is not
* there, get out of MST mode
*/
drm_dbg_kms(&i915->drm,
"MST device may have disappeared %d vs %d\n",
intel_dp->is_mst,
intel_dp->mst_mgr.mst_state);
intel_dp->is_mst = false;
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
intel_dp->is_mst);
return IRQ_NONE;
case 1:
return IRQ_NONE;
default:
break;
}
- }
- if (!intel_dp->is_mst) {
bool handled;
handled = intel_dp_short_pulse(intel_dp);
if (!handled)
if (intel_dp_check_mst_status(intel_dp) < 0) return IRQ_NONE;
Since we no longer need the tristate return, can you follow up with a conversion to bool return? I'd vote to make it match the semantics of intel_dp_short_pulse() so we get one step closer to unifying the hpd_irq handling across the board.
Ok, makes sense.
} else if (!intel_dp_short_pulse(intel_dp)) {
return IRQ_NONE;
}
return IRQ_HANDLED;
-- 2.23.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
-- Ville Syrjälä Intel
Currently MST on a port can get enabled/disabled from the hotplug work and get disabled from the short pulse work in a racy way. Fix this by relying on the MST state checking in the hotplug work and just schedule a hotplug work from the short pulse handler if some problem happened during the MST interrupt handling.
This removes the explicit MST disabling in case of an AUX failure, but if AUX fails, then probably the detection will also fail during the scheduled hotplug work and it's not guaranteed that we'll see intermittent errors anyway.
While at it also simplify the error checking of the MST interrupt handler.
v2: - Convert intel_dp_check_mst_status() to return bool. (Ville) - Change the intel_dp->is_mst check to an assert, since after this patch the condition can't change after we checked it previously. - Document the return value from intel_dp_check_mst_status().
Cc: José Roberto de Souza jose.souza@intel.com Cc: Ville Syrjälä ville.syrjala@linux.intel.com Reviewed-by: José Roberto de Souza jose.souza@intel.com (v1) Signed-off-by: Imre Deak imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 67 ++++++++++--------------- 1 file changed, 27 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 55fda074c0ad..4b6e7cf577dd 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5556,35 +5556,47 @@ static void intel_dp_handle_test_request(struct intel_dp *intel_dp) "Could not write test response to sink\n"); }
-static int +/** + * intel_dp_check_mst_status - service any pending MST interrupts, check link status + * @intel_dp: Intel DP struct + * + * Read any pending MST interrupts, call MST core to handle these and ack the + * interrupts. Check if the main and AUX link state is ok. + * + * Returns: + * - %true if pending interrupts were serviced (or no interrupts were + * pending) w/o detecting an error condition. + * - %false if an error condition - like AUX failure or a loss of link - is + * detected, which needs servicing from the hotplug work. + */ +static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); - bool need_retrain = false; - - if (!intel_dp->is_mst) - return -EINVAL; + bool link_ok = true;
+ drm_WARN_ON_ONCE(&i915->drm, !intel_dp->is_mst); drm_WARN_ON_ONCE(&i915->drm, intel_dp->active_mst_links < 0);
for (;;) { u8 esi[DP_DPRX_ESI_LEN] = {}; - bool bret, handled; + bool handled; int retry;
- bret = intel_dp_get_sink_irq_esi(intel_dp, esi); - if (!bret) { + if (!intel_dp_get_sink_irq_esi(intel_dp, esi)) { drm_dbg_kms(&i915->drm, "failed to get ESI - device may have failed\n"); - return -EINVAL; + link_ok = false; + + break; }
/* check link status - esi[10] = 0x200c */ - if (intel_dp->active_mst_links > 0 && !need_retrain && + if (intel_dp->active_mst_links > 0 && link_ok && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) { drm_dbg_kms(&i915->drm, "channel EQ not ok, retraining\n"); - need_retrain = true; + link_ok = false; }
drm_dbg_kms(&i915->drm, "got esi %3ph\n", esi); @@ -5604,7 +5616,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) } }
- return need_retrain; + return link_ok; }
static bool @@ -7255,35 +7267,10 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) }
if (intel_dp->is_mst) { - switch (intel_dp_check_mst_status(intel_dp)) { - case -EINVAL: - /* - * If we were in MST mode, and device is not - * there, get out of MST mode - */ - drm_dbg_kms(&i915->drm, - "MST device may have disappeared %d vs %d\n", - intel_dp->is_mst, - intel_dp->mst_mgr.mst_state); - intel_dp->is_mst = false; - drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, - intel_dp->is_mst); - - return IRQ_NONE; - case 1: - return IRQ_NONE; - default: - break; - } - } - - if (!intel_dp->is_mst) { - bool handled; - - handled = intel_dp_short_pulse(intel_dp); - - if (!handled) + if (!intel_dp_check_mst_status(intel_dp)) return IRQ_NONE; + } else if (!intel_dp_short_pulse(intel_dp)) { + return IRQ_NONE; }
return IRQ_HANDLED;
dri-devel@lists.freedesktop.org