aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/typec/ucsi/ucsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/typec/ucsi/ucsi.c')
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c95
1 files changed, 90 insertions, 5 deletions
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 4459bc68aa33..d5a6aac86327 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -189,7 +189,7 @@ int ucsi_resume(struct ucsi *ucsi)
u64 command;
/* Restore UCSI notification enable mask after system resume */
- command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL;
+ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
return ucsi_send_command(ucsi, command, NULL, 0);
}
@@ -318,6 +318,82 @@ err:
return ret;
}
+static int
+ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
+{
+ int max_altmodes = UCSI_MAX_ALTMODES;
+ struct typec_altmode_desc desc;
+ struct ucsi_altmode alt;
+ struct ucsi_altmode orig[UCSI_MAX_ALTMODES];
+ struct ucsi_altmode updated[UCSI_MAX_ALTMODES];
+ struct ucsi *ucsi = con->ucsi;
+ bool multi_dp = false;
+ u64 command;
+ int ret;
+ int len;
+ int i;
+ int k = 0;
+
+ if (recipient == UCSI_RECIPIENT_CON)
+ max_altmodes = con->ucsi->cap.num_alt_modes;
+
+ memset(orig, 0, sizeof(orig));
+ memset(updated, 0, sizeof(updated));
+
+ /* First get all the alternate modes */
+ for (i = 0; i < max_altmodes; i++) {
+ memset(&alt, 0, sizeof(alt));
+ command = UCSI_GET_ALTERNATE_MODES;
+ command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
+ command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_GET_ALTMODE_OFFSET(i);
+ len = ucsi_run_command(con->ucsi, command, &alt, sizeof(alt));
+ /*
+ * We are collecting all altmodes first and then registering.
+ * Some type-C device will return zero length data beyond last
+ * alternate modes. We should not return if length is zero.
+ */
+ if (len < 0)
+ return len;
+
+ /* We got all altmodes, now break out and register them */
+ if (!len || !alt.svid)
+ break;
+
+ orig[k].mid = alt.mid;
+ orig[k].svid = alt.svid;
+ k++;
+ }
+ /*
+ * Update the original altmode table as some ppms may report
+ * multiple DP altmodes.
+ */
+ if (recipient == UCSI_RECIPIENT_CON)
+ multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated);
+
+ /* now register altmodes */
+ for (i = 0; i < max_altmodes; i++) {
+ memset(&desc, 0, sizeof(desc));
+ if (multi_dp && recipient == UCSI_RECIPIENT_CON) {
+ desc.svid = updated[i].svid;
+ desc.vdo = updated[i].mid;
+ } else {
+ desc.svid = orig[i].svid;
+ desc.vdo = orig[i].mid;
+ }
+ desc.roles = TYPEC_PORT_DRD;
+
+ if (!desc.svid)
+ return 0;
+
+ ret = ucsi_register_altmode(con, &desc, recipient);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
{
int max_altmodes = UCSI_MAX_ALTMODES;
@@ -336,6 +412,9 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0])
return 0;
+ if (con->ucsi->ops->update_altmodes)
+ return ucsi_register_altmodes_nvidia(con, recipient);
+
if (recipient == UCSI_RECIPIENT_CON)
max_altmodes = con->ucsi->cap.num_alt_modes;
@@ -589,6 +668,11 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num)
{
struct ucsi_connector *con = &ucsi->connector[num - 1];
+ if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) {
+ dev_dbg(ucsi->dev, "Bogus connector change event\n");
+ return;
+ }
+
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
schedule_work(&con->work);
}
@@ -656,7 +740,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
ucsi_reset_ppm(con->ucsi);
mutex_unlock(&con->ucsi->ppm_lock);
- c = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL;
+ c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy;
ucsi_send_command(con->ucsi, c, NULL, 0);
ucsi_reset_connector(con, true);
@@ -890,8 +974,8 @@ int ucsi_init(struct ucsi *ucsi)
}
/* Enable basic notifications */
- command = UCSI_SET_NOTIFICATION_ENABLE;
- command |= UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
+ ucsi->ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
+ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
ret = ucsi_run_command(ucsi, command, NULL, 0);
if (ret < 0)
goto err_reset;
@@ -923,7 +1007,8 @@ int ucsi_init(struct ucsi *ucsi)
}
/* Enable all notifications */
- command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL;
+ ucsi->ntfy = UCSI_ENABLE_NTFY_ALL;
+ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
ret = ucsi_run_command(ucsi, command, NULL, 0);
if (ret < 0)
goto err_unregister;