[PATCH 09/14] drm/i915/tc: Fix initial TC mode on disabled legacy ports

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Atm, a TC port's initial mode will be read out as TBT mode in any case
the PHY ownership is not held. This isn't correct for legacy ports which
should be used only in legacy mode.

Fix the above initial mode to be disconnected mode for a legacy port and
TBT mode for DP-alt/TBT ports. Determine the port type by checking first
the HPD state and then the legacy VBT flag (so the HPD state can correct
a bogus VBT flag). If a sink is connected on a disabled port the PHY
will get also connected (switching it to legacy mode on a legacy port).

Also connect the PHY on a legacy port if it's enabled but BIOS
incorrectly left it in the disconnected state for some reason.

Signed-off-by: Imre Deak <imre.deak@xxxxxxxxx>
---
 drivers/gpu/drm/i915/display/intel_tc.c | 67 ++++++++++++++++++++++---
 1 file changed, 61 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
index f66129494cc40..35e6339caa32f 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.c
+++ b/drivers/gpu/drm/i915/display/intel_tc.c
@@ -558,6 +558,16 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port)
 	}
 }
 
+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);
@@ -609,11 +619,34 @@ tc_phy_hpd_live_mode(struct intel_digital_port *dig_port)
 	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;
 
 	/*
@@ -624,9 +657,11 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port)
 	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 &&
@@ -758,6 +793,7 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port)
 	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);
 
@@ -773,14 +809,32 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port)
 	 * 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. */
@@ -831,7 +885,8 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port)
 		 * 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,
-- 
2.37.1




[Index of Archives]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux