diff mbox series

[6/7] platform/chrome: cros_ec_typec: Add support for DP altmode via drm_bridge

Message ID 20250416000208.3568635-7-swboyd@chromium.org (mailing list archive)
State New
Headers show
Series platform/chrome: Support for USB DP altmode muxing w/ DT | expand

Commit Message

Stephen Boyd April 16, 2025, 12:02 a.m. UTC
On Trogdor platforms, the USB DP altmode is entered and exited by the EC
depending on if DP altmode is possible and if HPD is asserted for a
port. Trogdor has two USB-C connectors but the AP only supports one DP
controller, so the first USB-C connector to assert HPD "wins". The DP
controller on the AP is fixed to output two lanes DP that goes to an
analog mux that steers the DP lanes to one of the two USB-C connectors.
The HPD state in the DP altmode is "captured" by the EC and redriven
from a GPIO on the EC to the AP's GPIO that is muxed to the DisplayPort
controller inside the AP SoC. This allows both HPD high/low and HPD IRQ
to be signaled from the EC as well as making DP altmode possible on
either USB-C connector except at the same time.

Add a drm_bridge to the ChromeOS EC driver to represent this analog mux
on Trogdor and teach the kernel that DP altmode is using this DP
controller on the AP. When the DT node has a graph binding, we assume
that we're muxing DP to one of many USB-C connectors and we terminate
the bridge chain here. In almost all cases we want this bridge to be the
one that signals HPD because the EC is the one managing HPD and
redriving the GPIO, except for in the case that the DP altmode driver is
enabled in which case HPD will be signaled with
drm_bridge_connector_oob_hotplug_event(). Unfortunately Trogdor EC
firmwares have a bug where HPD state isn't discoverable properly, so we
skip signaling HPD in that case if the "no-hpd" property exists in the
node.

Cc: Benson Leung <bleung@chromium.org>
Cc: Tzung-Bi Shih <tzungbi@kernel.org>
Cc: <chrome-platform@lists.linux.dev>
Cc: Pin-yen Lin <treapking@chromium.org>
Cc: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Cc: Ɓukasz Bartosik <ukaszb@chromium.org>
Cc: Jameson Thies <jthies@google.com>
Cc: Andrei Kuchynski <akuchynski@chromium.org>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---
 drivers/platform/chrome/Kconfig         |  1 +
 drivers/platform/chrome/cros_ec_typec.c | 50 +++++++++++++++++++++++++
 drivers/platform/chrome/cros_ec_typec.h |  7 ++++
 3 files changed, 58 insertions(+)
diff mbox series

Patch

diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 1b2f2bd09662..0ed8637b8853 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -247,6 +247,7 @@  config CROS_EC_TYPEC
 	depends on MFD_CROS_EC_DEV && TYPEC
 	depends on CROS_USBPD_NOTIFY
 	depends on USB_ROLE_SWITCH
+	depends on DRM_BRIDGE
 	default MFD_CROS_EC_DEV
 	select CROS_EC_TYPEC_ALTMODES if TYPEC_DP_ALTMODE
 	select CROS_EC_TYPEC_ALTMODES if TYPEC_TBT_ALTMODE
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 2cbe29f08064..27324cf0c0c6 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -9,6 +9,7 @@ 
 #include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
 #include <linux/platform_data/cros_ec_commands.h>
 #include <linux/platform_data/cros_usbpd_notify.h>
 #include <linux/platform_device.h>
@@ -16,6 +17,8 @@ 
 #include <linux/usb/typec_dp.h>
 #include <linux/usb/typec_tbt.h>
 
+#include <drm/drm_bridge.h>
+
 #include "cros_ec_typec.h"
 #include "cros_typec_vdm.h"
 #include "cros_typec_altmode.h"
@@ -337,6 +340,9 @@  static int cros_typec_init_ports(struct cros_typec_data *typec)
 	u32 port_num = 0;
 
 	nports = device_get_child_node_count(dev);
