diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index aa630b95dafe9..dd6f7b99e698a 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -290,6 +290,11 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) static void tps6598x_drop_partner(struct tps6598x *tps, u32 status) { + if (tps->partner_altmode_dp) { + typec_altmode_update_active(tps->partner_altmode_dp, false); + typec_unregister_altmode(tps->partner_altmode_dp); + tps->partner_altmode_dp = NULL; + } if (!IS_ERR(tps->partner)) typec_unregister_partner(tps->partner); tps->partner = NULL; @@ -1171,6 +1176,11 @@ static void cd321x_update_work(struct work_struct *work) /* Process partner disconnection or change */ if (!new_connected || partner_changed) { + if (tps->partner_altmode_dp) { + typec_altmode_update_active(tps->partner_altmode_dp, false); + typec_unregister_altmode(tps->partner_altmode_dp); + tps->partner_altmode_dp = NULL; + } if (!IS_ERR(tps->partner)) typec_unregister_partner(tps->partner); tps->partner = NULL; @@ -1245,6 +1255,29 @@ static void cd321x_update_work(struct work_struct *work) typec_partner_set_identity(tps->partner); } + /* The CD321x firmware owns VDM discovery and Enter Mode. Publish the + * discovered DP mode only after it reports an entered DP connection; + * keep Linux's DP Alt Mode driver from issuing a second Enter Mode. + */ + if (dp_connected && !tps->partner_altmode_dp) { + struct typec_altmode_desc desc = { + .svid = USB_TYPEC_DP_SID, + .mode = USB_TYPEC_DP_MODE, + .vdo = le32_to_cpu(st.dp_sid_status.mode_data), + .mode_selection = true, + }; + + tps->partner_altmode_dp = typec_partner_register_altmode(tps->partner, + &desc); + if (IS_ERR(tps->partner_altmode_dp)) { + ret = PTR_ERR(tps->partner_altmode_dp); + tps->partner_altmode_dp = NULL; + goto retry; + } + } + if (tps->partner_altmode_dp) + typec_altmode_update_active(tps->partner_altmode_dp, dp_connected); + /* Update the TypeC MUX/PHY state */ if (dp_route_was_active && dp_connected && !was_disconnected && !dp_mode_changed && st.dp_sid_event) { @@ -1997,7 +2030,8 @@ static int cd321x_register_port_altmodes(struct cd321x *cd321x) memset(&desc, 0, sizeof(desc)); desc.svid = USB_TYPEC_DP_SID; desc.mode = USB_TYPEC_DP_MODE; - desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D)); + desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | + BIT(DP_PIN_ASSIGN_D) | BIT(DP_PIN_ASSIGN_E)); desc.vdo |= DP_CAP_DFP_D; amode = typec_port_register_altmode(cd321x->tps.port, &desc); if (IS_ERR(amode)) @@ -2111,6 +2145,7 @@ EXPORT_SYMBOL_IF_KUNIT(cd321x_init_work); static int cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) { + struct fwnode_handle *connector_fwnode; struct cd321x *cd321x = container_of(tps, struct cd321x, tps); int ret; @@ -2130,9 +2165,12 @@ cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) goto err_unregister_altmodes; } - cd321x->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); - if (IS_ERR(cd321x->connector_fwnode)) - cd321x->connector_fwnode = NULL; + if (fwnode_property_present(fwnode, "displayport")) + connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); + else + connector_fwnode = fwnode_handle_get(fwnode); + if (!IS_ERR_OR_NULL(connector_fwnode)) + cd321x->connector_fwnode = connector_fwnode; cd321x->tbt_switch = cd321x_tbt_switch_get(tps->dev, fwnode); if (IS_ERR(cd321x->tbt_switch)) { diff --git a/drivers/usb/typec/tipd/spmi.c b/drivers/usb/typec/tipd/spmi.c index ba89b4b57a255..4f0ceca25ee7f 100644 --- a/drivers/usb/typec/tipd/spmi.c +++ b/drivers/usb/typec/tipd/spmi.c @@ -318,9 +318,14 @@ static int sn201202x_probe(struct spmi_device *device) return ret; ret = tipd_init(tps); - if (ret) + if (ret) { spmi_command_sleep(device); - return ret; + return ret; + } + + /* Events can latch before the IRQ is registered. Consume that state now. */ + tps->data->irq_handler(tps->irq, tps); + return 0; } static void sn201202x_remove(struct spmi_device *device) diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index caac3cb771bd4..f6d51724a1f5d 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -371,6 +371,7 @@ struct tps6598x { const struct tipd_data *data; struct tipd_debugfs *debugfs; + struct typec_altmode *partner_altmode_dp; }; /* TPS_REG_USB4_STATUS */