|
17 | 17 | #include <linux/usb/typec_altmode.h>
|
18 | 18 | #include <linux/usb/typec_dp.h>
|
19 | 19 | #include <linux/usb/typec_mux.h>
|
| 20 | +#include <linux/usb/typec_tbt.h> |
20 | 21 | #include <linux/usb/role.h>
|
21 | 22 |
|
22 | 23 | #define DRV_NAME "cros-ec-typec"
|
23 | 24 |
|
24 | 25 | /* Supported alt modes. */
|
25 | 26 | enum {
|
26 | 27 | CROS_EC_ALTMODE_DP = 0,
|
| 28 | + CROS_EC_ALTMODE_TBT, |
27 | 29 | CROS_EC_ALTMODE_MAX,
|
28 | 30 | };
|
29 | 31 |
|
@@ -165,6 +167,14 @@ static void cros_typec_register_port_altmodes(struct cros_typec_data *typec,
|
165 | 167 | port->p_altmode[CROS_EC_ALTMODE_DP].svid = USB_TYPEC_DP_SID;
|
166 | 168 | port->p_altmode[CROS_EC_ALTMODE_DP].mode = USB_TYPEC_DP_MODE;
|
167 | 169 |
|
| 170 | + /* |
| 171 | + * Register TBT compatibility alt mode. The EC will not enter the mode |
| 172 | + * if it doesn't support it, so it's safe to register it unconditionally |
| 173 | + * here for now. |
| 174 | + */ |
| 175 | + port->p_altmode[CROS_EC_ALTMODE_TBT].svid = USB_TYPEC_TBT_SID; |
| 176 | + port->p_altmode[CROS_EC_ALTMODE_TBT].mode = TYPEC_ANY_MODE; |
| 177 | + |
168 | 178 | port->state.alt = NULL;
|
169 | 179 | port->state.mode = TYPEC_STATE_USB;
|
170 | 180 | port->state.data = NULL;
|
@@ -391,6 +401,62 @@ static int cros_typec_usb_safe_state(struct cros_typec_port *port)
|
391 | 401 | return typec_mux_set(port->mux, &port->state);
|
392 | 402 | }
|
393 | 403 |
|
| 404 | +/* |
| 405 | + * Spoof the VDOs that were likely communicated by the partner for TBT alt |
| 406 | + * mode. |
| 407 | + */ |
| 408 | +static int cros_typec_enable_tbt(struct cros_typec_data *typec, |
| 409 | + int port_num, |
| 410 | + struct ec_response_usb_pd_control_v2 *pd_ctrl) |
| 411 | +{ |
| 412 | + struct cros_typec_port *port = typec->ports[port_num]; |
| 413 | + struct typec_thunderbolt_data data; |
| 414 | + int ret; |
| 415 | + |
| 416 | + if (typec->pd_ctrl_ver < 2) { |
| 417 | + dev_err(typec->dev, |
| 418 | + "PD_CTRL version too old: %d\n", typec->pd_ctrl_ver); |
| 419 | + return -ENOTSUPP; |
| 420 | + } |
| 421 | + |
| 422 | + /* Device Discover Mode VDO */ |
| 423 | + data.device_mode = TBT_MODE; |
| 424 | + |
| 425 | + if (pd_ctrl->control_flags & USB_PD_CTRL_TBT_LEGACY_ADAPTER) |
| 426 | + data.device_mode = TBT_SET_ADAPTER(TBT_ADAPTER_TBT3); |
| 427 | + |
| 428 | + /* Cable Discover Mode VDO */ |
| 429 | + data.cable_mode = TBT_MODE; |
| 430 | + data.cable_mode |= TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed); |
| 431 | + |
| 432 | + if (pd_ctrl->control_flags & USB_PD_CTRL_OPTICAL_CABLE) |
| 433 | + data.cable_mode |= TBT_CABLE_OPTICAL; |
| 434 | + |
| 435 | + if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_LINK_UNIDIR) |
| 436 | + data.cable_mode |= TBT_CABLE_LINK_TRAINING; |
| 437 | + |
| 438 | + if (pd_ctrl->cable_gen) |
| 439 | + data.cable_mode |= TBT_CABLE_ROUNDED; |
| 440 | + |
| 441 | + /* Enter Mode VDO */ |
| 442 | + data.enter_vdo = TBT_SET_CABLE_SPEED(pd_ctrl->cable_speed); |
| 443 | + |
| 444 | + if (pd_ctrl->control_flags & USB_PD_CTRL_ACTIVE_CABLE) |
| 445 | + data.enter_vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; |
| 446 | + |
| 447 | + if (!port->state.alt) { |
| 448 | + port->state.alt = &port->p_altmode[CROS_EC_ALTMODE_TBT]; |
| 449 | + ret = cros_typec_usb_safe_state(port); |
| 450 | + if (ret) |
| 451 | + return ret; |
| 452 | + } |
| 453 | + |
| 454 | + port->state.data = &data; |
| 455 | + port->state.mode = TYPEC_TBT_MODE; |
| 456 | + |
| 457 | + return typec_mux_set(port->mux, &port->state); |
| 458 | +} |
| 459 | + |
394 | 460 | /* Spoof the VDOs that were likely communicated by the partner. */
|
395 | 461 | static int cros_typec_enable_dp(struct cros_typec_data *typec,
|
396 | 462 | int port_num,
|
@@ -448,7 +514,9 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
|
448 | 514 | if (ret)
|
449 | 515 | return ret;
|
450 | 516 |
|
451 |
| - if (mux_flags & USB_PD_MUX_DP_ENABLED) { |
| 517 | + if (mux_flags & USB_PD_MUX_TBT_COMPAT_ENABLED) { |
| 518 | + ret = cros_typec_enable_tbt(typec, port_num, pd_ctrl); |
| 519 | + } else if (mux_flags & USB_PD_MUX_DP_ENABLED) { |
452 | 520 | ret = cros_typec_enable_dp(typec, port_num, pd_ctrl);
|
453 | 521 | } else if (mux_flags & USB_PD_MUX_SAFE_MODE) {
|
454 | 522 | ret = cros_typec_usb_safe_state(port);
|
|
0 commit comments