+	/* Don't count any 'ports' child node */
+	if (of_graph_is_present(dev->of_node))
+		nports--;
 	if (nports == 0) {
 		dev_err(dev, "No port entries found.\n");
 		return -ENODEV;
@@ -350,6 +356,10 @@  static int cros_typec_init_ports(struct cros_typec_data *typec)
 	/* DT uses "reg" to specify port number. */
 	port_prop = dev->of_node ? "reg" : "port-number";
 	device_for_each_child_node(dev, fwnode) {
+		/* An OF graph isn't a connector */
+		if (fwnode_name_eq(fwnode, "ports"))
+			continue;
+
 		if (fwnode_property_read_u32(fwnode, port_prop, &port_num)) {
 			ret = -EINVAL;
 			dev_err(dev, "No port-number for port, aborting.\n");
@@ -417,6 +427,42 @@  static int cros_typec_init_ports(struct cros_typec_data *typec)
 	return ret;
 }
 
+static int cros_typec_dp_bridge_attach(struct drm_bridge *bridge,
+				       enum drm_bridge_attach_flags flags)
+{
+	return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
+}
+
+static const struct drm_bridge_funcs cros_typec_dp_bridge_funcs = {
+	.attach	= cros_typec_dp_bridge_attach,
+};
+
+static int cros_typec_init_dp_bridge(struct cros_typec_data *typec)
+{
+	struct device *dev = typec->dev;
+	struct cros_typec_dp_bridge *dp_bridge;
+	struct drm_bridge *bridge;
+	struct device_node *np = dev->of_node;
+
+	/* Not capable of DP altmode switching. Ignore. */
+	if (!of_graph_is_present(np))
+		return 0;
+
+	dp_bridge = devm_kzalloc(dev, sizeof(*dp_bridge), GFP_KERNEL);
+	if (!dp_bridge)
+		return -ENOMEM;
+	typec->dp_bridge = dp_bridge;
+
+	bridge = &dp_bridge->bridge;
+	bridge->funcs = &cros_typec_dp_bridge_funcs;
+	bridge->of_node = np;
+	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
+	if (!device_property_read_bool(dev, "no-hpd"))
+		bridge->ops |= DRM_BRIDGE_OP_HPD;
+
+	return devm_drm_bridge_add(dev, bridge);
+}
+
 static int cros_typec_usb_safe_state(struct cros_typec_port *port)
 {
 	int ret;
@@ -1276,6 +1322,10 @@  static int cros_typec_probe(struct platform_device *pdev)
 		typec->num_ports = EC_USB_PD_MAX_PORTS;
 	}
 
+	ret = cros_typec_init_dp_bridge(typec);
+	if (ret < 0)
+		return ret;
+
 	ret = cros_typec_init_ports(typec);
 	if (ret < 0)
 		return ret;
diff --git a/drivers/platform/chrome/cros_ec_typec.h b/drivers/platform/chrome/cros_ec_typec.h
index 9fd5342bb0ad..090f8f5c0492 100644
--- a/drivers/platform/chrome/cros_ec_typec.h
+++ b/drivers/platform/chrome/cros_ec_typec.h
@@ -14,6 +14,8 @@ 
 #include <linux/usb/typec_retimer.h>
 #include <linux/workqueue.h>
 
+#include <drm/drm_bridge.h>
+
 /* Supported alt modes. */
 enum {
 	CROS_EC_ALTMODE_DP = 0,
@@ -35,6 +37,7 @@  struct cros_typec_data {
 	unsigned int pd_ctrl_ver;
 	/* Array of ports, indexed by port number. */
 	struct cros_typec_port *ports[EC_USB_PD_MAX_PORTS];
+	struct cros_typec_dp_bridge *dp_bridge;
 	struct notifier_block nb;
 	struct work_struct port_work;
 	bool typec_cmd_supported;
@@ -83,4 +86,8 @@  struct cros_typec_port {
 	struct cros_typec_data *typec_data;
 };
 
+struct cros_typec_dp_bridge {
+	struct drm_bridge bridge;
+};
+
 #endif /*  __CROS_EC_TYPEC__ */