Skip to contentSkip to conversation

Add Type-C DP support for RK3399 EVB IND board

PATCH In review Chaoyi Chen 6 participants 26 messages Mar 4 – Jun 30

Subsystems (10)
lore
Share & tools
lore
#0 lore raw

v15 cover Add Type-C DP support for RK3399 EVB IND board

From: Chaoyi Chen <[email protected]>

This series focuses on adding Type-C DP support for USBDP PHY and DP driver. The USBDP PHY and DP will perceive the changes in cable status based on the USB PD and Type-C state machines provided by TCPM. Before this, the USBDP PHY and DP controller of RK3399 sensed cable state changes through extcon, and devices such as the RK3399 Gru-Chromebook rely on them. This series should not break them.

====
1. DisplayPort HPD status notify

Before v7, I implemented a variety of DP HPD status notify. However, they all had various problems and it was difficult to become a generic solution.

Under the guidance of Heikki and Dmitry, a decoupled notification method between the TypeC and DRM subsystems was introduced in v7. First, a notification is sent when TypeC registers a new altmode. Then, a generic DP AUX HPD bridge is implemented on the DRM side.

During v7-v10, we added a new notifier in typec to notify the altmode device register event. With the help of Greg and Heikki, we implemented the reuse of notifiers for the type bus itself in patch1 of v11.

The USB subsystem related parts have already been merged into the usb-next branch in v13 [0][1]. Therefore, this series no longer includes these patches starting from v14. Thanks to Greg and Heikki!

[0]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=67ab45426215c7fdccb65aecd4cac15bbe4dfcbb
[1]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=4dee13db29de6dd869af9b3827e1ff569644e838

That makes it redundant for each Type-C controller driver to implement a similar DP AUX HPD bridge in embedded scenarios.

====
2. Altmode switching and orientation switching for USBDP PHY

For USB Type-C interfaces, an external Type-C controller chip assists by detecting cable attachment, determining plug orientation, and reporting USB PD message. The USB/DP combo PHY supports software configurable pin mapping and DisplayPort lane assignment. Based on these message, the combo PHY can perform both altmode switching and orientation switching via software.

The RK3399 EVB IND board has a Type-C interface DisplayPort. It use fusb302 chip as Type-C controller. The connection diagram is shown below:

fusb302 chip +---> USB2.0 PHY ----> DWC3 USB controller

             |
             +---> USB/DP PHY0 +--> CDN-DP controller
                               |
                               +--> DWC3 USB controller

====
3. Multiple bridge model for RK3399 CDN-DP

The RK3399 has two USB/DP combo PHY and one CDN-DP controller. And the CDN-DP can be switched to output to one of the PHYs.

USB/DP PHY0 ---+

               | <----> CDN-DP controller

USB/DP PHY1 ---+

In previous versions, if both PHY ports were connected to DP, the CDN-DP driver would select the first PHY port for output.

On Dmitry's suggestion, we introduced a multi-bridge model to support flexible selection of the output PHY port. For each PHY port, a separate encoder and bridge are registered.

The change is based on the DRM AUX HPD bridge, rather than the extcon approach. This requires the DT to correctly describe the connections between the first bridge in bridge chain and DP controller. And Once the first bridge is obtained, we can get the last bridge corresponding to the USB-C connector, and then set the DRM connector's fwnode to the corresponding one to enable HPD notification.

====
Patch1 add generic USB Type-C DP HPD bridge (Dmitry, Heikki). Patch2 add new API drm_aux_bridge_register_from_node() (Neil) . Patch3 add new Type-C mode switch for RK3399 USBDP phy binding (Krzysztof). Patch4 add typec_mux and typec_switch for RK3399 USBDP PHY. Patch5 add DRM AUX bridge support for RK3399 USBDP PHY (Neil). Patch6 drops CDN-DP's extcon dependency when Type-C is present (Dmitry). Patch7 add multiple bridges to support PHY port selection (Dmitry, Luca). Patch8 add missing dp_out port for RK3399 CDN-DP.
Patch9 add Type-C DP support for RK3399 EVB IND board (Diederik, Peter).

Changes in v15:
- Link to V14: https://lore.kernel.org/all/[email protected]/
- Improve clarity by inlining drm_bridge_get() in assignment (Luca).

Changes in v14:
- Link to V13: https://lore.kernel.org/all/[email protected]/
- Drop the patches for the USB Type-C subsusytem part, as they have already been merged into usb-next.

Changes in v13:
- Link to V12: https://lore.kernel.org/all/[email protected]/
- Only register drm dp hpd bridge for typec port altmode device.

Changes in v12:
- Link to V11: https://lore.kernel.org/all/[email protected]/
- Add missing Signed-off-by line.

Changes in v11:
- Link to V10: https://lore.kernel.org/all/[email protected]/
- Switch to using typec bus notifiers.

Changes in v10:
- Link to V9: https://lore.kernel.org/all/[email protected]/
- Notify TYPEC_ALTMODE_UNREGISTERED when altmode removed.
- Add drm_aux_bridge_register_from_node().
- Fix refcount usage of drm_bridge.

Changes in v9:
- Link to V8: https://lore.kernel.org/all/[email protected]/
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add usb role switch for Type-C.
- Remove USB2 PHY in Type-C connection.
- ...

Changes in v8:
- Link to V7: https://lore.kernel.org/all/[email protected]/
- Export all typec device types for identification.
- Merge generic DP HPD bridge into one module.
- Fix coding style.

Changes in v7:
- Link to V6: https://lore.kernel.org/all/[email protected]/
- Add notifier functions for Type-C core.
- Add generic USB Type-C DP HPD bridge.

Changes in v6:
- Link to V5: https://lore.kernel.org/all/[email protected]/
- Fix depend in Kconfig.
- Check DP svid in tcphy_typec_mux_set().
- Remove mode setting in tcphy_orien_sw_set().
- Rename some variable names.
- Attach the DP bridge to the next bridge.

Changes in v5:
- Link to V4: https://lore.kernel.org/all/[email protected]/
- Remove the calls related to drm_aux_hpd_bridge_notify().
- Place the helper functions in the same compilation unit.
- Add more comments about parent device.
- Add DRM AUX bridge support for RK3399 USBDP PHY
- By parsing the HPD bridge chain, set the connector's of_node to the of_node corresponding to the USB-C connector.
- Return EDID cache when other port is already enabled.

Changes in v4:
- Link to V3: https://lore.kernel.org/all/[email protected]/
- Add default HPD device for DisplayPort altmode.
- Introduce multiple bridges for CDN-DP.
- ...

Changes in v3:
- Link to V2: https://lore.kernel.org/all/[email protected]/
- Add more descriptions to clarify the role of the PHY in switching.
- Fix wrong vdo value.
- Fix port node in usb-c-connector.

Changes in v2:
- Link to V1: https://lore.kernel.org/all/[email protected]/
- Reuse dp-port/usb3-port in rk3399-typec-phy binding.
- Fix compile error when CONFIG_TYPEC is not enabled.
- Notify DP HPD state by USB/DP PHY.
- Ignore duplicate HPD events.
- Add endpoint to link DP PHY and DP controller.
- Fix devicetree coding style.

Chaoyi Chen (9):
drm/bridge: Implement generic USB Type-C DP HPD bridge drm/bridge: aux: Add drm_aux_bridge_register_from_node()
dt-bindings: phy: rockchip: rk3399-typec-phy: Support mode-switch
phy: rockchip: phy-rockchip-typec: Add typec_mux/typec_switch support
phy: rockchip: phy-rockchip-typec: Add DRM AUX bridge drm/rockchip: cdn-dp: Support handle lane info without extcon drm/rockchip: cdn-dp: Add multiple bridges to support PHY port
selection
arm64: dts: rockchip: Add missing dp_out port for RK3399 CDN-DP
arm64: dts: rockchip: rk3399-evb-ind: Add support for DisplayPort

 .../phy/rockchip,rk3399-typec-phy.yaml        |   6 +
 arch/arm64/boot/dts/rockchip/rk3399-base.dtsi |  10 +-
 .../boot/dts/rockchip/rk3399-evb-ind.dts      | 147 +++++++
 drivers/gpu/drm/bridge/Kconfig                |  10 +
 drivers/gpu/drm/bridge/Makefile               |   1 +
 drivers/gpu/drm/bridge/aux-bridge.c           |  24 +-
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  |  49 +++
 drivers/gpu/drm/rockchip/Kconfig              |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c        | 349 +++++++++++++---
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |  18 +-
 drivers/phy/rockchip/Kconfig                  |   3 +
 drivers/phy/rockchip/phy-rockchip-typec.c     | 373 +++++++++++++++++-
 include/drm/bridge/aux-bridge.h               |   6 +
 13 files changed, 913 insertions(+), 84 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
Signature · 1 line
2.51.1
#1 lore raw

PATCH v15 1/9 drm/bridge: Implement generic USB Type-C DP HPD bridge

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +10 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+
 menu "Display Interface Bridges"
 	depends on DRM && DRM_BRIDGE
 
drivers/gpu/drm/bridge/Makefile +1 -0
index c7dc03182e59..a3a0393d2e72 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DRM_AUX_BRIDGE) += aux-bridge.o
 obj-$(CONFIG_DRM_AUX_HPD_BRIDGE) += aux-hpd-bridge.o
+obj-$(CONFIG_DRM_AUX_HPD_TYPEC_BRIDGE) += aux-hpd-typec-dp-bridge.o
 obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
 obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
 obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +49 -0

49 lines not on this page · raw message

Signature · 1 line
2.51.1
Reviewed-by on v15 1/9
#2 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

Quoted 93 lines · On Wednesday, 4 March 2026 10:41:44 Central European Summer Time Chaoyi Chen…

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +10 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+
 menu "Display Interface Bridges"
 	depends on DRM && DRM_BRIDGE
 
drivers/gpu/drm/bridge/Makefile +1 -0
index c7dc03182e59..a3a0393d2e72 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DRM_AUX_BRIDGE) += aux-bridge.o
 obj-$(CONFIG_DRM_AUX_HPD_BRIDGE) += aux-hpd-bridge.o
+obj-$(CONFIG_DRM_AUX_HPD_TYPEC_BRIDGE) += aux-hpd-typec-dp-bridge.o
 obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
 obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
 obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +0 -0
new file mode 100644
index 000000000000..d915e0fb0668

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 1

Quoted 1 line
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +1 -0
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+

Having a copyright statement added here is likely required. Something like

/*
* Copyright (C) 2026 Rockchip Electronics Co., Ltd. *
* Author: Chaoyi Chen <[email protected]> */

will suffice. While I hope it's never relevant for a driver this small, explicit copyright statements in this form have a legal effect in some jurisdictions like the US, where by law it automatically dismisses the defense that the copyright holder wasn't known.

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 11

Quoted 10 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +10 -0
+#include <linux/of.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+
+#include <drm/bridge/aux-bridge.h>
+
+static int drm_typec_bus_event(struct notifier_block *nb,
+			       unsigned long action, void *data)
+{
+	struct device *dev = (struct device *)data;

Small nitpick: The explicit (struct device *) cast of data isn't needed here. Just

    struct device *dev = data;

will work fine. The benefit is that if the type of "data" ever changes (e.g. from void *data to const void *data) then we're not silencing a warning/error through an explicit cast.

Quoted 6 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +6 -0
+	struct typec_altmode *alt = to_typec_altmode(dev);
+
+	if (action != BUS_NOTIFY_ADD_DEVICE)
+		goto done;
+
+	/*

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 27

Quoted 10 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +10 -0
+	 * alt->dev.parent->parent : USB-C controller device
+	 * alt->dev.parent         : USB-C connector device
+	 */
+	if (is_typec_port_altmode(&alt->dev) && alt->svid == USB_TYPEC_DP_SID)
+		drm_dp_hpd_bridge_register(alt->dev.parent->parent,
+					   to_of_node(alt->dev.parent->fwnode));
+
+done:
+	return NOTIFY_OK;
+}

Nitpicking: the "done" label and "goto done" here is redundant. You could remove the label and replace the goto with another "return NOTIFY_OK;". There's no functional change here though.

Quoted 13 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +12 -0
+
+static struct notifier_block drm_typec_event_nb = {
+	.notifier_call = drm_typec_bus_event,
+};
+
+static void drm_aux_hpd_typec_dp_bridge_module_exit(void)
+{
+	bus_unregister_notifier(&typec_bus, &drm_typec_event_nb);
+}
+
+static int __init drm_aux_hpd_typec_dp_bridge_module_init(void)
+{

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 49

Quoted 10 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +10 -0
+	bus_register_notifier(&typec_bus, &drm_typec_event_nb);
+
+	return 0;
+}
+
+module_init(drm_aux_hpd_typec_dp_bridge_module_init);
+module_exit(drm_aux_hpd_typec_dp_bridge_module_exit);
+
+MODULE_DESCRIPTION("DRM TYPEC DP HPD BRIDGE");
+MODULE_LICENSE("GPL");

Feel free to add yourself as MODULE_AUTHOR here. :)

With those things changed:

Kind regards,
Nicolas Frattaroli

#3 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

Hello Nicolas,

Quoted 105 lines · Nicolas Frattaroli
Quoted 93 lines · On Wednesday, 4 March 2026 10:41:44 Central European Summer Time Chaoyi Chen…

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +10 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+
 menu "Display Interface Bridges"
 	depends on DRM && DRM_BRIDGE
 
drivers/gpu/drm/bridge/Makefile +1 -0
index c7dc03182e59..a3a0393d2e72 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DRM_AUX_BRIDGE) += aux-bridge.o
 obj-$(CONFIG_DRM_AUX_HPD_BRIDGE) += aux-hpd-bridge.o
+obj-$(CONFIG_DRM_AUX_HPD_TYPEC_BRIDGE) += aux-hpd-typec-dp-bridge.o
 obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
 obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
 obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +0 -0
new file mode 100644
index 000000000000..d915e0fb0668

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 1

Quoted 1 line
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +1 -0
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+

Having a copyright statement added here is likely required. Something like

/*
* Copyright (C) 2026 Rockchip Electronics Co., Ltd. *
* Author: Chaoyi Chen <[email protected]> */

Quoted 6 lines

will suffice. While I hope it's never relevant for a driver this small, explicit copyright statements in this form have a legal effect in some jurisdictions like the US, where by law it automatically dismisses the defense that the copyright holder wasn't known.

Oh! I hadn't noticed that. Thank you for your legal advice.

Quoted 14 lines

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 11

Quoted 10 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +10 -0
+#include <linux/of.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+
+#include <drm/bridge/aux-bridge.h>
+
+static int drm_typec_bus_event(struct notifier_block *nb,
+			       unsigned long action, void *data)
+{
+	struct device *dev = (struct device *)data;

Small nitpick: The explicit (struct device *) cast of data isn't needed here. Just

Quoted 6 lines
    struct device *dev = data;

will work fine. The benefit is that if the type of "data" ever changes (e.g. from void *data to const void *data) then we're not silencing a warning/error through an explicit cast.

Yep. Will fix in v2.

Quoted 12 lines
Quoted 12 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +12 -0
+	struct typec_altmode *alt = to_typec_altmode(dev);
+
+	if (action != BUS_NOTIFY_ADD_DEVICE)
+		goto done;
+
+	/*
+	 * alt->dev.parent->parent : USB-C controller device
+	 * alt->dev.parent         : USB-C connector device
+	 */
+	if (is_typec_port_altmode(&alt->dev) && alt->svid == USB_TYPEC_DP_SID)
+		drm_dp_hpd_bridge_register(alt->dev.parent->parent,
+					   to_of_node(alt->dev.parent->fwnode));
Quoted 9 lines

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 27

Quoted 4 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +4 -0
+
+done:
+	return NOTIFY_OK;
+}

Nitpicking: the "done" label and "goto done" here is redundant. You could remove the label and replace the goto with another "return NOTIFY_OK;". There's no functional change here though.

Will fix in v2.

Quoted 35 lines
Quoted 13 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +12 -0
+
+static struct notifier_block drm_typec_event_nb = {
+	.notifier_call = drm_typec_bus_event,
+};
+
+static void drm_aux_hpd_typec_dp_bridge_module_exit(void)
+{
+	bus_unregister_notifier(&typec_bus, &drm_typec_event_nb);
+}
+
+static int __init drm_aux_hpd_typec_dp_bridge_module_init(void)
+{

drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c line 49

Quoted 10 lines
drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c new file +10 -0
+	bus_register_notifier(&typec_bus, &drm_typec_event_nb);
+
+	return 0;
+}
+
+module_init(drm_aux_hpd_typec_dp_bridge_module_init);
+module_exit(drm_aux_hpd_typec_dp_bridge_module_exit);
+
+MODULE_DESCRIPTION("DRM TYPEC DP HPD BRIDGE");
+MODULE_LICENSE("GPL");

Feel free to add yourself as MODULE_AUTHOR here. :)

With those things changed:

Kind regards,
Nicolas Frattaroli

Signature · 7 lines
Best, 
Chaoyi

-- 
linux-phy mailing list
[email protected]
https://lists.infradead.org/mailman/listinfo/linux-phy
#4 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

In reply to Chaoyi Chen
Quoted 62 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +0 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 

drivers/gpu/drm/bridge/Kconfig config DRM_AUX_HPD_BRIDGE · line 42

Quoted 10 lines
drivers/gpu/drm/bridge/Kconfig +10 -0
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+

Should CONFIG_TYPEC_DP_ALTMODE select this one? Otherwise, we need to do it manually.

$ grep -nr --include=Kconfig "select DRM_AUX_HPD_BRIDGE" .
./drivers/soc/qcom/Kconfig:118: select DRM_AUX_HPD_BRIDGE
./drivers/usb/typec/ucsi/Kconfig:88:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/ucsi/Kconfig:99:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:62:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:85:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF

Thanks,
Xu Yang

#5 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

Hello Xu Yang,

Quoted 77 lines · Xu Yang
Quoted 62 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +0 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 

drivers/gpu/drm/bridge/Kconfig config DRM_AUX_HPD_BRIDGE · line 42

Quoted 10 lines
drivers/gpu/drm/bridge/Kconfig +10 -0
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+

Should CONFIG_TYPEC_DP_ALTMODE select this one? Otherwise, we need to do it manually.

Quoted 7 lines
$ grep -nr --include=Kconfig "select DRM_AUX_HPD_BRIDGE" .
./drivers/soc/qcom/Kconfig:118: select DRM_AUX_HPD_BRIDGE
./drivers/usb/typec/ucsi/Kconfig:88:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/ucsi/Kconfig:99:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:62:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:85:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF

That's a fair point. But based on the previous discussion, Heikki point out that configurations in the TYPEC subsystem should not select configurations from DRM.

Signature · 2 lines
Best, 
Chaoyi
#6 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

Quoted 88 lines · Chaoyi Chen

Hello Xu Yang,

Quoted 77 lines · Xu Yang
Quoted 62 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +0 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 

drivers/gpu/drm/bridge/Kconfig config DRM_AUX_HPD_BRIDGE · line 42

Quoted 10 lines
drivers/gpu/drm/bridge/Kconfig +10 -0
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+

Should CONFIG_TYPEC_DP_ALTMODE select this one? Otherwise, we need to do it manually.

Quoted 7 lines
$ grep -nr --include=Kconfig "select DRM_AUX_HPD_BRIDGE" .
./drivers/soc/qcom/Kconfig:118: select DRM_AUX_HPD_BRIDGE
./drivers/usb/typec/ucsi/Kconfig:88:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/ucsi/Kconfig:99:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:62:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:85:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
Quoted 3 lines

That's a fair point. But based on the previous discussion, Heikki point out that configurations in the TYPEC subsystem should not select configurations from DRM.

I have just reviewed your previous patchsets but I did't find such opinion from Heikki. Otherwise, why are tcpm.c/ucsi.c already allowed to add the above select condition in their configs?

I think Heikki means that the DRM_AUX_HPD_BRIDGE shouldn't been selected at the top level of Type-C subsystem. Because not all Type-C devices support the DP function.

As a generic Type-C DP HPD bridge, displayport.c will likely need to use it in the future. Therefore, allowing displayport.c to select DRM_AUX_HPD_BRIDGE makes sense.

According to my testing, it's impossible to build this driver in unless TYPEC_FUSB302 or a relevant CONFIG is built in to select DRM_AUX_HPD_BRIDGE, or the defconfig is modified.

Thanks,
Xu Yang

#7 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

Hello Xu Yang, Heikki

Quoted 97 lines · Xu Yang
Quoted 88 lines · Chaoyi Chen

Hello Xu Yang,

Quoted 77 lines · Xu Yang
Quoted 62 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The HPD function of Type-C DP is implemented through drm_connector_oob_hotplug_event(). For embedded DP, it is required that the DRM connector fwnode corresponds to the Type-C port fwnode.

To describe the relationship between the DP controller and the Type-C port device, we usually using drm_bridge to build a bridge chain.

Now several USB-C controller drivers have already implemented the DP HPD bridge function provided by aux-hpd-bridge.c, it will build a DP HPD bridge on USB-C connector port device.

But this requires the USB-C controller driver to manually register the HPD bridge. If the driver does not implement this feature, the bridge will not be create.

So this patch implements a generic DP HPD bridge based on aux-hpd-bridge.c. It will monitor Type-C bus events, and when a Type-C port device containing the DP svid is registered, it will create an HPD bridge for it without the need for the USB-C controller driver to implement it.

---

(no changes since v14)

Changes in v13:
- Only register drm dp hpd bridge for typec port altmode device.

(no changes since v12)

Changes in v11:
- Switch to using typec bus notifiers.

(no changes since v10)

Changes in v9:
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add more commit comment about problem background.

Changes in v8:
- Merge generic DP HPD bridge into one module.
---

 drivers/gpu/drm/bridge/Kconfig                | 10 ++++
 drivers/gpu/drm/bridge/Makefile               |  1 +
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  | 49 +++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
drivers/gpu/drm/bridge/Kconfig +0 -0
index a250afd8d662..559487aa09a9 100644
@@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
 	  Simple bridge that terminates the bridge chain and provides HPD
 	  support.
 

drivers/gpu/drm/bridge/Kconfig config DRM_AUX_HPD_BRIDGE · line 42

Quoted 10 lines
drivers/gpu/drm/bridge/Kconfig +10 -0
+if DRM_AUX_HPD_BRIDGE
+config DRM_AUX_HPD_TYPEC_BRIDGE
+	tristate
+	depends on TYPEC || !TYPEC
+	default TYPEC
+	help
+	  Simple bridge that terminates the bridge chain and provides HPD
+	  support. It build bridge on each USB-C connector device node.
+endif
+

Should CONFIG_TYPEC_DP_ALTMODE select this one? Otherwise, we need to do it manually.

Quoted 7 lines
$ grep -nr --include=Kconfig "select DRM_AUX_HPD_BRIDGE" .
./drivers/soc/qcom/Kconfig:118: select DRM_AUX_HPD_BRIDGE
./drivers/usb/typec/ucsi/Kconfig:88:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/ucsi/Kconfig:99:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:62:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
./drivers/usb/typec/tcpm/Kconfig:85:    select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
Quoted 3 lines

That's a fair point. But based on the previous discussion, Heikki point out that configurations in the TYPEC subsystem should not select configurations from DRM.

I have just reviewed your previous patchsets but I did't find such opinion from Heikki. Otherwise, why are tcpm.c/ucsi.c already allowed to add the above select condition in their configs?

Quoted 9 lines

I think Heikki means that the DRM_AUX_HPD_BRIDGE shouldn't been selected at the top level of Type-C subsystem. Because not all Type-C devices support the DP function.

As a generic Type-C DP HPD bridge, displayport.c will likely need to use it in the future. Therefore, allowing displayport.c to select DRM_AUX_HPD_BRIDGE makes sense.

According to my testing, it's impossible to build this driver in unless TYPEC_FUSB302 or a relevant CONFIG is built in to select DRM_AUX_HPD_BRIDGE, or the defconfig is modified.

Yep. For me, I select DRM_AUX_HPD_BRIDGE in DRM.

@Heikki, what do you think of the following change? Should we add a new patch for this? Thanks.

drivers/usb/typec/altmodes/Kconfig +1 -0
index 7867fa7c405d..f89cdf3c949b 100644
@@ -5,6 +5,7 @@ menu "USB Type-C Alternate Mode drivers"
 config TYPEC_DP_ALTMODE
        tristate "DisplayPort Alternate Mode driver"
        depends on DRM
+       select DRM_AUX_HPD_BRIDGE 
        help
          DisplayPort USB Type-C Alternate Mode allows DisplayPort
          displays and adapters to be attached to the USB Type-C
Signature · 2 lines
Best, 
Chaoyi
maintainer
#8 lore raw

Re: [PATCH v15 1/9] drm/bridge: Implement generic USB Type-C DP HPD bridge

Quoted 8 lines

@Heikki, what do you think of the following change? Should we add a new patch for this? Thanks.

drivers/usb/typec/altmodes/Kconfig +0 -0
index 7867fa7c405d..f89cdf3c949b 100644

drivers/usb/typec/altmodes/Kconfig menu "USB Type-C Alternate Mode drivers" · line 11

Quoted 7 lines
drivers/usb/typec/altmodes/Kconfig +1 -0
@@ -5,6 +5,7 @@ menu "USB Type-C Alternate Mode drivers"
 config TYPEC_DP_ALTMODE
        tristate "DisplayPort Alternate Mode driver"
        depends on DRM
+       select DRM_AUX_HPD_BRIDGE 
        help
          DisplayPort USB Type-C Alternate Mode allows DisplayPort
          displays and adapters to be attached to the USB Type-C

This look good to me. Please do add the patch for this.

thanks,

Signature · 1 line
heikki
#9 lore raw

PATCH v15 2/9 drm/bridge: aux: Add drm_aux_bridge_register_from_node()

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The drm_aux_bridge_register() uses the device->of_node as the bridge->of_node.

This patch adds drm_aux_bridge_register_from_node() to allow specifying the of_node corresponding to the bridge.

---

(no changes since v11)
---

 drivers/gpu/drm/bridge/aux-bridge.c | 24 ++++++++++++++++++++++--
 include/drm/bridge/aux-bridge.h     |  6 ++++++
 2 files changed, 28 insertions(+), 2 deletions(-)
drivers/gpu/drm/bridge/aux-bridge.c +22 -2
index b3e4cdff61d6..52dff4601c2d 100644
@@ -35,6 +35,7 @@ static void drm_aux_bridge_unregister_adev(void *_adev)
 /**
  * drm_aux_bridge_register - Create a simple bridge device to link the chain
  * @parent: device instance providing this bridge
+ * @np: device node pointer corresponding to this bridge instance
  *
  * Creates a simple DRM bridge that doesn't implement any drm_bridge
  * operations. Such bridges merely fill a place in the bridge chain linking
@@ -42,7 +43,7 @@ static void drm_aux_bridge_unregister_adev(void *_adev)
  *
  * Return: zero on success, negative error code on failure
  */
