133 lines
4.2 KiB
Diff
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);
|