Re: [PATCH v2 10/12] usb: typec: tcpm: add discover svids and discover modes support for sop'

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Dec 14, 2023 at 11:08:54PM +0000, RD Babiera wrote:
> 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 <rdbabiera@xxxxxxxxxx>
> ---
> Changes since v1:
> * Changes to tcpm_altmode_enter/exit/vdm are moved to next patch
> * adev_action changes are moved to next patch
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 163 +++++++++++++++++++++++++++++-----
>  1 file changed, 139 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index e21bc2eea3fc..61433dc4c917 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -465,7 +465,9 @@ struct tcpm_port {
>  
>  	/* Alternate mode data */
>  	struct pd_mode_data mode_data;
> +	struct pd_mode_data mode_data_prime;
>  	struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX];
> +	struct typec_altmode *plug_prime_altmode[ALTMODE_DISCOVERY_MAX];
>  	struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX];
>  
>  	/* Deadline in jiffies to exit src_try_wait state */
> @@ -1629,9 +1631,11 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p
>  	}
>  }
>  
> -static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt)
> +static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt,
> +			       enum tcpm_transmit_type rx_sop_type)
>  {
> -	struct pd_mode_data *pmdata = &port->mode_data;
> +	struct pd_mode_data *pmdata = rx_sop_type == TCPC_TX_SOP_PRIME ?
> +				      &port->mode_data_prime : &port->mode_data;
>  	int i;
>  
>  	for (i = 1; i < cnt; i++) {
> @@ -1677,14 +1681,29 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt)
>  	return false;
>  }
>  
> -static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt)
> +static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
> +			       enum tcpm_transmit_type rx_sop_type)
>  {
>  	struct pd_mode_data *pmdata = &port->mode_data;
>  	struct typec_altmode_desc *paltmode;
>  	int i;
>  
> -	if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
> -		/* Already logged in svdm_consume_svids() */
> +	switch (rx_sop_type) {
> +	case TCPC_TX_SOP_PRIME:
> +		pmdata = &port->mode_data_prime;
> +		if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) {
> +			/* Already logged in svdm_consume_svids() */
> +			return;
> +		}
> +		break;
> +	case TCPC_TX_SOP:
> +		pmdata = &port->mode_data;
> +		if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
> +			/* Already logged in svdm_consume_svids() */
> +			return;
> +		}
> +		break;
> +	default:
>  		return;
>  	}
>  
> @@ -1722,7 +1741,28 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
>  	}
>  }
>  
> +static void tcpm_register_plug_altmodes(struct tcpm_port *port)
> +{
> +	struct pd_mode_data *modep = &port->mode_data_prime;
> +	struct typec_altmode *altmode;
> +	int i;
> +
> +	typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes);
> +
> +	for (i = 0; i < modep->altmodes; i++) {
> +		altmode = typec_plug_register_altmode(port->plug_prime,
> +						&modep->altmode_desc[i]);
> +		if (IS_ERR(altmode)) {
> +			tcpm_log(port, "Failed to register plug SVID 0x%04x",
> +				 modep->altmode_desc[i].svid);
> +			altmode = NULL;
> +		}
> +		port->plug_prime_altmode[i] = altmode;
> +	}
> +}
> +
>  #define supports_modal(port)	PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
> +#define supports_modal_cable(port)     PD_IDH_MODAL_SUPP((port)->cable_ident.id_header)
>  #define supports_host(port)    PD_IDH_HOST_SUPP((port->partner_ident.id_header))
>  
>  /*
> @@ -1800,6 +1840,15 @@ static bool tcpm_attempt_vconn_swap_discovery(struct tcpm_port *port)
>  	return false;
>  }
>  
> +
> +static bool tcpm_cable_vdm_supported(struct tcpm_port *port)
> +{
> +	return !IS_ERR_OR_NULL(port->cable) &&
> +	       typec_cable_is_active(port->cable) &&
> +	       supports_modal_cable(port) &&
> +	       tcpm_can_communicate_sop_prime(port);
> +}
> +
>  static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
>  			const u32 *p, int cnt, u32 *response,
>  			enum adev_actions *adev_action,
> @@ -1807,8 +1856,8 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
>  			enum tcpm_transmit_type *response_tx_sop_type)
>  {
>  	struct typec_port *typec = port->typec_port;
> -	struct typec_altmode *pdev;
> -	struct pd_mode_data *modep;
> +	struct typec_altmode *pdev, *pdev_prime;
> +	struct pd_mode_data *modep, *modep_prime;
>  	int svdm_version;
>  	int rlen = 0;
>  	int cmd_type;
> @@ -1829,6 +1878,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
>  
>  	switch (rx_sop_type) {
>  	case TCPC_TX_SOP_PRIME:
> +		modep_prime = &port->mode_data_prime;
> +		pdev_prime = typec_match_altmode(port->plug_prime_altmode,
> +						 ALTMODE_DISCOVERY_MAX,
> +						 PD_VDO_VID(p[0]),
> +						 PD_VDO_OPOS(p[0]));
>  		if (!IS_ERR_OR_NULL(port->cable)) {
>  			svdm_version = typec_get_cable_svdm_version(typec);
>  			if (PD_VDO_SVDM_VER(p[0]) < svdm_version)
> @@ -1836,11 +1890,21 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
>  		}
>  		break;
>  	case TCPC_TX_SOP:
> +		modep = &port->mode_data;
> +		pdev = typec_match_altmode(port->partner_altmode,
> +					   ALTMODE_DISCOVERY_MAX,
> +					   PD_VDO_VID(p[0]),
> +					   PD_VDO_OPOS(p[0]));
>  		svdm_version = typec_get_negotiated_svdm_version(typec);
>  		if (svdm_version < 0)
>  			return 0;
>  		break;
>  	default:
> +		modep = &port->mode_data;
> +		pdev = typec_match_altmode(port->partner_altmode,
> +					   ALTMODE_DISCOVERY_MAX,
> +					   PD_VDO_VID(p[0]),
> +					   PD_VDO_OPOS(p[0]));
>  		svdm_version = typec_get_negotiated_svdm_version(typec);
>  		if (svdm_version < 0)
>  			return 0;
> @@ -1932,6 +1996,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
>  		 * SOP'		Discover Identity
>  		 * SOP		Discover SVIDs
>  		 *		Discover Modes
> +		 * (Active Cables)
> +		 * SOP'		Discover SVIDs
> +		 *		Discover Modes
>  		 *
>  		 * Perform Discover SOP' if the port can communicate with cable
>  		 * plug.
> @@ -2011,26 +2078,62 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
>  			}
>  			break;
>  		case CMD_DISCOVER_SVID:
> +			*response_tx_sop_type = rx_sop_type;
>  			/* 6.4.4.3.2 */
> -			if (svdm_consume_svids(port, p, cnt)) {
> +			if (svdm_consume_svids(port, p, cnt, rx_sop_type)) {
>  				response[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID);
>  				rlen = 1;
> -			} else if (modep->nsvids && supports_modal(port)) {
> -				response[0] = VDO(modep->svids[0], 1, svdm_version,
> -						  CMD_DISCOVER_MODES);
> -				rlen = 1;
> +			} else {
> +				if (rx_sop_type == TCPC_TX_SOP) {
> +					if (modep->nsvids && supports_modal(port)) {
> +						response[0] = VDO(modep->svids[0], 1, svdm_version,
> +								CMD_DISCOVER_MODES);
> +						rlen = 1;
> +					}
> +				} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
> +					if (modep_prime->nsvids) {
> +						response[0] = VDO(modep_prime->svids[0], 1,
> +								  svdm_version, CMD_DISCOVER_MODES);
> +						rlen = 1;
> +					}
> +				}
>  			}
>  			break;
>  		case CMD_DISCOVER_MODES:
> -			/* 6.4.4.3.3 */
> -			svdm_consume_modes(port, p, cnt);
> -			modep->svid_index++;
> -			if (modep->svid_index < modep->nsvids) {
> -				u16 svid = modep->svids[modep->svid_index];
> -				response[0] = VDO(svid, 1, svdm_version, CMD_DISCOVER_MODES);
> -				rlen = 1;
> -			} else {
> -				tcpm_register_partner_altmodes(port);
> +			if (rx_sop_type == TCPC_TX_SOP) {
> +				/* 6.4.4.3.3 */
> +				svdm_consume_modes(port, p, cnt, rx_sop_type);
> +				modep->svid_index++;
> +				if (modep->svid_index < modep->nsvids) {
> +					u16 svid = modep->svids[modep->svid_index];
> +					*response_tx_sop_type = TCPC_TX_SOP;
> +					response[0] = VDO(svid, 1, svdm_version,
> +							  CMD_DISCOVER_MODES);
> +					rlen = 1;
> +				} else if (tcpm_cable_vdm_supported(port)) {
> +					*response_tx_sop_type = TCPC_TX_SOP_PRIME;
> +					response[0] = VDO(USB_SID_PD, 1,
> +							  typec_get_cable_svdm_version(typec),
> +							  CMD_DISCOVER_SVID);
> +					rlen = 1;
> +				} else {
> +					tcpm_register_partner_altmodes(port);
> +				}
> +			} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
> +				/* 6.4.4.3.3 */
> +				svdm_consume_modes(port, p, cnt, rx_sop_type);
> +				modep_prime->svid_index++;
> +				if (modep_prime->svid_index < modep_prime->nsvids) {
> +					u16 svid = modep_prime->svids[modep_prime->svid_index];
> +					*response_tx_sop_type = TCPC_TX_SOP_PRIME;
> +					response[0] = VDO(svid, 1,
> +							  typec_get_cable_svdm_version(typec),
> +							  CMD_DISCOVER_MODES);
> +					rlen = 1;
> +				} else {
> +					tcpm_register_plug_altmodes(port);
> +					tcpm_register_partner_altmodes(port);
> +				}
>  			}
>  			break;
>  		case CMD_ENTER_MODE:
> @@ -2411,6 +2514,16 @@ static void vdm_run_state_machine(struct tcpm_port *port)
>  				tcpm_queue_vdm(port, response[0], &response[1],
>  					       0, TCPC_TX_SOP);
>  				break;
> +			/*
> +			 * If Discover SVIDs or Discover Modes fail, then
> +			 * proceed with Alt Mode discovery process on SOP.
> +			 */
> +			case CMD_DISCOVER_SVID:
> +				tcpm_register_partner_altmodes(port);
> +				break;
> +			case CMD_DISCOVER_MODES:
> +				tcpm_register_partner_altmodes(port);
> +				break;
>  			default:
>  				break;
>  			}
> @@ -4123,14 +4236,16 @@ static void tcpm_typec_disconnect(struct tcpm_port *port)
>  static void tcpm_unregister_altmodes(struct tcpm_port *port)
>  {
>  	struct pd_mode_data *modep = &port->mode_data;
> +	struct pd_mode_data *modep_prime = &port->mode_data_prime;
>  	int i;
>  
> -	for (i = 0; i < modep->altmodes; i++) {
> -		typec_unregister_altmode(port->partner_altmode[i]);
> -		port->partner_altmode[i] = NULL;
> +	for (i = 0; i < modep_prime->altmodes; i++) {
> +		typec_unregister_altmode(port->plug_prime_altmode[i]);
> +		port->plug_prime_altmode[i] = NULL;
>  	}

I'm probable missing something, but where are the partner altmodes now
unregistered?

>  	memset(modep, 0, sizeof(*modep));
> +	memset(modep_prime, 0, sizeof(*modep_prime));
>  }
>  
>  static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capable)

thanks,

-- 
heikki




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux