diff --git a/config/kernel/linux-rockchip64-edge.config b/config/kernel/linux-rockchip64-edge.config index 582379a0d..7786a2d98 100644 --- a/config/kernel/linux-rockchip64-edge.config +++ b/config/kernel/linux-rockchip64-edge.config @@ -2635,6 +2635,7 @@ CONFIG_TYPEC_UCSI=m CONFIG_UCSI_CCG=m CONFIG_TYPEC_TPS6598X=m CONFIG_TYPEC_HD3SS3220=m +CONFIG_TYPEC_EXTCON=m CONFIG_TYPEC_MUX_FSA4480=m CONFIG_TYPEC_MUX_PI3USB30532=m CONFIG_TYPEC_DP_ALTMODE=m diff --git a/patch/kernel/archive/rockchip64-6.16/board-nanopc-t4-add-typec-dp.patch b/patch/kernel/archive/rockchip64-6.16/board-nanopc-t4-add-typec-dp.patch index 7a8a5204b..02ac318fd 100644 --- a/patch/kernel/archive/rockchip64-6.16/board-nanopc-t4-add-typec-dp.patch +++ b/patch/kernel/archive/rockchip64-6.16/board-nanopc-t4-add-typec-dp.patch @@ -1,12 +1,12 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: tonymac32 -Date: Wed, 17 Feb 2021 00:54:00 -0500 -Subject: Patching something +From: hyx0329 +Date: Mon, 2 Jun 2025 07:59:27 +0000 +Subject: Enable type-c dp alt mode for nanopc t4 in the device tree -Signed-off-by: tonymac32 +Signed-off-by: hyx0329 --- - arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 96 ++++++++++ - 1 file changed, 96 insertions(+) + arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 116 ++++++++++ + 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 index 111111111111..222222222222 100644 @@ -20,24 +20,34 @@ index 111111111111..222222222222 100644 #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>; }; - }; - -+&cdn_dp { -+ status = "okay"; -+ extcon = <&fusb0>; -+ phys = <&tcphy0_dp>; ++ ++ typec_extcon_bridge: typec-extcon { ++ compatible = "linux,typec-extcon-bridge"; ++ usb-role-switch; ++ orientation-switch; ++ mode-switch; ++ svid = /bits/ 16 <0xff01>; ++ }; +}; + ++&cdn_dp { ++ status = "okay"; ++ extcon = <&typec_extcon_bridge>; ++ phys = <&tcphy0_dp>; + }; + &cpu_thermal { - trips { - cpu_warm: cpu_warm { -@@ -94,6 +101,50 @@ map3 { +@@ -94,6 +109,59 @@ map3 { }; }; +&fusb0 { ++ usb-role-switch = <&typec_extcon_bridge>; ++ extcon = <&typec_extcon_bridge>; + + connector { + compatible = "usb-c-connector"; @@ -48,17 +58,24 @@ index 111111111111..222222222222 100644 + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <5000000>; -+ -+ extcon-cables = <1 2 5 6 9 10 12 44>; -+ typec-altmodes = <0xff01 1 0x001c0000 1>; -+ ++ ++ mode-switch = <&typec_extcon_bridge>; ++ orientation-switch = <&typec_extcon_bridge>; ++ ++ altmodes { ++ dp { ++ svid = /bits/ 16 <0xff01>; ++ vdo = <0x1c46>; ++ }; ++ }; ++ + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + usb_con_hs: endpoint { -+ remote-endpoint = ++ remote-endpoint = + <&u2phy0_typec_hs>; + }; + }; @@ -66,30 +83,30 @@ index 111111111111..222222222222 100644 + reg = <1>; + + usb_con_ss: endpoint { -+ remote-endpoint = ++ remote-endpoint = + <&tcphy0_typec_ss>; + }; + }; + port@2 { + reg = <2>; + usb_con_dp: endpoint { -+ remote-endpoint = ++ remote-endpoint = + <&tcphy0_typec_dp>; + }; + }; + }; -+ }; ++ }; +}; + &pcie0 { ep-gpios = <&gpio2 RK_PA4 GPIO_ACTIVE_HIGH>; num-lanes = <4>; -@@ -114,12 +165,57 @@ &sdhci { +@@ -114,12 +182,60 @@ &sdhci { mmc-hs400-enhanced-strobe; }; +&tcphy0 { -+ extcon = <&fusb0>; ++ extcon = <&typec_extcon_bridge>; + status = "okay"; +}; + @@ -110,7 +127,8 @@ index 111111111111..222222222222 100644 +}; + +&u2phy0 { -+ extcon = <&fusb0>; ++ extcon = <&typec_extcon_bridge>; ++ extcon,ignore-usb; /* let extcon handle role switch */ +}; + &u2phy0_host { @@ -138,7 +156,9 @@ index 111111111111..222222222222 100644 +}; + +&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 { diff --git a/patch/kernel/archive/rockchip64-6.16/board-pbp-add-dp-alt-mode.patch b/patch/kernel/archive/rockchip64-6.16/board-pbp-add-dp-alt-mode.patch index d171b3ff9..e410e4d8f 100644 --- a/patch/kernel/archive/rockchip64-6.16/board-pbp-add-dp-alt-mode.patch +++ b/patch/kernel/archive/rockchip64-6.16/board-pbp-add-dp-alt-mode.patch @@ -1,417 +1,104 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dan Johansen -Date: Tue, 2 Jun 2020 20:20:29 +0200 -Subject: add-dp-alt-mode-to-PBP +From: hyx0329 +Date: Mon, 2 Jun 2025 07:33:51 +0000 +Subject: Add dp alt mode to pinebook pro +Signed-off-by: hyx0329 --- - arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 5 + - drivers/phy/rockchip/phy-rockchip-typec.c | 17 ++ - drivers/usb/typec/altmodes/displayport.c | 52 +++- - drivers/usb/typec/tcpm/tcpm.c | 138 +++++++++- - 4 files changed, 209 insertions(+), 3 deletions(-) + arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | 36 +++++++++- + 1 file changed, 33 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 index 111111111111..222222222222 100644 --- a/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 { - - &emmc_phy { - status = "okay"; -+ extcon = <&fusb0>; +@@ -373,6 +373,14 @@ mains_charger: dc-charger { + pinctrl-names = "default"; + pinctrl-0 = <&dc_det_pin>; + }; ++ ++ typec_extcon_bridge: typec-extcon { ++ compatible = "linux,typec-extcon-bridge"; ++ usb-role-switch; ++ orientation-switch; ++ mode-switch; ++ svid = /bits/ 16 <0xff01>; ++ }; }; - &gpu { -@@ -705,6 +706,9 @@ connector { - ; - try-power-role = "sink"; + &cpu_b0 { +@@ -399,6 +407,12 @@ &cpu_l3 { + cpu-supply = <&vdd_cpu_l>; + }; -+ extcon-cables = <1 2 5 6 9 10 12 44>; -+ typec-altmodes = <0xff01 1 0x001c0000 1>; ++&cdn_dp { ++ 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 = +- ; ++ ; + source-pdos = +- ; ++ ; + try-power-role = "sink"; ++ mode-switch = <&typec_extcon_bridge>; ++ orientation-switch = <&typec_extcon_bridge>; ++ ++ altmodes { ++ dp { ++ svid = /bits/ 16 <0xff01>; ++ vdo = <0x1c46>; ++ }; ++ }; + ports { #address-cells = <1>; - #size-cells = <0>; -@@ -970,6 +974,7 @@ spiflash: flash@0 { +@@ -970,6 +995,7 @@ spiflash: flash@0 { }; &tcphy0 { -+ extcon = <&fusb0>; ++ extcon = <&typec_extcon_bridge>; status = "okay"; }; -diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c -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 - #include - #include -+#include - #include - #include - #include -@@ -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); - } +@@ -1003,6 +1029,8 @@ &tsadc { - pm_runtime_enable(dev); -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,6 +9,8 @@ - */ + &u2phy0 { + status = "okay"; ++ extcon = <&typec_extcon_bridge>; ++ extcon,ignore-usb; - #include -+#include -+#include - #include - #include - #include -@@ -74,6 +76,8 @@ struct dp_altmode { - struct typec_altmode *plug_prime; + u2phy0_otg: otg-port { + status = "okay"; +@@ -1079,7 +1107,9 @@ &usbdrd3_0 { }; -+void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect); -+ - static int dp_altmode_notify(struct dp_altmode *dp) - { - unsigned long conf; -@@ -82,7 +86,9 @@ static int dp_altmode_notify(struct dp_altmode *dp) - 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; - } + &usbdrd_dwc3_0 { +- dr_mode = "host"; ++ dr_mode = "otg"; ++ extcon = <&typec_extcon_bridge>; ++ snps,usb3-phy-reset-quirk; + status = "okay"; + }; -@@ -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 - #include - #include -+#include - #include - #include - #include -@@ -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 diff --git a/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4-lts.dts b/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4-lts.dts index c79940457..1ae712ac0 100644 --- a/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4-lts.dts +++ b/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4-lts.dts @@ -89,7 +89,6 @@ pinctrl-names = "default"; pinctrl-0 = <&vcc5v0_typec_en>; regulator-name = "vbus-5v"; - regulator-always-on; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; 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 { @@ -497,7 +503,7 @@ &cdn_dp { status = "okay"; - extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>; phys = <&tcphy0_dp>; }; @@ -785,6 +791,8 @@ interrupt-parent = <&gpio1>; interrupts = ; vbus-supply = <&vbus_typec>; + usb-role-switch = <&typec_extcon_bridge>; + extcon = <&typec_extcon_bridge>; status = "okay"; connector { @@ -798,9 +806,17 @@ source-pdos = ; try-power-role = "sink"; + mode-switch = <&typec_extcon_bridge>; + orientation-switch = <&typec_extcon_bridge>; - extcon-cables = <1 2 5 6 9 10 12 44>; - typec-altmodes = <0xff01 1 0x001c0000 1>; + altmodes { + 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 { #address-cells = <1>; @@ -984,7 +1000,7 @@ }; &tcphy0 { - extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>; status = "okay"; }; @@ -1010,6 +1026,8 @@ &u2phy0 { status = "okay"; + extcon = <&typec_extcon_bridge>; + extcon,ignore-usb; /* u2phy must not handle role switch */ u2phy0_otg: otg-port { status = "okay"; @@ -1043,7 +1061,6 @@ &usbdrd3_0 { status = "okay"; - extcon = <&fusb0>; }; &usbdrd3_1 { @@ -1052,6 +1069,10 @@ &usbdrd_dwc3_0 { 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 { diff --git a/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4.dts b/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4.dts index 3d85bd051..3a1b6d2b4 100644 --- a/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4.dts +++ b/patch/kernel/archive/rockchip64-6.16/dt/rk3399-orangepi-4.dts @@ -67,7 +67,6 @@ pinctrl-names = "default"; pinctrl-0 = <&vcc5v0_typec_en>; regulator-name = "vbus_typec"; - regulator-always-on; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; vin-supply = <&vcc5v0_sys>; @@ -334,6 +333,14 @@ */ 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 { @@ -456,7 +463,7 @@ &cdn_dp { status = "okay"; - extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>; phys = <&tcphy0_dp>; }; @@ -740,6 +747,8 @@ interrupt-parent = <&gpio1>; interrupts = ; vbus-supply = <&vbus_typec>; + usb-role-switch = <&typec_extcon_bridge>; + extcon = <&typec_extcon_bridge>; status = "okay"; connector { @@ -753,9 +762,17 @@ source-pdos = ; try-power-role = "sink"; + mode-switch = <&typec_extcon_bridge>; + orientation-switch = <&typec_extcon_bridge>; - extcon-cables = <1 2 5 6 9 10 12 44>; - typec-altmodes = <0xff01 1 0x001c0000 1>; + altmodes { + 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 { #address-cells = <1>; @@ -949,7 +966,7 @@ }; &tcphy0 { - extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>; status = "okay"; }; @@ -975,6 +992,8 @@ &u2phy0 { status = "okay"; + extcon = <&typec_extcon_bridge>; + extcon,ignore-usb; /* u2phy must not handle role switch */ u2phy0_otg: otg-port { status = "okay"; @@ -1007,7 +1026,6 @@ &usbdrd3_0 { status = "okay"; - extcon = <&fusb0>; }; &usbdrd3_1 { @@ -1016,6 +1034,10 @@ &usbdrd_dwc3_0 { 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 { diff --git a/patch/kernel/archive/rockchip64-6.16/dt/rk3399-tinker-2.dts b/patch/kernel/archive/rockchip64-6.16/dt/rk3399-tinker-2.dts index e929de3e2..996ef1ad1 100644 --- a/patch/kernel/archive/rockchip64-6.16/dt/rk3399-tinker-2.dts +++ b/patch/kernel/archive/rockchip64-6.16/dt/rk3399-tinker-2.dts @@ -5,7 +5,9 @@ */ /dts-v1/; +#include #include +#include #include #include "rk3399.dtsi" #include "rk3399-op1.dtsi" @@ -17,7 +19,7 @@ chosen { stdout-path = "serial2:1500000n8"; }; - + board_info: board-info { compatible = "board-info"; @@ -34,38 +36,54 @@ pmic-reset = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>; }; - + clkin_gmac: external-gmac-clock { compatible = "fixed-clock"; clock-frequency = <125000000>; clock-output-names = "clkin_gmac"; #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 { compatible = "gpio-leds"; pwr-led { gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>; + color = ; linux,default-trigger = "default-on"; retain-state-suspended = <1>; }; act-led { gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>; + color = ; linux,default-trigger="mmc0"; }; rsv-led { gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_HIGH>; + color = ; linux,default-trigger="heartbeat"; }; }; - + vcc_lcd: vcc-lcd { compatible = "regulator-fixed"; regulator-name = "vcc_lcd"; - gpio = <&gpio4 30 GPIO_ACTIVE_HIGH>; + gpio = <&gpio4 RK_PD6 GPIO_ACTIVE_HIGH>; startup-delay-us = <20000>; enable-active-high; regulator-min-microvolt = <3300000>; @@ -73,7 +91,7 @@ regulator-boot-on; vin-supply = <&vcc5v0_sys>; }; - + vcc3v3_sys: vcc3v3-sys { compatible = "regulator-fixed"; regulator-name = "vcc3v3_sys"; @@ -101,14 +119,14 @@ regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; }; - + vcc_phy: vcc-phy-regulator { compatible = "regulator-fixed"; regulator-name = "vcc_phy"; regulator-always-on; regulator-boot-on; }; - + vbus_typec: vbus-5vout { compatible = "regulator-fixed"; enable-active-high; @@ -124,7 +142,7 @@ regulator-off-in-suspend; }; }; - + vdd_log: vdd-log { compatible = "pwm-regulator"; pwms = <&pwm2 0 25000 1>; @@ -138,18 +156,27 @@ rockchip,pwm_id= <2>; rockchip,pwm_voltage = <900000>; }; - + xin32k: xin32k { compatible = "fixed-clock"; clock-frequency = <32768>; clock-output-names = "xin32k"; #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 { status = "okay"; - extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>; + phys = <&tcphy0_dp>; }; &cpu_l0 { @@ -189,7 +216,7 @@ phy-mode = "rgmii"; pinctrl-names = "default"; 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-delays-us = <0 16000 72000>; tx_delay = <0x25>; @@ -414,7 +441,7 @@ regulator-always-on; regulator-boot-on; status = "okay"; - + regulator-initial-state = <3>; regulator-state-mem { regulator-off-in-suspend; @@ -422,7 +449,7 @@ }; }; -&i2c3 { +&i2c3 { status = "okay"; }; @@ -439,8 +466,10 @@ interrupt-parent = <&gpio1>; interrupts = ; vbus-supply = <&vbus_typec>; + usb-role-switch = <&typec_extcon_bridge>; + extcon = <&typec_extcon_bridge>; status = "okay"; - + connector { compatible = "usb-c-connector"; data-role = "dual"; @@ -448,13 +477,21 @@ op-sink-microwatt = <1000000>; power-role = "dual"; sink-pdos = - ; + ; source-pdos = - ; + ; try-power-role = "sink"; + mode-switch = <&typec_extcon_bridge>; + orientation-switch = <&typec_extcon_bridge>; - extcon-cables = <1 2 5 6 9 10 12 44>; - typec-altmodes = <0xff01 1 0x001c0000 1>; + altmodes { + 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 { #address-cells = <1>; @@ -488,12 +525,8 @@ }; }; }; - - - - }; - + vdd_gpu: vdd_gpu@60 { compatible = "fcs,fan53200"; reg = <0x60>; @@ -510,16 +543,15 @@ regulator-always-on; regulator-boot-on; status = "okay"; - + regulator-initial-state = <3>; regulator-state-mem { regulator-off-in-suspend; }; }; -}; +}; &i2c8 { - m24c08: m24c08@50 { compatible = "atmel,24c08"; reg = <0x50>; @@ -541,7 +573,7 @@ }; &pcie0 { - ep-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; + ep-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; num-lanes = <4>; pinctrl-names = "default"; pinctrl-0 = <&pcie_clkreqn_cpm>; @@ -594,7 +626,7 @@ }; &tcphy0 { - extcon = <&fusb0>; + extcon = <&typec_extcon_bridge>; status = "okay"; }; @@ -628,6 +660,8 @@ &u2phy0 { status = "okay"; + extcon = <&typec_extcon_bridge>; + extcon,ignore-usb; /* u2phy must not handle role switch */ u2phy0_otg: otg-port { status = "okay"; @@ -664,7 +698,10 @@ &usbdrd_dwc3_0 { 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 { @@ -697,12 +734,23 @@ }; &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_int_l: pmic-int-l { rockchip,pins = - <1 18 RK_FUNC_GPIO &pcfg_pull_up>, - <0 9 RK_FUNC_GPIO &pcfg_pull_none>; /* GPIO0_B1 */ + <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>, + <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>; }; vsel1_gpio: vsel1-gpio { rockchip,pins = @@ -711,7 +759,7 @@ vsel2_gpio: vsel2-gpio { rockchip,pins = <1 14 RK_FUNC_GPIO &pcfg_pull_down>; - }; + }; }; usb2 { @@ -720,26 +768,12 @@ <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; }; }; - + usb-typec { vcc5v0_typec0_en_pin: vcc5v0-typec0-en-pin { 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 { @@ -748,4 +782,12 @@ &vopb_mmu { status = "okay"; -}; +}; + +&vopl { + status = "okay"; +}; + +&vopl_mmu { + status = "okay"; +}; diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch new file mode 100644 index 000000000..c970d2a6a --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-Revert-usb-typec-tcpm-unregister-existing-source-cap.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-notify-typec-dp-hpd-state-through-extcon.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-notify-typec-dp-hpd-state-through-extcon.patch new file mode 100644 index 000000000..c11b8ca48 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-notify-typec-dp-hpd-state-through-extcon.patch @@ -0,0 +1,111 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: hyx0329 +Date: Tue, 3 Jun 2025 12:14:37 +0000 +Subject: notify typec dp hpd state through extcon + +Signed-off-by: hyx0329 +--- + 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 ++#include ++#include + #include + #include ++#include + #include + #include + #include +@@ -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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-phy-rockchip-inno-usb2-Decrease-delay-between-po.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-phy-rockchip-inno-usb2-Decrease-delay-between-po.patch new file mode 100644 index 000000000..5940d4cae --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-phy-rockchip-inno-usb2-Decrease-delay-between-po.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-rockchip-inno-usb2-More-robust-charger-detection.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-rockchip-inno-usb2-More-robust-charger-detection.patch new file mode 100644 index 000000000..e3f56b4dc --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-rockchip-inno-usb2-More-robust-charger-detection.patch @@ -0,0 +1,113 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-rockchip-naneng-Add-fallback-for-old-DTs.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-rockchip-naneng-Add-fallback-for-old-DTs.patch new file mode 100644 index 000000000..609c8531a --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-phy-rockchip-naneng-Add-fallback-for-old-DTs.patch @@ -0,0 +1,244 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-dwc3-Extend-reset-quirk-support-to-include-role-.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-dwc3-Extend-reset-quirk-support-to-include-role-.patch new file mode 100644 index 000000000..82c665cb1 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-dwc3-Extend-reset-quirk-support-to-include-role-.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch new file mode 100644 index 000000000..c6c998415 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-dwc3-Track-the-power-state-of-usb3_generic_phy.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch new file mode 100644 index 000000000..2d09619fe --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-altmodes-displayport-Respect-DP_CAP_RECEPT.patch @@ -0,0 +1,99 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch new file mode 100644 index 000000000..dcaf37392 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-tcpm-Fix-PD-devices-capabilities-registrat.patch @@ -0,0 +1,95 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch new file mode 100644 index 000000000..42b6d7ce3 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-tcpm-Unregister-altmodes-before-registerin.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +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 +--- + 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 + diff --git a/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch new file mode 100644 index 000000000..3965ce67d --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.16/rk3399-usbc-usb-typec-typec-extcon-Add-typec-extcon-bridge-drive.patch @@ -0,0 +1,383 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= +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 +--- + 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 ++ * ++ * This driver bridges standard type-c interfaces to drivers that ++ * expect extcon interface. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 "); ++MODULE_DESCRIPTION("typec -> extcon bridge driver"); +-- +Armbian +