@@ -6968,6 +6968,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
+ if (intel_port_is_tc(dev_priv, port) && !intel_dig_port->tc_legacy_port)
+ intel_dig_port->tc_delay_wa_needed = true;
+
return true;
fail:
@@ -1257,7 +1257,7 @@ struct intel_digital_port {
/* Used for DP and ICL+ TypeC/DP and TypeC/HDMI ports. */
enum aux_ch aux_ch;
enum intel_display_power_domain ddi_io_power_domain;
- bool tc_legacy_port:1;
+ bool tc_legacy_port:1, tc_delay_wa_needed:1;
enum tc_port_type tc_type;
void (*write_infoframe)(struct intel_encoder *encoder,
@@ -343,7 +343,7 @@ static void i915_digport_work_func(struct work_struct *work)
}
static bool
-i915_hotplug_iterate(struct drm_device *dev, u32 hpd_event_bits)
+i915_hotplug_iterate(struct drm_device *dev, u32 hpd_event_bits, u32 *hpd_tc_delay_wa)
{
struct drm_connector_list_iter conn_iter;
struct drm_connector *connector;
@@ -356,16 +356,27 @@ i915_hotplug_iterate(struct drm_device *dev, u32 hpd_event_bits)
intel_connector = to_intel_connector(connector);
intel_encoder = intel_connector->encoder;
-
if (!intel_encoder)
continue;
if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+ bool ret;
+
DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n",
connector->name, intel_encoder->hpd_pin);
- changed |= intel_encoder->hotplug(intel_encoder,
- intel_connector);
+ ret = intel_encoder->hotplug(intel_encoder,
+ intel_connector);
+ changed |= ret;
+
+ if (hpd_tc_delay_wa && !ret &&
+ connector->status != connector_status_connected) {
+ struct intel_digital_port *dig_port = enc_to_dig_port(&intel_encoder->base);
+
+ if (dig_port && dig_port->tc_delay_wa_needed &&
+ !dig_port->dp.is_mst)
+ *hpd_tc_delay_wa |= (1 << intel_encoder->hpd_pin);
+ }
}
}
drm_connector_list_iter_end(&conn_iter);
@@ -382,7 +393,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
container_of(work, struct drm_i915_private, hotplug.hotplug_work);
struct drm_device *dev = &dev_priv->drm;
bool changed;
- u32 hpd_event_bits;
+ u32 hpd_event_bits, hpd_tc_delay_wa = 0;
mutex_lock(&dev->mode_config.mutex);
DRM_DEBUG_KMS("running encoder hotplug functions\n");
@@ -397,12 +408,28 @@ static void i915_hotplug_work_func(struct work_struct *work)
spin_unlock_irq(&dev_priv->irq_lock);
- changed = i915_hotplug_iterate(dev, hpd_event_bits);
+ changed = i915_hotplug_iterate(dev, hpd_event_bits, &hpd_tc_delay_wa);
mutex_unlock(&dev->mode_config.mutex);
if (changed)
drm_kms_helper_hotplug_event(dev);
+
+ /*
+ * Unpowered type-c dongles can take some time to boot and be
+ * responsible, so here giving some type to those dongles to power up
+ * and then probing again.
+ */
+ if (hpd_tc_delay_wa) {
+ msleep(500);
+
+ mutex_lock(&dev->mode_config.mutex);
+ changed = i915_hotplug_iterate(dev, hpd_tc_delay_wa, NULL);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ if (changed)
+ drm_kms_helper_hotplug_event(dev);
+ }
}
Unpowered type-c dongles can take some time to boot and be responsible, causing the probe to fail and sink never be detected without further actions from userspace. It was not a issue for older platforms because there was a hardware bridge between DDI/DP ports and type-c controller adding a implicit delay that hid this issue but ICL have type-c controllers integrated to the SOC bring this issue to users. So here after the first probe interation over every connector with a hotplug event set, it sleeps for half a second to give some time to dongles to be ready and then try to probe again every type-c connector that failed in the initial probe. Cc: Imre Deak <imre.deak@intel.com> Signed-off-by: José Roberto de Souza <jose.souza@intel.com> --- drivers/gpu/drm/i915/intel_dp.c | 3 +++ drivers/gpu/drm/i915/intel_drv.h | 2 +- drivers/gpu/drm/i915/intel_hotplug.c | 39 +++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 7 deletions(-)