Files
omarchy-pkgs/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch
T

133 lines
4.2 KiB
Diff

diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 263a89c5f324..5ee33a69b9cf 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -790,7 +790,6 @@ int dp_altmode_probe(struct typec_altmode *alt)
dp->alt = alt;
alt->desc = "DisplayPort";
- typec_altmode_set_ops(alt, &dp_altmode_ops);
if (plug) {
plug->desc = "Displayport";
@@ -811,6 +810,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (plug)
typec_altmode_set_drvdata(plug, dp);
+ if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt))
+ return 0;
+
+ typec_altmode_set_ops(alt, &dp_altmode_ops);
if (!alt->mode_selection) {
dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
schedule_work(&dp->work);
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 32250b94262a..2eccdddf1b1f 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
alt->desc = "Thunderbolt3";
typec_altmode_set_drvdata(alt, tbt);
+
+ if (typec_cable_altmode_unsupported(alt))
+ return 0;
+
typec_altmode_set_ops(alt, &tbt_altmode_ops);
if (!alt->mode_selection && tbt_ready(alt)) {
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 0595e8cb83aa..54c7810b563b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable)
}
EXPORT_SYMBOL_GPL(typec_cable_is_active);
+enum typec_cable_altmode_support {
+ CABLE_SUPPORT_UNKNOWN,
+ CABLE_SUPPORTED,
+ CABLE_NOT_SUPPORTED,
+};
+
+static enum typec_cable_altmode_support
+typec_cable_check_altmode_support(struct typec_cable *cable,
+ struct typec_altmode *alt)
+{
+ struct typec_altmode *plug;
+ u32 speed;
+
+ /*
+ * Check if the cable has an e-marker, supports modal operation, and the
+ * SOP' altmode nodes are created.
+ */
+ plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
+ if (plug) {
+ typec_altmode_put_plug(plug);
+ return CABLE_SUPPORTED;
+ }
+
+ /* The identity is not specified */
+ if (!cable->identity)
+ return CABLE_SUPPORT_UNKNOWN;
+
+ /* Non-e-marked cable */
+ if (!cable->identity->id_header)
+ return CABLE_NOT_SUPPORTED;
+
+ switch (PD_IDH_PTYPE(cable->identity->id_header)) {
+ case IDH_PTYPE_PCABLE:
+ speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]);
+ if (speed == CABLE_USB2_ONLY)
+ return CABLE_NOT_SUPPORTED;
+ return CABLE_SUPPORTED;
+ case IDH_PTYPE_ACABLE:
+ /*
+ * Active cables must establish an SOP' communication
+ * node. Since that check failed at the beginning of
+ * this function, this active cable does not support
+ * this specific altmode.
+ */
+ return CABLE_NOT_SUPPORTED;
+ }
+
+ return CABLE_SUPPORT_UNKNOWN;
+}
+
+/**
+ * typec_cable_altmode_unsupported - Check if a cable restricts altmode
+ * @alt: The Alternate Mode to evaluate
+ *
+ * Returns true if the connected cable is incapable of handling the altmode.
+ */
+bool typec_cable_altmode_unsupported(struct typec_altmode *alt)
+{
+ enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN;
+ struct typec_cable *cable;
+
+ cable = typec_cable_get(typec_altmode2port(alt));
+ if (cable) {
+ support = typec_cable_check_altmode_support(cable, alt);
+ typec_cable_put(cable);
+ }
+
+ return support == CABLE_NOT_SUPPORTED;
+}
+EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported);
+
/**
* typec_cable_set_identity - Report result from Discover Identity command
* @cable: The cable updated identity values
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index d61ec38216fa..10a783b738ef 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable);
struct typec_cable *typec_cable_get(struct typec_port *port);
void typec_cable_put(struct typec_cable *cable);
int typec_cable_is_active(struct typec_cable *cable);
+bool typec_cable_altmode_unsupported(struct typec_altmode *alt);
struct typec_plug *typec_register_plug(struct typec_cable *cable,
struct typec_plug_desc *desc);