Skip to content

Commit 41d9d75

Browse files
RD Babieragregkh
authored andcommitted
usb: typec: tcpm: add discover svids and discover modes support for sop'
Adds Discover SVIDs and Discover Modes support for SOP' and Alt Mode SVDM support over SOP'. tcpm_port adds separate Alt Mode data for SOP'. svdm_consume_svids and svdm_consume_modes take the received SVDM's SOP* type to store svids/modes separately, and tcpm_register_plug_altmodes registers the active cable's alt modes. In tcpm_pd_svdm, the port will send Discover SVIDs to SOP' after Discover Modes on SOP if the connected cable is an active cable. Discover Modes on SOP' is sent following Discover SVIDs on SOP. Registering partner alt modes is delayed when an active cable is present until Discover Modes completes on SOP', or if the Discover SVIDs/Discover Modes request on SOP' encounters a transmission error. Signed-off-by: RD Babiera <[email protected]> Reviewed-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent af8b627 commit 41d9d75

File tree

1 file changed

+140
-21
lines changed

1 file changed

+140
-21
lines changed

drivers/usb/typec/tcpm/tcpm.c

Lines changed: 140 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,9 @@ struct tcpm_port {
477477

478478
/* Alternate mode data */
479479
struct pd_mode_data mode_data;
480+
struct pd_mode_data mode_data_prime;
480481
struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX];
482+
struct typec_altmode *plug_prime_altmode[ALTMODE_DISCOVERY_MAX];
481483
struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX];
482484

483485
/* Deadline in jiffies to exit src_try_wait state */
@@ -1636,9 +1638,11 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p
16361638
}
16371639
}
16381640

1639-
static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt)
1641+
static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt,
1642+
enum tcpm_transmit_type rx_sop_type)
16401643
{
1641-
struct pd_mode_data *pmdata = &port->mode_data;
1644+
struct pd_mode_data *pmdata = rx_sop_type == TCPC_TX_SOP_PRIME ?
1645+
&port->mode_data_prime : &port->mode_data;
16421646
int i;
16431647

16441648
for (i = 1; i < cnt; i++) {
@@ -1684,14 +1688,29 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt)
16841688
return false;
16851689
}
16861690

1687-
static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt)
1691+
static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
1692+
enum tcpm_transmit_type rx_sop_type)
16881693
{
16891694
struct pd_mode_data *pmdata = &port->mode_data;
16901695
struct typec_altmode_desc *paltmode;
16911696
int i;
16921697

1693-
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
1694-
/* Already logged in svdm_consume_svids() */
1698+
switch (rx_sop_type) {
1699+
case TCPC_TX_SOP_PRIME:
1700+
pmdata = &port->mode_data_prime;
1701+
if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) {
1702+
/* Already logged in svdm_consume_svids() */
1703+
return;
1704+
}
1705+
break;
1706+
case TCPC_TX_SOP:
1707+
pmdata = &port->mode_data;
1708+
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
1709+
/* Already logged in svdm_consume_svids() */
1710+
return;
1711+
}
1712+
break;
1713+
default:
16951714
return;
16961715
}
16971716

@@ -1729,7 +1748,28 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
17291748
}
17301749
}
17311750

1751+
static void tcpm_register_plug_altmodes(struct tcpm_port *port)
1752+
{
1753+
struct pd_mode_data *modep = &port->mode_data_prime;
1754+
struct typec_altmode *altmode;
1755+
int i;
1756+
1757+
typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes);
1758+
1759+
for (i = 0; i < modep->altmodes; i++) {
1760+
altmode = typec_plug_register_altmode(port->plug_prime,
1761+
&modep->altmode_desc[i]);
1762+
if (IS_ERR(altmode)) {
1763+
tcpm_log(port, "Failed to register plug SVID 0x%04x",
1764+
modep->altmode_desc[i].svid);
1765+
altmode = NULL;
1766+
}
1767+
port->plug_prime_altmode[i] = altmode;
1768+
}
1769+
}
1770+
17321771
#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
1772+
#define supports_modal_cable(port) PD_IDH_MODAL_SUPP((port)->cable_ident.id_header)
17331773
#define supports_host(port) PD_IDH_HOST_SUPP((port->partner_ident.id_header))
17341774

