platform/chrome: cros_ec_typec: Add TBT compat support

Add mux control support for Thunderbolt compatibility mode.

Suggested-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Co-developed-by: Azhar Shaikh <azhar.shaikh@intel.com>
Co-developed-by: Casey Bowman <casey.g.bowman@intel.com>
Signed-off-by: Prashant Malani <pmalani@chromium.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
This commit is contained in:
Prashant Malani 2020-06-24 01:09:24 -07:00 committed by Enric Balletbo i Serra
parent 5e48a03bb9
commit 5b30bd35aa

View file

@ -17,6 +17,7 @@
#include <linux/usb/typec_altmode.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_tbt.h>
#include <linux/usb/role.h>
#define DRV_NAME "cros-ec-typec"
@ -24,6 +25,7 @@
/* Supported alt modes. */
enum {
CROS_EC_ALTMODE_DP = 0,
CROS_EC_ALTMODE_TBT,
CROS_EC_ALTMODE_MAX,
};
@ -165,6 +167,14 @@ static void cros_typec_register_port_altmodes(struct cros_typec_data *typec,
port->p_altmode[CROS_EC_ALTMODE_DP].svid = USB_TYPEC_DP_SID;
port->p_altmode[CROS_EC_ALTMODE_DP].mode = USB_TYPEC_DP_MODE;
/*
* Register TBT compatibility alt mode. The EC will not enter the mode
* if it doesn't support it, so it's safe to register it unconditionally
* here for now.
*/
port->p_altmode[CROS_EC_ALTMODE_TBT].svid = USB_TYPEC_TBT_SID;
port->p_altmode[CROS_EC_ALTMODE_TBT].mode = TYPEC_ANY_MODE;
port->state.alt = NULL;
port->state.mode = TYPEC_STATE_USB;
port->state.data = NULL;
@ -391,6 +401,62 @@ static int cros_typec_usb_safe_state(struct cros_typec_port *port)
return typec_mux_set(port->mux, &port->state);
}
/*
* Spoof the VDOs that were likely communicated by the partner for TBT alt
* mode.
*/
static int cros_typec_enable_tbt(struct cros_typec_data *typec,
int port_num,
struct ec_response_usb_pd_control_v2 *pd_ctrl)
{
struct cros_typec_port *port = typec->ports[port_num];
struct typec_thunderbolt_data data;
int ret;
if (typec->pd_ctrl_ver < 2) {
dev_err(typec->dev,
"PD_CTRL version too old: %d\n", typec->pd_ctrl_ver);
return -ENOTSUPP;
}
/* Device Discover Mode VDO */
data.device_mode = TBT_MODE;
if (pd_ctrl->control_flags & USB_PD_CTRL_TBT_LEGACY_ADAPTER)
data.device_mode = TBT_SET_ADAPTER(TBT_ADAPTER_TBT3);
/* Cable Discover Mode VDO */
data.cable_mode = TBT_MODE;
data.cable_mode |= TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed);
if (pd_ctrl->control_flags & USB_PD_CTRL_OPTICAL_CABLE)
data.cable_mode |= TBT_CABLE_OPTICAL;
if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_LINK_UNIDIR)
data.cable_mode |= TBT_CABLE_LINK_TRAINING;
if (pd_ctrl->cable_gen)
data.cable_mode |= TBT_CABLE_ROUNDED;
/* Enter Mode VDO */
data.enter_vdo = TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed);
if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_CABLE)
data.enter_vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
if (!port->state.alt) {
port->state.alt = &port->p_altmode[CROS_EC_ALTMODE_TBT];
ret = cros_typec_usb_safe_state(port);
if (ret)
return ret;
}
port->state.data = &data;
port->state.mode = TYPEC_TBT_MODE;
return typec_mux_set(port->mux, &port->state);
}
/* Spoof the VDOs that were likely communicated by the partner. */
static int cros_typec_enable_dp(struct cros_typec_data *typec,
int port_num,
@ -448,7 +514,9 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
if (ret)
return ret;
if (mux_flags & USB_PD_MUX_DP_ENABLED) {
if (mux_flags & USB_PD_MUX_TBT_COMPAT_ENABLED) {
ret = cros_typec_enable_tbt(typec, port_num, pd_ctrl);
} else if (mux_flags & USB_PD_MUX_DP_ENABLED) {
ret = cros_typec_enable_dp(typec, port_num, pd_ctrl);
} else if (mux_flags & USB_PD_MUX_SAFE_MODE) {
ret = cros_typec_usb_safe_state(port);