-int drm_aux_bridge_register(struct device *parent)
+int drm_aux_bridge_register_from_node(struct device *parent, struct device_node *np)
 {
 	struct auxiliary_device *adev;
 	int ret;
@@ -62,7 +63,10 @@ int drm_aux_bridge_register(struct device *parent)
 	adev->dev.parent = parent;
 	adev->dev.release = drm_aux_bridge_release;
 
-	device_set_of_node_from_dev(&adev->dev, parent);
+	if (np)
+		device_set_node(&adev->dev, of_fwnode_handle(np));
+	else
+		device_set_of_node_from_dev(&adev->dev, parent);
 
 	ret = auxiliary_device_init(adev);
 	if (ret) {
@@ -80,6 +84,22 @@ int drm_aux_bridge_register(struct device *parent)
 
 	return devm_add_action_or_reset(parent, drm_aux_bridge_unregister_adev, adev);
 }
+EXPORT_SYMBOL_GPL(drm_aux_bridge_register_from_node);
+
+/**
+ * drm_aux_bridge_register - Create a simple bridge device to link the chain
+ * @parent: device instance providing this bridge
+ *
+ * Creates a simple DRM bridge that doesn't implement any drm_bridge
+ * operations. Such bridges merely fill a place in the bridge chain linking
+ * surrounding DRM bridges.
+ *
+ * Return: zero on success, negative error code on failure
+ */
+int drm_aux_bridge_register(struct device *parent)
+{
+	return drm_aux_bridge_register_from_node(parent, NULL);
+}
 EXPORT_SYMBOL_GPL(drm_aux_bridge_register);
 
 struct drm_aux_bridge_data {
include/drm/bridge/aux-bridge.h +6 -0
index c2f5a855512f..7dd1f17a1354 100644
@@ -13,11 +13,17 @@ struct auxiliary_device;
 
 #if IS_ENABLED(CONFIG_DRM_AUX_BRIDGE)
 int drm_aux_bridge_register(struct device *parent);
+int drm_aux_bridge_register_from_node(struct device *parent, struct device_node *np);
 #else
 static inline int drm_aux_bridge_register(struct device *parent)
 {
 	return 0;
 }
+
+static inline int drm_aux_bridge_register_from_node(struct device *parent, struct device_node *np)
+{
+	return 0;
+}
 #endif
 
 #if IS_ENABLED(CONFIG_DRM_AUX_HPD_BRIDGE)
Signature · 1 line
2.51.1
Reviewed-by on v15 2/9
#10 lore raw

Re: [PATCH v15 2/9] drm/bridge: aux: Add drm_aux_bridge_register_from_node()

Quoted 24 lines · On Wednesday, 4 March 2026 10:41:45 Central European Summer Time Chaoyi Chen…

From: Chaoyi Chen <[email protected]>

The drm_aux_bridge_register() uses the device->of_node as the bridge->of_node.

This patch adds drm_aux_bridge_register_from_node() to allow specifying the of_node corresponding to the bridge.

---

(no changes since v11)
---

 drivers/gpu/drm/bridge/aux-bridge.c | 24 ++++++++++++++++++++++--
 include/drm/bridge/aux-bridge.h     |  6 ++++++
 2 files changed, 28 insertions(+), 2 deletions(-)
drivers/gpu/drm/bridge/aux-bridge.c +0 -0
index b3e4cdff61d6..52dff4601c2d 100644

drivers/gpu/drm/bridge/aux-bridge.c static void drm_aux_bridge_unregister_adev(void *_adev) · line 36

Quoted 2 lines
drivers/gpu/drm/bridge/aux-bridge.c +0 -0
@@ -35,6 +35,7 @@ static void drm_aux_bridge_unregister_adev(void *_adev)
 /**
  * drm_aux_bridge_register - Create a simple bridge device to link the chain

Function name needs to be changed here as well. You can validate kernel doc strings for a single file with:

./tools/docs/kernel-doc -v -none drivers/gpu/drm/bridge/aux-bridge.c

With that fixed:

Kind regards,
Nicolas Frattaroli

Quoted 72 lines
drivers/gpu/drm/bridge/aux-bridge.c +22 -2
  * @parent: device instance providing this bridge
+ * @np: device node pointer corresponding to this bridge instance
  *
  * Creates a simple DRM bridge that doesn't implement any drm_bridge
  * operations. Such bridges merely fill a place in the bridge chain linking
@@ -42,7 +43,7 @@ static void drm_aux_bridge_unregister_adev(void *_adev)
  *
  * Return: zero on success, negative error code on failure
  */
-int drm_aux_bridge_register(struct device *parent)
+int drm_aux_bridge_register_from_node(struct device *parent, struct device_node *np)
 {
 	struct auxiliary_device *adev;
 	int ret;
@@ -62,7 +63,10 @@ int drm_aux_bridge_register(struct device *parent)
 	adev->dev.parent = parent;
 	adev->dev.release = drm_aux_bridge_release;
 
-	device_set_of_node_from_dev(&adev->dev, parent);
+	if (np)
+		device_set_node(&adev->dev, of_fwnode_handle(np));
+	else
+		device_set_of_node_from_dev(&adev->dev, parent);
 
 	ret = auxiliary_device_init(adev);
 	if (ret) {
@@ -80,6 +84,22 @@ int drm_aux_bridge_register(struct device *parent)
 
 	return devm_add_action_or_reset(parent, drm_aux_bridge_unregister_adev, adev);
 }
+EXPORT_SYMBOL_GPL(drm_aux_bridge_register_from_node);
+
+/**
+ * drm_aux_bridge_register - Create a simple bridge device to link the chain
+ * @parent: device instance providing this bridge
+ *
+ * Creates a simple DRM bridge that doesn't implement any drm_bridge
+ * operations. Such bridges merely fill a place in the bridge chain linking
+ * surrounding DRM bridges.
+ *
+ * Return: zero on success, negative error code on failure
+ */
+int drm_aux_bridge_register(struct device *parent)
+{
+	return drm_aux_bridge_register_from_node(parent, NULL);
+}
 EXPORT_SYMBOL_GPL(drm_aux_bridge_register);
 
 struct drm_aux_bridge_data {
include/drm/bridge/aux-bridge.h +6 -0
index c2f5a855512f..7dd1f17a1354 100644
@@ -13,11 +13,17 @@ struct auxiliary_device;
 
 #if IS_ENABLED(CONFIG_DRM_AUX_BRIDGE)
 int drm_aux_bridge_register(struct device *parent);
+int drm_aux_bridge_register_from_node(struct device *parent, struct device_node *np);
 #else
 static inline int drm_aux_bridge_register(struct device *parent)
 {
 	return 0;
 }
+
+static inline int drm_aux_bridge_register_from_node(struct device *parent, struct device_node *np)
+{
+	return 0;
+}
 #endif
 
 #if IS_ENABLED(CONFIG_DRM_AUX_HPD_BRIDGE)
#11 lore raw

PATCH v15 3/9 dt-bindings: phy: rockchip: rk3399-typec-phy: Support mode-switch

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The RK3399 SoC integrates two USB/DP combo PHYs, each of which supports software-configurable pin mapping and DisplayPort lane assignment. These capabilities enable the PHY itself to handle both mode switching and orientation switching, based on the Type-C plug orientation and USB PD negotiation results.

While an external Type-C controller is still required to detect cable attachment and report USB PD events, the actual mode and orientation switching is performed internally by the PHY through software configuration. This allows the PHY to act as a Type-C multiplexer for both data role and DP altmode configuration.

To reflect this hardware design, this patch introduces a new "mode-switch" property for the dp-port node in the device tree bindings. This property indicates that the connected PHY is capable of handling Type-C mode switching itself.

---

(no changes since v5)

Changes in v4:
- Remove "|" in description.

Changes in v3:
- Add more descriptions to clarify the role of the PHY in switching.

Changes in v2:
- Reuse dp-port/usb3-port in rk3399-typec-phy binding.
---

 .../devicetree/bindings/phy/rockchip,rk3399-typec-phy.yaml  | 6 ++++++
 1 file changed, 6 insertions(+)
Documentation/devicetree/bindings/phy/rockchip,rk3399-typec-phy.yaml +6 -0
index 91c011f68cd0..83ebcde096ea 100644
@@ -51,6 +51,12 @@ properties:
       '#phy-cells':
         const: 0
 
+      mode-switch:
+        description:
+          Indicates the PHY can handle altmode switching. In this case,
+          requires an external USB Type-C controller to report USB PD message.
+        type: boolean
+
       port:
         $ref: /schemas/graph.yaml#/properties/port
         description: Connection to USB Type-C connector
Signature · 1 line
2.51.1
#12 lore raw

PATCH v15 4/9 phy: rockchip: phy-rockchip-typec: Add typec_mux/typec_switch support

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

This patch add support for Type-C Port Controller Manager. Each PHY will register typec_mux and typec_switch when external Type-C controller is present. Type-C events are handled by TCPM without extcon.

The extcon device should still be supported.

---

(no changes since v7)

Changes in v6:
- Fix depend in Kconfig.
- Check DP svid in tcphy_typec_mux_set().
- Remove mode setting in tcphy_orien_sw_set().

(no changes since v5)

Changes in v4:
- Remove notify DP HPD state by USB/DP PHY.

(no changes since v3)

Changes in v2:
- Fix compile error when CONFIG_TYPEC is not enabled.
- Notify DP HPD state by USB/DP PHY.
---

 drivers/phy/rockchip/Kconfig              |   1 +
 drivers/phy/rockchip/phy-rockchip-typec.c | 368 +++++++++++++++++++++-
 2 files changed, 353 insertions(+), 16 deletions(-)
drivers/phy/rockchip/Kconfig +1 -0
index 14698571b607..db4adc7c53da 100644
@@ -119,6 +119,7 @@ config PHY_ROCKCHIP_SNPS_PCIE3
 config PHY_ROCKCHIP_TYPEC
 	tristate "Rockchip TYPEC PHY Driver"
 	depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
+	depends on TYPEC || TYPEC=n
 	select EXTCON
 	select GENERIC_PHY
 	select RESET_CONTROLLER
drivers/phy/rockchip/phy-rockchip-typec.c +352 -16

507 lines not on this page · raw message

Signature · 1 line
2.51.1
#13 lore raw

Re: [PATCH v15 4/9] phy: rockchip: phy-rockchip-typec: Add typec_mux/typec_switch support

Quoted 158 lines · On Wednesday, 4 March 2026 10:41:47 Central European Summer Time Chaoyi Chen…

From: Chaoyi Chen <[email protected]>

This patch add support for Type-C Port Controller Manager. Each PHY will register typec_mux and typec_switch when external Type-C controller is present. Type-C events are handled by TCPM without extcon.

The extcon device should still be supported.

---

(no changes since v7)

Changes in v6:
- Fix depend in Kconfig.
- Check DP svid in tcphy_typec_mux_set().
- Remove mode setting in tcphy_orien_sw_set().

(no changes since v5)

Changes in v4:
- Remove notify DP HPD state by USB/DP PHY.

(no changes since v3)

Changes in v2:
- Fix compile error when CONFIG_TYPEC is not enabled.
- Notify DP HPD state by USB/DP PHY.
---

 drivers/phy/rockchip/Kconfig              |   1 +
 drivers/phy/rockchip/phy-rockchip-typec.c | 368 +++++++++++++++++++++-
 2 files changed, 353 insertions(+), 16 deletions(-)
drivers/phy/rockchip/Kconfig +1 -0
index 14698571b607..db4adc7c53da 100644
@@ -119,6 +119,7 @@ config PHY_ROCKCHIP_SNPS_PCIE3
 config PHY_ROCKCHIP_TYPEC
 	tristate "Rockchip TYPEC PHY Driver"
 	depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
+	depends on TYPEC || TYPEC=n
 	select EXTCON
 	select GENERIC_PHY
 	select RESET_CONTROLLER
drivers/phy/rockchip/phy-rockchip-typec.c +51 -2
index d9701b6106d5..1f5b4142cbe4 100644
@@ -54,6 +54,8 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -286,12 +288,23 @@
 #define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
 
 #define PMA_LANE_CFG			(0xc000 << 2)
+#define PMA_LANE3_DP_LANE_SEL(x)	(((x) & 0x3) << 14)
+#define PMA_LANE3_INTERFACE_SEL(x)	(((x) & 0x1) << 12)
+#define PMA_LANE2_DP_LANE_SEL(x)	(((x) & 0x3) << 10)
+#define PMA_LANE2_INTERFACE_SEL(x)	(((x) & 0x1) << 8)
+#define PMA_LANE1_DP_LANE_SEL(x)	(((x) & 0x3) << 6)
+#define PMA_LANE1_INTERFACE_SEL(x)	(((x) & 0x1) << 4)
+#define PMA_LANE0_DP_LANE_SEL(x)	(((x) & 0x3) << 2)
+#define PMA_LANE0_INTERFACE_SEL(x)	(((x) & 0x1) << 0)
 #define PIPE_CMN_CTRL1			(0xc001 << 2)
 #define PIPE_CMN_CTRL2			(0xc002 << 2)
 #define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
 #define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
 #define PIPE_RCV_DET_INH		(0xc005 << 2)
 #define DP_MODE_CTL			(0xc008 << 2)
+#define PHY_DP_POWER_STATE_ACK_MASK	GENMASK(7, 4)
+#define PHY_DP_POWER_STATE_ACK_SHIFT	4
+#define PHY_DP_POWER_STATE_MASK		GENMASK(3, 0)
 #define DP_CLK_CTL			(0xc009 << 2)
 #define STS				(0xc00F << 2)
 #define PHY_ISO_CMN_CTRL		(0xc010 << 2)
@@ -327,8 +340,15 @@
 
 #define DP_MODE_A0			BIT(4)
 #define DP_MODE_A2			BIT(6)
-#define DP_MODE_ENTER_A0		0xc101
-#define DP_MODE_ENTER_A2		0xc104
+
+#define DP_MODE_MASK			0xf
+#define DP_MODE_ENTER_A0		BIT(0)
+#define DP_MODE_ENTER_A2		BIT(2)
+#define DP_MODE_ENTER_A3		BIT(3)
+#define DP_MODE_A0_ACK			BIT(4)
+#define DP_MODE_A2_ACK			BIT(6)
+#define DP_MODE_A3_ACK			BIT(7)
+#define DP_LINK_RESET_DEASSERTED	BIT(8)
 
 #define PHY_MODE_SET_TIMEOUT		100000
 
@@ -340,6 +360,31 @@
 #define MODE_DFP_USB			BIT(1)
 #define MODE_DFP_DP			BIT(2)
 
+enum phy_dp_lane_num {
+	PHY_DP_LANE_0 = 0,
+	PHY_DP_LANE_1,
+	PHY_DP_LANE_2,
+	PHY_DP_LANE_3,
+};
+
+enum phy_pma_if {
+	PMA_IF_PIPE_PCS = 0,
+	PMA_IF_PHY_DP,
+};
+
+enum phy_typec_role {
+	TYPEC_PHY_USB = 0,
+	TYPEC_PHY_DP,
+	TYPEC_PHY_MAX,
+};
+
+enum phy_dp_power_state {
+	PHY_DP_POWER_STATE_A0 = 0,
+	PHY_DP_POWER_STATE_A1,
+	PHY_DP_POWER_STATE_A2,
+	PHY_DP_POWER_STATE_A3,
+};
+
 struct usb3phy_reg {
 	u32 offset;
 	u32 enable_bit;
@@ -372,18 +417,22 @@ struct rockchip_typec_phy {
 	struct device *dev;
 	void __iomem *base;
 	struct extcon_dev *extcon;
+	struct typec_mux_dev *mux;
+	struct typec_switch_dev *sw;
 	struct regmap *grf_regs;
 	struct clk *clk_core;
 	struct clk *clk_ref;
 	struct reset_control *uphy_rst;
 	struct reset_control *pipe_rst;
 	struct reset_control *tcphy_rst;
+	struct phy *phys[TYPEC_PHY_MAX];
 	const struct rockchip_usb3phy_port_cfg *port_cfgs;
 	/* mutex to protect access to individual PHYs */
 	struct mutex lock;
 
 	bool flip;
 	u8 mode;
+	u8 new_mode;
 };
 
 struct phy_reg {

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 508

Quoted 6 lines
drivers/phy/rockchip/phy-rockchip-typec.c +3 -0
@@ -454,6 +503,99 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
 	{ /* sentinel */ }
 };
 
+static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
+				       bool value);
+

If possible, please avoid forward declarations of this static function and just move the implementation here if it needs to be declared earlier.

Quoted 5 lines
drivers/phy/rockchip/phy-rockchip-typec.c +5 -0
+static int tcphy_dp_set_power_state(struct rockchip_typec_phy *tcphy,
+				    enum phy_dp_power_state state)
+{
+	u32 ack, reg, sts = BIT(state);
+	int ret;

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 523

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+
+	/*
+	 * Power state changes must not be requested until after the cmn_ready
+	 * signal has gone active.
+	 */
+	reg = readl(tcphy->base + PMA_CMN_CTRL1);
+	if (!(reg & CMN_READY)) {
+		dev_err(tcphy->dev, "cmn_ready in the inactive state\n");
+		return -EINVAL;
+	}

You can use readl in the if condition directly here, since reg isn't used otherwise, but I'm also fine with it as-is if you think it helps readability.

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 533

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+
+	reg = readl(tcphy->base + DP_MODE_CTL);
+	reg &= ~PHY_DP_POWER_STATE_MASK;
+	reg |= sts;
+	writel(reg, tcphy->base + DP_MODE_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + DP_MODE_CTL,
+				 ack, (((ack & PHY_DP_POWER_STATE_ACK_MASK) >>
+				 PHY_DP_POWER_STATE_ACK_SHIFT) == sts), 10,
+				 PHY_MODE_SET_TIMEOUT);

Here please use FIELD_GET() from <linux/bitfield.h> like this:

	ret = readl_poll_timeout(tcphy->base + DP_MODE_CTL, ack,
				 FIELD_GET(PHY_DP_POWER_STATE_ACK_MASK, ack) == sts,
				 10, PHY_MODE_SET_TIMEOUT);

PHY_DP_POWER_STATE_ACK_SHIFT is then no longer needed.

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 534

Quoted 1 line
drivers/phy/rockchip/phy-rockchip-typec.c +1 -0
+	if (ret < 0) {

Nitpick: if (ret) { suffices here, readl_poll_timeout returns 0 on success and negative errno on failure.

Quoted 68 lines
drivers/phy/rockchip/phy-rockchip-typec.c +64 -0
+		dev_err(tcphy->dev, "failed to enter power state %d\n", state);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * For the TypeC PHY, the 4 lanes are mapping to the USB TypeC receptacle pins
+ * as follows:
+ *   -------------------------------------------------------------------
+ *	PHY Lanes/Module Pins			TypeC Receptacle Pins
+ *   -------------------------------------------------------------------
+ *	Lane0 (tx_p/m_ln_0)			TX1+/TX1- (pins A2/A3)
+ *	Lane1 (tx_rx_p/m_ln_1)			RX1+/RX1- (pins B11/B10)
+ *	Lane2 (tx_rx_p/m_ln_2)			RX2+/RX2- (pins A11/A10)
+ *	Lane3 (tx_p/m_ln_3)			TX2+/TX2- (pins B2/B3)
+ *   -------------------------------------------------------------------
+ *
+ * USB and DP lanes mapping to TypeC PHY lanes for each of pin assignment
+ * options (normal connector orientation) described in the VESA DisplayPort
+ * Alt Mode on USB TypeC Standard as follows:
+ *
+ * ----------------------------------------------------------------------
+ *	PHY Lanes	A	B	C	D	E	F
+ * ----------------------------------------------------------------------
+ *	  0	       ML1     SSTX    ML2     SSTX    ML2     SSTX
+ *	  1	       ML3     SSRX    ML3     SSRX    ML3     SSRX
+ *	  2	       ML2     ML1     ML0     ML0     ML0     ML0
+ *	  3	       ML0     ML0     ML1     ML1     ML1     ML1
+ * ----------------------------------------------------------------------
+ */
+static void tcphy_set_lane_mapping(struct rockchip_typec_phy *tcphy, u8 mode)
+{
+	/*
+	 * The PMA_LANE_CFG register is used to select whether a PMA lane
+	 * is mapped for USB or PHY DP. The PMA_LANE_CFG register is
+	 * configured based on a normal connector orientation. Logic in the
+	 * PHY automatically handles the flipped connector case based on the
+	 * setting of orientation of TypeC PHY.
+	 */
+	if (mode == MODE_DFP_DP) {
+		/* This maps to VESA DP Alt Mode pin assignments C and E. */
+		writel(PMA_LANE3_DP_LANE_SEL(PHY_DP_LANE_1) |
+		       PMA_LANE3_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE2_DP_LANE_SEL(PHY_DP_LANE_0) |
+		       PMA_LANE2_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE1_DP_LANE_SEL(PHY_DP_LANE_3) |
+		       PMA_LANE1_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE0_DP_LANE_SEL(PHY_DP_LANE_2) |
+		       PMA_LANE0_INTERFACE_SEL(PMA_IF_PHY_DP),
+		       tcphy->base + PMA_LANE_CFG);
+	} else {
+		/* This maps to VESA DP Alt Mode pin assignments D and F. */
+		writel(PMA_LANE3_DP_LANE_SEL(PHY_DP_LANE_1) |
+		       PMA_LANE3_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE2_DP_LANE_SEL(PHY_DP_LANE_0) |
+		       PMA_LANE2_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE1_INTERFACE_SEL(PMA_IF_PIPE_PCS) |
+		       PMA_LANE0_INTERFACE_SEL(PMA_IF_PIPE_PCS),
+		       tcphy->base + PMA_LANE_CFG);
+	}
+}
+
 static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 {
 	u32 i, rdata;

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) · line 894

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +2 -0
@@ -743,8 +885,10 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 	tcphy_dp_aux_set_flip(tcphy);
 
 	tcphy_cfg_24m(tcphy);
+	tcphy_set_lane_mapping(tcphy, mode);
 
 	if (mode == MODE_DFP_DP) {
+		tcphy_cfg_usb3_to_usb2_only(tcphy, true);
 		tcphy_cfg_dp_pll(tcphy);
 		for (i = 0; i < 4; i++)
 			tcphy_dp_cfg_lane(tcphy, i);

Is there a difference between the values tcphy_set_lane_mapping() writes to PMA_LANE_CFG, and what this if block writes to PMA_LANE_CFG at the end (either PIN_ASSIGN_C_E or PIN_ASSIGN_D_F)?

If not, then I think think the second write to it may be redundant.

Quoted 24 lines
drivers/phy/rockchip/phy-rockchip-typec.c +6 -2
@@ -768,7 +912,10 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 		writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
 	}
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	reset_control_deassert(tcphy->uphy_rst);
 
@@ -811,8 +958,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	u8 mode;
 	int ret, ufp, dp;
 
+	/* If extcon not exist, try to use tcpm mode */
 	if (!edev)
-		return MODE_DFP_USB;
+		return tcphy->new_mode;
 
 	ufp = extcon_get_state(edev, EXTCON_USB);
 	dp = extcon_get_state(edev, EXTCON_DISP_DP);

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) · line 1007

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +7 -0
@@ -850,6 +998,71 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	return mode;
 }
 
+#if IS_ENABLED(CONFIG_TYPEC)
+static int tcphy_orien_sw_set(struct typec_switch_dev *sw,
+			      enum typec_orientation orien)
+{
+	struct rockchip_typec_phy *tcphy = typec_switch_get_drvdata(sw);
+
+	mutex_lock(&tcphy->lock);

Instead of this you can use

	guard(mutex)(&tcphy->lock);

from <linux/cleanup.h>

and get rid of the manual unlock and goto. The lock held by the guard statement will be dropped as soon as the scope of automatic variable declaration is left, so no manual goto unwind needs to be done.

Quoted 22 lines
drivers/phy/rockchip/phy-rockchip-typec.c +22 -0
+
+	if (orien == TYPEC_ORIENTATION_NONE) {
+		tcphy->new_mode = MODE_DISCONNECT;
+		goto unlock_ret;
+	}
+
+	tcphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
+
+unlock_ret:
+	mutex_unlock(&tcphy->lock);
+	return 0;
+}
+
+static void udphy_orien_switch_unregister(void *data)
+{
+	struct rockchip_typec_phy *tcphy = data;
+
+	typec_switch_unregister(tcphy->sw);
+}
+
+static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy)
+{

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) · line 1039

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+	struct typec_switch_desc sw_desc = { };
+	struct device_node *np;
+	int ret = 0;
+
+	np = of_get_child_by_name(tcphy->dev->of_node, "usb3-port");
+	if (!np)
+		return 0;
+
+	if (!of_property_read_bool(np, "orientation-switch"))
+		goto put_np;

np isn't needed after this check. Instead of manual freeing of np with a goto, you can use the __free(device_node) attribute.

	static int foo(struct rockchip_typec_phy *tcphy)
	{
		/* if return can happen before np assigned, NULL-init it here */
		struct device_node __free(device_node) *np;
		
		np = of_get_child_by_name(tcphy->dev->of_node, "usb3-port");
		if (!np)
			return 0;

		if (!of_property_read_bool(np, "orientation-switch"))
			return 0; /* no more manual put needed, done when scope left */

		/* ... etc etc. ... */

		return devm_add_action_or_reset(tcphy->dev, ...)
	}

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) · line 1048

Quoted 9 lines
drivers/phy/rockchip/phy-rockchip-typec.c +9 -0
+
+	sw_desc.drvdata = tcphy;
+	sw_desc.fwnode = device_get_named_child_node(tcphy->dev, "usb3-port");
+	sw_desc.set = tcphy_orien_sw_set;
+
+	tcphy->sw = typec_switch_register(tcphy->dev, &sw_desc);
+	if (IS_ERR(tcphy->sw)) {
+		dev_err(tcphy->dev, "Error register typec orientation switch: %ld\n",
+			PTR_ERR(tcphy->sw));

Instead of %ld, use %pe and drop the PTR_ERR(), so just:

	dev_err(tcphy->dev, "Error register typec orientation switch: %pe\n",
	        tcphy->sw);
Quoted 60 lines
drivers/phy/rockchip/phy-rockchip-typec.c +26 -9
+		ret = PTR_ERR(tcphy->sw);
+		goto put_np;
+	}
+
+	ret = devm_add_action_or_reset(tcphy->dev, udphy_orien_switch_unregister, tcphy);
+
+put_np:
+	of_node_put(np);
+	return ret;
+}
+#else
+static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy)
+{
+	return 0;
+}
+#endif
+
 static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
 				       bool value)
 {
@@ -989,14 +1202,9 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 
 	tcphy_dp_aux_calibration(tcphy);
 
-	writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
-
-	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A0, 1000,
-				 PHY_MODE_SET_TIMEOUT);
-	if (ret < 0) {
-		writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
-		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A0);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A0 power state\n");
 		goto power_on_finish;
 	}
 
@@ -1013,6 +1221,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 static int rockchip_dp_phy_power_off(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	int ret;
 
 	mutex_lock(&tcphy->lock);
 
@@ -1021,7 +1230,11 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
 
 	tcphy->mode &= ~MODE_DFP_DP;
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A2);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A2 power state\n");
+		goto unlock;
+	}
 
 	if (tcphy->mode == MODE_DISCONNECT)
 		tcphy_phy_deinit(tcphy);

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1258

Quoted 9 lines
drivers/phy/rockchip/phy-rockchip-typec.c +6 -0
@@ -1037,6 +1250,93 @@ static const struct phy_ops rockchip_dp_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
+#if IS_ENABLED(CONFIG_TYPEC)
+static int tcphy_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
+{
+	struct rockchip_typec_phy *tcphy = typec_mux_get_drvdata(mux);
+	struct typec_displayport_data *data;
+	int hpd = 0;

hpd doesn't need to be initialised here.

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1260

Quoted 2 lines
drivers/phy/rockchip/phy-rockchip-typec.c +2 -0
+
+	mutex_lock(&tcphy->lock);

Prefer guard(mutex)(&tcphy->lock); here to combat potential for future lock leaking bugs.

Quoted 17 lines
drivers/phy/rockchip/phy-rockchip-typec.c +17 -0
+
+	switch (state->mode) {
+	case TYPEC_STATE_SAFE:
+		fallthrough;
+	case TYPEC_STATE_USB:
+		tcphy->new_mode = MODE_DFP_USB;
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], 0);
+		break;
+	case TYPEC_DP_STATE_C:
+	case TYPEC_DP_STATE_E:
+		if (state->alt->svid != USB_TYPEC_DP_SID)
+			break;
+		tcphy->new_mode = MODE_DFP_DP;
+		data = state->data;
+		hpd = !!(data->status & DP_STATUS_HPD_STATE);
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], hpd ? 4 : 0);
+		break;

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1287

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+	case TYPEC_DP_STATE_D:
+		if (state->alt->svid != USB_TYPEC_DP_SID)
+			break;
+		tcphy->new_mode = MODE_DFP_DP | MODE_DFP_USB;
+		data = state->data;
+		hpd = !!(data->status & DP_STATUS_HPD_STATE);
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], hpd ? 2 : 0);
+		break;
+	default:
+		break;

Might be good to return -EINVAL here. No additional ret local needed with above guard statement. :)

Quoted 16 lines
drivers/phy/rockchip/phy-rockchip-typec.c +16 -0
+	}
+
+	mutex_unlock(&tcphy->lock);
+
+	return 0;
+}
+
+static void tcphy_typec_mux_unregister(void *data)
+{
+	struct rockchip_typec_phy *tcphy = data;
+
+	typec_mux_unregister(tcphy->mux);
+}
+
+static int tcphy_setup_typec_mux(struct rockchip_typec_phy *tcphy)
+{

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1313

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+	struct typec_mux_desc mux_desc = {};
+	struct device_node *np;
+	int ret = 0;
+
+	np = of_get_child_by_name(tcphy->dev->of_node, "dp-port");
+	if (!np)
+		return 0;
+
+	if (!of_property_read_bool(np, "mode-switch"))
+		goto put_np;

__free attribute on np can get rid of the manual goto put_np stuff here as well.

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1322

Quoted 9 lines
drivers/phy/rockchip/phy-rockchip-typec.c +9 -0
+
+	mux_desc.drvdata = tcphy;
+	mux_desc.fwnode = device_get_named_child_node(tcphy->dev, "dp-port");
+	mux_desc.set = tcphy_typec_mux_set;
+
+	tcphy->mux = typec_mux_register(tcphy->dev, &mux_desc);
+	if (IS_ERR(tcphy->mux)) {
+		dev_err(tcphy->dev, "Error register typec mux: %ld\n",
+			PTR_ERR(tcphy->mux));

%pe format specifier again.

Quoted 31 lines
drivers/phy/rockchip/phy-rockchip-typec.c +24 -0
+		ret = PTR_ERR(tcphy->mux);
+		goto put_np;
+	}
+
+	ret = devm_add_action_or_reset(tcphy->dev, tcphy_typec_mux_unregister, tcphy);
+
+put_np:
+	of_node_put(np);
+	return ret;
+}
+#else
+static int tcphy_setup_typec_mux(struct rockchip_typec_phy *tcphy)
+{
+	return 0;
+}
+#endif
+
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
 {
@@ -1095,6 +1395,25 @@ static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
 	tcphy->mode = MODE_DISCONNECT;
 }
 
+static int typec_dp_lane_get(struct rockchip_typec_phy *tcphy)
+{
+	int dp_lanes;
+
+	switch (tcphy->new_mode) {
+	case MODE_DFP_DP:
+		dp_lanes = 4;

drivers/phy/rockchip/phy-rockchip-typec.c static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) · line 1414

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+		break;
+	case MODE_DFP_DP | MODE_DFP_USB:
+		dp_lanes = 2;
+		break;
+	default:
+		dp_lanes = 0;
+		break;
+	}
+
+	return dp_lanes;

dp_lanes local doesn't need to exist here, you can just return from the switch statement directly:

	static int typec_dp_lane_get(struct rockchip_typec_phy *tcphy)
	{
		switch (tcphy->new_mode) {
		case MODE_DFP_DP:
			return 4;
		case MODE_DFP_DP | MODE_DFP_USB:
			return 2;
		default:
			return 0;
		}
	}
Quoted 22 lines
drivers/phy/rockchip/phy-rockchip-typec.c +4 -0
+}
+
 static int rockchip_typec_phy_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1142,6 +1461,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 		return ret;
 
 	tcphy->dev = dev;
+	tcphy->new_mode = MODE_DFP_USB;
 	platform_set_drvdata(pdev, tcphy);
 	mutex_init(&tcphy->lock);
 
@@ -1151,6 +1471,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 	if (IS_ERR(tcphy->extcon)) {
 		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
 			tcphy->extcon = NULL;
+			dev_info(dev, "extcon not exist, try to use typec mux\n");
 		} else {
 			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
 				dev_err(dev, "Invalid or missing extcon\n");

drivers/phy/rockchip/phy-rockchip-typec.c static int rockchip_typec_phy_probe(struct platform_device *pdev) · line 1488

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +7 -0
@@ -1158,19 +1479,34 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 		}
 	}
 
