Register SOP' alternate modes with a Type-C Connector Class cable plug. Alternate modes are queried from the PPM using the GET_ALTERNATE_MODES command with recipient set to SOP'. Signed-off-by: Jameson Thies <jthies@xxxxxxxxxx> --- Tested on v6.6 kernel. SOP' GET_ALTERNATE_MODE responses from the PPM are correctly registered as to the cable plug. redrix-rev3 /sys/class/typec # ls port2-cable/port2-plug0/ device port2-plug0.0 port2-plug0.1 power subsystem uevent drivers/usb/typec/ucsi/ucsi.c | 60 +++++++++++++++++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 2 ++ 2 files changed, 62 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 6d6443e61faa..9b541547917b 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -399,6 +399,27 @@ static int ucsi_register_altmode(struct ucsi_connector *con, con->partner_altmode[i] = alt; break; + case UCSI_RECIPIENT_SOP_P: + i = ucsi_next_altmode(con->plug_altmode); + if (i < 0) { + ret = i; + goto err; + } + + ret = ucsi_altmode_next_mode(con->plug_altmode, desc->svid); + if (ret < 0) + return ret; + + desc->mode = ret; + + alt = typec_plug_register_altmode(con->plug, desc); + if (IS_ERR(alt)) { + ret = PTR_ERR(alt); + goto err; + } + + con->plug_altmode[i] = alt; + break; default: return -EINVAL; } @@ -566,6 +587,9 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) case UCSI_RECIPIENT_SOP: adev = con->partner_altmode; break; + case UCSI_RECIPIENT_SOP_P: + adev = con->plug_altmode; + break; default: return; } @@ -801,6 +825,33 @@ static void ucsi_unregister_partner_pdos(struct ucsi_connector *con) con->partner_pd = NULL; } +static int ucsi_register_plug(struct ucsi_connector *con) +{ + struct typec_plug *plug; + struct typec_plug_desc desc = {.index = TYPEC_PLUG_SOP_P}; + + plug = typec_register_plug(con->cable, &desc); + if (IS_ERR(plug)) { + dev_err(con->ucsi->dev, + "con%d: failed to register plug (%ld)\n", con->num, + PTR_ERR(plug)); + return PTR_ERR(plug); + } + + con->plug = plug; + return 0; +} + +static void ucsi_unregister_plug(struct ucsi_connector *con) +{ + if (!con->plug) + return; + + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP_P); + typec_unregister_plug(con->plug); + con->plug = NULL; +} + static int ucsi_register_cable(struct ucsi_connector *con) { struct typec_cable *cable; @@ -842,6 +893,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con) if (!con->cable) return; + ucsi_unregister_plug(con); typec_unregister_cable(con->cable); con->cable = NULL; memset(&con->cable_identity, 0, sizeof(con->cable_identity)); @@ -1046,6 +1098,14 @@ static int ucsi_check_cable(struct ucsi_connector *con) if (ret < 0) return ret; + ret = ucsi_register_plug(con); + if (ret < 0) + return ret; + + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P); + if (ret < 0) + return ret; + return 0; } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index b89fae82e8ce..32daf5f58650 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -429,9 +429,11 @@ struct ucsi_connector { struct typec_port *port; struct typec_partner *partner; struct typec_cable *cable; + struct typec_plug *plug; struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES]; struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES]; + struct typec_altmode *plug_altmode[UCSI_MAX_ALTMODES]; struct typec_capability typec_cap; -- 2.44.0.rc0.258.g7320e95886-goog