@@ -4951,6 +4951,17 @@ void intel_ddi_init(struct drm_i915_private *dev_priv,
encoder = &dig_port->base;
encoder->devdata = devdata;
+ /*
+ * FIXME: We currently need to store dedicated_external because devdata
+ * does not live long enough for when intel_encoder_is_tc() is called on
+ * the unbind path. This needs to be fixed by making sure that the VBT
+ * data is kept long enough, so that
+ * intel_bios_encoder_is_dedicated_external() can be called directly
+ * from intel_encoder_is_tc().
+ */
+ if (intel_bios_encoder_is_dedicated_external(devdata))
+ dig_port->dedicated_external = true;
+
if (DISPLAY_VER(dev_priv) >= 13 && port >= PORT_D_XELPD) {
drm_encoder_init(&dev_priv->drm, &encoder->base, &intel_ddi_funcs,
DRM_MODE_ENCODER_TMDS,
@@ -1940,6 +1940,10 @@ bool intel_encoder_is_snps(struct intel_encoder *encoder)
bool intel_encoder_is_tc(struct intel_encoder *encoder)
{
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (dig_port && dig_port->dedicated_external)
+ return false;
return intel_phy_is_tc(i915, intel_encoder_to_phy(encoder));
}
@@ -1875,6 +1875,7 @@ struct intel_digital_port {
struct intel_lspcon lspcon;
enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool);
bool release_cl2_override;
+ bool dedicated_external;
u8 max_lanes;
/* Used for DP and ICL+ TypeC/DP and TypeC/HDMI ports. */
enum aux_ch aux_ch;
@@ -3386,6 +3386,13 @@ static int icl_get_tc_phy_dplls(struct intel_atomic_state *state,
return ret;
}
+static bool icl_encoder_has_tc_port(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ return dig_port && dig_port->tc;
+}
+
static int icl_compute_dplls(struct intel_atomic_state *state,
struct intel_crtc *crtc,
struct intel_encoder *encoder)
@@ -3395,8 +3402,10 @@ static int icl_compute_dplls(struct intel_atomic_state *state,
if (intel_phy_is_combo(i915, phy))
return icl_compute_combo_phy_dpll(state, crtc);
- else if (intel_phy_is_tc(i915, phy))
+ else if (intel_phy_is_tc(i915, phy) && icl_encoder_has_tc_port(encoder))
return icl_compute_tc_phy_dplls(state, crtc);
+ else if (intel_phy_is_tc(i915, phy))
+ return icl_compute_combo_phy_dpll(state, crtc);
MISSING_CASE(phy);
@@ -3412,8 +3421,10 @@ static int icl_get_dplls(struct intel_atomic_state *state,
if (intel_phy_is_combo(i915, phy))
return icl_get_combo_phy_dpll(state, crtc, encoder);
- else if (intel_phy_is_tc(i915, phy))
+ else if (intel_phy_is_tc(i915, phy) && icl_encoder_has_tc_port(encoder))
return icl_get_tc_phy_dplls(state, crtc, encoder);
+ else if (intel_phy_is_tc(i915, phy))
+ return icl_get_combo_phy_dpll(state, crtc, encoder);
MISSING_CASE(phy);
@@ -105,7 +105,7 @@ static bool intel_tc_port_in_mode(struct intel_digital_port *dig_port,
enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
struct intel_tc_port *tc = to_tc_port(dig_port);
- return intel_phy_is_tc(i915, phy) && tc->mode == mode;
+ return intel_phy_is_tc(i915, phy) && tc && tc->mode == mode;
}
bool intel_tc_port_in_tbt_alt_mode(struct intel_digital_port *dig_port)
BugLink: https://bugs.launchpad.net/bugs/2164507 Starting with Xe3p_LPD, the VBT has a new field, called in the driver "dedicated_external", which tells that a Type-C capable port is physically connected to a PHY outside of the Type-C subsystem. When that's the case, the driver must not do the extra Type-C programming for that port. Update intel_encoder_is_tc() to check for that case. While at it, add a note to intel_phy_is_tc() to remind us that it is about whether the respective port is a Type-C capable port rather than the PHY itself. (Maybe it would be a nice idea to rename intel_phy_is_tc()?) Note that this was handled with a new bool member added to struct intel_digital_port instead of having querying the VBT directly because VBT memory is freed (intel_bios_driver_remove) before encoder cleanup (intel_ddi_encoder_destroy), which would cause an oops to happen when the latter calls intel_encoder_is_tc(). This could be fixed by keeping VBT data around longer, but that's left for a follow-up work, if deemed necessary. v2: - Drop printing info about dedicated external, now that we are doing it when parsing the VBT. (Jani) - Add a FIXME comment on the code explaining why we need to store dedicated_external in struct intel_digital_port. (Jani) v3: - Simplify the code by using NULL check for dig_port to avoid using intel_encoder_is_dig_port(). (Imre) Cc: Imre Deak <imre.deak@intel.com> Cc: Jani Nikula <jani.nikula@linux.intel.com> Cc: Shekhar Chauhan <shekhar.chauhan@intel.com> Signed-off-by: Gustavo Sousa <gustavo.sousa@intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Link: https://patch.msgid.link/20251202012306.9315-4-matthew.s.atwood@intel.com Signed-off-by: Matt Roper <matthew.d.roper@intel.com> (backported from commit df5dd52a6de486e32576ac5851b4e9429182274b) [an: adjusted context for Jammy 6.8 and added guards for the observed 6.8 shutdown panic path where intel_phy_is_tc() is true but dig_port->tc is NULL. Without these guards, fbcon restore during shutdown can still enter TC mode/DPLL handling and dereference dig_port->tc.] Signed-off-by: ChunAn Wu <an.wu@canonical.com> --- drivers/gpu/drm/i915/display/intel_ddi.c | 11 +++++++++++ drivers/gpu/drm/i915/display/intel_display.c | 4 ++++ .../gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 15 +++++++++++++-- drivers/gpu/drm/i915/display/intel_tc.c | 2 +- 5 files changed, 30 insertions(+), 3 deletions(-)