+	ret = tcphy_setup_orien_switch(tcphy);
+	if (ret)
+		return ret;
+
+	ret = tcphy_setup_typec_mux(tcphy);
+	if (ret)
+		return ret;

If they are just used in the probe function, you can make the error prints in tcphy_setup_orien_switch() and tcphy_setup_typec_mux() use dev_err_probe instead. That way, if the probe function fails, the error message is shown in the devices_deferred debugfs file.

Quoted 18 lines
drivers/phy/rockchip/phy-rockchip-typec.c +7 -2
+
 	pm_runtime_enable(dev);
 
 	for_each_available_child_of_node(np, child_np) {
 		struct phy *phy;
 
-		if (of_node_name_eq(child_np, "dp-port"))
+		if (of_node_name_eq(child_np, "dp-port")) {
 			phy = devm_phy_create(dev, child_np,
 					      &rockchip_dp_phy_ops);
-		else if (of_node_name_eq(child_np, "usb3-port"))
+			if (!IS_ERR(phy)) {
+				tcphy->phys[TYPEC_PHY_DP] = phy;
+				phy_set_bus_width(phy, typec_dp_lane_get(tcphy));
+			}
+		} else if (of_node_name_eq(child_np, "usb3-port")) {
 			phy = devm_phy_create(dev, child_np,

drivers/phy/rockchip/phy-rockchip-typec.c static int rockchip_typec_phy_probe(struct platform_device *pdev) · line 1512

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +4 -1
 					      &rockchip_usb3_phy_ops);
-		else
+			if (!IS_ERR(phy))
+				tcphy->phys[TYPEC_PHY_USB] = phy;
+		} else {
 			continue;
+		}
 
 		if (IS_ERR(phy)) {
 			dev_err(dev, "failed to create phy: %pOFn\n",

Kind regards,
Nicolas Frattaroli

#14 lore raw

Re: [PATCH v15 4/9] phy: rockchip: phy-rockchip-typec: Add typec_mux/typec_switch support

Hello Nicolas,

Thank you for your review! :) In the next version, I will follow your suggestions to make the changes and split this patch into a new series.

Quoted 162 lines · Nicolas Frattaroli
Quoted 161 lines · On Wednesday, 4 March 2026 10:41:47 Central European Summer Time Chaoyi Chen…

From: Chaoyi Chen <[email protected]>

This patch add support for Type-C Port Controller Manager. Each PHY will register typec_mux and typec_switch when external Type-C controller is present. Type-C events are handled by TCPM without extcon.

The extcon device should still be supported.

---

(no changes since v7)

Changes in v6:
- Fix depend in Kconfig.
- Check DP svid in tcphy_typec_mux_set().
- Remove mode setting in tcphy_orien_sw_set().

(no changes since v5)

Changes in v4:
- Remove notify DP HPD state by USB/DP PHY.

(no changes since v3)

Changes in v2:
- Fix compile error when CONFIG_TYPEC is not enabled.
- Notify DP HPD state by USB/DP PHY.
---

 drivers/phy/rockchip/Kconfig              |   1 +
 drivers/phy/rockchip/phy-rockchip-typec.c | 368 +++++++++++++++++++++-
 2 files changed, 353 insertions(+), 16 deletions(-)
drivers/phy/rockchip/Kconfig +1 -0
index 14698571b607..db4adc7c53da 100644
@@ -119,6 +119,7 @@ config PHY_ROCKCHIP_SNPS_PCIE3
 config PHY_ROCKCHIP_TYPEC
 	tristate "Rockchip TYPEC PHY Driver"
 	depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
+	depends on TYPEC || TYPEC=n
 	select EXTCON
 	select GENERIC_PHY
 	select RESET_CONTROLLER
drivers/phy/rockchip/phy-rockchip-typec.c +51 -2
index d9701b6106d5..1f5b4142cbe4 100644
@@ -54,6 +54,8 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -286,12 +288,23 @@
 #define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
 
 #define PMA_LANE_CFG			(0xc000 << 2)
+#define PMA_LANE3_DP_LANE_SEL(x)	(((x) & 0x3) << 14)
+#define PMA_LANE3_INTERFACE_SEL(x)	(((x) & 0x1) << 12)
+#define PMA_LANE2_DP_LANE_SEL(x)	(((x) & 0x3) << 10)
+#define PMA_LANE2_INTERFACE_SEL(x)	(((x) & 0x1) << 8)
+#define PMA_LANE1_DP_LANE_SEL(x)	(((x) & 0x3) << 6)
+#define PMA_LANE1_INTERFACE_SEL(x)	(((x) & 0x1) << 4)
+#define PMA_LANE0_DP_LANE_SEL(x)	(((x) & 0x3) << 2)
+#define PMA_LANE0_INTERFACE_SEL(x)	(((x) & 0x1) << 0)
 #define PIPE_CMN_CTRL1			(0xc001 << 2)
 #define PIPE_CMN_CTRL2			(0xc002 << 2)
 #define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
 #define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
 #define PIPE_RCV_DET_INH		(0xc005 << 2)
 #define DP_MODE_CTL			(0xc008 << 2)
+#define PHY_DP_POWER_STATE_ACK_MASK	GENMASK(7, 4)
+#define PHY_DP_POWER_STATE_ACK_SHIFT	4
+#define PHY_DP_POWER_STATE_MASK		GENMASK(3, 0)
 #define DP_CLK_CTL			(0xc009 << 2)
 #define STS				(0xc00F << 2)
 #define PHY_ISO_CMN_CTRL		(0xc010 << 2)
@@ -327,8 +340,15 @@
 
 #define DP_MODE_A0			BIT(4)
 #define DP_MODE_A2			BIT(6)
-#define DP_MODE_ENTER_A0		0xc101
-#define DP_MODE_ENTER_A2		0xc104
+
+#define DP_MODE_MASK			0xf
+#define DP_MODE_ENTER_A0		BIT(0)
+#define DP_MODE_ENTER_A2		BIT(2)
+#define DP_MODE_ENTER_A3		BIT(3)
+#define DP_MODE_A0_ACK			BIT(4)
+#define DP_MODE_A2_ACK			BIT(6)
+#define DP_MODE_A3_ACK			BIT(7)
+#define DP_LINK_RESET_DEASSERTED	BIT(8)
 
 #define PHY_MODE_SET_TIMEOUT		100000
 
@@ -340,6 +360,31 @@
 #define MODE_DFP_USB			BIT(1)
 #define MODE_DFP_DP			BIT(2)
 
+enum phy_dp_lane_num {
+	PHY_DP_LANE_0 = 0,
+	PHY_DP_LANE_1,
+	PHY_DP_LANE_2,
+	PHY_DP_LANE_3,
+};
+
+enum phy_pma_if {
+	PMA_IF_PIPE_PCS = 0,
+	PMA_IF_PHY_DP,
+};
+
+enum phy_typec_role {
+	TYPEC_PHY_USB = 0,
+	TYPEC_PHY_DP,
+	TYPEC_PHY_MAX,
+};
+
+enum phy_dp_power_state {
+	PHY_DP_POWER_STATE_A0 = 0,
+	PHY_DP_POWER_STATE_A1,
+	PHY_DP_POWER_STATE_A2,
+	PHY_DP_POWER_STATE_A3,
+};
+
 struct usb3phy_reg {
 	u32 offset;
 	u32 enable_bit;
@@ -372,18 +417,22 @@ struct rockchip_typec_phy {
 	struct device *dev;
 	void __iomem *base;
 	struct extcon_dev *extcon;
+	struct typec_mux_dev *mux;
+	struct typec_switch_dev *sw;
 	struct regmap *grf_regs;
 	struct clk *clk_core;
 	struct clk *clk_ref;
 	struct reset_control *uphy_rst;
 	struct reset_control *pipe_rst;
 	struct reset_control *tcphy_rst;
+	struct phy *phys[TYPEC_PHY_MAX];
 	const struct rockchip_usb3phy_port_cfg *port_cfgs;
 	/* mutex to protect access to individual PHYs */
 	struct mutex lock;
 
 	bool flip;
 	u8 mode;
+	u8 new_mode;
 };
 
 struct phy_reg {
@@ -454,6 +503,99 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
 	{ /* sentinel */ }
 };
 
Quoted 8 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 508

Quoted 3 lines
drivers/phy/rockchip/phy-rockchip-typec.c +3 -0
+static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
+				       bool value);
+

If possible, please avoid forward declarations of this static function and just move the implementation here if it needs to be declared earlier.

Then I also need to move "property_enable()" to here. Okay, I will fix this in next version.

Quoted 11 lines
Quoted 11 lines
drivers/phy/rockchip/phy-rockchip-typec.c +11 -0
+static int tcphy_dp_set_power_state(struct rockchip_typec_phy *tcphy,
+				    enum phy_dp_power_state state)
+{
+	u32 ack, reg, sts = BIT(state);
+	int ret;
+
+	/*
+	 * Power state changes must not be requested until after the cmn_ready
+	 * signal has gone active.
+	 */
+	reg = readl(tcphy->base + PMA_CMN_CTRL1);
Quoted 9 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 523

Quoted 4 lines
drivers/phy/rockchip/phy-rockchip-typec.c +4 -0
+	if (!(reg & CMN_READY)) {
+		dev_err(tcphy->dev, "cmn_ready in the inactive state\n");
+		return -EINVAL;
+	}

You can use readl in the if condition directly here, since reg isn't used otherwise, but I'm also fine with it as-is if you think it helps readability.

Hmmm. From the context, the current version should be more readable.

Quoted 13 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 533

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+
+	reg = readl(tcphy->base + DP_MODE_CTL);
+	reg &= ~PHY_DP_POWER_STATE_MASK;
+	reg |= sts;
+	writel(reg, tcphy->base + DP_MODE_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + DP_MODE_CTL,
+				 ack, (((ack & PHY_DP_POWER_STATE_ACK_MASK) >>
+				 PHY_DP_POWER_STATE_ACK_SHIFT) == sts), 10,
+				 PHY_MODE_SET_TIMEOUT);

Here please use FIELD_GET() from <linux/bitfield.h> like this:

Quoted 6 lines
	ret = readl_poll_timeout(tcphy->base + DP_MODE_CTL, ack,
				 FIELD_GET(PHY_DP_POWER_STATE_ACK_MASK, ack) == sts,
				 10, PHY_MODE_SET_TIMEOUT);

PHY_DP_POWER_STATE_ACK_SHIFT is then no longer needed.

Great. Will fix in next version.

Quoted 88 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { · line 534

Quoted 1 line
drivers/phy/rockchip/phy-rockchip-typec.c +1 -0
+	if (ret < 0) {

Nitpick: if (ret) { suffices here, readl_poll_timeout returns 0 on success and negative errno on failure.

Quoted 68 lines
drivers/phy/rockchip/phy-rockchip-typec.c +64 -0
+		dev_err(tcphy->dev, "failed to enter power state %d\n", state);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * For the TypeC PHY, the 4 lanes are mapping to the USB TypeC receptacle pins
+ * as follows:
+ *   -------------------------------------------------------------------
+ *	PHY Lanes/Module Pins			TypeC Receptacle Pins
+ *   -------------------------------------------------------------------
+ *	Lane0 (tx_p/m_ln_0)			TX1+/TX1- (pins A2/A3)
+ *	Lane1 (tx_rx_p/m_ln_1)			RX1+/RX1- (pins B11/B10)
+ *	Lane2 (tx_rx_p/m_ln_2)			RX2+/RX2- (pins A11/A10)
+ *	Lane3 (tx_p/m_ln_3)			TX2+/TX2- (pins B2/B3)
+ *   -------------------------------------------------------------------
+ *
+ * USB and DP lanes mapping to TypeC PHY lanes for each of pin assignment
+ * options (normal connector orientation) described in the VESA DisplayPort
+ * Alt Mode on USB TypeC Standard as follows:
+ *
+ * ----------------------------------------------------------------------
+ *	PHY Lanes	A	B	C	D	E	F
+ * ----------------------------------------------------------------------
+ *	  0	       ML1     SSTX    ML2     SSTX    ML2     SSTX
+ *	  1	       ML3     SSRX    ML3     SSRX    ML3     SSRX
+ *	  2	       ML2     ML1     ML0     ML0     ML0     ML0
+ *	  3	       ML0     ML0     ML1     ML1     ML1     ML1
+ * ----------------------------------------------------------------------
+ */
+static void tcphy_set_lane_mapping(struct rockchip_typec_phy *tcphy, u8 mode)
+{
+	/*
+	 * The PMA_LANE_CFG register is used to select whether a PMA lane
+	 * is mapped for USB or PHY DP. The PMA_LANE_CFG register is
+	 * configured based on a normal connector orientation. Logic in the
+	 * PHY automatically handles the flipped connector case based on the
+	 * setting of orientation of TypeC PHY.
+	 */
+	if (mode == MODE_DFP_DP) {
+		/* This maps to VESA DP Alt Mode pin assignments C and E. */
+		writel(PMA_LANE3_DP_LANE_SEL(PHY_DP_LANE_1) |
+		       PMA_LANE3_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE2_DP_LANE_SEL(PHY_DP_LANE_0) |
+		       PMA_LANE2_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE1_DP_LANE_SEL(PHY_DP_LANE_3) |
+		       PMA_LANE1_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE0_DP_LANE_SEL(PHY_DP_LANE_2) |
+		       PMA_LANE0_INTERFACE_SEL(PMA_IF_PHY_DP),
+		       tcphy->base + PMA_LANE_CFG);
+	} else {
+		/* This maps to VESA DP Alt Mode pin assignments D and F. */
+		writel(PMA_LANE3_DP_LANE_SEL(PHY_DP_LANE_1) |
+		       PMA_LANE3_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE2_DP_LANE_SEL(PHY_DP_LANE_0) |
+		       PMA_LANE2_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE1_INTERFACE_SEL(PMA_IF_PIPE_PCS) |
+		       PMA_LANE0_INTERFACE_SEL(PMA_IF_PIPE_PCS),
+		       tcphy->base + PMA_LANE_CFG);
+	}
+}
+
 static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 {
 	u32 i, rdata;

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) · line 894

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +2 -0
@@ -743,8 +885,10 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 	tcphy_dp_aux_set_flip(tcphy);
 
 	tcphy_cfg_24m(tcphy);
+	tcphy_set_lane_mapping(tcphy, mode);
 
 	if (mode == MODE_DFP_DP) {
+		tcphy_cfg_usb3_to_usb2_only(tcphy, true);
 		tcphy_cfg_dp_pll(tcphy);
 		for (i = 0; i < 4; i++)
 			tcphy_dp_cfg_lane(tcphy, i);

Is there a difference between the values tcphy_set_lane_mapping() writes to PMA_LANE_CFG, and what this if block writes to PMA_LANE_CFG at the end (either PIN_ASSIGN_C_E or PIN_ASSIGN_D_F)?

Quoted 2 lines

If not, then I think think the second write to it may be redundant.

That's right. I will remove them.

Quoted 37 lines
Quoted 24 lines
drivers/phy/rockchip/phy-rockchip-typec.c +6 -2
@@ -768,7 +912,10 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 		writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
 	}
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
+	writel(val, tcphy->base + DP_MODE_CTL);
 
 	reset_control_deassert(tcphy->uphy_rst);
 
@@ -811,8 +958,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	u8 mode;
 	int ret, ufp, dp;
 
+	/* If extcon not exist, try to use tcpm mode */
 	if (!edev)
-		return MODE_DFP_USB;
+		return tcphy->new_mode;
 
 	ufp = extcon_get_state(edev, EXTCON_USB);
 	dp = extcon_get_state(edev, EXTCON_DISP_DP);

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) · line 1007

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +7 -0
@@ -850,6 +998,71 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	return mode;
 }
 
+#if IS_ENABLED(CONFIG_TYPEC)
+static int tcphy_orien_sw_set(struct typec_switch_dev *sw,
+			      enum typec_orientation orien)
+{
+	struct rockchip_typec_phy *tcphy = typec_switch_get_drvdata(sw);
+
+	mutex_lock(&tcphy->lock);

Instead of this you can use

Quoted 8 lines
	guard(mutex)(&tcphy->lock);

from <linux/cleanup.h>

and get rid of the manual unlock and goto. The lock held by the guard statement will be dropped as soon as the scope of automatic variable declaration is left, so no manual goto unwind needs to be done.

Thanks. I will try it.

Quoted 36 lines
Quoted 22 lines
drivers/phy/rockchip/phy-rockchip-typec.c +22 -0
+
+	if (orien == TYPEC_ORIENTATION_NONE) {
+		tcphy->new_mode = MODE_DISCONNECT;
+		goto unlock_ret;
+	}
+
+	tcphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
+
+unlock_ret:
+	mutex_unlock(&tcphy->lock);
+	return 0;
+}
+
+static void udphy_orien_switch_unregister(void *data)
+{
+	struct rockchip_typec_phy *tcphy = data;
+
+	typec_switch_unregister(tcphy->sw);
+}
+
+static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy)
+{

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) · line 1039

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+	struct typec_switch_desc sw_desc = { };
+	struct device_node *np;
+	int ret = 0;
+
+	np = of_get_child_by_name(tcphy->dev->of_node, "usb3-port");
+	if (!np)
+		return 0;
+
+	if (!of_property_read_bool(np, "orientation-switch"))
+		goto put_np;

np isn't needed after this check. Instead of manual freeing of np with a goto, you can use the __free(device_node) attribute.

Quoted 17 lines
	static int foo(struct rockchip_typec_phy *tcphy)
	{
		/* if return can happen before np assigned, NULL-init it here */
		struct device_node __free(device_node) *np;
		
		np = of_get_child_by_name(tcphy->dev->of_node, "usb3-port");
		if (!np)
			return 0;

		if (!of_property_read_bool(np, "orientation-switch"))
			return 0; /* no more manual put needed, done when scope left */

		/* ... etc etc. ... */

		return devm_add_action_or_reset(tcphy->dev, ...)
	}

Will fix in next version.

Quoted 12 lines

drivers/phy/rockchip/phy-rockchip-typec.c static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) · line 1048

Quoted 9 lines
drivers/phy/rockchip/phy-rockchip-typec.c +9 -0
+
+	sw_desc.drvdata = tcphy;
+	sw_desc.fwnode = device_get_named_child_node(tcphy->dev, "usb3-port");
+	sw_desc.set = tcphy_orien_sw_set;
+
+	tcphy->sw = typec_switch_register(tcphy->dev, &sw_desc);
+	if (IS_ERR(tcphy->sw)) {
+		dev_err(tcphy->dev, "Error register typec orientation switch: %ld\n",
+			PTR_ERR(tcphy->sw));

Instead of %ld, use %pe and drop the PTR_ERR(), so just:

Quoted 3 lines
	dev_err(tcphy->dev, "Error register typec orientation switch: %pe\n",
	        tcphy->sw);

Will fix in next version.

Quoted 63 lines
Quoted 63 lines
drivers/phy/rockchip/phy-rockchip-typec.c +26 -9
+		ret = PTR_ERR(tcphy->sw);
+		goto put_np;
+	}
+
+	ret = devm_add_action_or_reset(tcphy->dev, udphy_orien_switch_unregister, tcphy);
+
+put_np:
+	of_node_put(np);
+	return ret;
+}
+#else
+static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy)
+{
+	return 0;
+}
+#endif
+
 static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
 				       bool value)
 {
@@ -989,14 +1202,9 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 
 	tcphy_dp_aux_calibration(tcphy);
 
-	writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
-
-	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A0, 1000,
-				 PHY_MODE_SET_TIMEOUT);
-	if (ret < 0) {
-		writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
-		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A0);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A0 power state\n");
 		goto power_on_finish;
 	}
 
