A DP HPD IRQ reports a sink-side event and does not imply a Type-C routing change. Reprogramming the retimer for every IRQ can reset or disturb its datapath while link training or video transmission is in progress, causing intermittent DisplayPort link failures.
Cache the successfully programmed DP retimer mode, HPD level, and orientation. When an IRQ arrives with the same state, leave the retimer untouched while continuing to notify DRM so MST sink IRQ handling is preserved. Invalidate the cache on non-DP modes and after a failed retimer update so later DP transitions are always configured. Signed-off-by: Yongxing Mou <[email protected]> --- drivers/soc/qcom/pmic_glink_altmode.c | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index e903d4910e9c..5ae554dc01d9 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -121,6 +121,12 @@ struct pmic_glink_altmode_port { u8 hpd_state; u8 hpd_irq; u8 mux_ctrl; + + /* Last DP state successfully programmed into the retimer. */ + bool retimer_dp_valid; + u8 retimer_dp_mode; + bool retimer_dp_hpd_state; + enum typec_orientation retimer_dp_orientation; }; #define work_to_altmode(w) container_of((w), struct pmic_glink_altmode, enable_work) @@ -180,6 +186,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, bool hpd_irq) { struct typec_displayport_data dp_data = {}; + bool retimer_state_unchanged; int ret; dp_data.status = DP_STATUS_ENABLED; @@ -200,10 +207,31 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode, port->retimer_state.alt = &port->dp_alt; port->retimer_state.data = &dp_data; port->retimer_state.mode = TYPEC_MODAL_STATE(mode); + retimer_state_unchanged = port->retimer_dp_valid && + port->retimer_dp_mode == mode && + port->retimer_dp_hpd_state == hpd_state && + port->retimer_dp_orientation == port->orientation; + + /* + * An HPD IRQ is a sink-side DP interrupt, not a Type-C routing change. + * Avoid rewriting an already configured retimer while DP link training or + * video transmission is in progress. The DRM HPD IRQ notification below + * is deliberately still delivered. + */ + if (hpd_irq && retimer_state_unchanged) + return; ret = typec_retimer_set(port->typec_retimer, &port->retimer_state); - if (ret) + if (ret) { + /* A failed write may have left the retimer only partly configured. */ + port->retimer_dp_valid = false; dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret); + } else { + port->retimer_dp_valid = true; + port->retimer_dp_mode = mode; + port->retimer_dp_hpd_state = hpd_state; + port->retimer_dp_orientation = port->orientation; + } } static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode, @@ -214,6 +242,8 @@ static void pmic_glink_altmode_enable_tbt(struct pmic_glink_altmode *altmode, u32 cable_speed; int ret; + port->retimer_dp_valid = false; + /* Device Discover Mode VDO */ tbt_data.device_mode = TBT_MODE; tbt_data.device_mode |= TBT_SET_ADAPTER(TBT_ADAPTER_TBT3); @@ -271,6 +301,8 @@ static void pmic_glink_altmode_enable_usb4(struct pmic_glink_altmode *altmode, struct enter_usb_data data = {}; int ret; + port->retimer_dp_valid = false; + data.eudo = FIELD_PREP(EUDO_USB_MODE_MASK, EUDO_USB_MODE_USB4); if (tbt->usb_speed == 0) { @@ -307,6 +339,8 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode, { int ret; + port->retimer_dp_valid = false; + port->state.alt = NULL; port->state.data = NULL; port->state.mode = TYPEC_STATE_USB; @@ -329,6 +363,8 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode, { int ret; + port->retimer_dp_valid = false; + port->state.alt = NULL; port->state.data = NULL; port->state.mode = TYPEC_STATE_SAFE; -- 2.43.0
