rockchip64-6.16: Partial fix of DP alt mode on some rk3399 boards

The old method carried along with board-pbp-add-dp-alt-mode.patch only
makes typec work in one(normal) orientation. This patch introduces a
proper extcon driver and makes the workaround cleaner, so orientation
switch is working.

Improvements:
- type-c DP on rk3399 works with both orientations
- type-c USB 3.0 on rk3399 works with both orientations, with minor
  issues, see below

Caveats:
- Powered USB-C hubs may be not recognized, and can be worked around by
  loading a gadget driver, or manually toggling the mode once for each
  connection.
- Some dual-role devices(phone, tablet) may be not recognized.

Affected boards:
- TinkerBoard 2/2S
- Pinebook Pro
- NanoPC T4
- Orange Pi 4
- Orange Pi 4 LTS

Tested on tinkerboard 2s. This patch contains other minor fixes for
tinker2's device tree, including adding a missing fan node, adding color
labels to leds.

The 2 patches adding dp support for nanopc t4 and pinebook pro are also
updated accordingly.

The device trees of Orange Pi 4 / 4 LTS are also updated to match the
new implementation.
This commit is contained in:
hyx0329
2025-06-29 09:31:35 +08:00
committed by Paolo
parent 544952fb4c
commit 6e39531b62
17 changed files with 1561 additions and 478 deletions

View File

@@ -2635,6 +2635,7 @@ CONFIG_TYPEC_UCSI=m
CONFIG_UCSI_CCG=m CONFIG_UCSI_CCG=m
CONFIG_TYPEC_TPS6598X=m CONFIG_TYPEC_TPS6598X=m
CONFIG_TYPEC_HD3SS3220=m CONFIG_TYPEC_HD3SS3220=m
CONFIG_TYPEC_EXTCON=m
CONFIG_TYPEC_MUX_FSA4480=m CONFIG_TYPEC_MUX_FSA4480=m
CONFIG_TYPEC_MUX_PI3USB30532=m CONFIG_TYPEC_MUX_PI3USB30532=m
CONFIG_TYPEC_DP_ALTMODE=m CONFIG_TYPEC_DP_ALTMODE=m

View File

