Some EC firmwares on Trogdor/Strongbad boards don't properly indicate the state of DP HPD on a type-c port. Instead, the EC only indicates that DP mode is entered or exited for a type-c port. To make matters worse, on these firmwares the DP signal is muxed between two USB type-c connectors, so we can't use the HPD state to figure out which type-c port is actually displaying DP. Read the state of the EC's analog mux from the hpd notification callback to figure out which type-c port is displaying DP. This circumvents the entire host command/message interface, because it doesn't work all the time. Only do this when we have the mux-gpios property in DT, indicating that we have to read the EC gpio state to figure this out. For now we only support a single gpio "bit", so there can only be two USB type-c ports. Cc: Prashant Malani <pmalani@xxxxxxxxxxxx> Cc: Benson Leung <bleung@xxxxxxxxxxxx> Cc: Tzung-Bi Shih <tzungbi@xxxxxxxxxx> Cc: <chrome-platform@xxxxxxxxxxxxxxx> Cc: Pin-yen Lin <treapking@xxxxxxxxxxxx> Signed-off-by: Stephen Boyd <swboyd@xxxxxxxxxxxx> --- drivers/platform/chrome/cros_typec_switch.c | 33 ++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_typec_switch.c b/drivers/platform/chrome/cros_typec_switch.c index c22c2531327a..edd628eab7da 100644 --- a/drivers/platform/chrome/cros_typec_switch.c +++ b/drivers/platform/chrome/cros_typec_switch.c @@ -8,6 +8,7 @@ #include <linux/acpi.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> @@ -26,6 +27,7 @@ struct cros_typec_dp_bridge { /* TODO: Add mutex lock to protect active_port with respect to drm/typec framework calls */ struct cros_typec_port *active_port; + struct gpio_desc *mux_gpio; struct cros_typec_switch_data *sdata; size_t max_lanes; bool hpd_enabled; @@ -453,6 +455,29 @@ static void cros_typec_dp_bridge_hpd_disable(struct drm_bridge *bridge) typec_dp_bridge->hpd_enabled = false; } +static void cros_typec_dp_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) +{ + struct cros_typec_dp_bridge *typec_dp_bridge; + struct cros_typec_switch_data *sdata; + struct gpio_desc *mux_gpio; + int mux_val; + + typec_dp_bridge = bridge_to_cros_typec_dp_bridge(bridge); + mux_gpio = typec_dp_bridge->mux_gpio; + + /* + * Some ECs don't notify AP when HPD goes high or low so we have to + * read the EC GPIO that controls the mux to figure out which type-c + * port is connected to DP. + */ + if (mux_gpio) { + sdata = typec_dp_bridge->sdata; + mux_val = gpiod_get_value_cansleep(mux_gpio); + typec_dp_bridge->active_port = sdata->ports[mux_val]; + } +} + static const struct drm_bridge_funcs cros_typec_dp_bridge_funcs = { .attach = cros_typec_dp_bridge_attach, .atomic_check = cros_typec_dp_bridge_atomic_check, @@ -461,6 +486,7 @@ static const struct drm_bridge_funcs cros_typec_dp_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .hpd_enable = cros_typec_dp_bridge_hpd_enable, .hpd_disable = cros_typec_dp_bridge_hpd_disable, + .hpd_notify = cros_typec_dp_bridge_hpd_notify, }; static int cros_typec_register_dp_bridge(struct cros_typec_switch_data *sdata, @@ -478,6 +504,10 @@ static int cros_typec_register_dp_bridge(struct cros_typec_switch_data *sdata, typec_dp_bridge->sdata = sdata; sdata->typec_dp_bridge = typec_dp_bridge; + typec_dp_bridge->mux_gpio = devm_gpiod_get_optional(dev, "mux", 0); + if (IS_ERR(typec_dp_bridge->mux_gpio)) + return PTR_ERR(typec_dp_bridge->mux_gpio); + num_lanes = fwnode_property_count_u32(fwnode, "data-lanes"); if (num_lanes < 0) num_lanes = 4; @@ -488,7 +518,8 @@ static int cros_typec_register_dp_bridge(struct cros_typec_switch_data *sdata, bridge->funcs = &cros_typec_dp_bridge_funcs; bridge->of_node = dev->of_node; bridge->type = DRM_MODE_CONNECTOR_DisplayPort; - bridge->ops |= DRM_BRIDGE_OP_HPD; + if (!fwnode_property_present(dev_fwnode(dev), "no-hpd")) + bridge->ops |= DRM_BRIDGE_OP_HPD; return devm_drm_bridge_add(dev, bridge); } -- https://chromeos.dev