17351775
/*
@@ -1807,15 +1847,24 @@ static bool tcpm_attempt_vconn_swap_discovery(struct tcpm_port *port)
18071847
return false;
18081848
}
18091849

1850+
1851+
static bool tcpm_cable_vdm_supported(struct tcpm_port *port)
1852+
{
1853+
return !IS_ERR_OR_NULL(port->cable) &&
1854+
typec_cable_is_active(port->cable) &&
1855+
supports_modal_cable(port) &&
1856+
tcpm_can_communicate_sop_prime(port);
1857+
}
1858+
18101859
static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
18111860
const u32 *p, int cnt, u32 *response,
18121861
enum adev_actions *adev_action,
18131862
enum tcpm_transmit_type rx_sop_type,
18141863
enum tcpm_transmit_type *response_tx_sop_type)
18151864
{
18161865
struct typec_port *typec = port->typec_port;
1817-
struct typec_altmode *pdev;
1818-
struct pd_mode_data *modep;
1866+
struct typec_altmode *pdev, *pdev_prime;
1867+
struct pd_mode_data *modep, *modep_prime;
18191868
int svdm_version;
18201869
int rlen = 0;
18211870
int cmd_type;
@@ -1836,18 +1885,33 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
18361885

18371886
switch (rx_sop_type) {
18381887
case TCPC_TX_SOP_PRIME:
1888+
modep_prime = &port->mode_data_prime;
1889+
pdev_prime = typec_match_altmode(port->plug_prime_altmode,
1890+
ALTMODE_DISCOVERY_MAX,
1891+
PD_VDO_VID(p[0]),
1892+
PD_VDO_OPOS(p[0]));
18391893
if (!IS_ERR_OR_NULL(port->cable)) {
18401894
svdm_version = typec_get_cable_svdm_version(typec);
18411895
if (PD_VDO_SVDM_VER(p[0]) < svdm_version)
18421896
typec_cable_set_svdm_version(port->cable, svdm_version);
18431897
}
18441898
break;
18451899
case TCPC_TX_SOP:
1900+
modep = &port->mode_data;
1901+
pdev = typec_match_altmode(port->partner_altmode,
1902+
ALTMODE_DISCOVERY_MAX,
1903+
PD_VDO_VID(p[0]),
1904+
PD_VDO_OPOS(p[0]));
18461905
svdm_version = typec_get_negotiated_svdm_version(typec);
18471906
if (svdm_version < 0)
18481907
return 0;
18491908
break;
18501909
default:
1910+
modep = &port->mode_data;
1911+
pdev = typec_match_altmode(port->partner_altmode,
1912+
ALTMODE_DISCOVERY_MAX,
1913+
PD_VDO_VID(p[0]),
1914+
PD_VDO_OPOS(p[0]));
18511915
svdm_version = typec_get_negotiated_svdm_version(typec);
18521916
if (svdm_version < 0)
18531917
return 0;
@@ -1939,6 +2003,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
19392003
* SOP' Discover Identity
19402004
* SOP Discover SVIDs
19412005
* Discover Modes
2006+
* (Active Cables)
2007+
* SOP' Discover SVIDs
2008+
* Discover Modes
19422009
*
19432010
* Perform Discover SOP' if the port can communicate with cable
19442011
* plug.
@@ -2018,26 +2085,62 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
20182085
}
20192086
break;
20202087
case CMD_DISCOVER_SVID:
2088+
*response_tx_sop_type = rx_sop_type;
20212089
/* 6.4.4.3.2 */
2022-
if (svdm_consume_svids(port, p, cnt)) {
2090+
if (svdm_consume_svids(port, p, cnt, rx_sop_type)) {
20232091
response[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
20242092
rlen = 1;
2025-
} else if (modep->nsvids && supports_modal(port)) {
2026-
response[0] = VDO(modep->svids[0], 1, svdm_version,
2027-
CMD_DISCOVER_MODES);
2028-
rlen = 1;
2093+
} else {
2094+
if (rx_sop_type == TCPC_TX_SOP) {
2095+
if (modep->nsvids && supports_modal(port)) {
2096+
response[0] = VDO(modep->svids[0], 1, svdm_version,
2097+
CMD_DISCOVER_MODES);
2098+
rlen = 1;
2099+
}
2100+
} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
2101+
if (modep_prime->nsvids) {
2102+
response[0] = VDO(modep_prime->svids[0], 1,
2103+
svdm_version, CMD_DISCOVER_MODES);
2104+
rlen = 1;
2105+
}
2106+
}
20292107
}
20302108
break;
20312109
case CMD_DISCOVER_MODES:
2032-
/* 6.4.4.3.3 */
2033-
svdm_consume_modes(port, p, cnt);
2034-
modep->svid_index++;
2035-
if (modep->svid_index < modep->nsvids) {
2036-
u16 svid = modep->svids[modep->svid_index];
2037-
response[0] = VDO(svid, 1, svdm_version, CMD_DISCOVER_MODES);
2038-
rlen = 1;
2039-
} else {
2040-
tcpm_register_partner_altmodes(port);
2110+
if (rx_sop_type == TCPC_TX_SOP) {
2111+
/* 6.4.4.3.3 */
2112+
svdm_consume_modes(port, p, cnt, rx_sop_type);
2113+
modep->svid_index++;
2114+
if (modep->svid_index < modep->nsvids) {
2115+
u16 svid = modep->svids[modep->svid_index];
2116+
*response_tx_sop_type = TCPC_TX_SOP;
2117+
response[0] = VDO(svid, 1, svdm_version,
2118+
CMD_DISCOVER_MODES);
2119+
rlen = 1;
2120+
} else if (tcpm_cable_vdm_supported(port)) {
2121+
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
2122+
response[0] = VDO(USB_SID_PD, 1,
2123+
typec_get_cable_svdm_version(typec),
2124+
CMD_DISCOVER_SVID);
2125+
rlen = 1;
2126+
} else {
2127+
tcpm_register_partner_altmodes(port);
2128+
}
2129+
} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
2130+
/* 6.4.4.3.3 */
2131+
svdm_consume_modes(port, p, cnt, rx_sop_type);
2132+
modep_prime->svid_index++;
2133+
if (modep_prime->svid_index < modep_prime->nsvids) {
2134+
u16 svid = modep_prime->svids[modep_prime->svid_index];
2135+
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
2136+
response[0] = VDO(svid, 1,
2137+
typec_get_cable_svdm_version(typec),
2138+
CMD_DISCOVER_MODES);
2139+
rlen = 1;
2140+
} else {
2141+
tcpm_register_plug_altmodes(port);
2142+
tcpm_register_partner_altmodes(port);
2143+
}
20412144
}
20422145
break;
20432146
case CMD_ENTER_MODE:
@@ -2418,6 +2521,16 @@ static void vdm_run_state_machine(struct tcpm_port *port)
24182521
tcpm_queue_vdm(port, response[0], &response[1],
24192522
0, TCPC_TX_SOP);
24202523
break;
2524+
/*
2525+
* If Discover SVIDs or Discover Modes fail, then
2526+
* proceed with Alt Mode discovery process on SOP.
2527+
*/
2528+
case CMD_DISCOVER_SVID:
2529+
tcpm_register_partner_altmodes(port);
2530+
break;
2531+
case CMD_DISCOVER_MODES:
2532+
tcpm_register_partner_altmodes(port);
2533+
break;
24212534
default:
24222535
break;
24232536
}
@@ -4124,14 +4237,20 @@ static void tcpm_typec_disconnect(struct tcpm_port *port)
41244237
static void tcpm_unregister_altmodes(struct tcpm_port *port)
41254238
{
41264239
struct pd_mode_data *modep = &port->mode_data;
4240+
struct pd_mode_data *modep_prime = &port->mode_data_prime;
41274241
int i;
41284242

41294243
for (i = 0; i < modep->altmodes; i++) {
41304244
typec_unregister_altmode(port->partner_altmode[i]);
41314245
port->partner_altmode[i] = NULL;
41324246
}
4247+
for (i = 0; i < modep_prime->altmodes; i++) {
4248+
typec_unregister_altmode(port->plug_prime_altmode[i]);
4249+
port->plug_prime_altmode[i] = NULL;
4250+
}
41334251

41344252
memset(modep, 0, sizeof(*modep));
4253+
memset(modep_prime, 0, sizeof(*modep_prime));
41354254
}
41364255

41374256
static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capable)

0 commit comments

Comments
 (0)