@@ -1,12 +1,12 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: tonymac32 <tonymckahan@gmail.com> From: hyx0329 <hyx0329@outlook.com>
Date: Wed, 17 Feb 2021 00:54:00 -0500 Date: Mon, 2 Jun 2025 07:59:27 +0000
Subject: Patching something Subject: Enable type-c dp alt mode for nanopc t4 in the device tree
Signed-off-by: tonymac32 <tonymckahan@gmail.com> Signed-off-by: hyx0329 <hyx0329@outlook.com>
--- ---
arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 96 ++++++++++ arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 116 ++++++++++
1 file changed, 96 insertions(+) 1 file changed, 116 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts
index 111111111111..222222222222 100644 index 111111111111..222222222222 100644
@@ -20,24 +20,34 @@ index 111111111111..222222222222 100644
#include "rk3399-nanopi4.dtsi" #include "rk3399-nanopi4.dtsi"
/ { / {
@@ -66,6 +67,12 @@ fan: pwm-fan { @@ -64,6 +65,20 @@ fan: pwm-fan {
fan-supply = <&vcc12v0_sys>;
pwms = <&pwm1 0 50000 0>;
}; };
}; +
+ typec_extcon_bridge: typec-extcon {
+&cdn_dp { + compatible = "linux,typec-extcon-bridge";
+ status = "okay"; + usb-role-switch;
+ extcon = <&fusb0>; + orientation-switch;
+ phys = <&tcphy0_dp>; + mode-switch;
+ svid = /bits/ 16 <0xff01>;
+ };
+}; +};
+ +
+&cdn_dp {
+ status = "okay";
+ extcon = <&typec_extcon_bridge>;
+ phys = <&tcphy0_dp>;
};
&cpu_thermal { &cpu_thermal {
trips { @@ -94,6 +109,59 @@ map3 {
cpu_warm: cpu_warm {
@@ -94,6 +101,50 @@ map3 {
}; };
}; };
+&fusb0 { +&fusb0 {
+ usb-role-switch = <&typec_extcon_bridge>;
+ extcon = <&typec_extcon_bridge>;
+ +
+ connector { + connector {
+ compatible = "usb-c-connector"; + compatible = "usb-c-connector";
@@ -49,8 +59,15 @@ index 111111111111..222222222222 100644
+ sink-pdos = <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM)>; + sink-pdos = <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM)>;
+ op-sink-microwatt = <5000000>; + op-sink-microwatt = <5000000>;
+ +
+ extcon-cables = <1 2 5 6 9 10 12 44>; + mode-switch = <&typec_extcon_bridge>;
+ typec-altmodes = <0xff01 1 0x001c0000 1>; + orientation-switch = <&typec_extcon_bridge>;
+
+ altmodes {
+ dp {
+ svid = /bits/ 16 <0xff01>;
+ vdo = <0x1c46>;
+ };
+ };
+ +
+ ports { + ports {
+ #address-cells = <1>; + #address-cells = <1>;
@@ -84,12 +101,12 @@ index 111111111111..222222222222 100644
&pcie0 { &pcie0 {
ep-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; ep-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>;
num-lanes = <4>; num-lanes = <4>;
@@ -114,12 +165,57 @@ &sdhci { @@ -114,12 +182,60 @@ &sdhci {
mmc-hs400-enhanced-strobe; mmc-hs400-enhanced-strobe;
}; };
+&tcphy0 { +&tcphy0 {
+ extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>;
+ status = "okay"; + status = "okay";
+}; +};
+ +
@@ -110,7 +127,8 @@ index 111111111111..222222222222 100644
+}; +};
+ +
+&u2phy0 { +&u2phy0 {
+ extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>;
+ extcon,ignore-usb; /* let extcon handle role switch */
+}; +};
+ +
&u2phy0_host { &u2phy0_host {
@@ -138,7 +156,9 @@ index 111111111111..222222222222 100644
+}; +};
+ +
+&usbdrd_dwc3_0 { +&usbdrd_dwc3_0 {
+ extcon = <&fusb0>; + dr_mode = "otg"; /* MUST be otg so phy can reset properly */
+ extcon = <&typec_extcon_bridge>;
+ snps,usb3-phy-reset-quirk;
}; };
&vcc5v0_sys { &vcc5v0_sys {

View File

@@ -1,417 +1,104 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dan Johansen <strit@manjaro.org> From: hyx0329 <hyx0329@outlook.com>
Date: Tue, 2 Jun 2020 20:20:29 +0200 Date: Mon, 2 Jun 2025 07:33:51 +0000
Subject: add-dp-alt-mode-to-PBP Subject: Add dp alt mode to pinebook pro
Signed-off-by: hyx0329 <hyx0329@outlook.com>
--- ---
arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 5 + arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 36 +++++++++-
drivers/phy/rockchip/phy-rockchip-typec.c | 17 ++ 1 file changed, 33 insertions(+), 3 deletions(-)
drivers/usb/typec/altmodes/displayport.c | 52 +++-
drivers/usb/typec/tcpm/tcpm.c | 138 +++++++++-
4 files changed, 209 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
index 111111111111..222222222222 100644 index 111111111111..222222222222 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts
@@ -421,6 +421,7 @@ edp_out_panel: endpoint@0 { @@ -373,6 +373,14 @@ mains_charger: dc-charger {
pinctrl-names = "default";
&emmc_phy { pinctrl-0 = <&dc_det_pin>;
status = "okay"; };
+ extcon = <&fusb0>; +
+ typec_extcon_bridge: typec-extcon {
+ compatible = "linux,typec-extcon-bridge";
+ usb-role-switch;
+ orientation-switch;
+ mode-switch;
+ svid = /bits/ 16 <0xff01>;
+ };
}; };
&gpu { &cpu_b0 {
@@ -705,6 +706,9 @@ connector { @@ -399,6 +407,12 @@ &cpu_l3 {
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; cpu-supply = <&vdd_cpu_l>;
try-power-role = "sink"; };
+ extcon-cables = <1 2 5 6 9 10 12 44>; +&cdn_dp {
+ typec-altmodes = <0xff01 1 0x001c0000 1>; + status = "okay";
+ extcon = <&typec_extcon_bridge>;
+ phys = <&tcphy0_dp>;
+};
+ +
&edp {
force-hpd;
pinctrl-names = "default";
@@ -692,6 +706,8 @@ fusb0: fusb30x@22 {
pinctrl-names = "default";
pinctrl-0 = <&fusb0_int_pin>;
vbus-supply = <&vbus_typec>;
+ usb-role-switch = <&typec_extcon_bridge>;
+ extcon = <&typec_extcon_bridge>;
connector {
compatible = "usb-c-connector";
@@ -700,10 +716,19 @@ connector {
op-sink-microwatt = <1000000>;
power-role = "dual";
sink-pdos =
- <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>;
+ <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>;
source-pdos =
- <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
+ <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>;
try-power-role = "sink";
+ mode-switch = <&typec_extcon_bridge>;
+ orientation-switch = <&typec_extcon_bridge>;
+
+ altmodes {
+ dp {
+ svid = /bits/ 16 <0xff01>;
+ vdo = <0x1c46>;
+ };
+ };
ports { ports {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; @@ -970,6 +995,7 @@ spiflash: flash@0 {
@@ -970,6 +974,7 @@ spiflash: flash@0 {
}; };
&tcphy0 { &tcphy0 {
+ extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
}; };
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -1003,6 +1029,8 @@ &tsadc {
index 111111111111..222222222222 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -40,6 +40,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -1156,6 +1157,22 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
dev_err(dev, "Invalid or missing extcon\n");
return PTR_ERR(tcphy->extcon);
}
+ } else {
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(tcphy->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_sync(tcphy->extcon, EXTCON_USB);
+ extcon_sync(tcphy->extcon, EXTCON_USB_HOST);
+ extcon_sync(tcphy->extcon, EXTCON_DISP_DP);
}
pm_runtime_enable(dev); &u2phy0 {
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c status = "okay";
index 111111111111..222222222222 100644 + extcon = <&typec_extcon_bridge>;
--- a/drivers/usb/typec/altmodes/displayport.c + extcon,ignore-usb;
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -9,6 +9,8 @@
*/
#include <linux/delay.h> u2phy0_otg: otg-port {
+#include <linux/extcon.h> status = "okay";
+#include <linux/extcon-provider.h> @@ -1079,7 +1107,9 @@ &usbdrd3_0 {
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/property.h>
@@ -74,6 +76,8 @@ struct dp_altmode {
struct typec_altmode *plug_prime;
}; };
+void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect); &usbdrd_dwc3_0 {
+ - dr_mode = "host";
static int dp_altmode_notify(struct dp_altmode *dp) + dr_mode = "otg";
{ + extcon = <&typec_extcon_bridge>;
unsigned long conf; + snps,usb3-phy-reset-quirk;
@@ -82,7 +86,9 @@ static int dp_altmode_notify(struct dp_altmode *dp) status = "okay";
if (dp->data.conf) { };
state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
conf = TYPEC_MODAL_STATE(state);
+ dp_altmode_update_extcon(dp, false);
} else {
+ dp_altmode_update_extcon(dp, true);
conf = TYPEC_STATE_USB;
}
@@ -182,6 +188,40 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
return ret;
}
+void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect) {
+ const struct device *dev = &dp->port->dev;
+ struct extcon_dev* edev = NULL;
+
+ while (dev) {
+ edev = extcon_find_edev_by_node(dev->of_node);
+ if(!IS_ERR(edev)) {
+ break;
+ }
+ dev = dev->parent;
+ }
+
+ if (IS_ERR_OR_NULL(edev)) {
+ return;
+ }
+
+ if (disconnect || !dp->data.conf) {
+ extcon_set_state_sync(edev, EXTCON_DISP_DP, false);
+ } else {
+ union extcon_property_value extcon_true = { .intval = true };
+ extcon_set_state(edev, EXTCON_DISP_DP, true);
+ if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf) & DP_PIN_ASSIGN_MULTI_FUNC_MASK) {
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
+ extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS,
+ extcon_true);
+ } else {
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
+ }
+ extcon_sync(edev, EXTCON_DISP_DP);
+ extcon_set_state_sync(edev, EXTCON_USB, false);
+ }
+
+}
+
static int dp_altmode_configured(struct dp_altmode *dp)
{
sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
@@ -298,6 +338,8 @@ static void dp_altmode_work(struct work_struct *work)
case DP_STATE_EXIT:
if (typec_altmode_exit(dp->alt))
dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
+ else
+ dp_altmode_update_extcon(dp, true);
break;
case DP_STATE_EXIT_PRIME:
if (typec_cable_altmode_exit(dp->plug_prime, TYPEC_PLUG_SOP_P))
@@ -737,8 +779,14 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
!(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
- return -ENODEV;
+ DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) {
+ dev_err(&alt->dev, "No compatible pin configuration found:"\
+ "%04lx -> %04lx, %04lx <- %04lx",
+ DP_CAP_PIN_ASSIGN_DFP_D(port->vdo), DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo),
+ DP_CAP_PIN_ASSIGN_UFP_D(port->vdo), DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo));
+ return -ENODEV;
+ }
+
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -8,6 +8,7 @@
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/device.h>
+#include <linux/extcon-provider.h>
#include <linux/hrtimer.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
@@ -580,6 +581,11 @@ struct tcpm_port {
unsigned int message_id_prime;
unsigned int rx_msgid_prime;
+#ifdef CONFIG_EXTCON
+ struct extcon_dev *extcon;
+ unsigned int *extcon_cables;
+#endif
+
/* Timer deadline values configured at runtime */
struct pd_timings timings;
@@ -983,6 +989,35 @@ static void tcpm_ams_finish(struct tcpm_port *port)
port->ams = NONE_AMS;
}
+static void tcpm_update_extcon_data(struct tcpm_port *port, bool attached) {
+#ifdef CONFIG_EXTCON
+ unsigned int *capability = port->extcon_cables;
+ if (port->data_role == TYPEC_HOST) {
+ extcon_set_state(port->extcon, EXTCON_USB, false);
+ extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
+ } else {
+ extcon_set_state(port->extcon, EXTCON_USB, true);
+ extcon_set_state(port->extcon, EXTCON_USB_HOST, attached);
+ }
+ while (*capability != EXTCON_NONE) {
+ if (attached) {
+ union extcon_property_value val;
+ val.intval = (port->polarity == TYPEC_POLARITY_CC2);
+ extcon_set_property(port->extcon, *capability,
+ EXTCON_PROP_USB_TYPEC_POLARITY, val);
+ } else {
+ extcon_set_state(port->extcon, *capability, false);
+ }
+ extcon_sync(port->extcon, *capability);
+ capability++;
+ }
+ tcpm_log(port, "Extcon update (%s): %s, %s",
+ attached ? "attached" : "detached",
+ port->data_role == TYPEC_HOST ? "host" : "device",
+ port->polarity == TYPEC_POLARITY_CC1 ? "normal" : "flipped");
+#endif
+}
+
static int tcpm_pd_transmit(struct tcpm_port *port,
enum tcpm_transmit_type tx_sop_type,
const struct pd_message *msg)
@@ -1222,6 +1257,8 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, int state,
typec_set_data_role(port->typec_port, data);
typec_set_pwr_role(port->typec_port, role);
+ tcpm_update_extcon_data(port, attached);
+
return 0;
}
@@ -1845,7 +1882,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
paltmode->mode = i;
paltmode->vdo = p[i];
- tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
+ tcpm_log(port, "Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
pmdata->altmodes, paltmode->svid,
paltmode->mode, paltmode->vdo);
@@ -1869,6 +1906,8 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
tcpm_log(port, "Failed to register partner SVID 0x%04x",
modep->altmode_desc[i].svid);
altmode = NULL;
+ } else {
+ tcpm_log(port, "Registered altmode 0x%04x", modep->altmode_desc[i].svid);
}
port->partner_altmode[i] = altmode;
}
@@ -2245,11 +2284,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
u16 svid = modep->svids[modep->svid_index];
+ tcpm_log(port, "More modes available, sending discover");
*response_tx_sop_type = TCPC_TX_SOP;
response[0] = VDO(svid, 1, svdm_version,
CMD_DISCOVER_MODES);
rlen = 1;
} else if (tcpm_cable_vdm_supported(port)) {
+ tcpm_log(port, "Got all patner modes, registering");
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
response[0] = VDO(USB_SID_PD, 1,
typec_get_cable_svdm_version(typec),
@@ -4471,6 +4512,7 @@ static void tcpm_typec_disconnect(struct tcpm_port *port)
port->cable = NULL;
if (port->connected) {
if (port->partner) {
+ tcpm_update_extcon_data(port, false);
typec_partner_set_usb_power_delivery(port->partner, NULL);
typec_unregister_partner(port->partner);
port->partner = NULL;
@@ -4565,6 +4607,8 @@ static void tcpm_detach(struct tcpm_port *port)
}
tcpm_reset_port(port);
+
+ tcpm_update_extcon_data(port, false);
}
static void tcpm_src_detach(struct tcpm_port *port)
@@ -7274,6 +7318,64 @@ static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fw
port->timings.snk_bc12_cmpletion_time = val;
}
+unsigned int default_supported_cables[] = {
+ EXTCON_NONE
+};
+
+static int tcpm_fw_get_caps_late(struct tcpm_port *port,
+ struct fwnode_handle *fwnode)
+{
+ int ret, i;
+ ret = fwnode_property_count_u32(fwnode, "typec-altmodes");
+ if (ret > 0) {
+ u32 *props;
+ if (ret % 4) {
+ dev_err(port->dev, "Length of typec altmode array must be divisible by 4");
+ return -EINVAL;
+ }
+
+ props = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
+ if (!props) {
+ dev_err(port->dev, "Failed to allocate memory for altmode properties");
+ return -ENOMEM;
+ }
+
+ if(fwnode_property_read_u32_array(fwnode, "typec-altmodes", props, ret) < 0) {
+ dev_err(port->dev, "Failed to read altmodes from port");
+ return -EINVAL;
+ }
+
+ i = 0;
+ while (ret > 0 && i < ARRAY_SIZE(port->port_altmode)) {
+ struct typec_altmode *alt;
+ struct typec_altmode_desc alt_desc = {
+ .svid = props[i * 4],
+ .mode = props[i * 4 + 1],
+ .vdo = props[i * 4 + 2],
+ .roles = props[i * 4 + 3],
+ };
+
+
+ tcpm_log(port, "Adding altmode SVID: 0x%04x, mode: %d, vdo: %u, role: %d",
+ alt_desc.svid, alt_desc.mode, alt_desc.vdo, alt_desc.roles);
+ alt = typec_port_register_altmode(port->typec_port,
+ &alt_desc);
+ if (IS_ERR(alt)) {
+ tcpm_log(port,
+ "%s: failed to register port alternate mode 0x%x",
+ dev_name(port->dev), alt_desc.svid);
+ break;
+ }
+ typec_altmode_set_drvdata(alt, port);
+ alt->ops = &tcpm_altmode_ops;
+ port->port_altmode[i] = alt;
+ i++;
+ ret -= 4;
+ }
+ }
+ return 0;
+}
+
static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode)
{
struct fwnode_handle *capabilities, *caps = NULL;
@@ -7287,6 +7389,23 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode
if (!fwnode)
return -EINVAL;
+#ifdef CONFIG_EXTCON
+ ret = fwnode_property_count_u32(fwnode, "extcon-cables");
+ if (ret > 0) {
+ port->extcon_cables = devm_kzalloc(port->dev, sizeof(u32) * ret, GFP_KERNEL);
+ if (!port->extcon_cables) {
+ dev_err(port->dev, "Failed to allocate memory for extcon cable types. "\
+ "Using default tyes");
+ goto extcon_default;
+ }
+ fwnode_property_read_u32_array(fwnode, "extcon-cables", port->extcon_cables, ret);
+ } else {
+extcon_default:
+ dev_info(port->dev, "No cable types defined, using default cables");
+ port->extcon_cables = default_supported_cables;
+ }
+#endif
+
/*
* This fwnode has a "compatible" property, but is never populated as a
* struct device. Instead we simply parse it to read the properties.
@@ -7856,6 +7975,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
tcpm_fw_get_pd_revision(port, tcpc->fwnode);
port->try_role = port->typec_caps.prefer_role;
+#ifdef CONFIG_EXTCON
+ port->extcon = devm_extcon_dev_allocate(dev, port->extcon_cables);
+ if (IS_ERR(port->extcon)) {
+ dev_err(dev, "Failed to allocate extcon device: %ld", PTR_ERR(port->extcon));
+ goto out_destroy_wq;
+ }
+ if((err = devm_extcon_dev_register(dev, port->extcon))) {
+ dev_err(dev, "Failed to register extcon device: %d", err);
+ goto out_destroy_wq;
+ }
+#endif
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
@@ -7905,6 +8035,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
&tcpm_cable_ops);
port->registered = true;
+ err = tcpm_fw_get_caps_late(port, tcpc->fwnode);
+ if (err < 0) {
+ dev_err(dev, "Failed to get altmodes from fwnode");
+ goto out_destroy_wq;
+ }
+
mutex_lock(&port->lock);
tcpm_init(port);
mutex_unlock(&port->lock);
-- --
Armbian Armbian

View File

@@ -89,7 +89,6 @@
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_typec_en>; pinctrl-0 = <&vcc5v0_typec_en>;
regulator-name = "vbus-5v"; regulator-name = "vbus-5v";
regulator-always-on;
regulator-min-microvolt = <5000000>; regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>; regulator-max-microvolt = <5000000>;
vin-supply = <&vcc5v0_sys>; vin-supply = <&vcc5v0_sys>;
@@ -381,6 +380,13 @@
}; };
typec_extcon_bridge: typec-extcon {
compatible = "linux,typec-extcon-bridge";
usb-role-switch;
orientation-switch;
mode-switch;
svid = /bits/ 16 <0xff01>;
};
}; };
&cpu_l0 { &cpu_l0 {
@@ -497,7 +503,7 @@
&cdn_dp { &cdn_dp {
status = "okay"; status = "okay";
extcon = <&fusb0>; extcon = <&typec_extcon_bridge>;
phys = <&tcphy0_dp>; phys = <&tcphy0_dp>;
}; };
@@ -785,6 +791,8 @@
interrupt-parent = <&gpio1>; interrupt-parent = <&gpio1>;
interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>; interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
vbus-supply = <&vbus_typec>; vbus-supply = <&vbus_typec>;
usb-role-switch = <&typec_extcon_bridge>;
extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
connector { connector {
@@ -798,9 +806,17 @@
source-pdos = source-pdos =
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
try-power-role = "sink"; try-power-role = "sink";
mode-switch = <&typec_extcon_bridge>;
orientation-switch = <&typec_extcon_bridge>;
extcon-cables = <1 2 5 6 9 10 12 44>; altmodes {
typec-altmodes = <0xff01 1 0x001c0000 1>; dp {
svid = /bits/ 16 <0xff01>;
/* what is this? megous uses 0x1c46 for pinebook pro.
* dp alt spec is not open so it's based on reverse engineering and guess work */
vdo = <0x1c46>;
};
};
ports { ports {
#address-cells = <1>; #address-cells = <1>;
@@ -984,7 +1000,7 @@
}; };
&tcphy0 { &tcphy0 {
extcon = <&fusb0>; extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
}; };
@@ -1010,6 +1026,8 @@
&u2phy0 { &u2phy0 {
status = "okay"; status = "okay";
extcon = <&typec_extcon_bridge>;
extcon,ignore-usb; /* u2phy must not handle role switch */
u2phy0_otg: otg-port { u2phy0_otg: otg-port {
status = "okay"; status = "okay";
@@ -1043,7 +1061,6 @@
&usbdrd3_0 { &usbdrd3_0 {
status = "okay"; status = "okay";
extcon = <&fusb0>;
}; };
&usbdrd3_1 { &usbdrd3_1 {
@@ -1052,6 +1069,10 @@
&usbdrd_dwc3_0 { &usbdrd_dwc3_0 {
status = "okay"; status = "okay";
/* MUST be otg when DP is used so phy can reset properly */
dr_mode = "otg";
extcon = <&typec_extcon_bridge>;
snps,usb3-phy-reset-quirk;
}; };
&usbdrd_dwc3_1 { &usbdrd_dwc3_1 {

View File

@@ -67,7 +67,6 @@
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_typec_en>; pinctrl-0 = <&vcc5v0_typec_en>;
regulator-name = "vbus_typec"; regulator-name = "vbus_typec";
regulator-always-on;
regulator-min-microvolt = <5000000>; regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>; regulator-max-microvolt = <5000000>;
vin-supply = <&vcc5v0_sys>; vin-supply = <&vcc5v0_sys>;
@@ -334,6 +333,14 @@
*/ */
reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* GPIO0_B2 */ reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* GPIO0_B2 */
}; };
typec_extcon_bridge: typec-extcon {
compatible = "linux,typec-extcon-bridge";
usb-role-switch;
orientation-switch;
mode-switch;
svid = /bits/ 16 <0xff01>;
};
}; };
&cpu_l0 { &cpu_l0 {
@@ -456,7 +463,7 @@
&cdn_dp { &cdn_dp {
status = "okay"; status = "okay";
extcon = <&fusb0>; extcon = <&typec_extcon_bridge>;
phys = <&tcphy0_dp>; phys = <&tcphy0_dp>;
}; };
@@ -740,6 +747,8 @@
interrupt-parent = <&gpio1>; interrupt-parent = <&gpio1>;
interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>; interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
vbus-supply = <&vbus_typec>; vbus-supply = <&vbus_typec>;
usb-role-switch = <&typec_extcon_bridge>;
extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
connector { connector {
@@ -753,9 +762,17 @@
source-pdos = source-pdos =
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>;
try-power-role = "sink"; try-power-role = "sink";
mode-switch = <&typec_extcon_bridge>;
orientation-switch = <&typec_extcon_bridge>;
extcon-cables = <1 2 5 6 9 10 12 44>; altmodes {
typec-altmodes = <0xff01 1 0x001c0000 1>; dp {
svid = /bits/ 16 <0xff01>;
/* what is this? megous uses 0x1c46 for pinebook pro.
* dp alt spec is not open so it's based on reverse engineering and guess work */
vdo = <0x1c46>;
};
};
ports { ports {
#address-cells = <1>; #address-cells = <1>;
@@ -949,7 +966,7 @@
}; };
&tcphy0 { &tcphy0 {
extcon = <&fusb0>; extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
}; };
@@ -975,6 +992,8 @@
&u2phy0 { &u2phy0 {
status = "okay"; status = "okay";
extcon = <&typec_extcon_bridge>;
extcon,ignore-usb; /* u2phy must not handle role switch */
u2phy0_otg: otg-port { u2phy0_otg: otg-port {
status = "okay"; status = "okay";
@@ -1007,7 +1026,6 @@
&usbdrd3_0 { &usbdrd3_0 {
status = "okay"; status = "okay";
extcon = <&fusb0>;
}; };
&usbdrd3_1 { &usbdrd3_1 {
@@ -1016,6 +1034,10 @@
&usbdrd_dwc3_0 { &usbdrd_dwc3_0 {
status = "okay"; status = "okay";
/* MUST be otg when DP is used so phy can reset properly */
dr_mode = "otg";
extcon = <&typec_extcon_bridge>;
snps,usb3-phy-reset-quirk;
}; };
&usbdrd_dwc3_1 { &usbdrd_dwc3_1 {

View File

@@ -5,7 +5,9 @@
*/ */
/dts-v1/; /dts-v1/;
#include <dt-bindings/leds/common.h>
#include <dt-bindings/pwm/pwm.h> #include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/usb/pd.h> #include <dt-bindings/usb/pd.h>
#include "rk3399.dtsi" #include "rk3399.dtsi"
#include "rk3399-op1.dtsi" #include "rk3399-op1.dtsi"
@@ -42,22 +44,38 @@
#clock-cells = <0>; #clock-cells = <0>;
}; };
fan: gpio-fan {
compatible = "gpio-fan";
gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>;
/* arbitrary speed values,
* actually only full speed and lower speed. */
gpio-fan,speed-map = <50 0>,
<100 1>;
#cooling-cells = <2>;
fan-supply = <&vcc5v0_sys>;
pinctrl-names = "default";
pinctrl-0 = <&gpio_fan_pin>;
};
gpio-leds { gpio-leds {
compatible = "gpio-leds"; compatible = "gpio-leds";
pwr-led { pwr-led {
gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>; gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_RED>;
linux,default-trigger = "default-on"; linux,default-trigger = "default-on";
retain-state-suspended = <1>; retain-state-suspended = <1>;
}; };
act-led { act-led {
gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>; gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
linux,default-trigger="mmc0"; linux,default-trigger="mmc0";
}; };
rsv-led { rsv-led {
gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_HIGH>; gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_YELLOW>;
linux,default-trigger="heartbeat"; linux,default-trigger="heartbeat";
}; };
}; };
@@ -65,7 +83,7 @@
vcc_lcd: vcc-lcd { vcc_lcd: vcc-lcd {
compatible = "regulator-fixed"; compatible = "regulator-fixed";
regulator-name = "vcc_lcd"; regulator-name = "vcc_lcd";
gpio = <&gpio4 30 GPIO_ACTIVE_HIGH>; gpio = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>;
startup-delay-us = <20000>; startup-delay-us = <20000>;
enable-active-high; enable-active-high;
regulator-min-microvolt = <3300000>; regulator-min-microvolt = <3300000>;
@@ -145,11 +163,20 @@
clock-output-names = "xin32k"; clock-output-names = "xin32k";
#clock-cells = <0>; #clock-cells = <0>;
}; };
typec_extcon_bridge: typec-extcon {
compatible = "linux,typec-extcon-bridge";
usb-role-switch;
orientation-switch;
mode-switch;
svid = /bits/ 16 <0xff01>;
};
}; };
&cdn_dp { &cdn_dp {
status = "okay"; status = "okay";
extcon = <&fusb0>; extcon = <&typec_extcon_bridge>;
phys = <&tcphy0_dp>;
}; };
&cpu_l0 { &cpu_l0 {
@@ -189,7 +216,7 @@
phy-mode = "rgmii"; phy-mode = "rgmii";
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&rgmii_pins>; pinctrl-0 = <&rgmii_pins>;
snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>; snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
snps,reset-active-low; snps,reset-active-low;
snps,reset-delays-us = <0 16000 72000>; snps,reset-delays-us = <0 16000 72000>;
tx_delay = <0x25>; tx_delay = <0x25>;
@@ -439,6 +466,8 @@
interrupt-parent = <&gpio1>; interrupt-parent = <&gpio1>;
interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>; interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
vbus-supply = <&vbus_typec>; vbus-supply = <&vbus_typec>;
usb-role-switch = <&typec_extcon_bridge>;
extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
connector { connector {
@@ -448,13 +477,21 @@
op-sink-microwatt = <1000000>; op-sink-microwatt = <1000000>;
power-role = "dual"; power-role = "dual";
sink-pdos = sink-pdos =
<PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>; <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_EXTPOWER)>;
source-pdos = source-pdos =
<PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_EXTPOWER)>;
try-power-role = "sink"; try-power-role = "sink";
mode-switch = <&typec_extcon_bridge>;
orientation-switch = <&typec_extcon_bridge>;
extcon-cables = <1 2 5 6 9 10 12 44>; altmodes {
typec-altmodes = <0xff01 1 0x001c0000 1>; dp {
svid = /bits/ 16 <0xff01>;
/* what is this? megous uses 0x1c46 for pinebook pro.
* dp alt spec is not open so it's based on reverse engineering and guess work */
vdo = <0x1c46>;
};
};
ports { ports {
#address-cells = <1>; #address-cells = <1>;
@@ -488,10 +525,6 @@
}; };
}; };
}; };
}; };
vdd_gpu: vdd_gpu@60 { vdd_gpu: vdd_gpu@60 {
@@ -519,7 +552,6 @@
}; };
&i2c8 { &i2c8 {
m24c08: m24c08@50 { m24c08: m24c08@50 {
compatible = "atmel,24c08"; compatible = "atmel,24c08";
reg = <0x50>; reg = <0x50>;
@@ -541,7 +573,7 @@
}; };
&pcie0 { &pcie0 {
ep-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; ep-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>;
num-lanes = <4>; num-lanes = <4>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pcie_clkreqn_cpm>; pinctrl-0 = <&pcie_clkreqn_cpm>;
@@ -594,7 +626,7 @@
}; };
&tcphy0 { &tcphy0 {
extcon = <&fusb0>; extcon = <&typec_extcon_bridge>;
status = "okay"; status = "okay";
}; };
@@ -628,6 +660,8 @@
&u2phy0 { &u2phy0 {
status = "okay"; status = "okay";
extcon = <&typec_extcon_bridge>;
extcon,ignore-usb; /* u2phy must not handle role switch */
u2phy0_otg: otg-port { u2phy0_otg: otg-port {
status = "okay"; status = "okay";
@@ -664,7 +698,10 @@
&usbdrd_dwc3_0 { &usbdrd_dwc3_0 {
status = "okay"; status = "okay";
dr_mode = "host"; /* MUST be otg when DP is used so phy can reset properly */
dr_mode = "otg";
extcon = <&typec_extcon_bridge>;
snps,usb3-phy-reset-quirk;
}; };
&usbdrd3_1 { &usbdrd3_1 {
@@ -697,12 +734,23 @@
}; };
&pinctrl { &pinctrl {
fan {
gpio_fan_pin: gpio-fan-pin {
rockchip,pins = <4 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
fusb30x {
fusb0_int: fusb0-int {
rockchip,pins = <1 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
pmic { pmic {
pmic_int_l: pmic-int-l { pmic_int_l: pmic-int-l {
rockchip,pins = rockchip,pins =
<1 18 RK_FUNC_GPIO &pcfg_pull_up>, <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>,
<0 9 RK_FUNC_GPIO &pcfg_pull_none>; /* GPIO0_B1 */ <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
}; };
vsel1_gpio: vsel1-gpio { vsel1_gpio: vsel1-gpio {
rockchip,pins = rockchip,pins =
@@ -726,20 +774,6 @@
rockchip,pins = <1 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>; rockchip,pins = <1 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>;
}; };
}; };
fusb30x {
fusb0_int: fusb0-int {
rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
};
&vopl {
status = "okay";
};
&vopl_mmu {
status = "okay";
}; };
&vopb { &vopb {
@@ -749,3 +783,11 @@
&vopb_mmu { &vopb_mmu {
status = "okay"; status = "okay";
}; };
&vopl {
status = "okay";
};
&vopl_mmu {
status = "okay";
};

View File

@@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Tue, 7 May 2024 18:39:12 +0200
Subject: Revert "usb: typec: tcpm: unregister existing source caps before
re-registration"
This reverts commit 230ecdf71a644c9c73e0e6735b33173074ae3f94.
---
drivers/usb/typec/tcpm/tcpm.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -3110,7 +3110,7 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
{
struct usb_power_delivery_desc desc = { port->negotiated_rev };
struct usb_power_delivery_capabilities_desc caps = { };
- struct usb_power_delivery_capabilities *cap = port->partner_source_caps;
+ struct usb_power_delivery_capabilities *cap;
if (!port->partner_pd)
port->partner_pd = usb_power_delivery_register(NULL, &desc);
@@ -3120,11 +3120,6 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
caps.role = TYPEC_SOURCE;
- if (cap) {
- usb_power_delivery_unregister_capabilities(cap);
- port->partner_source_caps = NULL;
- }
-
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
return PTR_ERR(cap);
--
Armbian

View File

@@ -0,0 +1,111 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: hyx0329 <hyx0329@outlook.com>
Date: Tue, 3 Jun 2025 12:14:37 +0000
Subject: notify typec dp hpd state through extcon
Signed-off-by: hyx0329 <hyx0329@outlook.com>
---
drivers/usb/typec/altmodes/displayport.c | 57 ++++++++++
1 file changed, 57 insertions(+)
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -9,8 +9,11 @@
*/
#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
#include <linux/mutex.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/property.h>
#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec_dp.h>
@@ -74,6 +77,57 @@ struct dp_altmode {
struct typec_altmode *plug_prime;
};
+/* Notify DP hotplug change via extcon. Current rockchip cdn-dp driver need this signal
+ * to reset DP link. */
+static void dp_altmode_update_extcon_hpd_status(struct dp_altmode *dp, bool new_hpd) {
+ const struct device *dev = &dp->alt->dev;
+ struct extcon_dev* edev = NULL;
+ int ret;
+
+ /* skip if hpd is not changed */
+ if (dp->hpd == new_hpd)
+ return;
+
+ /* find the extcon associated with cdn-dp
+ * an unrelated extcon might be found, but this is a workaround anyway */
+ while (dev) {
+ /* Case1: dev is the extcon provider */
+ edev = extcon_find_edev_by_node(dev->of_node);
+ if (!IS_ERR(edev)) {
+ break;
+ }
+
+ /* Case2: dev links to one extcon */
+ if (of_property_present(dev->of_node, "extcon")) {
+ edev = extcon_get_edev_by_phandle(dev, 0);
+ if (!IS_ERR(edev)) {
+ break;
+ }
+ }
+
+ dev = dev->parent;
+ }
+
+ if (IS_ERR_OR_NULL(edev)) {
+ dev_info(&dp->alt->dev,
+ "no extcon found for current port, not notifying hpd change");
+ return;
+ }
+
+ ret = extcon_set_property_capability(edev, EXTCON_DISP_DP, EXTCON_PROP_DISP_HPD);
+ if (ret) {
+ dev_info(&dp->alt->dev,
+ "updating hpd on target extcon is not supported(%d)\n", ret);
+ return;
+ }
+
+ dev_info(&dp->alt->dev, "dp-hpd=%d (port=%x alt=%x)\n",
+ (int)new_hpd, dp->port->vdo, dp->alt->vdo);
+ extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_PROP_DISP_HPD,
+ (union extcon_property_value)(int)new_hpd);
+ extcon_sync(edev, EXTCON_DISP_DP);
+}
+
static int dp_altmode_notify(struct dp_altmode *dp)
{
unsigned long conf;
@@ -167,6 +221,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
dp->state = dp->plug_prime ? DP_STATE_CONFIGURE_PRIME :
DP_STATE_CONFIGURE;
if (dp->hpd != hpd) {
+ dp_altmode_update_extcon_hpd_status(dp, hpd);
dp->hpd = hpd;
dp->pending_hpd = true;
}
@@ -175,6 +230,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
drm_connector_oob_hotplug_event(dp->connector_fwnode,
hpd ? connector_status_connected :
connector_status_disconnected);
+ dp_altmode_update_extcon_hpd_status(dp, hpd);
dp->hpd = hpd;
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
}
@@ -374,6 +430,7 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
if (dp->hpd) {
drm_connector_oob_hotplug_event(dp->connector_fwnode,
connector_status_disconnected);
+ dp_altmode_update_extcon_hpd_status(dp, false);
dp->hpd = false;
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
}
--
Armbian

View File

@@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
Date: Sun, 30 Jan 2022 23:44:42 +0100
Subject: phy: phy-rockchip-inno-usb2: Decrease delay between port init and
charger detection
When used on systems with TCPM, the charger detection via USB2.0 PHY
will take too much time during boot, so the fusb302 driver will not
be able to get the result, and will set incorrect current_max on
its power supply device.
This will lead to failure to charge when the device is powered up
while connected to non-PD charger.
Decreasing delay of otg_sm_work from 6s to 500ms ensures that
port type detection via the PHY will finish well in time for
TCPM to be able to use its result when Rp=default is detected
via CC pins.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 111111111111..222222222222 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -536,7 +536,7 @@ static int rockchip_usb2phy_init(struct phy *phy)
goto out;
schedule_delayed_work(&rport->otg_sm_work,
- OTG_SCHEDULE_DELAY * 3);
+ msecs_to_jiffies(500));
} else {
/* If OTG works in host only mode, do nothing. */
dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
--
Armbian

View File

@@ -0,0 +1,113 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
Date: Fri, 4 Feb 2022 02:25:34 +0100
Subject: phy: rockchip-inno-usb2: More robust charger detection extcon updates
Make sure we always clear the charger detection results. Allow to
disable updates of EXTCON_USB state via extcon,ignore-usb DT property.
The previous method was smart, but assumed !vbus_branch would
always be called.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 39 ++++++----
1 file changed, 23 insertions(+), 16 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 111111111111..222222222222 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -656,21 +656,27 @@ static const struct phy_ops rockchip_usb2phy_ops = {
.owner = THIS_MODULE,
};
+const int rockchip_usb2phy_cable_types[] = {
+ EXTCON_CHG_USB_SDP,
+ EXTCON_CHG_USB_DCP,
+ EXTCON_CHG_USB_CDP,
+ EXTCON_CHG_USB_ACA,
+};
+
static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
{
struct rockchip_usb2phy_port *rport =
container_of(work, struct rockchip_usb2phy_port,
otg_sm_work.work);
struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
- static unsigned int cable;
+ unsigned int cable = 0, i;
unsigned long delay;
- bool vbus_attach, sch_work, notify_charger;
+ bool vbus_attach, sch_work;
vbus_attach = property_enabled(rphy->grf,
&rport->port_cfg->utmi_bvalid);
sch_work = false;
- notify_charger = false;
delay = OTG_SCHEDULE_DELAY;
dev_dbg(&rport->phy->dev, "%s otg sm work\n",
usb_otg_state_string(rport->state));
@@ -699,14 +705,12 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
dev_dbg(&rport->phy->dev, "sdp cable is connected\n");
rockchip_usb2phy_power_on(rport->phy);
rport->state = OTG_STATE_B_PERIPHERAL;
- notify_charger = true;
sch_work = true;
cable = EXTCON_CHG_USB_SDP;
break;
case POWER_SUPPLY_TYPE_USB_DCP:
dev_dbg(&rport->phy->dev, "dcp cable is connected\n");
rockchip_usb2phy_power_off(rport->phy);
- notify_charger = true;
sch_work = true;
cable = EXTCON_CHG_USB_DCP;
break;
@@ -714,7 +718,6 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
dev_dbg(&rport->phy->dev, "cdp cable is connected\n");
rockchip_usb2phy_power_on(rport->phy);
rport->state = OTG_STATE_B_PERIPHERAL;
- notify_charger = true;
sch_work = true;
cable = EXTCON_CHG_USB_CDP;
break;
@@ -726,22 +729,26 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
break;
}
} else {
- notify_charger = true;
rphy->chg_state = USB_CHG_STATE_UNDEFINED;
rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
}
- if (rport->vbus_attached != vbus_attach) {
- rport->vbus_attached = vbus_attach;
+ if (rphy->edev && extcon_get_state(rphy->edev, cable) != vbus_attach) {
+ for (i = 0; i < ARRAY_SIZE(rockchip_usb2phy_cable_types); i++) {
+ int type = rockchip_usb2phy_cable_types[i];
- if (notify_charger && rphy->edev) {
- extcon_set_state_sync(rphy->edev,
- cable, vbus_attach);
- if (cable == EXTCON_CHG_USB_SDP)
- extcon_set_state_sync(rphy->edev,
- EXTCON_USB,
- vbus_attach);
+ if (extcon_get_state(rphy->edev, type))
+ extcon_set_state_sync(rphy->edev, type, 0);
}
+
+ if (vbus_attach)
+ extcon_set_state_sync(rphy->edev, cable, vbus_attach);
+
+ if ((cable == EXTCON_CHG_USB_SDP || cable == POWER_SUPPLY_TYPE_USB_CDP)
+ && extcon_get_state(rphy->edev, EXTCON_USB) != vbus_attach
+ && !of_property_read_bool(rphy->dev->of_node, "extcon,ignore-usb"))
+ extcon_set_state_sync(rphy->edev,
+ EXTCON_USB, vbus_attach);
}
break;
case OTG_STATE_B_PERIPHERAL:
--
Armbian

View File

@@ -0,0 +1,244 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Mon, 17 Mar 2025 10:57:59 +0100
Subject: phy: rockchip: naneng: Add fallback for old DTs
See https://lore.kernel.org/lkml/20250106070000.605284-1-amadeus@jmu.edu.cn/
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/dwc3/core.c | 46 +++++++++-
drivers/usb/dwc3/core.h | 12 +++
drivers/usb/dwc3/drd.c | 34 ++++---
3 files changed, 77 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -152,7 +152,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
}
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
- reg |= DWC3_GCTL_PRTCAPDIR(mode);
+ reg |= DWC3_GCTL_PRTCAPDIR(mode & DWC3_GCTL_PRTCAP_OTG);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
dwc->current_dr_role = mode;
@@ -191,6 +191,7 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_host_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
@@ -210,12 +211,43 @@ static void __dwc3_set_mode(struct work_struct *work)
* Only perform GCTL.CoreSoftReset when there's DRD role switching.
*/
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
- DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
+ DWC3_VER_IS_PRIOR(DWC31, 190A) || dwc->usb3_phy_reset_quirk) &&
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
+ /*
+ * RK3399 TypeC PHY needs to be powered off and powered on again
+ * for it to apply the correct Type-C plug orientation setting
+ * and reconfigure itself.
+ *
+ * For that purpose we observe complete USB disconnect via
+ * extcon in drd.c and pass it to __dwc3_set_mode as
+ * desired_dr_role == 0.
+ *
+ * We thus handle transitions between three states of
+ * desired_dr_role here:
+ *
+ * - DWC3_GCTL_PRTCAP_HOST
+ * - DWC3_GCTL_PRTCAP_DEVICE
+ * - DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED - almost equivalent to
+ * DWC3_GCTL_PRTCAP_DEVICE, present only to distinguish
+ * disconnected state, and so that set_mode is called when
+ * user plugs in the device to the host.
+ */
+ if (dwc->usb3_phy_powered && dwc->usb3_phy_reset_quirk)
+ for (int j = 0; j < dwc->num_usb3_ports; j++)
+ phy_power_off(dwc->usb3_generic_phy[j]);
+
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ if (dwc->usb3_phy_reset_quirk) {
+ for (int j = 0; j < dwc->num_usb3_ports; j++) {
+ ret = phy_power_on(dwc->usb3_generic_phy[j]);
+ //XXX: bleh
+ dwc->usb3_phy_powered = ret >= 0;
+ }
+ }
+
/*
* Wait for internal clocks to synchronized. DWC_usb31 and
* DWC_usb32 may need at least 50ms (less for DWC_usb3). To
@@ -257,6 +289,7 @@ static void __dwc3_set_mode(struct work_struct *work)
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
dwc3_core_soft_reset(dwc);
dwc3_event_buffers_setup(dwc);
@@ -1844,6 +1877,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_split_quirk = device_property_read_bool(dev,
"snps,dis-split-quirk");
+ dwc->usb3_phy_reset_quirk = device_property_read_bool(dev,
+ "snps,usb3-phy-reset-quirk");
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -2438,6 +2473,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (pm_runtime_suspended(dwc->dev))
break;
dwc3_gadget_suspend(dwc);
@@ -2498,11 +2534,12 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
- dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
+ dwc3_set_prtcap(dwc, dwc->current_dr_role, true);
dwc3_gadget_resume(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
@@ -2566,6 +2603,7 @@ static int dwc3_runtime_checks(struct dwc3 *dwc)
{
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (dwc->connected)
return -EBUSY;
break;
@@ -2604,6 +2642,7 @@ int dwc3_runtime_resume(struct dwc3 *dwc)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (dwc->pending_events) {
pm_runtime_put(dev);
dwc->pending_events = false;
@@ -2628,6 +2667,7 @@ int dwc3_runtime_idle(struct dwc3 *dwc)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
if (dwc3_runtime_checks(dwc))
return -EBUSY;
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -264,6 +264,12 @@
#define DWC3_GCTL_PRTCAP_HOST 1
#define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3
+/* This is not a real register value, but a special state used for
+ * current_dr_role to mean DWC3_GCTL_PRTCAP_DEVICE in disconnected
+ * state. Value is chosen so that masking with register width
+ * produces DWC3_GCTL_PRTCAP_DEVICE value.
+ */
+#define DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED 6
#define DWC3_GCTL_CORESOFTRESET BIT(11)
#define DWC3_GCTL_SOFITPSYNC BIT(10)
@@ -1152,6 +1158,10 @@ struct dwc3_scratchpad_array {
* @sys_wakeup: set if the device may do system wakeup.
* @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2.
+ * @usb3_phy_reset_quirk: set to power cycle the USB3 PHY during mode
+ * changes. Useful on RK3399 that needs this
+ * to apply Type-C orientation changes in
+ * Type-C phy driver.
* @susphy_state: state of DWC3_GUSB2PHYCFG_SUSPHY + DWC3_GUSB3PIPECTL_SUSPHY
* before PM suspend.
* @imod_interval: set the interrupt moderation interval in 250ns
@@ -1392,6 +1402,8 @@ struct dwc3 {
unsigned suspended:1;
unsigned susphy_state:1;
+ unsigned usb3_phy_reset_quirk:1;
+
u16 imod_interval;
int max_cfg_eps;
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -417,15 +417,28 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
static void dwc3_drd_update(struct dwc3 *dwc)
{
- int id;
+ u32 mode = DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED;
+ int ret;
if (dwc->edev) {
- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
- if (id < 0)
- id = 0;
- dwc3_set_mode(dwc, id ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ ret = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+ if (ret > 0)
+ mode = DWC3_GCTL_PRTCAP_HOST;
+
+ if (dwc->usb3_phy_reset_quirk) {
+ /*
+ * With this quirk enabled, we want to pass 0
+ * to dwc3_set_mode to signal no USB connection
+ * state.
+ */
+ ret = extcon_get_state(dwc->edev, EXTCON_USB);
+ if (ret > 0)
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ } else {
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ }
+
+ dwc3_set_mode(dwc, mode);
}
}
@@ -434,9 +447,7 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
{
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
- dwc3_set_mode(dwc, event ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_drd_update(dwc);
return NOTIFY_DONE;
}
@@ -547,8 +558,7 @@ int dwc3_drd_init(struct dwc3 *dwc)
if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
- ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
- &dwc->edev_nb);
+ ret = extcon_register_notifier_all(dwc->edev, &dwc->edev_nb);
if (ret < 0) {
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
--
Armbian

View File

@@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Sat, 2 Mar 2024 14:33:21 +0100
Subject: usb: dwc3: Extend reset quirk support to include role-switch
Originally my reset quirk patch only supported extcon mode changes
via extcon interface. Support role-switch, too.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/dwc3/drd.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -458,7 +458,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
enum usb_role role)
{
struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
- u32 mode;
+ u32 mode = DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED;
switch (role) {
case USB_ROLE_HOST:
@@ -468,6 +468,8 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
mode = DWC3_GCTL_PRTCAP_DEVICE;
break;
default:
+ if (dwc->usb3_phy_reset_quirk)
+ break;
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
mode = DWC3_GCTL_PRTCAP_HOST;
else
@@ -487,6 +489,9 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
spin_lock_irqsave(&dwc->lock, flags);
switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED:
+ role = USB_ROLE_NONE;
+ break;
case DWC3_GCTL_PRTCAP_HOST:
role = USB_ROLE_HOST;
break;
@@ -518,6 +523,8 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
} else {
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
mode = DWC3_GCTL_PRTCAP_DEVICE;
+ if (dwc->usb3_phy_reset_quirk)
+ mode = DWC3_GCTL_PRTCAP_DEVICE_DISCONNECTED;
}
dwc3_set_mode(dwc, mode);
--
Armbian

View File

@@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Sun, 30 Apr 2023 18:54:51 +0200
Subject: usb: dwc3: Track the power state of usb3_generic_phy
We will need to manage power state of this phy inisde set_mode work,
without any ability to perform recovery if power on fails, so we'll
need to track result of power on separately, to be able to balance
the phy on/off calls.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/dwc3/core.c | 7 +++++--
drivers/usb/dwc3/core.h | 2 ++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -931,6 +931,7 @@ static int dwc3_phy_power_on(struct dwc3 *dwc)
goto err_power_off_usb3_phy;
}
+ dwc->usb3_phy_powered = true;
return 0;
err_power_off_usb3_phy:
@@ -951,8 +952,10 @@ static void dwc3_phy_power_off(struct dwc3 *dwc)
{
int i;
- for (i = 0; i < dwc->num_usb3_ports; i++)
- phy_power_off(dwc->usb3_generic_phy[i]);
+ if (dwc->usb3_phy_powered)
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_power_off(dwc->usb3_generic_phy[i]);
+ dwc->usb3_phy_powered = false;
for (i = 0; i < dwc->num_usb2_ports; i++)
phy_power_off(dwc->usb2_generic_phy[i]);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 111111111111..222222222222 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1224,6 +1224,8 @@ struct dwc3 {
u8 num_usb2_ports;
u8 num_usb3_ports;
+ bool usb3_phy_powered;
+
bool phys_ready;
struct ulpi *ulpi;
--
Armbian

View File

@@ -0,0 +1,99 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Mon, 5 Sep 2022 00:56:07 +0200
Subject: usb: typec: altmodes: displayport: Respect DP_CAP_RECEPTACLE bit
DP_CAP_RECEPTACLE swaps the meaning of pin assignments. So eg.
pin assignments for DFP_D plug are stored in DP_CAP_UFP_D_PIN_ASSIGN
bits. Yes, it's slightly confusing. :)
Make the kernel report lack of match in supported pin configurations
between connected ports, so that the user is not confused why their
USB-C dock doesn't have working Alt-DP mode, in case the dock returns
wrong VDO.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/typec/altmodes/displayport.c | 50 +++++++++-
1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -170,11 +170,29 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
/* Account for active cable capabilities */
if (dp->plug_prime)
pin_assign &= DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo);
+
+ /*
+ * The Display Port Alt mode standard is not publicly available,
+ * so this is based on guesswork and real VDOs received from
+ * receptacle based and plug based Type-C alt mode supporting
+ * docks to make configuration work in practice:
+ *
+ * Plug (captive cable) based dock: port=c46 alt=c05
+ * Recpetacle based dock: port=c46 alt=c0045
+ *
+ pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
+ pin_assign &= dp->alt->vdo & DP_CAP_RECEPTACLE ?
+ DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) :
+ DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+ */
break;
default:
break;
}
+ dev_info(&dp->alt->dev, "con=%d pin_assign=%x (port=%x alt=%x)\n",
+ (int)con, (unsigned)pin_assign, dp->port->vdo, dp->alt->vdo);
+
/* Determining the initial pin assignment. */
if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
/* Is USB together with DP preferred */
@@ -787,15 +805,37 @@ int dp_altmode_probe(struct typec_altmode *alt)
struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P);
struct fwnode_handle *fwnode;
struct dp_altmode *dp;
+ u32 port_pins, alt_pins;
/* FIXME: Port can only be DFP_U. */
- /* Make sure we have compatible pin configurations */
- if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
- DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
- !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
+ /*
+ * When port is a receptacle DP_CAP_xFP_D_PIN_ASSIGN macros have the
+ * regular meaning. When the port is a plug, the meaning is swapped.
+ *
+ * Check if we have any matching DFP_D<->UFP_D or UFP_D<->DFP_D pin assignment.
+ */
+ port_pins = port->vdo & DP_CAP_RECEPTACLE ?
+ DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) << 8 :
+ DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) << 8;
+
+ alt_pins = alt->vdo & DP_CAP_RECEPTACLE ?
+ DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) << 8 :
+ DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo) | DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo) << 8;
+
+ /* Can't plug plug into a plug */
+ if (!(port->vdo & DP_CAP_RECEPTACLE) && !(alt->vdo & DP_CAP_RECEPTACLE)) {
+ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (both are reported as plugs!)\n",
+ port->vdo, alt->vdo);
+ return -ENODEV;
+ }
+
+ /* Make sure we have compatiple pin configurations */
+ if (!(port_pins & alt_pins)) {
+ dev_warn(&alt->dev, "Our Alt-DP VDO 0x%06x and peer port VDO 0x%06x are not compatible (no shared pinconf %04x<->%04x (UUDD))\n",
+ port->vdo, alt->vdo, port_pins, alt_pins);
return -ENODEV;
+ }
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
--
Armbian

View File

@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Sat, 18 Feb 2023 00:38:44 +0100
Subject: usb: typec: tcpm: Fix PD devices/capabilities registration
Unregister caps before registering them. Store NULL to the struct
if registration fails, so that next attempt can succeed.
Fixes "sysfs: cannot create duplicate filename
'/devices/virtual/usb_power_delivery/pd1/source-capabilities'" error.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/typec/tcpm/tcpm.c | 37 ++++++++--
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -3111,15 +3111,22 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
struct usb_power_delivery_desc desc = { port->negotiated_rev };
struct usb_power_delivery_capabilities_desc caps = { };
struct usb_power_delivery_capabilities *cap;
+ struct usb_power_delivery *partner_pd;
+
+ if (!port->partner_pd) {
+ partner_pd = usb_power_delivery_register(NULL, &desc);
+ if (IS_ERR(partner_pd))
+ return PTR_ERR(partner_pd);
- if (!port->partner_pd)
- port->partner_pd = usb_power_delivery_register(NULL, &desc);
- if (IS_ERR(port->partner_pd))
- return PTR_ERR(port->partner_pd);
+ port->partner_pd = partner_pd;
+ }
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
caps.role = TYPEC_SOURCE;
+ usb_power_delivery_unregister_capabilities(port->partner_source_caps);
+ port->partner_source_caps = NULL;
+
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
return PTR_ERR(cap);
@@ -3134,15 +3141,22 @@ static int tcpm_register_sink_caps(struct tcpm_port *port)
struct usb_power_delivery_desc desc = { port->negotiated_rev };
struct usb_power_delivery_capabilities_desc caps = { };
struct usb_power_delivery_capabilities *cap;
+ struct usb_power_delivery *partner_pd;
+
+ if (!port->partner_pd) {
+ partner_pd = usb_power_delivery_register(NULL, &desc);
+ if (IS_ERR(partner_pd))
+ return PTR_ERR(partner_pd);
- if (!port->partner_pd)
- port->partner_pd = usb_power_delivery_register(NULL, &desc);
- if (IS_ERR(port->partner_pd))
- return PTR_ERR(port->partner_pd);
+ port->partner_pd = partner_pd;
+ }
memcpy(caps.pdo, port->sink_caps, sizeof(u32) * port->nr_sink_caps);
caps.role = TYPEC_SINK;
+ usb_power_delivery_unregister_capabilities(port->partner_sink_caps);
+ port->partner_sink_caps = NULL;
+
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
return PTR_ERR(cap);
@@ -7205,10 +7219,17 @@ static int tcpm_port_register_pd(struct tcpm_port *port)
port->pds[i] = usb_power_delivery_register(port->dev, &desc);
if (IS_ERR(port->pds[i])) {
ret = PTR_ERR(port->pds[i]);
+ port->pds[i] = NULL;
goto err_unregister;
}
port->pd_list[i]->pd = port->pds[i];
+ usb_power_delivery_unregister_capabilities(port->pd_list[i]->source_cap);
+ port->pd_list[i]->source_cap = NULL;
+
+ usb_power_delivery_unregister_capabilities(port->pd_list[i]->sink_cap);
+ port->pd_list[i]->sink_cap = NULL;
+
if (port->pd_list[i]->source_desc.pdo[0]) {
cap = usb_power_delivery_register_capabilities(port->pds[i],
&port->pd_list[i]->source_desc);
--
Armbian

View File

@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Sun, 14 Aug 2022 16:23:28 +0200
Subject: usb: typec: tcpm: Unregister altmodes before registering new ones
Just in case there are already altmodes registered. Otherwise the
kernel will fail with:
[50554.467172] sysfs: cannot create duplicate filename '/bus/typec/devices/port0-partner.0'
[50554.467198] CPU: 2 PID: 897 Comm: 4-0022 Tainted: G C 5.18.15-1-MANJARO-ARM #1
[50554.467208] Hardware name: Pine64 PinePhonePro (DT)
[50554.467214] Call trace:
[50554.467218] dump_backtrace.part.0+0xcc/0xe0
[50554.467241] show_stack+0x18/0x6c
[50554.467249] dump_stack_lvl+0x64/0x80
[50554.467262] dump_stack+0x18/0x34
[50554.467270] sysfs_warn_dup+0x60/0x7c
[50554.467279] sysfs_do_create_link_sd+0xf0/0x100
[50554.467286] sysfs_create_link+0x24/0x44
[50554.467292] bus_add_device+0x64/0x120
[50554.467304] device_add+0x320/0x890
[50554.467312] device_register+0x20/0x30
[50554.467318] typec_register_altmode+0x1e4/0x35c
[50554.467330] typec_partner_register_altmode+0x10/0x20
[50554.467339] tcpm_pd_rx_handler+0x1804/0x18e0
[50554.467346] kthread_worker_fn+0xa0/0x180
[50554.467357] kthread+0x108/0x10c
[50554.467364] ret_from_fork+0x10/0x20
[50554.467528] typec port0-partner: failed to register alternate mode (-17)
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/typec/tcpm/tcpm.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1863,6 +1863,9 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
return;
for (i = 0; i < modep->altmodes; i++) {
+ typec_unregister_altmode(port->partner_altmode[i]);
+ port->partner_altmode[i] = NULL;
+
altmode = typec_partner_register_altmode(port->partner,
&modep->altmode_desc[i]);
if (IS_ERR(altmode)) {
--
Armbian

View File

@@ -0,0 +1,383 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
Date: Sun, 7 Nov 2021 19:24:40 +0100
Subject: usb: typec: typec-extcon: Add typec -> extcon bridge driver
This bridge connects standard Type C port interfaces for controling
muxes, switches and usb roles to muxes, switches and usb role
drivers controlled via extcon interface.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
drivers/usb/typec/Kconfig | 7 +
drivers/usb/typec/Makefile | 1 +
drivers/usb/typec/typec-extcon.c | 330 ++++++++++
3 files changed, 338 insertions(+)
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -110,6 +110,13 @@ config TYPEC_WUSB3801
If you choose to build this driver as a dynamically linked module, the
module will be called wusb3801.ko.
+config TYPEC_EXTCON
+ tristate "Type-C switch/mux -> extcon interface bridge driver"
+ depends on USB_ROLE_SWITCH
+ help
+ Say Y or M here if your system needs bridging between typec class
+ and extcon interfaces.
+
source "drivers/usb/typec/mux/Kconfig"
source "drivers/usb/typec/altmodes/Kconfig"
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 111111111111..222222222222 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o
obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o
obj-$(CONFIG_TYPEC_RT1719) += rt1719.o
obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o
+obj-$(CONFIG_TYPEC_EXTCON) += typec-extcon.o
obj-$(CONFIG_TYPEC) += mux/
diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/usb/typec/typec-extcon.c
@@ -0,0 +1,330 @@
+/*
+ * typec -> extcon bridge
+ * Copyright (c) 2021 Ondřej Jirman <megi@xff.cz>
+ *
+ * This driver bridges standard type-c interfaces to drivers that
+ * expect extcon interface.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/extcon-provider.h>
+
+struct typec_extcon {
+ struct device *dev;
+
+ /* consumers */
+ struct usb_role_switch *role_sw;
+ struct typec_switch_dev *sw;
+ struct typec_mux_dev *mux;
+
+ /* providers */
+ struct extcon_dev *extcon;
+ struct notifier_block extcon_nb;
+
+ /* cached state from typec controller */
+ enum usb_role role;
+ enum typec_orientation orientation;
+ struct typec_altmode alt;
+ unsigned long mode;
+ bool has_alt;
+ struct mutex lock;
+};
+
+static const unsigned int typec_extcon_cable[] = {
+ EXTCON_DISP_DP,
+
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+
+ EXTCON_CHG_USB_SDP,
+ EXTCON_CHG_USB_CDP,
+ EXTCON_CHG_USB_DCP,
+ EXTCON_CHG_USB_ACA,
+
+ EXTCON_NONE,
+};
+
+static void typec_extcon_set_cable(struct typec_extcon *tce, int id, bool on,
+ union extcon_property_value prop_ss,
+ union extcon_property_value prop_or)
+{
+ union extcon_property_value cur_ss, cur_or;
+ bool prop_diff = false;
+ int ret;
+
+ ret = extcon_get_property(tce->extcon, id,
+ EXTCON_PROP_USB_SS, &cur_ss);
+ if (ret || cur_ss.intval != prop_ss.intval)
+ prop_diff = true;
+
+ ret = extcon_get_property(tce->extcon, id,
+ EXTCON_PROP_USB_TYPEC_POLARITY, &cur_or);
+ if (ret || cur_or.intval != prop_or.intval)
+ prop_diff = true;
+
+ if (!on && extcon_get_state(tce->extcon, id)) {
+ extcon_set_state_sync(tce->extcon, id, false);
+ } else if (on && (!extcon_get_state(tce->extcon, id) || prop_diff)) {
+ extcon_set_state(tce->extcon, id, true);
+ extcon_set_property(tce->extcon, id,
+ EXTCON_PROP_USB_SS, prop_ss);
+ extcon_set_property(tce->extcon, id,
+ EXTCON_PROP_USB_TYPEC_POLARITY, prop_or);
+ extcon_sync(tce->extcon, id);
+ }
+}
+
+static int typec_extcon_sync_extcon(struct typec_extcon *tce)
+{
+ union extcon_property_value prop_ss, prop_or;
+ bool has_dp = false;
+
+ mutex_lock(&tce->lock);
+
+ /* connector is disconnected */
+ if (tce->orientation == TYPEC_ORIENTATION_NONE) {
+ typec_extcon_set_cable(tce, EXTCON_USB, false, prop_ss, prop_or);
+ typec_extcon_set_cable(tce, EXTCON_USB_HOST, false, prop_ss, prop_or);
+ typec_extcon_set_cable(tce, EXTCON_DISP_DP, false, prop_ss, prop_or);
+
+ goto out_unlock;
+ }
+
+ prop_or.intval = tce->orientation == TYPEC_ORIENTATION_NORMAL ? 0 : 1;
+ prop_ss.intval = 0;
+
+ if (tce->has_alt && tce->alt.svid == USB_TYPEC_DP_SID) {
+ switch (tce->mode) {
+ case TYPEC_STATE_SAFE:
+ break;
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ has_dp = true;
+ break;
+ case TYPEC_DP_STATE_D:
+ has_dp = true;
+ fallthrough;
+ case TYPEC_STATE_USB:
+ prop_ss.intval = 1;
+ break;
+ default:
+ dev_err(tce->dev, "unhandled mux mode=%lu\n", tce->mode);
+ break;
+ }
+ }
+
+ typec_extcon_set_cable(tce, EXTCON_USB,
+ tce->role == USB_ROLE_DEVICE, prop_ss, prop_or);
+ typec_extcon_set_cable(tce, EXTCON_USB_HOST,
+ tce->role == USB_ROLE_HOST, prop_ss, prop_or);
+
+ typec_extcon_set_cable(tce, EXTCON_DISP_DP, has_dp, prop_ss, prop_or);
+
+out_unlock:
+ mutex_unlock(&tce->lock);
+ return 0;
+}
+
+static int typec_extcon_sw_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct typec_extcon *tce = typec_switch_get_drvdata(sw);
+
+ dev_dbg(tce->dev, "SW SET: orientation=%d\n", orientation);
+
+ mutex_lock(&tce->lock);
+ tce->orientation = orientation;
+ mutex_unlock(&tce->lock);
+
+ typec_extcon_sync_extcon(tce);
+
+ return 0;
+}
+
+static int typec_extcon_mux_set(struct typec_mux_dev *mux,
+ struct typec_mux_state *state)
+{
+ struct typec_extcon *tce = typec_mux_get_drvdata(mux);
+ struct typec_altmode *alt = state->alt;
+
+ dev_dbg(tce->dev, "MUX SET: state->mode=%lu\n", state->mode);
+ if (alt)
+ dev_dbg(tce->dev, " ...alt: svid=%04hx mode=%d vdo=%08x active=%u\n",
+ alt->svid, alt->mode, alt->vdo, alt->active);
+
+ mutex_lock(&tce->lock);
+ tce->mode = state->mode;
+ tce->has_alt = alt != NULL;
+ if (alt)
+ tce->alt = *alt;
+ mutex_unlock(&tce->lock);
+
+ typec_extcon_sync_extcon(tce);
+
+ return 0;
+}
+
+static int typec_extcon_usb_set_role(struct usb_role_switch *sw,
+ enum usb_role role)
+{
+ struct typec_extcon *tce = usb_role_switch_get_drvdata(sw);
+
+ dev_dbg(tce->dev, "ROLE SET: role=%d\n", role);
+
+ mutex_lock(&tce->lock);
+ tce->role = role;
+ mutex_unlock(&tce->lock);
+
+ typec_extcon_sync_extcon(tce);
+
+ return 0;
+}
+
+static int typec_extcon_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct typec_extcon *tce = container_of(nb, struct typec_extcon, extcon_nb);
+
+ bool sdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_SDP);
+ bool cdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_CDP);
+ bool dcp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_DCP);
+ bool usb = extcon_get_state(tce->extcon, EXTCON_USB);
+ bool usb_host = extcon_get_state(tce->extcon, EXTCON_USB_HOST);
+ bool dp = extcon_get_state(tce->extcon, EXTCON_DISP_DP);
+
+ dev_info(tce->dev, "extcon changed sdp=%d cdp=%d dcp=%d usb=%d usb_host=%d dp=%d\n",
+ sdp, cdp, dcp, usb, usb_host, dp);
+
+ return NOTIFY_OK;
+}
+
+static int typec_extcon_probe(struct platform_device *pdev)
+{
+ struct typec_switch_desc sw_desc = { };
+ struct typec_mux_desc mux_desc = { };
+ struct usb_role_switch_desc role_desc = { };
+ struct device *dev = &pdev->dev;
+ struct typec_extcon *tce;
+ int ret = 0;
+
+ tce = devm_kzalloc(dev, sizeof(*tce), GFP_KERNEL);
+ if (!tce)
+ return -ENOMEM;
+
+ tce->dev = &pdev->dev;
+ mutex_init(&tce->lock);
+ tce->mode = TYPEC_STATE_SAFE;
+
+ sw_desc.drvdata = tce;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = typec_extcon_sw_set;
+
+ tce->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(tce->sw))
+ return dev_err_probe(dev, PTR_ERR(tce->sw),
+ "Error registering typec switch\n");
+
+ mux_desc.drvdata = tce;
+ mux_desc.fwnode = dev->fwnode;
+ mux_desc.set = typec_extcon_mux_set;
+
+ tce->mux = typec_mux_register(dev, &mux_desc);
+ if (IS_ERR(tce->mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(tce->mux),
+ "Error registering typec mux\n");
+ goto err_sw;
+ }
+
+ role_desc.driver_data = tce;
+ role_desc.fwnode = dev->fwnode;
+ role_desc.name = fwnode_get_name(dev->fwnode);
+ role_desc.set = typec_extcon_usb_set_role;
+
+ tce->role_sw = usb_role_switch_register(dev, &role_desc);
+ if (IS_ERR(tce->role_sw)) {
+ ret = dev_err_probe(dev, PTR_ERR(tce->role_sw),
+ "Error registering USB role switch\n");
+ goto err_mux;
+ }
+
+ tce->extcon = devm_extcon_dev_allocate(dev, typec_extcon_cable);
+ if (IS_ERR(tce->extcon)) {
+ ret = PTR_ERR(tce->extcon);
+ goto err_role;
+ }
+
+ ret = devm_extcon_dev_register(dev, tce->extcon);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to register extcon device\n");
+ goto err_role;
+ }
+
+ extcon_set_property_capability(tce->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tce->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+
+ tce->extcon_nb.notifier_call = typec_extcon_notifier;
+ ret = devm_extcon_register_notifier_all(dev, tce->extcon, &tce->extcon_nb);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to register extcon notifier\n");
+ goto err_role;
+ }
+
+ return 0;
+
+err_role:
+ usb_role_switch_unregister(tce->role_sw);
+err_mux:
+ typec_mux_unregister(tce->mux);
+err_sw:
+ typec_switch_unregister(tce->sw);
+ return ret;
+}
+
+static void typec_extcon_remove(struct platform_device *pdev)
+{
+ struct typec_extcon *tce = platform_get_drvdata(pdev);
+
+ usb_role_switch_unregister(tce->role_sw);
+ typec_mux_unregister(tce->mux);
+ typec_switch_unregister(tce->sw);
+}
+
+static struct of_device_id typec_extcon_of_match_table[] = {
+ { .compatible = "linux,typec-extcon-bridge" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, typec_extcon_of_match_table);
+
+static struct platform_driver typec_extcon_driver = {
+ .driver = {
+ .name = "typec-extcon",
+ .of_match_table = typec_extcon_of_match_table,
+ },
+ .probe = typec_extcon_probe,
+ .remove = typec_extcon_remove,
+};
+
+module_platform_driver(typec_extcon_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ondrej Jirman <megi@xff.cz>");
+MODULE_DESCRIPTION("typec -> extcon bridge driver");
--
Armbian