From a1bbffd1cbb883be7fe3da1d09c29d57cfbeb2da Mon Sep 17 00:00:00 2001 From: Dan Johansen Date: Tue, 2 Jun 2020 20:20:29 +0200 Subject: [PATCH] add-dp-alt-mode-to-PBP --- .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 5 + drivers/phy/rockchip/phy-rockchip-typec.c | 17 +++ drivers/usb/typec/altmodes/displayport.c | 58 +++++++- drivers/usb/typec/bus.c | 8 +- drivers/usb/typec/tcpm/tcpm.c | 139 +++++++++++++++++- 5 files changed, 221 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts index c49982dfd8fc..66cf08e8506f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts @@ -374,6 +374,7 @@ mains_charger: dc-charger { &cdn_dp { status = "okay"; + extcon = <&fusb0>; }; &cpu_b0 { @@ -708,6 +709,9 @@ connector { ; try-power-role = "sink"; + extcon-cables = <1 2 5 6 9 10 12 44>; + typec-altmodes = <0xff01 1 0x001c0000 1>; + ports { #address-cells = <1>; #size-cells = <0>; @@ -958,6 +962,7 @@ spiflash: flash@0 { }; &tcphy0 { + extcon = <&fusb0>; status = "okay"; }; diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 24563160197f..f5b497b4b97e 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 @@ -1160,6 +1161,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); diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 0edfb89e04a8..40dd68c20159 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -9,6 +9,8 @@ */ #include +#include +#include #include #include #include diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index ba1387ab5..4d2eaeab1 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -78,7 +78,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; } @@ -154,6 +156,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"); @@ -210,6 +252,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; default: break; @@ -520,8 +564,14 @@ int dp_altmode_probe(struct typec_altmode *alt) if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) & DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) && !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) & - DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) - return -ENODEV; + DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) { + dev_err(&alt->dev, "No compatible pin configuration found:"\ + "%04lx -> %04lx, %04lx <- %04lx", + DP_CAP_DFP_D_PIN_ASSIGN(port->vdo), DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo), + DP_CAP_UFP_D_PIN_ASSIGN(port->vdo), DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)); + return -ENODEV; + } + ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group); if (ret) diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index e8ddb81cb6df..cbc01d73739c 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -154,8 +154,14 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit); */ void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) { - struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; + struct typec_altmode *pdev; + WARN_ONCE(!adev, "typec bus attention: adev is NULL!"); + WARN_ONCE(!to_altmode(adev)->partner, "typec bus attention: partner is NULL!"); + if(!adev || !to_altmode(adev)->partner) { + return; + } + pdev = &to_altmode(adev)->partner->adev; if (pdev->ops && pdev->ops->attention) pdev->ops->attention(pdev, vdo); } diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 82b19ebd7838..6f00b17afc15 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 @@ -476,6 +477,12 @@ struct tcpm_port { * SNK_READY for non-pd link. */ bool slow_charger_loop; + +#ifdef CONFIG_EXTCON + struct extcon_dev *extcon; + unsigned int *extcon_cables; +#endif + #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -607,6 +613,35 @@ static void tcpm_debugfs_exit(const struct tcpm_port *port) { } #endif +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 type, const struct pd_message *msg) @@ -834,6 +869,8 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, typec_set_data_role(port->typec_port, data); typec_set_pwr_role(port->typec_port, role); + tcpm_update_extcon_data(port, attached); + return 0; } @@ -1044,7 +1081,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, paltmode->mode = i; paltmode->vdo = le32_to_cpu(payload[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); @@ -1064,7 +1101,9 @@ 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; } } @@ -1167,9 +1207,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, 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[0] = VDO(svid, 1, svdm_version, CMD_DISCOVER_MODES); rlen = 1; } else { + tcpm_log(port, "Got all patner modes, registering"); tcpm_register_partner_altmodes(port); } break; @@ -2693,6 +2735,7 @@ static int tcpm_src_attach(struct tcpm_port *port) static void tcpm_typec_disconnect(struct tcpm_port *port) { if (port->connected) { + tcpm_update_extcon_data(port, false); typec_unregister_partner(port->partner); port->partner = NULL; port->connected = false; @@ -2750,6 +2793,8 @@ static void tcpm_detach(struct tcpm_port *port) port->hard_reset_count = 0; tcpm_reset_port(port); + + tcpm_update_extcon_data(port, false); } static void tcpm_src_detach(struct tcpm_port *port) @@ -4424,6 +4469,64 @@ void tcpm_tcpc_reset(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); +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) { @@ -4434,6 +4537,23 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, 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. @@ -4766,6 +4886,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) goto out_destroy_wq; 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.fwnode = tcpc->fwnode; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ @@ -4793,6 +4924,12 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) goto out_role_sw_put; } + 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); -- 2.26.2 diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index c1d8c23ba..bb08742b3 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -68,6 +68,8 @@ struct dp_altmode { struct fwnode_handle *connector_fwnode; }; +void dp_altmode_update_extcon(struct dp_altmode *dp, bool disconnect); + static int dp_altmode_notify(struct dp_altmode *dp) { unsigned long conf;