@@ -162,6 +162,23 @@ Description: Lists the supported USB Modes. The default USB mode that is used
- usb3 (USB 3.2)
- usb4 (USB4)
+What: /sys/class/typec/<port>/altmode_priorities
+Date: June 2025
+Contact: Andrei Kuchynski <akuchynski@chromium.org>
+Description: Lists the alternate modes supported by the port and their
+ priorities. The priority setting determines the order in which
+ Displayport alt-mode, Thunderbolt alt-mode and USB4 mode will be
+ activated, indicating the preferred selection sequence. A value of -1
+ disables automatic entry into a specific mode, while lower numbers
+ indicate higher priority. The default priorities can be modified by
+ assigning new values. Modes without explicitly set values default to -1,
+ effectively disabling them.
+
+ Example values:
+ - "USB4=0 TBT=1 DP=2"
+ - "USB4=-1 TBT=0"
+ - "DP=-1 USB4=-1 TBT=-1"
+
USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
What: /sys/class/typec/<port>-partner/accessory_mode
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o
-typec-y := class.o mux.o bus.o pd.o retimer.o
+typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o
typec-$(CONFIG_ACPI) += port-mapper.o
obj-$(CONFIG_TYPEC) += altmodes/
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
@@ -19,6 +19,7 @@
#include "bus.h"
#include "class.h"
#include "pd.h"
+#include "mode_selection.h"
static DEFINE_IDA(typec_index_ida);
@@ -1942,6 +1943,25 @@ static ssize_t orientation_show(struct device *dev,
}
static DEVICE_ATTR_RO(orientation);
+static ssize_t altmode_priorities_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret = typec_mode_priorities_set(port, buf);
+
+ return ret ? : size;
+}
+
+static ssize_t altmode_priorities_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ return typec_mode_priorities_get(port, buf);
+}
+static DEVICE_ATTR_RW(altmode_priorities);
+
static struct attribute *typec_attrs[] = {
&dev_attr_data_role.attr,
&dev_attr_power_operation_mode.attr,
@@ -1954,6 +1974,7 @@ static struct attribute *typec_attrs[] = {
&dev_attr_port_type.attr,
&dev_attr_orientation.attr,
&dev_attr_usb_capability.attr,
+ &dev_attr_altmode_priorities.attr,
NULL,
};
@@ -1992,6 +2013,9 @@ static umode_t typec_attr_is_visible(struct kobject *kobj,
return 0;
if (!port->ops || !port->ops->default_usb_mode_set)
return 0444;
+ } else if (attr == &dev_attr_altmode_priorities.attr) {
+ if (!port->alt_mode_override)
+ return 0;
}
return attr->mode;
@@ -2652,6 +2676,8 @@ struct typec_port *typec_register_port(struct device *parent,
else if (cap->usb_capability & USB_CAPABILITY_USB2)
port->usb_mode = USB_MODE_USB2;
+ typec_mode_priorities_set(port, NULL);
+
device_initialize(&port->dev);
port->dev.class = &typec_class;
port->dev.parent = parent;
@@ -5,6 +5,7 @@
#include <linux/device.h>
#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
struct typec_mux;
struct typec_switch;
@@ -82,6 +83,7 @@ struct typec_port {
struct device *usb3_dev;
bool alt_mode_override;
+ int altmode_priorities[TYPEC_ALTMODE_MAX];
};
#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
new file mode 100644
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Google LLC.
+ */
+
+#include <linux/usb/typec_altmode.h>
+#include <linux/vmalloc.h>
+#include "class.h"
+
+#define MODE_PRIORITY_DISABLED -1
+
+static const char * const altmode_names[] = {
+ [TYPEC_ALTMODE_DP] = "DP",
+ [TYPEC_ALTMODE_TBT] = "TBT",
+ [TYPEC_ALTMODE_USB4] = "USB4",
+};
+static const char * const default_priorities = "USB4=0 TBT=1 DP=2";
+
+/* -------------------------------------------------------------------------- */
+/* port 'altmode_priorities' attribute */
+
+int typec_mode_priorities_set(struct typec_port *port,
+ const char *user_priorities)
+{
+ int priorities[TYPEC_ALTMODE_MAX];
+ const char *str_priority = user_priorities ? : default_priorities;
+ char *buf, *buf_free;
+ int ret = -EINVAL;
+ char *str_name;
+ int i;
+
+ buf = vmalloc(strlen(str_priority) + 1);
+ if (!buf)
+ return -ENOMEM;
+ strscpy(buf, str_priority, strlen(str_priority) + 1);
+ buf_free = buf;
+
+ for (i = 0; i < TYPEC_ALTMODE_MAX; i++)
+ priorities[i] = MODE_PRIORITY_DISABLED;
+
+ while ((str_name = strsep(&buf, " "))) {
+ char *str_value = strchr(str_name, '=');
+ int value;
+ int mode;
+
+ ret = -EINVAL;
+ if (!str_value)
+ goto parse_exit;
+ *str_value++ = '\0';
+
+ if (kstrtoint(str_value, 10, &value) ||
+ value < MODE_PRIORITY_DISABLED)
+ goto parse_exit;
+
+ if (value > MODE_PRIORITY_DISABLED) {
+ for (i = 0; i < TYPEC_ALTMODE_MAX; i++)
+ if (value == priorities[i])
+ goto parse_exit;
+ }
+
+ for (mode = 0; mode < TYPEC_ALTMODE_MAX &&
+ strcmp(str_name, altmode_names[mode]);)
+ mode++;
+ if (mode == TYPEC_ALTMODE_MAX ||
+ priorities[mode] != MODE_PRIORITY_DISABLED)
+ goto parse_exit;
+
+ priorities[mode] = value;
+ ret = 0;
+ }
+
+ for (i = 0; i < TYPEC_ALTMODE_MAX; i++)
+ port->altmode_priorities[i] = priorities[i];
+
+parse_exit:
+ vfree(buf_free);
+
+ return ret;
+}
+
+int typec_mode_priorities_get(struct typec_port *port, char *buf)
+{
+ ssize_t count = 0;
+ int i;
+
+ for (i = 0; i < TYPEC_ALTMODE_MAX; i++) {
+ if (i != TYPEC_ALTMODE_USB4 ||
+ port->cap->usb_capability & USB_CAPABILITY_USB4)
+ count += sysfs_emit_at(buf, count, "%s=%d ",
+ altmode_names[i], port->altmode_priorities[i]);
+ }
+ return count + sysfs_emit_at(buf, count, "\n");
+}
new file mode 100644
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+int typec_mode_priorities_set(struct typec_port *port,
+ const char *user_priorities);
+int typec_mode_priorities_get(struct typec_port *port, char *buf);
@@ -145,6 +145,13 @@ enum {
#define TYPEC_MODAL_STATE(_state_) ((_state_) + TYPEC_STATE_MODAL)
+enum {
+ TYPEC_ALTMODE_DP = 0,
+ TYPEC_ALTMODE_TBT,
+ TYPEC_ALTMODE_USB4,
+ TYPEC_ALTMODE_MAX,
+};
+
struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode,
enum typec_plug_index index);
void typec_altmode_put_plug(struct typec_altmode *plug);
This sysfs attribute specifies the preferred order for enabling DisplayPort alt-mode, Thunderbolt alt-mode, and USB4 mode. Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org> --- Documentation/ABI/testing/sysfs-class-typec | 17 ++++ drivers/usb/typec/Makefile | 2 +- drivers/usb/typec/class.c | 26 ++++++ drivers/usb/typec/class.h | 2 + drivers/usb/typec/mode_selection.c | 93 +++++++++++++++++++++ drivers/usb/typec/mode_selection.h | 5 ++ include/linux/usb/typec_altmode.h | 7 ++ 7 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/typec/mode_selection.c create mode 100644 drivers/usb/typec/mode_selection.h