@@ -1013,6 +1221,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 static int rockchip_dp_phy_power_off(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	int ret;
 
 	mutex_lock(&tcphy->lock);
 
@@ -1021,7 +1230,11 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
 
 	tcphy->mode &= ~MODE_DFP_DP;
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A2);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A2 power state\n");
+		goto unlock;
+	}
 
 	if (tcphy->mode == MODE_DISCONNECT)
 		tcphy_phy_deinit(tcphy);
@@ -1037,6 +1250,93 @@ static const struct phy_ops rockchip_dp_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
Quoted 9 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1258

Quoted 6 lines
drivers/phy/rockchip/phy-rockchip-typec.c +6 -0
+#if IS_ENABLED(CONFIG_TYPEC)
+static int tcphy_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
+{
+	struct rockchip_typec_phy *tcphy = typec_mux_get_drvdata(mux);
+	struct typec_displayport_data *data;
+	int hpd = 0;

hpd doesn't need to be initialised here.

That's right.

Quoted 6 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1260

Quoted 2 lines
drivers/phy/rockchip/phy-rockchip-typec.c +2 -0
+
+	mutex_lock(&tcphy->lock);

Prefer guard(mutex)(&tcphy->lock); here to combat potential for future lock leaking bugs.

Will fix in next version.

Quoted 22 lines
Quoted 22 lines
drivers/phy/rockchip/phy-rockchip-typec.c +22 -0
+
+	switch (state->mode) {
+	case TYPEC_STATE_SAFE:
+		fallthrough;
+	case TYPEC_STATE_USB:
+		tcphy->new_mode = MODE_DFP_USB;
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], 0);
+		break;
+	case TYPEC_DP_STATE_C:
+	case TYPEC_DP_STATE_E:
+		if (state->alt->svid != USB_TYPEC_DP_SID)
+			break;
+		tcphy->new_mode = MODE_DFP_DP;
+		data = state->data;
+		hpd = !!(data->status & DP_STATUS_HPD_STATE);
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], hpd ? 4 : 0);
+		break;
+	case TYPEC_DP_STATE_D:
+		if (state->alt->svid != USB_TYPEC_DP_SID)
+			break;
+		tcphy->new_mode = MODE_DFP_DP | MODE_DFP_USB;
+		data = state->data;
Quoted 9 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1287

Quoted 5 lines
drivers/phy/rockchip/phy-rockchip-typec.c +5 -0
+		hpd = !!(data->status & DP_STATUS_HPD_STATE);
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], hpd ? 2 : 0);
+		break;
+	default:
+		break;

Might be good to return -EINVAL here. No additional ret local needed with above guard statement. :)

It make sense. Will fix in next version.

Quoted 21 lines
Quoted 21 lines
drivers/phy/rockchip/phy-rockchip-typec.c +21 -0
+	}
+
+	mutex_unlock(&tcphy->lock);
+
+	return 0;
+}
+
+static void tcphy_typec_mux_unregister(void *data)
+{
+	struct rockchip_typec_phy *tcphy = data;
+
+	typec_mux_unregister(tcphy->mux);
+}
+
+static int tcphy_setup_typec_mux(struct rockchip_typec_phy *tcphy)
+{
+	struct typec_mux_desc mux_desc = {};
+	struct device_node *np;
+	int ret = 0;
+
+	np = of_get_child_by_name(tcphy->dev->of_node, "dp-port");
Quoted 9 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1313

Quoted 5 lines
drivers/phy/rockchip/phy-rockchip-typec.c +5 -0
+	if (!np)
+		return 0;
+
+	if (!of_property_read_bool(np, "mode-switch"))
+		goto put_np;

__free attribute on np can get rid of the manual goto put_np stuff here as well.

Will fix in next version.

Quoted 12 lines

drivers/phy/rockchip/phy-rockchip-typec.c static const struct phy_ops rockchip_dp_phy_ops = { · line 1322

Quoted 9 lines
drivers/phy/rockchip/phy-rockchip-typec.c +9 -0
+
+	mux_desc.drvdata = tcphy;
+	mux_desc.fwnode = device_get_named_child_node(tcphy->dev, "dp-port");
+	mux_desc.set = tcphy_typec_mux_set;
+
+	tcphy->mux = typec_mux_register(tcphy->dev, &mux_desc);
+	if (IS_ERR(tcphy->mux)) {
+		dev_err(tcphy->dev, "Error register typec mux: %ld\n",
+			PTR_ERR(tcphy->mux));

%pe format specifier again.

Will fix in next version.

Quoted 45 lines
Quoted 31 lines
drivers/phy/rockchip/phy-rockchip-typec.c +24 -0
+		ret = PTR_ERR(tcphy->mux);
+		goto put_np;
+	}
+
+	ret = devm_add_action_or_reset(tcphy->dev, tcphy_typec_mux_unregister, tcphy);
+
+put_np:
+	of_node_put(np);
+	return ret;
+}
+#else
+static int tcphy_setup_typec_mux(struct rockchip_typec_phy *tcphy)
+{
+	return 0;
+}
+#endif
+
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
 {
@@ -1095,6 +1395,25 @@ static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
 	tcphy->mode = MODE_DISCONNECT;
 }
 
+static int typec_dp_lane_get(struct rockchip_typec_phy *tcphy)
+{
+	int dp_lanes;
+
+	switch (tcphy->new_mode) {
+	case MODE_DFP_DP:
+		dp_lanes = 4;

drivers/phy/rockchip/phy-rockchip-typec.c static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) · line 1414

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +10 -0
+		break;
+	case MODE_DFP_DP | MODE_DFP_USB:
+		dp_lanes = 2;
+		break;
+	default:
+		dp_lanes = 0;
+		break;
+	}
+
+	return dp_lanes;

dp_lanes local doesn't need to exist here, you can just return from the switch statement directly:

Quoted 13 lines
	static int typec_dp_lane_get(struct rockchip_typec_phy *tcphy)
	{
		switch (tcphy->new_mode) {
		case MODE_DFP_DP:
			return 4;
		case MODE_DFP_DP | MODE_DFP_USB:
			return 2;
		default:
			return 0;
		}
	}

Exactly. Will fix in next version.

Quoted 25 lines
Quoted 25 lines
drivers/phy/rockchip/phy-rockchip-typec.c +4 -0
+}
+
 static int rockchip_typec_phy_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1142,6 +1461,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 		return ret;
 
 	tcphy->dev = dev;
+	tcphy->new_mode = MODE_DFP_USB;
 	platform_set_drvdata(pdev, tcphy);
 	mutex_init(&tcphy->lock);
 
@@ -1151,6 +1471,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 	if (IS_ERR(tcphy->extcon)) {
 		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
 			tcphy->extcon = NULL;
+			dev_info(dev, "extcon not exist, try to use typec mux\n");
 		} else {
 			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
 				dev_err(dev, "Invalid or missing extcon\n");
@@ -1158,19 +1479,34 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 		}
 	}
 
Quoted 13 lines

drivers/phy/rockchip/phy-rockchip-typec.c static int rockchip_typec_phy_probe(struct platform_device *pdev) · line 1488

Quoted 7 lines
drivers/phy/rockchip/phy-rockchip-typec.c +7 -0
+	ret = tcphy_setup_orien_switch(tcphy);
+	if (ret)
+		return ret;
+
+	ret = tcphy_setup_typec_mux(tcphy);
+	if (ret)
+		return ret;

If they are just used in the probe function, you can make the error prints in tcphy_setup_orien_switch() and tcphy_setup_typec_mux() use dev_err_probe instead. That way, if the probe function fails, the error message is shown in the devices_deferred debugfs file.

Nice, I will use it in next version.

