}
}
+static bool tc_phy_is_ready_and_owned(struct intel_digital_port *dig_port,
+ bool phy_is_ready, bool phy_is_owned)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+
+ drm_WARN_ON(&i915->drm, phy_is_owned && !phy_is_ready);
+
+ return phy_is_ready && phy_is_owned;
+}
+
static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
return hpd_mask_to_tc_mode(live_status_mask);
}
+static enum tc_port_mode
+get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port,
+ enum tc_port_mode live_mode)
+{
+ switch (live_mode) {
+ case TC_PORT_LEGACY:
+ return TC_PORT_DISCONNECTED;
+ case TC_PORT_DP_ALT:
+ case TC_PORT_TBT_ALT:
+ return TC_PORT_TBT_ALT;
+ default:
+ MISSING_CASE(live_mode);
+ fallthrough;
+ case TC_PORT_DISCONNECTED:
+ if (dig_port->tc_legacy_port)
+ return TC_PORT_DISCONNECTED;
+ else
+ return TC_PORT_TBT_ALT;
+ }
+}
+
static enum tc_port_mode
intel_tc_port_get_current_mode(struct intel_digital_port *dig_port)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
enum tc_port_mode live_mode = tc_phy_hpd_live_mode(dig_port);
+ bool phy_is_ready;
+ bool phy_is_owned;
enum tc_port_mode mode;
/*
if (dig_port->tc_legacy_port)
tc_phy_wait_for_ready(dig_port);
- if (!tc_phy_is_owned(dig_port) ||
- drm_WARN_ON(&i915->drm, !tc_phy_status_complete(dig_port)))
- return TC_PORT_TBT_ALT;
+ phy_is_ready = tc_phy_status_complete(dig_port);
+ phy_is_owned = tc_phy_is_owned(dig_port);
+
+ if (!tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned))
+ return get_tc_mode_in_phy_not_owned_state(dig_port, live_mode);
mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT;
if (live_mode != TC_PORT_DISCONNECTED &&
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
intel_wakeref_t tc_cold_wref;
enum intel_display_power_domain domain;
+ bool update_mode = false;
mutex_lock(&dig_port->tc_lock);
* intel_tc_port_sanitize_mode().
*/
dig_port->tc_init_mode = dig_port->tc_mode;
- dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain);
+ if (dig_port->tc_mode != TC_PORT_DISCONNECTED)
+ dig_port->tc_lock_wakeref =
+ tc_cold_block(dig_port, &dig_port->tc_lock_power_domain);
/*
* The PHY needs to be connected for AUX to work during HW readout and
* MST topology resume, but the PHY mode can only be changed if the
* port is disabled.
+ *
+ * An exception is the case where BIOS leaves the PHY incorrectly
+ * disconnected on an enabled legacy port. Work around that by
+ * connecting the PHY even though the port is enabled. This doesn't
+ * cause a problem as the PHY ownership state is ignored by the
+ * IOM/TCSS firmware (only display can own the PHY in that case).
*/
- if (!tc_port_is_enabled(dig_port))
+ if (!tc_port_is_enabled(dig_port)) {
+ update_mode = true;
+ } else if (dig_port->tc_mode == TC_PORT_DISCONNECTED) {
+ drm_WARN_ON(&i915->drm, !dig_port->tc_legacy_port);
+ drm_err(&i915->drm,
+ "Port %s: PHY disconnected on enabled port, connecting it\n",
+ dig_port->tc_port_name);
+ update_mode = true;
+ }
+
+ if (update_mode)
intel_tc_port_update_mode(dig_port, 1, false);
/* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */
* we'll just switch to disconnected mode from it here without
* a note.
*/
- if (dig_port->tc_init_mode != TC_PORT_TBT_ALT)
+ if (dig_port->tc_init_mode != TC_PORT_TBT_ALT &&
+ dig_port->tc_init_mode != TC_PORT_DISCONNECTED)
drm_dbg_kms(&i915->drm,
"Port %s: PHY left in %s mode on disabled port, disconnecting it\n",
dig_port->tc_port_name,