@@ -58,31 +58,50 @@ static void cros_typec_altmode_work(struct work_struct *work)
static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
- struct ec_params_typec_control req = {
- .port = adata->port->port_num,
- .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
- };
+ struct cros_ec_device *ec = adata->port->typec_data->ec;
+ unsigned int port = adata->port->port_num;
int svdm_version;
int ret;
if (!adata->ap_mode_entry) {
- dev_warn(&alt->dev,
- "EC does not support AP driven mode entry\n");
- return -EOPNOTSUPP;
+ struct ec_response_usb_pd_mux_info resp;
+ struct ec_params_usb_pd_mux_info req = {
+ .port = port,
+ };
+ uint8_t flags;
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ flags = USB_PD_MUX_DP_ENABLED;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ flags = USB_PD_MUX_TBT_COMPAT_ENABLED;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(ec, 0, EC_CMD_USB_PD_MUX_INFO,
+ &req, sizeof(req), &resp, sizeof(resp));
+ if (ret < 0)
+ return ret;
+
+ if (!(resp.flags & flags))
+ return -EINVAL;
+ } else {
+ struct ec_params_typec_control req = {
+ .port = port,
+ .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
+ };
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_DP;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_TBT;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(ec, 0, EC_CMD_TYPEC_CONTROL, &req, sizeof(req), NULL, 0);
+ if (ret < 0)
+ return ret;
}
- if (adata->sid == USB_TYPEC_DP_SID)
- req.mode_to_enter = CROS_EC_ALTMODE_DP;
- else if (adata->sid == USB_TYPEC_TBT_SID)
- req.mode_to_enter = CROS_EC_ALTMODE_TBT;
- else
- return -EOPNOTSUPP;
-
- ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
- &req, sizeof(req), NULL, 0);
- if (ret < 0)
- return ret;
-
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
@@ -97,31 +116,52 @@ static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
- return ret;
+
+ return 0;
}
static int cros_typec_altmode_exit(struct typec_altmode *alt)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
- struct ec_params_typec_control req = {
- .port = adata->port->port_num,
- .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
- };
+ struct cros_ec_device *ec = adata->port->typec_data->ec;
+ unsigned int port = adata->port->port_num;
int svdm_version;
int ret;
if (!adata->ap_mode_entry) {
- dev_warn(&alt->dev,
- "EC does not support AP driven mode exit\n");
- return -EOPNOTSUPP;
+ struct ec_response_usb_pd_mux_info resp;
+ struct ec_params_usb_pd_mux_info req = {
+ .port = port,
+ };
+ uint8_t flags;
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ flags = USB_PD_MUX_DP_ENABLED;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ flags = USB_PD_MUX_TBT_COMPAT_ENABLED;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(ec, 0, EC_CMD_USB_PD_MUX_INFO,
+ &req, sizeof(req), &resp, sizeof(resp));
+ if (ret < 0)
+ return ret;
+
+ if (resp.flags & flags)
+ return -EINVAL;
+ } else {
+ struct ec_params_typec_control req = {
+ .port = port,
+ .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
+ };
+
+ ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+
+ if (ret < 0)
+ return ret;
}
- ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
- &req, sizeof(req), NULL, 0);
-
- if (ret < 0)
- return ret;
-
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
@@ -136,7 +176,8 @@ static int cros_typec_altmode_exit(struct typec_altmode *alt)
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
- return ret;
+
+ return 0;
}
static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
@@ -254,9 +295,6 @@ static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
- if (!adata->ap_mode_entry)
- return -EOPNOTSUPP;
-
if (adata->sid == USB_TYPEC_DP_SID)
return cros_typec_displayport_vdm(alt, header, data, count);
Support ChromeOS EC firmwares that don't support AP mode entry. Check that the mode has been entered by querying the EC and reject mode entry attempts if the EC hasn't already entered the mode requested. This allows us to bind the DP altmode driver on devices that don't support AP mode entry, i.e. most ChromeOS devices where the EC controls mode entry. Cc: Benson Leung <bleung@chromium.org> Cc: Tzung-Bi Shih <tzungbi@kernel.org> Cc: <chrome-platform@lists.linux.dev> Cc: Pin-yen Lin <treapking@chromium.org> Cc: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> Cc: Ćukasz Bartosik <ukaszb@chromium.org> Cc: Jameson Thies <jthies@google.com> Cc: Andrei Kuchynski <akuchynski@chromium.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> --- drivers/platform/chrome/cros_typec_altmode.c | 112 +++++++++++++------ 1 file changed, 75 insertions(+), 37 deletions(-)