Quoted 34 lines
Quoted 18 lines
drivers/phy/rockchip/phy-rockchip-typec.c +7 -2
+
 	pm_runtime_enable(dev);
 
 	for_each_available_child_of_node(np, child_np) {
 		struct phy *phy;
 
-		if (of_node_name_eq(child_np, "dp-port"))
+		if (of_node_name_eq(child_np, "dp-port")) {
 			phy = devm_phy_create(dev, child_np,
 					      &rockchip_dp_phy_ops);
-		else if (of_node_name_eq(child_np, "usb3-port"))
+			if (!IS_ERR(phy)) {
+				tcphy->phys[TYPEC_PHY_DP] = phy;
+				phy_set_bus_width(phy, typec_dp_lane_get(tcphy));
+			}
+		} else if (of_node_name_eq(child_np, "usb3-port")) {
 			phy = devm_phy_create(dev, child_np,

drivers/phy/rockchip/phy-rockchip-typec.c static int rockchip_typec_phy_probe(struct platform_device *pdev) · line 1512

Quoted 10 lines
drivers/phy/rockchip/phy-rockchip-typec.c +4 -1
 					      &rockchip_usb3_phy_ops);
-		else
+			if (!IS_ERR(phy))
+				tcphy->phys[TYPEC_PHY_USB] = phy;
+		} else {
 			continue;
+		}
 
 		if (IS_ERR(phy)) {
 			dev_err(dev, "failed to create phy: %pOFn\n",

Kind regards,
Nicolas Frattaroli

Signature · 7 lines
Best, 
Chaoyi

-- 
linux-phy mailing list
[email protected]
https://lists.infradead.org/mailman/listinfo/linux-phy
#15 lore raw

PATCH v15 5/9 phy: rockchip: phy-rockchip-typec: Add DRM AUX bridge

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

Using the DRM_AUX_BRIDGE helper to create the transparent DRM bridge device.

---

(no changes since v11)

Changes in v10:
- Use drm_aux_bridge_register_from_node() instead.

(no changes since v7)

Changes in v6:
- Fix depend in Kconfig.
---

 drivers/phy/rockchip/Kconfig              | 2 ++
 drivers/phy/rockchip/phy-rockchip-typec.c | 5 +++++
 2 files changed, 7 insertions(+)
drivers/phy/rockchip/Kconfig +2 -0
index db4adc7c53da..bcb5476222fc 100644
@@ -120,6 +120,8 @@ config PHY_ROCKCHIP_TYPEC
 	tristate "Rockchip TYPEC PHY Driver"
 	depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
 	depends on TYPEC || TYPEC=n
+	depends on DRM || DRM=n
+	select DRM_AUX_BRIDGE if DRM_BRIDGE
 	select EXTCON
 	select GENERIC_PHY
 	select RESET_CONTROLLER
drivers/phy/rockchip/phy-rockchip-typec.c +5 -0
index 1f5b4142cbe4..e31b778c3537 100644
@@ -56,6 +56,7 @@
 #include <linux/phy/phy.h>
 #include <linux/usb/typec_dp.h>
 #include <linux/usb/typec_mux.h>
+#include <drm/bridge/aux-bridge.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -1312,6 +1313,10 @@ static int tcphy_setup_typec_mux(struct rockchip_typec_phy *tcphy)
 	if (!of_property_read_bool(np, "mode-switch"))
 		goto put_np;
 
+	ret = drm_aux_bridge_register_from_node(tcphy->dev, np);
+	if (ret)
+		goto put_np;
+
 	mux_desc.drvdata = tcphy;
 	mux_desc.fwnode = device_get_named_child_node(tcphy->dev, "dp-port");
 	mux_desc.set = tcphy_typec_mux_set;
Signature · 1 line
2.51.1
#16 lore raw

PATCH v15 6/9 drm/rockchip: cdn-dp: Support handle lane info without extcon

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

This patch add support for get PHY lane info without help of extcon.

There is no extcon needed if the Type-C controller is present. In this case, the lane info can be get from PHY instead of extcon.

The extcon device should still be supported if Type-C controller is not present.

---

(no changes since v5)

Changes in v4:
- Remove cdn_dp_hpd_notify().

(no changes since v3)

Changes in v2:
- Ignore duplicate HPD events.
---

 drivers/gpu/drm/rockchip/cdn-dp-core.c | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)
drivers/gpu/drm/rockchip/cdn-dp-core.c +17 -8
index b7e3f5dcf8d5..1e27301584a4 100644
@@ -156,6 +156,9 @@ static int cdn_dp_get_port_lanes(struct cdn_dp_port *port)
 	int dptx;
 	u8 lanes;
 
+	if (!edev)
+		return phy_get_bus_width(port->phy);
+
 	dptx = extcon_get_state(edev, EXTCON_DISP_DP);
 	if (dptx > 0) {
 		extcon_get_property(edev, EXTCON_DISP_DP,
@@ -219,7 +222,7 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
 	 * some docks need more time to power up.
 	 */
 	while (time_before(jiffies, timeout)) {
-		if (!extcon_get_state(port->extcon, EXTCON_DISP_DP))
+		if (port->extcon && !extcon_get_state(port->extcon, EXTCON_DISP_DP))
 			return false;
 
 		if (!cdn_dp_get_sink_count(dp, &sink_count))
@@ -385,11 +388,14 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
 		goto err_power_on;
 	}
 
-	ret = extcon_get_property(port->extcon, EXTCON_DISP_DP,
-				  EXTCON_PROP_USB_TYPEC_POLARITY, &property);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "get property failed\n");
-		goto err_power_on;
+	property.intval = 0;
+	if (port->extcon) {
+		ret = extcon_get_property(port->extcon, EXTCON_DISP_DP,
+					  EXTCON_PROP_USB_TYPEC_POLARITY, &property);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev, "get property failed\n");
+			goto err_power_on;
+		}
 	}
 
 	port->lanes = cdn_dp_get_port_lanes(port);
@@ -1028,6 +1034,9 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 	for (i = 0; i < dp->ports; i++) {
 		port = dp->port[i];
 
+		if (!port->extcon)
+			continue;
+
 		port->event_nb.notifier_call = cdn_dp_pd_event;
 		ret = devm_extcon_register_notifier(dp->dev, port->extcon,
 						    EXTCON_DISP_DP,
@@ -1120,14 +1129,14 @@ static int cdn_dp_probe(struct platform_device *pdev)
 		    PTR_ERR(phy) == -EPROBE_DEFER)
 			return -EPROBE_DEFER;
 
-		if (IS_ERR(extcon) || IS_ERR(phy))
+		if (IS_ERR(phy) || PTR_ERR(extcon) != -ENODEV)
 			continue;
 
 		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
 		if (!port)
 			return -ENOMEM;
 
-		port->extcon = extcon;
+		port->extcon = IS_ERR(extcon) ? NULL : extcon;
 		port->phy = phy;
 		port->dp = dp;
 		port->id = i;
Signature · 1 line
2.51.1
maintainer Reviewed-by on v15 6/9
#17 lore raw

Re: [PATCH v15 6/9] drm/rockchip: cdn-dp: Support handle lane info without extcon

Am Mittwoch, 4. März 2026, 10:41:49 Mitteleuropäische Sommerzeit schrieb Chaoyi Chen:

Quoted 11 lines

From: Chaoyi Chen <[email protected]>

This patch add support for get PHY lane info without help of extcon.

There is no extcon needed if the Type-C controller is present. In this case, the lane info can be get from PHY instead of extcon.

The extcon device should still be supported if Type-C controller is not present.

#18 lore raw

PATCH v15 7/9 drm/rockchip: cdn-dp: Add multiple bridges to support PHY port selection

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The RK3399 has two USB/DP combo PHY and one CDN-DP controller. And the CDN-DP can be switched to output to one of the PHYs. If both ports are plugged into DP, DP will select the first port for output.

This patch adds support for multiple bridges, enabling users to flexibly select the output port. For each PHY port, a separate encoder and bridge are registered.

The change is based on the DRM AUX HPD bridge, rather than the extcon approach. This requires the DT to correctly describe the connections between the first bridge in bridge chain and DP controller. For example, the bridge chain may be like this:

PHY aux birdge -> fsa4480 analog audio switch bridge -> onnn,nb7vpq904m USB reminder bridge -> USB-C controller AUX HPD bridge

In this case, the connection relationships among the PHY aux bridge and the DP contorller need to be described in DT.

In addition, the cdn_dp_parse_next_bridge_dt() will parses it and determines whether to register one or two bridges.

Since there is only one DP controller, only one of the PHY ports can output at a time. The key is how to switch between different PHYs, which is handled by cdn_dp_switch_port() and cdn_dp_enable().

There are two cases:

1. Neither bridge is enabled. In this case, both bridges can independently read the EDID, and the PHY port may switch before reading the EDID.

2. One bridge is already enabled. In this case, other bridges are not allowed to read the EDID. So we will try to return the cached EDID.

Since the scenario of two ports plug in at the same time is rare, I don't have a board which support two TypeC connector to test this. Therefore, I tested forced switching on a single PHY port, as well as output using a fake PHY port alongside a real PHY port.

---

Changes in v15:
- Improve clarity by inlining drm_bridge_get() in assignment (Luca).

(no changes since v11)

Changes in v10:
- Fix refcount usage of drm_bridge.
- Remove unused cdn_dp_next_bridge type.

Changes in v9:
- Select DRM_AUX_HPD_BRIDGE when using DP driver.

(no changes since v7)

Changes in v6:
- Rename some variable names.
- Attach the DP bridge to the next bridge.

Changes in v5:
- By parsing the HPD bridge chain, set the connector's of_node to the of_node corresponding to the USB-C connector.
- Return EDID cache when other port is already enabled.
---

 drivers/gpu/drm/rockchip/Kconfig       |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c | 324 ++++++++++++++++++++-----
 drivers/gpu/drm/rockchip/cdn-dp-core.h |  18 +-
 3 files changed, 286 insertions(+), 57 deletions(-)
drivers/gpu/drm/rockchip/Kconfig +1 -0
index b7b025814e72..10d9f29a3d44 100644
@@ -56,6 +56,7 @@ config ROCKCHIP_CDN_DP
 	select DRM_DISPLAY_HELPER
 	select DRM_BRIDGE_CONNECTOR
 	select DRM_DISPLAY_DP_HELPER
+	select DRM_AUX_HPD_BRIDGE
 	help
 	  This selects support for Rockchip SoC specific extensions
 	  for the cdn DP driver. If you want to enable Dp on
drivers/gpu/drm/rockchip/cdn-dp-core.c +269 -55

473 lines not on this page · raw message

drivers/gpu/drm/rockchip/cdn-dp-core.h +16 -2
index e9c30b9fd543..c10e423bbf06 100644
@@ -38,6 +38,8 @@ enum vic_pxl_encoding_format {
 	Y_ONLY = 0x10,
 };
 
+struct cdn_dp_device;
+
 struct video_info {
 	bool h_sync_polarity;
 	bool v_sync_polarity;
@@ -63,16 +65,28 @@ struct cdn_dp_port {
 	u8 id;
 };
 
+struct cdn_dp_bridge {
+	struct cdn_dp_device *parent;
+	struct drm_bridge bridge;
+	struct rockchip_encoder encoder;
+	bool connected;
+	bool enabled;
+	int id;
+};
+
 struct cdn_dp_device {
 	struct device *dev;
 	struct drm_device *drm_dev;
-	struct drm_bridge bridge;
-	struct rockchip_encoder encoder;
+	int bridge_count;
+	struct cdn_dp_bridge *bridge_list[MAX_PHY];
+	struct drm_bridge *next_bridge_list[MAX_PHY];
+	const struct drm_edid *edid_cache[MAX_PHY];
 	struct drm_display_mode mode;
 	struct platform_device *audio_pdev;
 	struct work_struct event_work;
 
 	struct mutex lock;
+	bool next_bridge_valid;
 	bool connected;
 	bool active;
 	bool suspended;
Signature · 1 line
2.51.1
maintainer Reviewed-by on v15 7/9
#19 lore raw

Re: [PATCH v15 7/9] drm/rockchip: cdn-dp: Add multiple bridges to support PHY port selection

Am Mittwoch, 4. März 2026, 10:41:50 Mitteleuropäische Sommerzeit schrieb Chaoyi Chen:

Quoted 37 lines

From: Chaoyi Chen <[email protected]>

The RK3399 has two USB/DP combo PHY and one CDN-DP controller. And the CDN-DP can be switched to output to one of the PHYs. If both ports are plugged into DP, DP will select the first port for output.

This patch adds support for multiple bridges, enabling users to flexibly select the output port. For each PHY port, a separate encoder and bridge are registered.

The change is based on the DRM AUX HPD bridge, rather than the extcon approach. This requires the DT to correctly describe the connections between the first bridge in bridge chain and DP controller. For example, the bridge chain may be like this:

PHY aux birdge -> fsa4480 analog audio switch bridge -> onnn,nb7vpq904m USB reminder bridge -> USB-C controller AUX HPD bridge

In this case, the connection relationships among the PHY aux bridge and the DP contorller need to be described in DT.

In addition, the cdn_dp_parse_next_bridge_dt() will parses it and determines whether to register one or two bridges.

Since there is only one DP controller, only one of the PHY ports can output at a time. The key is how to switch between different PHYs, which is handled by cdn_dp_switch_port() and cdn_dp_enable().

There are two cases:

1. Neither bridge is enabled. In this case, both bridges can independently read the EDID, and the PHY port may switch before reading the EDID.

2. One bridge is already enabled. In this case, other bridges are not allowed to read the EDID. So we will try to return the cached EDID.

Quoted 7 lines

Since the scenario of two ports plug in at the same time is rare, I don't have a board which support two TypeC connector to test this. Therefore, I tested forced switching on a single PHY port, as well as output using a fake PHY port alongside a real PHY port.

#20 lore raw

PATCH v15 8/9 arm64: dts: rockchip: Add missing dp_out port for RK3399 CDN-DP

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

Let's make the ports nodes of cdn_dp in the same style as the other display interface, and match the style of ports's yaml.

---

(no changes since v5)

Changes in v4:
- Remove unnecessary #address/#size-cells

(no changes since v1)
---

 arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
arch/arm64/boot/dts/rockchip/rk3399-base.dtsi +9 -1
index 4dcceb9136b7..93b42820998f 100644
@@ -618,7 +618,11 @@ cdn_dp: dp@fec00000 {
 		status = "disabled";
 
 		ports {
-			dp_in: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dp_in: port@0 {
+				reg = <0>;
 				#address-cells = <1>;
 				#size-cells = <0>;
 
@@ -632,6 +636,10 @@ dp_in_vopl: endpoint@1 {
 					remote-endpoint = <&vopl_out_dp>;
 				};
 			};
+
+			dp_out: port@1 {
+				reg = <1>;
+			};
 		};
 	};
 
Signature · 1 line
2.51.1
#21 lore raw

PATCH v15 9/9 arm64: dts: rockchip: rk3399-evb-ind: Add support for DisplayPort

In reply to Chaoyi Chen

From: Chaoyi Chen <[email protected]>

The RK3399 EVB IND board has a Type-C interface DisplayPort. It use fusb302 chip as Type-C controller.

fusb302 chip ---> USB/DP PHY0 <----> CDN-DP controller

---

(no changes since v10)

Changes in v9:
- Add usb role switch for Type-C.
- Remove USB2 PHY in Type-C connection.

(no changes since v4)

Changes in v3:
- Fix wrong vdo value.
- Fix port node in usb-c-connector.

Changes in v2:
- Add endpoint to link DP PHY and DP controller.
- Fix devicetree coding style.
---

 .../boot/dts/rockchip/rk3399-evb-ind.dts      | 147 ++++++++++++++++++
 1 file changed, 147 insertions(+)
arch/arm64/boot/dts/rockchip/rk3399-evb-ind.dts +147 -0
index 70aee1ab904c..be1e90f7a453 100644
@@ -4,6 +4,7 @@
  */
 
 /dts-v1/;
+#include <dt-bindings/usb/pd.h>
 #include "rk3399.dtsi"
 
 / {
@@ -19,6 +20,21 @@ chosen {
 		stdout-path = "serial2:1500000n8";
 	};
 
+	sound: sound {
+		compatible = "rockchip,rk3399-gru-sound";
+		rockchip,cpu = <&i2s0 &spdif>;
+	};
+
+	vbus_typec: regulator-vbus-typec {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&vcc5v0_typec0_en>;
+		regulator-name = "vbus_typec";
+		vin-supply = <&vcc5v0_sys>;
+	};
+
 	vcc5v0_sys: regulator-vcc5v0-sys {
 		compatible = "regulator-fixed";
 		enable-active-high;
@@ -31,6 +47,11 @@ vcc5v0_sys: regulator-vcc5v0-sys {
 	};
 };
 
+&cdn_dp {
+	phys = <&tcphy0_dp>;
+	status = "okay";
+};
+
 &cpu_b0 {
 	cpu-supply = <&vdd_cpu_b>;
 };
@@ -55,6 +76,12 @@ &cpu_l3 {
 	cpu-supply = <&vdd_cpu_l>;
 };
 
+&dp_out {
+	dp_controller_output: endpoint {
+		remote-endpoint = <&dp_phy_in>;
+	};
+};
+
 &emmc_phy {
 	status = "okay";
 };
@@ -341,6 +368,71 @@ regulator-state-mem {
 	};
 };
 
+&i2c4 {
+	i2c-scl-rising-time-ns = <475>;
+	i2c-scl-falling-time-ns = <26>;
+	status = "okay";
+
+	usbc0: typec-portc@22 {
+		compatible = "fcs,fusb302";
+		reg = <0x22>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&usbc0_int>;
+		vbus-supply = <&vbus_typec>;
+
+		usb_con: connector {
+			compatible = "usb-c-connector";
+			label = "USB-C";
+			data-role = "dual";
+			power-role = "dual";
+			try-power-role = "sink";
+			op-sink-microwatt = <1000000>;
+			sink-pdos =
+				<PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>;
+			source-pdos =
+				<PDO_FIXED(5000, 1500, PDO_FIXED_USB_COMM)>;
+
+			altmodes {
+				displayport {
+					svid = /bits/ 16 <0xff01>;
+					vdo = <0x00001c46>;
+				};
+			};
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+
+					usbc0_orien_sw: endpoint {
+						remote-endpoint = <&tcphy0_orientation_switch>;
+					};
+				};
+
+				port@1 {
+					reg = <1>;
+
+					usbc0_role_sw: endpoint {
+						remote-endpoint = <&dwc3_0_role_switch>;
+					};
+				};
+
+				port@2 {
+					reg = <2>;
+
+					dp_altmode_mux: endpoint {
+						remote-endpoint = <&tcphy0_typec_dp>;
+					};
+				};
+			};
+		};
+	};
+};
+
 &i2s2 {
 	status = "okay";
 };
@@ -354,6 +446,16 @@ &io_domains {
 };
 
 &pinctrl {
+	usb-typec {
+		usbc0_int: usbc0-int {
+			rockchip,pins = <1 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		vcc5v0_typec0_en: vcc5v0-typec0-en {
+			rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	pmic {
 		pmic_int_l: pmic-int-l {
 			rockchip,pins = <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
@@ -400,10 +502,48 @@ &sdmmc {
 	status = "okay";
 };
 
+&sound {
+	rockchip,codec = <&cdn_dp>;
+	status = "okay";
+};
+
+&spdif {
+	status = "okay";
+};
+
 &tcphy0 {
 	status = "okay";
 };
 
+&tcphy0_dp {
+	mode-switch;
+
+	port {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		tcphy0_typec_dp: endpoint@0 {
+			reg = <0>;
+			remote-endpoint = <&dp_altmode_mux>;
+		};
+
+		dp_phy_in: endpoint@1 {
+			reg = <1>;
+			remote-endpoint = <&dp_controller_output>;
+		};
+	};
+};
+
+&tcphy0_usb3 {
+	orientation-switch;
+
+	port {
+		tcphy0_orientation_switch: endpoint {
+			remote-endpoint = <&usbc0_orien_sw>;
+		};
+	};
+};
+
 &tcphy1 {
 	status = "okay";
 };
@@ -461,7 +601,14 @@ &usb_host1_ohci {
 };
 
 &usbdrd_dwc3_0 {
+	usb-role-switch;
 	status = "okay";
+
+	port {
+		dwc3_0_role_switch: endpoint {
+			remote-endpoint = <&usbc0_role_sw>;
+		};
+	};
 };
 
 &usbdrd3_0 {
Signature · 1 line
2.51.1
maintainer
#22 lore raw

Re: [PATCH v15 9/9] arm64: dts: rockchip: rk3399-evb-ind: Add support for DisplayPort

Am Mittwoch, 4. März 2026, 10:41:52 Mitteleuropäische Sommerzeit schrieb Chaoyi Chen:

Quoted 8 lines

From: Chaoyi Chen <[email protected]>

The RK3399 EVB IND board has a Type-C interface DisplayPort. It use fusb302 chip as Type-C controller.

fusb302 chip ---> USB/DP PHY0 <----> CDN-DP controller

Apart from what I replied to the cover-letter, it might be nice to split this into
- patch adding the fusb and general type-c parts
- patch adding the dp altmode

To make each part a bit shorter.

Heiko

maintainer
#23 lore raw
In reply to Chaoyi Chen

Hi,

Quoted 8 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

This series focuses on adding Type-C DP support for USBDP PHY and DP driver. The USBDP PHY and DP will perceive the changes in cable status based on the USB PD and Type-C state machines provided by TCPM. Before this, the USBDP PHY and DP controller of RK3399 sensed cable state changes through extcon, and devices such as the RK3399 Gru-Chromebook rely on them. This series should not break them.

What's the status with this series?
Are these inteded to go via the DRM tree?

thanks,

Quoted 196 lines

====
1. DisplayPort HPD status notify

Before v7, I implemented a variety of DP HPD status notify. However, they all had various problems and it was difficult to become a generic solution.

Under the guidance of Heikki and Dmitry, a decoupled notification method between the TypeC and DRM subsystems was introduced in v7. First, a notification is sent when TypeC registers a new altmode. Then, a generic DP AUX HPD bridge is implemented on the DRM side.

During v7-v10, we added a new notifier in typec to notify the altmode device register event. With the help of Greg and Heikki, we implemented the reuse of notifiers for the type bus itself in patch1 of v11.

The USB subsystem related parts have already been merged into the usb-next branch in v13 [0][1]. Therefore, this series no longer includes these patches starting from v14. Thanks to Greg and Heikki!

[0]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=67ab45426215c7fdccb65aecd4cac15bbe4dfcbb
[1]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=4dee13db29de6dd869af9b3827e1ff569644e838

That makes it redundant for each Type-C controller driver to implement a similar DP AUX HPD bridge in embedded scenarios.

====
2. Altmode switching and orientation switching for USBDP PHY

For USB Type-C interfaces, an external Type-C controller chip assists by detecting cable attachment, determining plug orientation, and reporting USB PD message. The USB/DP combo PHY supports software configurable pin mapping and DisplayPort lane assignment. Based on these message, the combo PHY can perform both altmode switching and orientation switching via software.

The RK3399 EVB IND board has a Type-C interface DisplayPort. It use fusb302 chip as Type-C controller. The connection diagram is shown below:

fusb302 chip +---> USB2.0 PHY ----> DWC3 USB controller

             |
             +---> USB/DP PHY0 +--> CDN-DP controller
                               |
                               +--> DWC3 USB controller

====
3. Multiple bridge model for RK3399 CDN-DP

The RK3399 has two USB/DP combo PHY and one CDN-DP controller. And the CDN-DP can be switched to output to one of the PHYs.

USB/DP PHY0 ---+

               | <----> CDN-DP controller

USB/DP PHY1 ---+

In previous versions, if both PHY ports were connected to DP, the CDN-DP driver would select the first PHY port for output.

On Dmitry's suggestion, we introduced a multi-bridge model to support flexible selection of the output PHY port. For each PHY port, a separate encoder and bridge are registered.

The change is based on the DRM AUX HPD bridge, rather than the extcon approach. This requires the DT to correctly describe the connections between the first bridge in bridge chain and DP controller. And Once the first bridge is obtained, we can get the last bridge corresponding to the USB-C connector, and then set the DRM connector's fwnode to the corresponding one to enable HPD notification.

====
Patch1 add generic USB Type-C DP HPD bridge (Dmitry, Heikki). Patch2 add new API drm_aux_bridge_register_from_node() (Neil) . Patch3 add new Type-C mode switch for RK3399 USBDP phy binding (Krzysztof). Patch4 add typec_mux and typec_switch for RK3399 USBDP PHY. Patch5 add DRM AUX bridge support for RK3399 USBDP PHY (Neil). Patch6 drops CDN-DP's extcon dependency when Type-C is present (Dmitry). Patch7 add multiple bridges to support PHY port selection (Dmitry, Luca). Patch8 add missing dp_out port for RK3399 CDN-DP.
Patch9 add Type-C DP support for RK3399 EVB IND board (Diederik, Peter).

Changes in v15:
- Link to V14: https://lore.kernel.org/all/[email protected]/
- Improve clarity by inlining drm_bridge_get() in assignment (Luca).

Changes in v14:
- Link to V13: https://lore.kernel.org/all/[email protected]/
- Drop the patches for the USB Type-C subsusytem part, as they have already been merged into usb-next.

Changes in v13:
- Link to V12: https://lore.kernel.org/all/[email protected]/
- Only register drm dp hpd bridge for typec port altmode device.

Changes in v12:
- Link to V11: https://lore.kernel.org/all/[email protected]/
- Add missing Signed-off-by line.

Changes in v11:
- Link to V10: https://lore.kernel.org/all/[email protected]/
- Switch to using typec bus notifiers.

Changes in v10:
- Link to V9: https://lore.kernel.org/all/[email protected]/
- Notify TYPEC_ALTMODE_UNREGISTERED when altmode removed.
- Add drm_aux_bridge_register_from_node().
- Fix refcount usage of drm_bridge.

Changes in v9:
- Link to V8: https://lore.kernel.org/all/[email protected]/
- Remove the exposed DRM_AUX_HPD_BRIDGE option, and select DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
- Add usb role switch for Type-C.
- Remove USB2 PHY in Type-C connection.
- ...

Changes in v8:
- Link to V7: https://lore.kernel.org/all/[email protected]/
- Export all typec device types for identification.
- Merge generic DP HPD bridge into one module.
- Fix coding style.

Changes in v7:
- Link to V6: https://lore.kernel.org/all/[email protected]/
- Add notifier functions for Type-C core.
- Add generic USB Type-C DP HPD bridge.

Changes in v6:
- Link to V5: https://lore.kernel.org/all/[email protected]/
- Fix depend in Kconfig.
- Check DP svid in tcphy_typec_mux_set().
- Remove mode setting in tcphy_orien_sw_set().
- Rename some variable names.
- Attach the DP bridge to the next bridge.

Changes in v5:
- Link to V4: https://lore.kernel.org/all/[email protected]/
- Remove the calls related to drm_aux_hpd_bridge_notify().
- Place the helper functions in the same compilation unit.
- Add more comments about parent device.
- Add DRM AUX bridge support for RK3399 USBDP PHY
- By parsing the HPD bridge chain, set the connector's of_node to the of_node corresponding to the USB-C connector.
- Return EDID cache when other port is already enabled.

Changes in v4:
- Link to V3: https://lore.kernel.org/all/[email protected]/
- Add default HPD device for DisplayPort altmode.
- Introduce multiple bridges for CDN-DP.
- ...

Changes in v3:
- Link to V2: https://lore.kernel.org/all/[email protected]/
- Add more descriptions to clarify the role of the PHY in switching.
- Fix wrong vdo value.
- Fix port node in usb-c-connector.

Changes in v2:
- Link to V1: https://lore.kernel.org/all/[email protected]/
- Reuse dp-port/usb3-port in rk3399-typec-phy binding.
- Fix compile error when CONFIG_TYPEC is not enabled.
- Notify DP HPD state by USB/DP PHY.
- Ignore duplicate HPD events.
- Add endpoint to link DP PHY and DP controller.
- Fix devicetree coding style.

Chaoyi Chen (9):
drm/bridge: Implement generic USB Type-C DP HPD bridge drm/bridge: aux: Add drm_aux_bridge_register_from_node()
dt-bindings: phy: rockchip: rk3399-typec-phy: Support mode-switch
phy: rockchip: phy-rockchip-typec: Add typec_mux/typec_switch support
phy: rockchip: phy-rockchip-typec: Add DRM AUX bridge drm/rockchip: cdn-dp: Support handle lane info without extcon drm/rockchip: cdn-dp: Add multiple bridges to support PHY port
selection
arm64: dts: rockchip: Add missing dp_out port for RK3399 CDN-DP
arm64: dts: rockchip: rk3399-evb-ind: Add support for DisplayPort

 .../phy/rockchip,rk3399-typec-phy.yaml        |   6 +
 arch/arm64/boot/dts/rockchip/rk3399-base.dtsi |  10 +-
 .../boot/dts/rockchip/rk3399-evb-ind.dts      | 147 +++++++
 drivers/gpu/drm/bridge/Kconfig                |  10 +
 drivers/gpu/drm/bridge/Makefile               |   1 +
 drivers/gpu/drm/bridge/aux-bridge.c           |  24 +-
 .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c  |  49 +++
 drivers/gpu/drm/rockchip/Kconfig              |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c        | 349 +++++++++++++---
 drivers/gpu/drm/rockchip/cdn-dp-core.h        |  18 +-
 drivers/phy/rockchip/Kconfig                  |   3 +
 drivers/phy/rockchip/phy-rockchip-typec.c     | 373 +++++++++++++++++-
 include/drm/bridge/aux-bridge.h               |   6 +
 13 files changed, 913 insertions(+), 84 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
Signature · 1 line
2.51.1
Signature · 1 line
heikki
#24 lore raw

Hello Heikki,

Quoted 12 lines · Heikki Krogerus

Hi,

Quoted 8 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

This series focuses on adding Type-C DP support for USBDP PHY and DP driver. The USBDP PHY and DP will perceive the changes in cable status based on the USB PD and Type-C state machines provided by TCPM. Before this, the USBDP PHY and DP controller of RK3399 sensed cable state changes through extcon, and devices such as the RK3399 Gru-Chromebook rely on them. This series should not break them.

Quoted 5 lines

What's the status with this series?
Are these inteded to go via the DRM tree?

thanks,

Thank you very much for your continued attention to this series. The maintainers seem quite busy... Despite there being no further review comments, this series have yet to be merged into the DRM tree.

And some of my other patches are in the same situation. Do you happen to know what the next steps should be? Thank you.

Signature · 7 lines
Best, 
Chaoyi

-- 
linux-phy mailing list
[email protected]
https://lists.infradead.org/mailman/listinfo/linux-phy
maintainer
#25 lore raw

Hi,

Am Mittwoch, 20. Mai 2026, 03:13:25 Mitteleuropäische Sommerzeit schrieb Chaoyi Chen:

Quoted 21 lines

Hello Heikki,

Quoted 12 lines · Heikki Krogerus

Hi,

Quoted 8 lines · Chaoyi Chen

From: Chaoyi Chen <[email protected]>

This series focuses on adding Type-C DP support for USBDP PHY and DP driver. The USBDP PHY and DP will perceive the changes in cable status based on the USB PD and Type-C state machines provided by TCPM. Before this, the USBDP PHY and DP controller of RK3399 sensed cable state changes through extcon, and devices such as the RK3399 Gru-Chromebook rely on them. This series should not break them.

Quoted 5 lines

What's the status with this series?
Are these inteded to go via the DRM tree?

thanks,

Quoted 6 lines

Thank you very much for your continued attention to this series. The maintainers seem quite busy... Despite there being no further review comments, this series have yet to be merged into the DRM tree.

And some of my other patches are in the same situation. Do you happen to know what the next steps should be? Thank you.

For this series, I would think it'd be best to split it a bit.

- Make patches 1, 2, 5-7 its own series
* patch 5 _should_ be included here, needs an Ack from the phy-maintainer to go through the drm-tree. It does look independent from the other phy changes (code changes themself), but needs the function from patch 2.

- Make patches 3+4 its own series, so that phy maintainers notice The code-changes themself seem independent of the rest
* Reference the drm-series and explicity note that the phy-patch in it needs an Ack from the phy maintainer to through the drm-tree Hopefully helps to get that
* patch 1 has a Reviewed-by from the type-c side, but needs someone knowing drm-bridge internals to agree

- Makes patches 8+9 its own series, reference both series from above as dependencies.

That would be my take here, to have the right maintainers notice they should do something.

Heiko