devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] typec: add support for the ON Semiconductor nb7vpq904m Type-C Linear Redriver
@ 2023-06-01  9:21 Neil Armstrong
  2023-06-01  9:21 ` [PATCH 1/2] dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings Neil Armstrong
  2023-06-01  9:21 ` [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver neil.armstrong
  0 siblings, 2 replies; 8+ messages in thread
From: Neil Armstrong @ 2023-06-01  9:21 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Heikki Krogerus, Liam Girdwood,
	Mark Brown, Dmitry Baryshkov
  Cc: linux-usb, devicetree, linux-kernel, Neil Armstrong

Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed
and DisplayPort ALT Mode Linear Redriver chip found on some devices
with a Type-C port.

The redriver compensates ultra High-Speeed DisplayPort and USB
Super Speed signal integrity losses mainly due to PCB & transmission
cables.

The redriver doesn't support SuperSpeed lines swapping, but
can support Type-C SBU lines swapping.

Support is designed as a Type-C Switch and Mux, and can propagate
orientation settings to the source endpoint, which is usually
a Super Speed PHY which does the data lanes swapping.

Bindings are added first and can handle the fact data lanes pairs
can be swapped on the PCB.

Compile-time dependencies:
- svid removal at [1]

[1] https://lore.kernel.org/all/20230526131434.46920-1-heikki.krogerus@linux.intel.com/

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
Dmitry Baryshkov (1):
      usb: typec: add support for the nb7vpq904m Type-C Linear Redriver

Neil Armstrong (1):
      dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings

 .../devicetree/bindings/usb/onnn,nb7vpq904m.yaml   | 141 ++++++
 drivers/usb/typec/mux/Kconfig                      |   8 +
 drivers/usb/typec/mux/Makefile                     |   1 +
 drivers/usb/typec/mux/nb7vpq904m.c                 | 526 +++++++++++++++++++++
 4 files changed, 676 insertions(+)
---
base-commit: ac9a78681b921877518763ba0e89202254349d1b
change-id: 20230601-topic-sm8x50-upstream-redriver-6e261edd5cb4

Best regards,
-- 
Neil Armstrong <neil.armstrong@linaro.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings
  2023-06-01  9:21 [PATCH 0/2] typec: add support for the ON Semiconductor nb7vpq904m Type-C Linear Redriver Neil Armstrong
@ 2023-06-01  9:21 ` Neil Armstrong
  2023-06-02 14:30   ` Krzysztof Kozlowski
  2023-06-01  9:21 ` [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver neil.armstrong
  1 sibling, 1 reply; 8+ messages in thread
From: Neil Armstrong @ 2023-06-01  9:21 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Heikki Krogerus, Liam Girdwood,
	Mark Brown, Dmitry Baryshkov
  Cc: linux-usb, devicetree, linux-kernel, Neil Armstrong

Document bindings for this ON Semiconductor Type-C USB SuperSpeed
and DisplayPort ALT Mode Linear Redriver.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 .../devicetree/bindings/usb/onnn,nb7vpq904m.yaml   | 141 +++++++++++++++++++++
 1 file changed, 141 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml b/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml
new file mode 100644
index 000000000000..c0989dc4d5bf
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/onnn,nb7vpq904m.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ON Semiconductor Type-C DisplayPort ALT Mode Linear Redriver
+
+maintainers:
+  - Bjorn Andersson <bjorn.andersson@linaro.org>
+
+properties:
+  compatible:
+    enum:
+      - onnn,nb7vpq904m
+
+  reg:
+    maxItems: 1
+
+  vcc-supply:
+    description: power supply (1.8V)
+
+  enable-gpios: true
+
+  mode-switch:
+    description: Flag the port as possible handle of altmode switching
+    type: boolean
+
+  orientation-switch:
+    description: Flag the port as possible handler of orientation switching
+    type: boolean
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Super Speed (SS) Output endpoint to the Type-C connector
+
+      port@1:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        description: Super Speed (SS) Input endpoint from the Super-Speed PHY
+        unevaluatedProperties: false
+
+        properties:
+          endpoint:
+            $ref: /schemas/graph.yaml#/$defs/endpoint-base
+            unevaluatedProperties: false
+
+            properties:
+              data-lanes:
+                $ref: /schemas/types.yaml#/definitions/uint32-array
+                description: |
+                  An array of physical data lane indexes. Position determines how
+                  lanes are connected to the redriver, It is assumed the same order
+                  is kept on the other side of the redriver.
+                  Lane number represents the following
+                  - 0 is RX2 lane
+                  - 1 is TX2 lane
+                  - 2 is TX1 lane
+                  - 3 is RX1 lane
+                  The position determines the physical port of the redriver, in the
+                  order A, B, C & D.
+                oneOf:
+                  - items:
+                      - const: 0
+                      - const: 1
+                      - const: 2
+                      - const: 3
+                    description: |
+                      This is the lanes default layout
+                      - Port A to RX2 lane
+                      - Port B to TX2 lane
+                      - Port C to TX1 lane
+                      - Port D to RX1 lane
+                  - items:
+                      - const: 2
+                      - const: 3
+                      - const: 0
+                      - const: 1
+                    description: |
+                      This is the USBRX2/USBTX2 and USBRX1/USBTX1 swapped lanes layout
+                      - Port A to RX1 lane
+                      - Port B to TX1 lane
+                      - Port C to TX2 lane
+                      - Port D to RX2 lane
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description:
+          Sideband Use (SBU) AUX lines endpoint to the Type-C connector for the purpose of
+          handling altmode muxing and orientation switching.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c13 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        typec-mux@32 {
+            compatible = "onnn,nb7vpq904m";
+            reg = <0x32>;
+
+            vcc-supply = <&vreg_l15b_1p8>;
+
+            mode-switch;
+            orientation-switch;
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+                    usb_con_ss: endpoint {
+                        remote-endpoint = <&typec_con_ss>;
+                    };
+                };
+                port@1 {
+                    reg = <1>;
+                    phy_con_ss: endpoint {
+                        remote-endpoint = <&usb_phy_ss>;
+                        data-lanes = <2 3 0 1>;
+                    };
+                };
+                port@2 {
+                    reg = <2>;
+                    usb_con_sbu: endpoint {
+                        remote-endpoint = <&typec_dp_aux>;
+                    };
+                };
+            };
+        };
+    };
+...

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver
  2023-06-01  9:21 [PATCH 0/2] typec: add support for the ON Semiconductor nb7vpq904m Type-C Linear Redriver Neil Armstrong
  2023-06-01  9:21 ` [PATCH 1/2] dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings Neil Armstrong
@ 2023-06-01  9:21 ` neil.armstrong
  2023-06-01  9:37   ` Dmitry Baryshkov
  2023-06-05  8:24   ` Heikki Krogerus
  1 sibling, 2 replies; 8+ messages in thread
From: neil.armstrong @ 2023-06-01  9:21 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Heikki Krogerus, Liam Girdwood,
	Mark Brown, Dmitry Baryshkov
  Cc: linux-usb, devicetree, linux-kernel, Neil Armstrong

From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>

Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed
and DisplayPort ALT Mode Linear Redriver chip found on some devices
with a Type-C port.

The redriver compensates ultra High-Speeed DisplayPort and USB
Super Speed signal integrity losses mainly due to PCB & transmission
cables.

The redriver doesn't support SuperSpeed lines swapping, but
can support Type-C SBU lines swapping.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/usb/typec/mux/Kconfig      |   8 +
 drivers/usb/typec/mux/Makefile     |   1 +
 drivers/usb/typec/mux/nb7vpq904m.c | 526 +++++++++++++++++++++++++++++++++++++
 3 files changed, 535 insertions(+)

diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index c46fa4f9d3df..8c4d6b8fb75c 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -35,4 +35,12 @@ config TYPEC_MUX_INTEL_PMC
 	  control the USB role switch and also the multiplexer/demultiplexer
 	  switches used with USB Type-C Alternate Modes.
 
+config TYPEC_MUX_NB7VPQ904M
+	tristate "On Semiconductor NB7VPQ904M Type-C redriver driver"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
+	  redriver chip found on some devices with a Type-C port.
+
 endmenu
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
index dda67e19b58b..76196096ef41 100644
--- a/drivers/usb/typec/mux/Makefile
+++ b/drivers/usb/typec/mux/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_TYPEC_MUX_FSA4480)		+= fsa4480.o
 obj-$(CONFIG_TYPEC_MUX_GPIO_SBU)	+= gpio-sbu-mux.o
 obj-$(CONFIG_TYPEC_MUX_PI3USB30532)	+= pi3usb30532.o
 obj-$(CONFIG_TYPEC_MUX_INTEL_PMC)	+= intel_pmc_mux.o
+obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M)	+= nb7vpq904m.o
diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
new file mode 100644
index 000000000000..2f85ad9e417a
--- /dev/null
+++ b/drivers/usb/typec/mux/nb7vpq904m.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OnSemi NB7VPQ904M Type-C driver
+ *
+ * Copyright (C) 2023 Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+ */
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of_graph.h>
+#include <drm/drm_bridge.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define NB7_CHNA		0
+#define NB7_CHNB		1
+#define NB7_CHNC		2
+#define NB7_CHND		3
+#define NB7_IS_CHAN_AD(channel) (channel == NB7_CHNA || channel == NB7_CHND)
+
+#define GEN_DEV_SET_REG			0x00
+
+#define GEN_DEV_SET_CHIP_EN		BIT(0)
+#define GEN_DEV_SET_CHNA_EN		BIT(4)
+#define GEN_DEV_SET_CHNB_EN		BIT(5)
+#define GEN_DEV_SET_CHNC_EN		BIT(6)
+#define GEN_DEV_SET_CHND_EN		BIT(7)
+
+#define GEN_DEV_SET_OP_MODE_MASK	GENMASK(3, 1)
+
+#define GEN_DEV_SET_OP_MODE_DP_CC2	0
+#define GEN_DEV_SET_OP_MODE_DP_CC1	1
+#define GEN_DEV_SET_OP_MODE_DP_4LANE	2
+#define GEN_DEV_SET_OP_MODE_USB		5
+
+#define EQ_SETTING_REG_BASE		0x01
+#define EQ_SETTING_REG(n)		(EQ_SETTING_REG_BASE + (n) * 2)
+#define EQ_SETTING_MASK			GENMASK(3, 1)
+
+#define OUTPUT_COMPRESSION_AND_POL_REG_BASE	0x02
+#define OUTPUT_COMPRESSION_AND_POL_REG(n)	(OUTPUT_COMPRESSION_AND_POL_REG_BASE + (n) * 2)
+#define OUTPUT_COMPRESSION_MASK		GENMASK(2, 1)
+
+#define FLAT_GAIN_REG_BASE		0x18
+#define FLAT_GAIN_REG(n)		(FLAT_GAIN_REG_BASE + (n) * 2)
+#define FLAT_GAIN_MASK			GENMASK(1, 0)
+
+#define LOSS_MATCH_REG_BASE		0x19
+#define LOSS_MATCH_REG(n)		(LOSS_MATCH_REG_BASE + (n) * 2)
+#define LOSS_MATCH_MASK			GENMASK(1, 0)
+
+#define AUX_CC_REG			0x09
+
+#define CHIP_VERSION_REG		0x17
+
+struct nb7vpq904m {
+	struct i2c_client *client;
+	struct gpio_desc *enable_gpio;
+	struct regulator *vcc_supply;
+	struct regmap *regmap;
+	struct typec_switch_dev *sw;
+	struct typec_mux_dev *mux;
+
+	bool swap_data_lanes;
+	struct typec_switch *typec_switch;
+
+	struct drm_bridge bridge;
+
+	struct mutex lock; /* protect non-concurrent mux & switch */
+
+	enum typec_orientation orientation;
+	unsigned long mode;
+	unsigned int svid;
+};
+
+static void nb7vpq904m_set_channel(struct nb7vpq904m *nb7, unsigned int channel, bool dp)
+{
+	u8 eq, out_comp, flat_gain, loss_match;
+
+	if (dp) {
+		eq = NB7_IS_CHAN_AD(channel) ? 0x6 : 0x4;
+		out_comp = 0x3;
+		flat_gain = NB7_IS_CHAN_AD(channel) ? 0x2 : 0x1;
+		loss_match = 0x3;
+	} else {
+		eq = 0x4;
+		out_comp = 0x3;
+		flat_gain = NB7_IS_CHAN_AD(channel) ? 0x3 : 0x1;
+		loss_match = NB7_IS_CHAN_AD(channel) ? 0x1 : 0x3;
+	}
+
+	regmap_update_bits(nb7->regmap, EQ_SETTING_REG(channel),
+			   EQ_SETTING_MASK, FIELD_PREP(EQ_SETTING_MASK, eq));
+	regmap_update_bits(nb7->regmap, OUTPUT_COMPRESSION_AND_POL_REG(channel),
+			   OUTPUT_COMPRESSION_MASK, FIELD_PREP(OUTPUT_COMPRESSION_MASK, out_comp));
+	regmap_update_bits(nb7->regmap, FLAT_GAIN_REG(channel),
+			   FLAT_GAIN_MASK, FIELD_PREP(FLAT_GAIN_MASK, flat_gain));
+	regmap_update_bits(nb7->regmap, LOSS_MATCH_REG(channel),
+			   LOSS_MATCH_MASK, FIELD_PREP(LOSS_MATCH_MASK, loss_match));
+}
+
+static int nb7vpq904m_set(struct nb7vpq904m *nb7)
+{
+	bool reverse = (nb7->orientation == TYPEC_ORIENTATION_REVERSE);
+
+	switch (nb7->mode) {
+	case TYPEC_STATE_SAFE:
+		regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+			     GEN_DEV_SET_CHIP_EN |
+			     GEN_DEV_SET_CHNA_EN |
+			     GEN_DEV_SET_CHNB_EN |
+			     GEN_DEV_SET_CHNC_EN |
+			     GEN_DEV_SET_CHND_EN |
+			     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+					GEN_DEV_SET_OP_MODE_USB));
+		nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
+		nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
+		nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
+		nb7vpq904m_set_channel(nb7, NB7_CHND, false);
+		regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
+
+		return 0;
+
+	case TYPEC_STATE_USB:
+		/*
+		 * Normal Orientation (CC1)
+		 * A -> USB RX
+		 * B -> USB TX
+		 * C -> X
+		 * D -> X
+		 * Flipped Orientation (CC2)
+		 * A -> X
+		 * B -> X
+		 * C -> USB TX
+		 * D -> USB RX
+		 *
+		 * Reversed if data lanes are swapped
+		 */
+		if (reverse ^ nb7->swap_data_lanes) {
+			regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+				     GEN_DEV_SET_CHIP_EN |
+				     GEN_DEV_SET_CHNA_EN |
+				     GEN_DEV_SET_CHNB_EN |
+				     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+						GEN_DEV_SET_OP_MODE_USB));
+			nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
+			nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
+		} else {
+			regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+				     GEN_DEV_SET_CHIP_EN |
+				     GEN_DEV_SET_CHNC_EN |
+				     GEN_DEV_SET_CHND_EN |
+				     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+						GEN_DEV_SET_OP_MODE_USB));
+			nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
+			nb7vpq904m_set_channel(nb7, NB7_CHND, false);
+		}
+		regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
+
+		return 0;
+
+	default:
+		if (nb7->svid != USB_TYPEC_DP_SID)
+			return -EINVAL;
+
+		break;
+	}
+
+	/* DP Altmode Setup */
+
+	regmap_write(nb7->regmap, AUX_CC_REG, reverse ? 0x1 : 0x0);
+
+	switch (nb7->mode) {
+	case TYPEC_DP_STATE_C:
+	case TYPEC_DP_STATE_E:
+		/*
+		 * Normal Orientation (CC1)
+		 * A -> DP3
+		 * B -> DP2
+		 * C -> DP1
+		 * D -> DP0
+		 * Flipped Orientation (CC2)
+		 * A -> DP0
+		 * B -> DP1
+		 * C -> DP2
+		 * D -> DP3
+		 */
+		regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+			     GEN_DEV_SET_CHIP_EN |
+			     GEN_DEV_SET_CHNA_EN |
+			     GEN_DEV_SET_CHNB_EN |
+			     GEN_DEV_SET_CHNC_EN |
+			     GEN_DEV_SET_CHND_EN |
+			     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+					GEN_DEV_SET_OP_MODE_DP_4LANE));
+		nb7vpq904m_set_channel(nb7, NB7_CHNA, true);
+		nb7vpq904m_set_channel(nb7, NB7_CHNB, true);
+		nb7vpq904m_set_channel(nb7, NB7_CHNC, true);
+		nb7vpq904m_set_channel(nb7, NB7_CHND, true);
+		break;
+
+	case TYPEC_DP_STATE_D:
+	case TYPEC_DP_STATE_F:
+		regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+			     GEN_DEV_SET_CHIP_EN |
+			     GEN_DEV_SET_CHNA_EN |
+			     GEN_DEV_SET_CHNB_EN |
+			     GEN_DEV_SET_CHNC_EN |
+			     GEN_DEV_SET_CHND_EN |
+			     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+					reverse ^ nb7->swap_data_lanes ?
+						GEN_DEV_SET_OP_MODE_DP_CC2
+						: GEN_DEV_SET_OP_MODE_DP_CC1));
+
+		/*
+		 * Normal Orientation (CC1)
+		 * A -> USB RX
+		 * B -> USB TX
+		 * C -> DP1
+		 * D -> DP0
+		 * Flipped Orientation (CC2)
+		 * A -> DP0
+		 * B -> DP1
+		 * C -> USB TX
+		 * D -> USB RX
+		 *
+		 * Reversed if data lanes are swapped
+		 */
+		if (nb7->swap_data_lanes) {
+			nb7vpq904m_set_channel(nb7, NB7_CHNA, !reverse);
+			nb7vpq904m_set_channel(nb7, NB7_CHNB, !reverse);
+			nb7vpq904m_set_channel(nb7, NB7_CHNC, reverse);
+			nb7vpq904m_set_channel(nb7, NB7_CHND, reverse);
+		} else {
+			nb7vpq904m_set_channel(nb7, NB7_CHNA, reverse);
+			nb7vpq904m_set_channel(nb7, NB7_CHNB, reverse);
+			nb7vpq904m_set_channel(nb7, NB7_CHNC, !reverse);
+			nb7vpq904m_set_channel(nb7, NB7_CHND, !reverse);
+		}
+		break;
+
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int nb7vpq904m_sw_set(struct typec_switch_dev *sw,
+			      enum typec_orientation orientation)
+{
+	struct nb7vpq904m *nb7 = typec_switch_get_drvdata(sw);
+	int ret;
+
+	ret = typec_switch_set(nb7->typec_switch, orientation);
+	if (ret)
+		return ret;
+
+	mutex_lock(&nb7->lock);
+
+	if (nb7->orientation != orientation) {
+		nb7->orientation = orientation;
+
+		ret = nb7vpq904m_set(nb7);
+	}
+
+	mutex_unlock(&nb7->lock);
+
+	return ret;
+}
+
+static int nb7vpq904m_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
+{
+	struct nb7vpq904m *nb7 = typec_mux_get_drvdata(mux);
+	int ret = 0;
+
+	mutex_lock(&nb7->lock);
+
+	if (nb7->mode != state->mode) {
+		nb7->mode = state->mode;
+
+		if (state->alt)
+			nb7->svid = state->alt->svid;
+		else
+			nb7->svid = 0; // No SVID
+
+		ret = nb7vpq904m_set(nb7);
+	}
+
+	mutex_unlock(&nb7->lock);
+
+	return ret;
+}
+
+#if IS_ENABLED(CONFIG_DRM)
+static int nb7vpq904m_bridge_attach(struct drm_bridge *bridge,
+				    enum drm_bridge_attach_flags flags)
+{
+	struct nb7vpq904m *nb7 = container_of(bridge, struct nb7vpq904m, bridge);
+	struct drm_bridge *next_bridge;
+
+	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+		return -EINVAL;
+
+	next_bridge = devm_drm_of_get_bridge(&nb7->client->dev, nb7->client->dev.of_node, 0, 0);
+	if (IS_ERR(next_bridge)) {
+		dev_err(&nb7->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
+		return PTR_ERR(next_bridge);
+	}
+
+	return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
+				 DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static const struct drm_bridge_funcs nb7vpq904m_bridge_funcs = {
+	.attach	= nb7vpq904m_bridge_attach,
+};
+
+static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
+{
+	nb7->bridge.funcs = &nb7vpq904m_bridge_funcs;
+	nb7->bridge.of_node = nb7->client->dev.of_node;
+
+	return devm_drm_bridge_add(&nb7->client->dev, &nb7->bridge);
+}
+#else
+static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
+{
+	return 0;
+}
+#endif
+
+static const struct regmap_config nb7_regmap = {
+	.max_register = 0x1f,
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+enum {
+	NORMAL_LANE_MAPPING,
+	INVERT_LANE_MAPPING,
+};
+
+#define DATA_LANES_COUNT	4
+
+static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
+	[NORMAL_LANE_MAPPING] = { 0, 1, 2, 3 },
+	[INVERT_LANE_MAPPING] = { 2, 3, 0, 1 },
+};
+
+static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
+{
+	struct device_node *ep;
+	u32 data_lanes[4];
+	int ret, i, j;
+
+	ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
+
+	if (ep) {
+		ret = of_property_count_u32_elems(ep, "data-lanes");
+		if (ret == -EINVAL)
+			/* Property isn't here, consider default mapping */
+			goto out_done;
+		if (ret < 0)
+			goto out_error;
+
+		if (ret != DATA_LANES_COUNT) {
+			dev_err(&nb7->client->dev, "expected 4 data lanes\n");
+			ret = -EINVAL;
+			goto out_error;
+		}
+
+		ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
+		if (ret)
+			goto out_error;
+
+		for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
+			for (j = 0; j < DATA_LANES_COUNT; j++) {
+				if (data_lanes[j] != supported_data_lane_mapping[i][j])
+					break;
+			}
+
+			if (j == DATA_LANES_COUNT)
+				break;
+		}
+
+		switch (i) {
+		case NORMAL_LANE_MAPPING:
+			break;
+		case INVERT_LANE_MAPPING:
+			nb7->swap_data_lanes = true;
+			dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
+			break;
+		default:
+			dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
+			ret = -EINVAL;
+			goto out_error;
+		}
+	}
+
+out_done:
+	ret = 0;
+
+out_error:
+	of_node_put(ep);
+
+	return ret;
+}
+
+static int nb7vpq904m_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct typec_switch_desc sw_desc = { };
+	struct typec_mux_desc mux_desc = { };
+	struct nb7vpq904m *nb7;
+	int ret;
+
+	nb7 = devm_kzalloc(dev, sizeof(*nb7), GFP_KERNEL);
+	if (!nb7)
+		return -ENOMEM;
+
+	nb7->client = client;
+
+	nb7->regmap = devm_regmap_init_i2c(client, &nb7_regmap);
+	if (IS_ERR(nb7->regmap)) {
+		dev_err(&client->dev, "Failed to allocate register map\n");
+		return PTR_ERR(nb7->regmap);
+	}
+
+	nb7->mode = TYPEC_STATE_SAFE;
+	nb7->orientation = TYPEC_ORIENTATION_NONE;
+
+	mutex_init(&nb7->lock);
+
+	nb7->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(nb7->enable_gpio))
+		return dev_err_probe(dev, PTR_ERR(nb7->enable_gpio),
+				     "unable to acquire enable gpio\n");
+
+	nb7->vcc_supply = devm_regulator_get_optional(dev, "vcc");
+	if (IS_ERR(nb7->vcc_supply))
+		return PTR_ERR(nb7->vcc_supply);
+
+	nb7->typec_switch = fwnode_typec_switch_get(dev->fwnode);
+	if (IS_ERR(nb7->typec_switch))
+		return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
+				     "failed to acquire orientation-switch\n");
+
+	ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
+	if (ret)
+		return ret;
+
+	ret = regulator_enable(nb7->vcc_supply);
+	if (ret)
+		dev_warn(dev, "Failed to enable vcc: %d\n", ret);
+
+	gpiod_set_value(nb7->enable_gpio, 1);
+
+	ret = nb7vpq904m_register_bridge(nb7);
+	if (ret)
+		return ret;
+
+	sw_desc.drvdata = nb7;
+	sw_desc.fwnode = dev->fwnode;
+	sw_desc.set = nb7vpq904m_sw_set;
+
+	nb7->sw = typec_switch_register(dev, &sw_desc);
+	if (IS_ERR(nb7->sw))
+		return dev_err_probe(dev, PTR_ERR(nb7->sw), "Error registering typec switch\n");
+
+	mux_desc.drvdata = nb7;
+	mux_desc.fwnode = dev->fwnode;
+	mux_desc.set = nb7vpq904m_mux_set;
+
+	nb7->mux = typec_mux_register(dev, &mux_desc);
+	if (IS_ERR(nb7->mux)) {
+		typec_switch_unregister(nb7->sw);
+		return dev_err_probe(dev, PTR_ERR(nb7->mux), "Error registering typec mux\n");
+	}
+
+	return 0;
+}
+
+static void nb7vpq904m_remove(struct i2c_client *client)
+{
+	struct nb7vpq904m *nb7 = i2c_get_clientdata(client);
+
+	typec_mux_unregister(nb7->mux);
+	typec_switch_unregister(nb7->sw);
+
+	gpiod_set_value(nb7->enable_gpio, 0);
+
+	regulator_disable(nb7->vcc_supply);
+}
+
+static const struct i2c_device_id nb7vpq904m_table[] = {
+	{ "nb7vpq904m" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table);
+
+static const struct of_device_id nb7vpq904m_of_table[] = {
+	{ .compatible = "onnn,nb7vpq904m" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, nb7vpq904m_of_table);
+
+static struct i2c_driver nb7vpq904m_driver = {
+	.driver = {
+		.name = "nb7vpq904m",
+		.of_match_table = nb7vpq904m_of_table,
+	},
+	.probe_new	= nb7vpq904m_probe,
+	.remove		= nb7vpq904m_remove,
+	.id_table	= nb7vpq904m_table,
+};
+
+module_i2c_driver(nb7vpq904m_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
+MODULE_DESCRIPTION("OnSemi NB7VPQ904M Type-C driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver
  2023-06-01  9:21 ` [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver neil.armstrong
@ 2023-06-01  9:37   ` Dmitry Baryshkov
  2023-06-01  9:56     ` neil.armstrong
  2023-06-05  8:24   ` Heikki Krogerus
  1 sibling, 1 reply; 8+ messages in thread
From: Dmitry Baryshkov @ 2023-06-01  9:37 UTC (permalink / raw)
  To: neil.armstrong, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Heikki Krogerus, Liam Girdwood, Mark Brown
  Cc: linux-usb, devicetree, linux-kernel, dri-devel

On 01/06/2023 12:21, neil.armstrong@linaro.org wrote:
> From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> 
> Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed
> and DisplayPort ALT Mode Linear Redriver chip found on some devices
> with a Type-C port.
> 
> The redriver compensates ultra High-Speeed DisplayPort and USB
> Super Speed signal integrity losses mainly due to PCB & transmission
> cables.
> 
> The redriver doesn't support SuperSpeed lines swapping, but
> can support Type-C SBU lines swapping.
> 
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
> ---
>   drivers/usb/typec/mux/Kconfig      |   8 +
>   drivers/usb/typec/mux/Makefile     |   1 +
>   drivers/usb/typec/mux/nb7vpq904m.c | 526 +++++++++++++++++++++++++++++++++++++
>   3 files changed, 535 insertions(+)
> 
> diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
> index c46fa4f9d3df..8c4d6b8fb75c 100644
> --- a/drivers/usb/typec/mux/Kconfig
> +++ b/drivers/usb/typec/mux/Kconfig
> @@ -35,4 +35,12 @@ config TYPEC_MUX_INTEL_PMC
>   	  control the USB role switch and also the multiplexer/demultiplexer
>   	  switches used with USB Type-C Alternate Modes.
>   
> +config TYPEC_MUX_NB7VPQ904M
> +	tristate "On Semiconductor NB7VPQ904M Type-C redriver driver"
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	  Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
> +	  redriver chip found on some devices with a Type-C port.
> +
>   endmenu
> diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
> index dda67e19b58b..76196096ef41 100644
> --- a/drivers/usb/typec/mux/Makefile
> +++ b/drivers/usb/typec/mux/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_TYPEC_MUX_FSA4480)		+= fsa4480.o
>   obj-$(CONFIG_TYPEC_MUX_GPIO_SBU)	+= gpio-sbu-mux.o
>   obj-$(CONFIG_TYPEC_MUX_PI3USB30532)	+= pi3usb30532.o
>   obj-$(CONFIG_TYPEC_MUX_INTEL_PMC)	+= intel_pmc_mux.o
> +obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M)	+= nb7vpq904m.o
> diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
> new file mode 100644
> index 000000000000..2f85ad9e417a
> --- /dev/null
> +++ b/drivers/usb/typec/mux/nb7vpq904m.c
> @@ -0,0 +1,526 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * OnSemi NB7VPQ904M Type-C driver
> + *
> + * Copyright (C) 2023 Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> + */
> +#include <linux/i2c.h>
> +#include <linux/mutex.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/of_graph.h>
> +#include <drm/drm_bridge.h>
> +#include <linux/usb/typec_dp.h>
> +#include <linux/usb/typec_mux.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define NB7_CHNA		0
> +#define NB7_CHNB		1
> +#define NB7_CHNC		2
> +#define NB7_CHND		3
> +#define NB7_IS_CHAN_AD(channel) (channel == NB7_CHNA || channel == NB7_CHND)
> +
> +#define GEN_DEV_SET_REG			0x00
> +
> +#define GEN_DEV_SET_CHIP_EN		BIT(0)
> +#define GEN_DEV_SET_CHNA_EN		BIT(4)
> +#define GEN_DEV_SET_CHNB_EN		BIT(5)
> +#define GEN_DEV_SET_CHNC_EN		BIT(6)
> +#define GEN_DEV_SET_CHND_EN		BIT(7)
> +
> +#define GEN_DEV_SET_OP_MODE_MASK	GENMASK(3, 1)
> +
> +#define GEN_DEV_SET_OP_MODE_DP_CC2	0
> +#define GEN_DEV_SET_OP_MODE_DP_CC1	1
> +#define GEN_DEV_SET_OP_MODE_DP_4LANE	2
> +#define GEN_DEV_SET_OP_MODE_USB		5
> +
> +#define EQ_SETTING_REG_BASE		0x01
> +#define EQ_SETTING_REG(n)		(EQ_SETTING_REG_BASE + (n) * 2)
> +#define EQ_SETTING_MASK			GENMASK(3, 1)
> +
> +#define OUTPUT_COMPRESSION_AND_POL_REG_BASE	0x02
> +#define OUTPUT_COMPRESSION_AND_POL_REG(n)	(OUTPUT_COMPRESSION_AND_POL_REG_BASE + (n) * 2)
> +#define OUTPUT_COMPRESSION_MASK		GENMASK(2, 1)
> +
> +#define FLAT_GAIN_REG_BASE		0x18
> +#define FLAT_GAIN_REG(n)		(FLAT_GAIN_REG_BASE + (n) * 2)
> +#define FLAT_GAIN_MASK			GENMASK(1, 0)
> +
> +#define LOSS_MATCH_REG_BASE		0x19
> +#define LOSS_MATCH_REG(n)		(LOSS_MATCH_REG_BASE + (n) * 2)
> +#define LOSS_MATCH_MASK			GENMASK(1, 0)
> +
> +#define AUX_CC_REG			0x09
> +
> +#define CHIP_VERSION_REG		0x17
> +
> +struct nb7vpq904m {
> +	struct i2c_client *client;
> +	struct gpio_desc *enable_gpio;
> +	struct regulator *vcc_supply;
> +	struct regmap *regmap;
> +	struct typec_switch_dev *sw;
> +	struct typec_mux_dev *mux;
> +
> +	bool swap_data_lanes;
> +	struct typec_switch *typec_switch;
> +
> +	struct drm_bridge bridge;
> +
> +	struct mutex lock; /* protect non-concurrent mux & switch */
> +
> +	enum typec_orientation orientation;
> +	unsigned long mode;
> +	unsigned int svid;
> +};
> +
> +static void nb7vpq904m_set_channel(struct nb7vpq904m *nb7, unsigned int channel, bool dp)
> +{
> +	u8 eq, out_comp, flat_gain, loss_match;
> +
> +	if (dp) {
> +		eq = NB7_IS_CHAN_AD(channel) ? 0x6 : 0x4;
> +		out_comp = 0x3;
> +		flat_gain = NB7_IS_CHAN_AD(channel) ? 0x2 : 0x1;
> +		loss_match = 0x3;
> +	} else {
> +		eq = 0x4;
> +		out_comp = 0x3;
> +		flat_gain = NB7_IS_CHAN_AD(channel) ? 0x3 : 0x1;
> +		loss_match = NB7_IS_CHAN_AD(channel) ? 0x1 : 0x3;
> +	}
> +
> +	regmap_update_bits(nb7->regmap, EQ_SETTING_REG(channel),
> +			   EQ_SETTING_MASK, FIELD_PREP(EQ_SETTING_MASK, eq));
> +	regmap_update_bits(nb7->regmap, OUTPUT_COMPRESSION_AND_POL_REG(channel),
> +			   OUTPUT_COMPRESSION_MASK, FIELD_PREP(OUTPUT_COMPRESSION_MASK, out_comp));
> +	regmap_update_bits(nb7->regmap, FLAT_GAIN_REG(channel),
> +			   FLAT_GAIN_MASK, FIELD_PREP(FLAT_GAIN_MASK, flat_gain));
> +	regmap_update_bits(nb7->regmap, LOSS_MATCH_REG(channel),
> +			   LOSS_MATCH_MASK, FIELD_PREP(LOSS_MATCH_MASK, loss_match));
> +}
> +
> +static int nb7vpq904m_set(struct nb7vpq904m *nb7)
> +{
> +	bool reverse = (nb7->orientation == TYPEC_ORIENTATION_REVERSE);
> +
> +	switch (nb7->mode) {
> +	case TYPEC_STATE_SAFE:
> +		regmap_write(nb7->regmap, GEN_DEV_SET_REG,
> +			     GEN_DEV_SET_CHIP_EN |
> +			     GEN_DEV_SET_CHNA_EN |
> +			     GEN_DEV_SET_CHNB_EN |
> +			     GEN_DEV_SET_CHNC_EN |
> +			     GEN_DEV_SET_CHND_EN |
> +			     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
> +					GEN_DEV_SET_OP_MODE_USB));
> +		nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
> +		nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
> +		nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
> +		nb7vpq904m_set_channel(nb7, NB7_CHND, false);
> +		regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
> +
> +		return 0;
> +
> +	case TYPEC_STATE_USB:
> +		/*
> +		 * Normal Orientation (CC1)
> +		 * A -> USB RX
> +		 * B -> USB TX
> +		 * C -> X
> +		 * D -> X
> +		 * Flipped Orientation (CC2)
> +		 * A -> X
> +		 * B -> X
> +		 * C -> USB TX
> +		 * D -> USB RX
> +		 *
> +		 * Reversed if data lanes are swapped
> +		 */
> +		if (reverse ^ nb7->swap_data_lanes) {
> +			regmap_write(nb7->regmap, GEN_DEV_SET_REG,
> +				     GEN_DEV_SET_CHIP_EN |
> +				     GEN_DEV_SET_CHNA_EN |
> +				     GEN_DEV_SET_CHNB_EN |
> +				     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
> +						GEN_DEV_SET_OP_MODE_USB));
> +			nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
> +			nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
> +		} else {
> +			regmap_write(nb7->regmap, GEN_DEV_SET_REG,
> +				     GEN_DEV_SET_CHIP_EN |
> +				     GEN_DEV_SET_CHNC_EN |
> +				     GEN_DEV_SET_CHND_EN |
> +				     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
> +						GEN_DEV_SET_OP_MODE_USB));
> +			nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
> +			nb7vpq904m_set_channel(nb7, NB7_CHND, false);
> +		}
> +		regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
> +
> +		return 0;
> +
> +	default:
> +		if (nb7->svid != USB_TYPEC_DP_SID)
> +			return -EINVAL;
> +
> +		break;
> +	}
> +
> +	/* DP Altmode Setup */
> +
> +	regmap_write(nb7->regmap, AUX_CC_REG, reverse ? 0x1 : 0x0);
> +
> +	switch (nb7->mode) {
> +	case TYPEC_DP_STATE_C:
> +	case TYPEC_DP_STATE_E:
> +		/*
> +		 * Normal Orientation (CC1)
> +		 * A -> DP3
> +		 * B -> DP2
> +		 * C -> DP1
> +		 * D -> DP0
> +		 * Flipped Orientation (CC2)
> +		 * A -> DP0
> +		 * B -> DP1
> +		 * C -> DP2
> +		 * D -> DP3
> +		 */
> +		regmap_write(nb7->regmap, GEN_DEV_SET_REG,
> +			     GEN_DEV_SET_CHIP_EN |
> +			     GEN_DEV_SET_CHNA_EN |
> +			     GEN_DEV_SET_CHNB_EN |
> +			     GEN_DEV_SET_CHNC_EN |
> +			     GEN_DEV_SET_CHND_EN |
> +			     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
> +					GEN_DEV_SET_OP_MODE_DP_4LANE));
> +		nb7vpq904m_set_channel(nb7, NB7_CHNA, true);
> +		nb7vpq904m_set_channel(nb7, NB7_CHNB, true);
> +		nb7vpq904m_set_channel(nb7, NB7_CHNC, true);
> +		nb7vpq904m_set_channel(nb7, NB7_CHND, true);
> +		break;
> +
> +	case TYPEC_DP_STATE_D:
> +	case TYPEC_DP_STATE_F:
> +		regmap_write(nb7->regmap, GEN_DEV_SET_REG,
> +			     GEN_DEV_SET_CHIP_EN |
> +			     GEN_DEV_SET_CHNA_EN |
> +			     GEN_DEV_SET_CHNB_EN |
> +			     GEN_DEV_SET_CHNC_EN |
> +			     GEN_DEV_SET_CHND_EN |
> +			     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
> +					reverse ^ nb7->swap_data_lanes ?
> +						GEN_DEV_SET_OP_MODE_DP_CC2
> +						: GEN_DEV_SET_OP_MODE_DP_CC1));
> +
> +		/*
> +		 * Normal Orientation (CC1)
> +		 * A -> USB RX
> +		 * B -> USB TX
> +		 * C -> DP1
> +		 * D -> DP0
> +		 * Flipped Orientation (CC2)
> +		 * A -> DP0
> +		 * B -> DP1
> +		 * C -> USB TX
> +		 * D -> USB RX
> +		 *
> +		 * Reversed if data lanes are swapped
> +		 */
> +		if (nb7->swap_data_lanes) {
> +			nb7vpq904m_set_channel(nb7, NB7_CHNA, !reverse);
> +			nb7vpq904m_set_channel(nb7, NB7_CHNB, !reverse);
> +			nb7vpq904m_set_channel(nb7, NB7_CHNC, reverse);
> +			nb7vpq904m_set_channel(nb7, NB7_CHND, reverse);
> +		} else {
> +			nb7vpq904m_set_channel(nb7, NB7_CHNA, reverse);
> +			nb7vpq904m_set_channel(nb7, NB7_CHNB, reverse);
> +			nb7vpq904m_set_channel(nb7, NB7_CHNC, !reverse);
> +			nb7vpq904m_set_channel(nb7, NB7_CHND, !reverse);
> +		}
> +		break;
> +
> +	default:
> +		return -ENOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static int nb7vpq904m_sw_set(struct typec_switch_dev *sw,
> +			      enum typec_orientation orientation)
> +{
> +	struct nb7vpq904m *nb7 = typec_switch_get_drvdata(sw);
> +	int ret;
> +
> +	ret = typec_switch_set(nb7->typec_switch, orientation);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&nb7->lock);
> +
> +	if (nb7->orientation != orientation) {
> +		nb7->orientation = orientation;
> +
> +		ret = nb7vpq904m_set(nb7);
> +	}
> +
> +	mutex_unlock(&nb7->lock);
> +
> +	return ret;
> +}
> +
> +static int nb7vpq904m_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
> +{
> +	struct nb7vpq904m *nb7 = typec_mux_get_drvdata(mux);
> +	int ret = 0;
> +
> +	mutex_lock(&nb7->lock);
> +
> +	if (nb7->mode != state->mode) {
> +		nb7->mode = state->mode;
> +
> +		if (state->alt)
> +			nb7->svid = state->alt->svid;
> +		else
> +			nb7->svid = 0; // No SVID
> +
> +		ret = nb7vpq904m_set(nb7);
> +	}
> +
> +	mutex_unlock(&nb7->lock);
> +
> +	return ret;
> +}
> +
> +#if IS_ENABLED(CONFIG_DRM)
> +static int nb7vpq904m_bridge_attach(struct drm_bridge *bridge,
> +				    enum drm_bridge_attach_flags flags)
> +{
> +	struct nb7vpq904m *nb7 = container_of(bridge, struct nb7vpq904m, bridge);
> +	struct drm_bridge *next_bridge;
> +
> +	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
> +		return -EINVAL;
> +
> +	next_bridge = devm_drm_of_get_bridge(&nb7->client->dev, nb7->client->dev.of_node, 0, 0);
> +	if (IS_ERR(next_bridge)) {
> +		dev_err(&nb7->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
> +		return PTR_ERR(next_bridge);
> +	}
> +
> +	return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
> +				 DRM_BRIDGE_ATTACH_NO_CONNECTOR);
> +}
> +
> +static const struct drm_bridge_funcs nb7vpq904m_bridge_funcs = {
> +	.attach	= nb7vpq904m_bridge_attach,
> +};
> +
> +static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
> +{
> +	nb7->bridge.funcs = &nb7vpq904m_bridge_funcs;
> +	nb7->bridge.of_node = nb7->client->dev.of_node;
> +
> +	return devm_drm_bridge_add(&nb7->client->dev, &nb7->bridge);
> +}
> +#else
> +static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
> +{
> +	return 0;
> +}
> +#endif

This is what I feared of when we started mixing USB-C and drm bridges. A 
part of me still thinks that the proper solution should involve OOB 
notifications.

If we can expect a sizeable amount of such drivers, can we have a 
generic drm helper for such passthrough bridges?

> +
> +static const struct regmap_config nb7_regmap = {
> +	.max_register = 0x1f,
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +};
> +
> +enum {
> +	NORMAL_LANE_MAPPING,
> +	INVERT_LANE_MAPPING,
> +};
> +
> +#define DATA_LANES_COUNT	4
> +
> +static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
> +	[NORMAL_LANE_MAPPING] = { 0, 1, 2, 3 },
> +	[INVERT_LANE_MAPPING] = { 2, 3, 0, 1 },
> +};
> +
> +static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
> +{
> +	struct device_node *ep;
> +	u32 data_lanes[4];
> +	int ret, i, j;
> +
> +	ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
> +
> +	if (ep) {
> +		ret = of_property_count_u32_elems(ep, "data-lanes");
> +		if (ret == -EINVAL)
> +			/* Property isn't here, consider default mapping */
> +			goto out_done;
> +		if (ret < 0)
> +			goto out_error;
> +
> +		if (ret != DATA_LANES_COUNT) {
> +			dev_err(&nb7->client->dev, "expected 4 data lanes\n");
> +			ret = -EINVAL;
> +			goto out_error;
> +		}
> +
> +		ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
> +		if (ret)
> +			goto out_error;
> +
> +		for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
> +			for (j = 0; j < DATA_LANES_COUNT; j++) {
> +				if (data_lanes[j] != supported_data_lane_mapping[i][j])
> +					break;
> +			}
> +
> +			if (j == DATA_LANES_COUNT)
> +				break;
> +		}
> +
> +		switch (i) {
> +		case NORMAL_LANE_MAPPING:
> +			break;
> +		case INVERT_LANE_MAPPING:
> +			nb7->swap_data_lanes = true;
> +			dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
> +			break;
> +		default:
> +			dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
> +			ret = -EINVAL;
> +			goto out_error;
> +		}
> +	}
> +
> +out_done:
> +	ret = 0;
> +
> +out_error:
> +	of_node_put(ep);
> +
> +	return ret;
> +}
> +
> +static int nb7vpq904m_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct typec_switch_desc sw_desc = { };
> +	struct typec_mux_desc mux_desc = { };
> +	struct nb7vpq904m *nb7;
> +	int ret;
> +
> +	nb7 = devm_kzalloc(dev, sizeof(*nb7), GFP_KERNEL);
> +	if (!nb7)
> +		return -ENOMEM;
> +
> +	nb7->client = client;
> +
> +	nb7->regmap = devm_regmap_init_i2c(client, &nb7_regmap);
> +	if (IS_ERR(nb7->regmap)) {
> +		dev_err(&client->dev, "Failed to allocate register map\n");
> +		return PTR_ERR(nb7->regmap);
> +	}
> +
> +	nb7->mode = TYPEC_STATE_SAFE;
> +	nb7->orientation = TYPEC_ORIENTATION_NONE;
> +
> +	mutex_init(&nb7->lock);
> +
> +	nb7->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(nb7->enable_gpio))
> +		return dev_err_probe(dev, PTR_ERR(nb7->enable_gpio),
> +				     "unable to acquire enable gpio\n");
> +
> +	nb7->vcc_supply = devm_regulator_get_optional(dev, "vcc");
> +	if (IS_ERR(nb7->vcc_supply))
> +		return PTR_ERR(nb7->vcc_supply);
> +
> +	nb7->typec_switch = fwnode_typec_switch_get(dev->fwnode);
> +	if (IS_ERR(nb7->typec_switch))
> +		return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
> +				     "failed to acquire orientation-switch\n");
> +
> +	ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
> +	if (ret)
> +		return ret;
> +
> +	ret = regulator_enable(nb7->vcc_supply);
> +	if (ret)
> +		dev_warn(dev, "Failed to enable vcc: %d\n", ret);
> +
> +	gpiod_set_value(nb7->enable_gpio, 1);
> +
> +	ret = nb7vpq904m_register_bridge(nb7);
> +	if (ret)
> +		return ret;
> +
> +	sw_desc.drvdata = nb7;
> +	sw_desc.fwnode = dev->fwnode;
> +	sw_desc.set = nb7vpq904m_sw_set;
> +
> +	nb7->sw = typec_switch_register(dev, &sw_desc);
> +	if (IS_ERR(nb7->sw))
> +		return dev_err_probe(dev, PTR_ERR(nb7->sw), "Error registering typec switch\n");
> +
> +	mux_desc.drvdata = nb7;
> +	mux_desc.fwnode = dev->fwnode;
> +	mux_desc.set = nb7vpq904m_mux_set;
> +
> +	nb7->mux = typec_mux_register(dev, &mux_desc);
> +	if (IS_ERR(nb7->mux)) {
> +		typec_switch_unregister(nb7->sw);
> +		return dev_err_probe(dev, PTR_ERR(nb7->mux), "Error registering typec mux\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static void nb7vpq904m_remove(struct i2c_client *client)
> +{
> +	struct nb7vpq904m *nb7 = i2c_get_clientdata(client);
> +
> +	typec_mux_unregister(nb7->mux);
> +	typec_switch_unregister(nb7->sw);
> +
> +	gpiod_set_value(nb7->enable_gpio, 0);
> +
> +	regulator_disable(nb7->vcc_supply);
> +}
> +
> +static const struct i2c_device_id nb7vpq904m_table[] = {
> +	{ "nb7vpq904m" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table);
> +
> +static const struct of_device_id nb7vpq904m_of_table[] = {
> +	{ .compatible = "onnn,nb7vpq904m" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, nb7vpq904m_of_table);
> +
> +static struct i2c_driver nb7vpq904m_driver = {
> +	.driver = {
> +		.name = "nb7vpq904m",
> +		.of_match_table = nb7vpq904m_of_table,
> +	},
> +	.probe_new	= nb7vpq904m_probe,
> +	.remove		= nb7vpq904m_remove,
> +	.id_table	= nb7vpq904m_table,
> +};
> +
> +module_i2c_driver(nb7vpq904m_driver);
> +
> +MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
> +MODULE_DESCRIPTION("OnSemi NB7VPQ904M Type-C driver");
> +MODULE_LICENSE("GPL");
> 

-- 
With best wishes
Dmitry


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver
  2023-06-01  9:37   ` Dmitry Baryshkov
@ 2023-06-01  9:56     ` neil.armstrong
  0 siblings, 0 replies; 8+ messages in thread
From: neil.armstrong @ 2023-06-01  9:56 UTC (permalink / raw)
  To: Dmitry Baryshkov, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Heikki Krogerus, Liam Girdwood, Mark Brown
  Cc: linux-usb, devicetree, linux-kernel, dri-devel

On 01/06/2023 11:37, Dmitry Baryshkov wrote:
> On 01/06/2023 12:21, neil.armstrong@linaro.org wrote:
>> From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>>
>> Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed
>> and DisplayPort ALT Mode Linear Redriver chip found on some devices
>> with a Type-C port.
>>
>> The redriver compensates ultra High-Speeed DisplayPort and USB
>> Super Speed signal integrity losses mainly due to PCB & transmission
>> cables.
>>
>> The redriver doesn't support SuperSpeed lines swapping, but
>> can support Type-C SBU lines swapping.
>>
>> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>> ---
>>   drivers/usb/typec/mux/Kconfig      |   8 +
>>   drivers/usb/typec/mux/Makefile     |   1 +
>>   drivers/usb/typec/mux/nb7vpq904m.c | 526 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 535 insertions(+)
>>
>> diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
>> index c46fa4f9d3df..8c4d6b8fb75c 100644
>> --- a/drivers/usb/typec/mux/Kconfig
>> +++ b/drivers/usb/typec/mux/Kconfig
>> @@ -35,4 +35,12 @@ config TYPEC_MUX_INTEL_PMC
>>         control the USB role switch and also the multiplexer/demultiplexer
>>         switches used with USB Type-C Alternate Modes.
>> +config TYPEC_MUX_NB7VPQ904M
>> +    tristate "On Semiconductor NB7VPQ904M Type-C redriver driver"
>> +    depends on I2C
>> +    select REGMAP_I2C
>> +    help
>> +      Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
>> +      redriver chip found on some devices with a Type-C port.
>> +
>>   endmenu
>> diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
>> index dda67e19b58b..76196096ef41 100644
>> --- a/drivers/usb/typec/mux/Makefile
>> +++ b/drivers/usb/typec/mux/Makefile
>> @@ -4,3 +4,4 @@ obj-$(CONFIG_TYPEC_MUX_FSA4480)        += fsa4480.o
>>   obj-$(CONFIG_TYPEC_MUX_GPIO_SBU)    += gpio-sbu-mux.o
>>   obj-$(CONFIG_TYPEC_MUX_PI3USB30532)    += pi3usb30532.o
>>   obj-$(CONFIG_TYPEC_MUX_INTEL_PMC)    += intel_pmc_mux.o
>> +obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M)    += nb7vpq904m.o
>> diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
>> new file mode 100644
>> index 000000000000..2f85ad9e417a
>> --- /dev/null
>> +++ b/drivers/usb/typec/mux/nb7vpq904m.c
>> @@ -0,0 +1,526 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * OnSemi NB7VPQ904M Type-C driver
>> + *
>> + * Copyright (C) 2023 Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>> + */
>> +#include <linux/i2c.h>
>> +#include <linux/mutex.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>> +#include <linux/of_graph.h>
>> +#include <drm/drm_bridge.h>
>> +#include <linux/usb/typec_dp.h>
>> +#include <linux/usb/typec_mux.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#define NB7_CHNA        0
>> +#define NB7_CHNB        1
>> +#define NB7_CHNC        2
>> +#define NB7_CHND        3
>> +#define NB7_IS_CHAN_AD(channel) (channel == NB7_CHNA || channel == NB7_CHND)
>> +
>> +#define GEN_DEV_SET_REG            0x00
>> +
>> +#define GEN_DEV_SET_CHIP_EN        BIT(0)
>> +#define GEN_DEV_SET_CHNA_EN        BIT(4)
>> +#define GEN_DEV_SET_CHNB_EN        BIT(5)
>> +#define GEN_DEV_SET_CHNC_EN        BIT(6)
>> +#define GEN_DEV_SET_CHND_EN        BIT(7)
>> +
>> +#define GEN_DEV_SET_OP_MODE_MASK    GENMASK(3, 1)
>> +
>> +#define GEN_DEV_SET_OP_MODE_DP_CC2    0
>> +#define GEN_DEV_SET_OP_MODE_DP_CC1    1
>> +#define GEN_DEV_SET_OP_MODE_DP_4LANE    2
>> +#define GEN_DEV_SET_OP_MODE_USB        5
>> +
>> +#define EQ_SETTING_REG_BASE        0x01
>> +#define EQ_SETTING_REG(n)        (EQ_SETTING_REG_BASE + (n) * 2)
>> +#define EQ_SETTING_MASK            GENMASK(3, 1)
>> +
>> +#define OUTPUT_COMPRESSION_AND_POL_REG_BASE    0x02
>> +#define OUTPUT_COMPRESSION_AND_POL_REG(n)    (OUTPUT_COMPRESSION_AND_POL_REG_BASE + (n) * 2)
>> +#define OUTPUT_COMPRESSION_MASK        GENMASK(2, 1)
>> +
>> +#define FLAT_GAIN_REG_BASE        0x18
>> +#define FLAT_GAIN_REG(n)        (FLAT_GAIN_REG_BASE + (n) * 2)
>> +#define FLAT_GAIN_MASK            GENMASK(1, 0)
>> +
>> +#define LOSS_MATCH_REG_BASE        0x19
>> +#define LOSS_MATCH_REG(n)        (LOSS_MATCH_REG_BASE + (n) * 2)
>> +#define LOSS_MATCH_MASK            GENMASK(1, 0)
>> +
>> +#define AUX_CC_REG            0x09
>> +
>> +#define CHIP_VERSION_REG        0x17
>> +
>> +struct nb7vpq904m {
>> +    struct i2c_client *client;
>> +    struct gpio_desc *enable_gpio;
>> +    struct regulator *vcc_supply;
>> +    struct regmap *regmap;
>> +    struct typec_switch_dev *sw;
>> +    struct typec_mux_dev *mux;
>> +
>> +    bool swap_data_lanes;
>> +    struct typec_switch *typec_switch;
>> +
>> +    struct drm_bridge bridge;
>> +
>> +    struct mutex lock; /* protect non-concurrent mux & switch */
>> +
>> +    enum typec_orientation orientation;
>> +    unsigned long mode;
>> +    unsigned int svid;
>> +};
>> +
>> +static void nb7vpq904m_set_channel(struct nb7vpq904m *nb7, unsigned int channel, bool dp)
>> +{
>> +    u8 eq, out_comp, flat_gain, loss_match;
>> +
>> +    if (dp) {
>> +        eq = NB7_IS_CHAN_AD(channel) ? 0x6 : 0x4;
>> +        out_comp = 0x3;
>> +        flat_gain = NB7_IS_CHAN_AD(channel) ? 0x2 : 0x1;
>> +        loss_match = 0x3;
>> +    } else {
>> +        eq = 0x4;
>> +        out_comp = 0x3;
>> +        flat_gain = NB7_IS_CHAN_AD(channel) ? 0x3 : 0x1;
>> +        loss_match = NB7_IS_CHAN_AD(channel) ? 0x1 : 0x3;
>> +    }
>> +
>> +    regmap_update_bits(nb7->regmap, EQ_SETTING_REG(channel),
>> +               EQ_SETTING_MASK, FIELD_PREP(EQ_SETTING_MASK, eq));
>> +    regmap_update_bits(nb7->regmap, OUTPUT_COMPRESSION_AND_POL_REG(channel),
>> +               OUTPUT_COMPRESSION_MASK, FIELD_PREP(OUTPUT_COMPRESSION_MASK, out_comp));
>> +    regmap_update_bits(nb7->regmap, FLAT_GAIN_REG(channel),
>> +               FLAT_GAIN_MASK, FIELD_PREP(FLAT_GAIN_MASK, flat_gain));
>> +    regmap_update_bits(nb7->regmap, LOSS_MATCH_REG(channel),
>> +               LOSS_MATCH_MASK, FIELD_PREP(LOSS_MATCH_MASK, loss_match));
>> +}
>> +
>> +static int nb7vpq904m_set(struct nb7vpq904m *nb7)
>> +{
>> +    bool reverse = (nb7->orientation == TYPEC_ORIENTATION_REVERSE);
>> +
>> +    switch (nb7->mode) {
>> +    case TYPEC_STATE_SAFE:
>> +        regmap_write(nb7->regmap, GEN_DEV_SET_REG,
>> +                 GEN_DEV_SET_CHIP_EN |
>> +                 GEN_DEV_SET_CHNA_EN |
>> +                 GEN_DEV_SET_CHNB_EN |
>> +                 GEN_DEV_SET_CHNC_EN |
>> +                 GEN_DEV_SET_CHND_EN |
>> +                 FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
>> +                    GEN_DEV_SET_OP_MODE_USB));
>> +        nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
>> +        nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
>> +        nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
>> +        nb7vpq904m_set_channel(nb7, NB7_CHND, false);
>> +        regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
>> +
>> +        return 0;
>> +
>> +    case TYPEC_STATE_USB:
>> +        /*
>> +         * Normal Orientation (CC1)
>> +         * A -> USB RX
>> +         * B -> USB TX
>> +         * C -> X
>> +         * D -> X
>> +         * Flipped Orientation (CC2)
>> +         * A -> X
>> +         * B -> X
>> +         * C -> USB TX
>> +         * D -> USB RX
>> +         *
>> +         * Reversed if data lanes are swapped
>> +         */
>> +        if (reverse ^ nb7->swap_data_lanes) {
>> +            regmap_write(nb7->regmap, GEN_DEV_SET_REG,
>> +                     GEN_DEV_SET_CHIP_EN |
>> +                     GEN_DEV_SET_CHNA_EN |
>> +                     GEN_DEV_SET_CHNB_EN |
>> +                     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
>> +                        GEN_DEV_SET_OP_MODE_USB));
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
>> +        } else {
>> +            regmap_write(nb7->regmap, GEN_DEV_SET_REG,
>> +                     GEN_DEV_SET_CHIP_EN |
>> +                     GEN_DEV_SET_CHNC_EN |
>> +                     GEN_DEV_SET_CHND_EN |
>> +                     FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
>> +                        GEN_DEV_SET_OP_MODE_USB));
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHND, false);
>> +        }
>> +        regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
>> +
>> +        return 0;
>> +
>> +    default:
>> +        if (nb7->svid != USB_TYPEC_DP_SID)
>> +            return -EINVAL;
>> +
>> +        break;
>> +    }
>> +
>> +    /* DP Altmode Setup */
>> +
>> +    regmap_write(nb7->regmap, AUX_CC_REG, reverse ? 0x1 : 0x0);
>> +
>> +    switch (nb7->mode) {
>> +    case TYPEC_DP_STATE_C:
>> +    case TYPEC_DP_STATE_E:
>> +        /*
>> +         * Normal Orientation (CC1)
>> +         * A -> DP3
>> +         * B -> DP2
>> +         * C -> DP1
>> +         * D -> DP0
>> +         * Flipped Orientation (CC2)
>> +         * A -> DP0
>> +         * B -> DP1
>> +         * C -> DP2
>> +         * D -> DP3
>> +         */
>> +        regmap_write(nb7->regmap, GEN_DEV_SET_REG,
>> +                 GEN_DEV_SET_CHIP_EN |
>> +                 GEN_DEV_SET_CHNA_EN |
>> +                 GEN_DEV_SET_CHNB_EN |
>> +                 GEN_DEV_SET_CHNC_EN |
>> +                 GEN_DEV_SET_CHND_EN |
>> +                 FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
>> +                    GEN_DEV_SET_OP_MODE_DP_4LANE));
>> +        nb7vpq904m_set_channel(nb7, NB7_CHNA, true);
>> +        nb7vpq904m_set_channel(nb7, NB7_CHNB, true);
>> +        nb7vpq904m_set_channel(nb7, NB7_CHNC, true);
>> +        nb7vpq904m_set_channel(nb7, NB7_CHND, true);
>> +        break;
>> +
>> +    case TYPEC_DP_STATE_D:
>> +    case TYPEC_DP_STATE_F:
>> +        regmap_write(nb7->regmap, GEN_DEV_SET_REG,
>> +                 GEN_DEV_SET_CHIP_EN |
>> +                 GEN_DEV_SET_CHNA_EN |
>> +                 GEN_DEV_SET_CHNB_EN |
>> +                 GEN_DEV_SET_CHNC_EN |
>> +                 GEN_DEV_SET_CHND_EN |
>> +                 FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
>> +                    reverse ^ nb7->swap_data_lanes ?
>> +                        GEN_DEV_SET_OP_MODE_DP_CC2
>> +                        : GEN_DEV_SET_OP_MODE_DP_CC1));
>> +
>> +        /*
>> +         * Normal Orientation (CC1)
>> +         * A -> USB RX
>> +         * B -> USB TX
>> +         * C -> DP1
>> +         * D -> DP0
>> +         * Flipped Orientation (CC2)
>> +         * A -> DP0
>> +         * B -> DP1
>> +         * C -> USB TX
>> +         * D -> USB RX
>> +         *
>> +         * Reversed if data lanes are swapped
>> +         */
>> +        if (nb7->swap_data_lanes) {
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNA, !reverse);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNB, !reverse);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNC, reverse);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHND, reverse);
>> +        } else {
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNA, reverse);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNB, reverse);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHNC, !reverse);
>> +            nb7vpq904m_set_channel(nb7, NB7_CHND, !reverse);
>> +        }
>> +        break;
>> +
>> +    default:
>> +        return -ENOTSUPP;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int nb7vpq904m_sw_set(struct typec_switch_dev *sw,
>> +                  enum typec_orientation orientation)
>> +{
>> +    struct nb7vpq904m *nb7 = typec_switch_get_drvdata(sw);
>> +    int ret;
>> +
>> +    ret = typec_switch_set(nb7->typec_switch, orientation);
>> +    if (ret)
>> +        return ret;
>> +
>> +    mutex_lock(&nb7->lock);
>> +
>> +    if (nb7->orientation != orientation) {
>> +        nb7->orientation = orientation;
>> +
>> +        ret = nb7vpq904m_set(nb7);
>> +    }
>> +
>> +    mutex_unlock(&nb7->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +static int nb7vpq904m_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
>> +{
>> +    struct nb7vpq904m *nb7 = typec_mux_get_drvdata(mux);
>> +    int ret = 0;
>> +
>> +    mutex_lock(&nb7->lock);
>> +
>> +    if (nb7->mode != state->mode) {
>> +        nb7->mode = state->mode;
>> +
>> +        if (state->alt)
>> +            nb7->svid = state->alt->svid;
>> +        else
>> +            nb7->svid = 0; // No SVID
>> +
>> +        ret = nb7vpq904m_set(nb7);
>> +    }
>> +
>> +    mutex_unlock(&nb7->lock);
>> +
>> +    return ret;
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_DRM)
>> +static int nb7vpq904m_bridge_attach(struct drm_bridge *bridge,
>> +                    enum drm_bridge_attach_flags flags)
>> +{
>> +    struct nb7vpq904m *nb7 = container_of(bridge, struct nb7vpq904m, bridge);
>> +    struct drm_bridge *next_bridge;
>> +
>> +    if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
>> +        return -EINVAL;
>> +
>> +    next_bridge = devm_drm_of_get_bridge(&nb7->client->dev, nb7->client->dev.of_node, 0, 0);
>> +    if (IS_ERR(next_bridge)) {
>> +        dev_err(&nb7->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
>> +        return PTR_ERR(next_bridge);
>> +    }
>> +
>> +    return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
>> +                 DRM_BRIDGE_ATTACH_NO_CONNECTOR);
>> +}
>> +
>> +static const struct drm_bridge_funcs nb7vpq904m_bridge_funcs = {
>> +    .attach    = nb7vpq904m_bridge_attach,
>> +};
>> +
>> +static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
>> +{
>> +    nb7->bridge.funcs = &nb7vpq904m_bridge_funcs;
>> +    nb7->bridge.of_node = nb7->client->dev.of_node;
>> +
>> +    return devm_drm_bridge_add(&nb7->client->dev, &nb7->bridge);
>> +}
>> +#else
>> +static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
>> +{
>> +    return 0;
>> +}
>> +#endif
> 
> This is what I feared of when we started mixing USB-C and drm bridges. A part of me still thinks that the proper solution should involve OOB notifications.
> 

OOB looks nice, but as I lookes, it would require a large amount of changes.

The advantage of bridge chaining is we can add specific ops for prepare/enable/disable/unprepare
for each elements of the USB-C data path.

> If we can expect a sizeable amount of such drivers, can we have a generic drm helper for such passthrough bridges?

Sure, we can add this to drm-bridge.c as a simple pass-throught bridge code

Neil

> 
>> +
>> +static const struct regmap_config nb7_regmap = {
>> +    .max_register = 0x1f,
>> +    .reg_bits = 8,
>> +    .val_bits = 8,
>> +};
>> +
>> +enum {
>> +    NORMAL_LANE_MAPPING,
>> +    INVERT_LANE_MAPPING,
>> +};
>> +
>> +#define DATA_LANES_COUNT    4
>> +
>> +static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
>> +    [NORMAL_LANE_MAPPING] = { 0, 1, 2, 3 },
>> +    [INVERT_LANE_MAPPING] = { 2, 3, 0, 1 },
>> +};
>> +
>> +static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
>> +{
>> +    struct device_node *ep;
>> +    u32 data_lanes[4];
>> +    int ret, i, j;
>> +
>> +    ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
>> +
>> +    if (ep) {
>> +        ret = of_property_count_u32_elems(ep, "data-lanes");
>> +        if (ret == -EINVAL)
>> +            /* Property isn't here, consider default mapping */
>> +            goto out_done;
>> +        if (ret < 0)
>> +            goto out_error;
>> +
>> +        if (ret != DATA_LANES_COUNT) {
>> +            dev_err(&nb7->client->dev, "expected 4 data lanes\n");
>> +            ret = -EINVAL;
>> +            goto out_error;
>> +        }
>> +
>> +        ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
>> +        if (ret)
>> +            goto out_error;
>> +
>> +        for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
>> +            for (j = 0; j < DATA_LANES_COUNT; j++) {
>> +                if (data_lanes[j] != supported_data_lane_mapping[i][j])
>> +                    break;
>> +            }
>> +
>> +            if (j == DATA_LANES_COUNT)
>> +                break;
>> +        }
>> +
>> +        switch (i) {
>> +        case NORMAL_LANE_MAPPING:
>> +            break;
>> +        case INVERT_LANE_MAPPING:
>> +            nb7->swap_data_lanes = true;
>> +            dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
>> +            break;
>> +        default:
>> +            dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
>> +            ret = -EINVAL;
>> +            goto out_error;
>> +        }
>> +    }
>> +
>> +out_done:
>> +    ret = 0;
>> +
>> +out_error:
>> +    of_node_put(ep);
>> +
>> +    return ret;
>> +}
>> +
>> +static int nb7vpq904m_probe(struct i2c_client *client)
>> +{
>> +    struct device *dev = &client->dev;
>> +    struct typec_switch_desc sw_desc = { };
>> +    struct typec_mux_desc mux_desc = { };
>> +    struct nb7vpq904m *nb7;
>> +    int ret;
>> +
>> +    nb7 = devm_kzalloc(dev, sizeof(*nb7), GFP_KERNEL);
>> +    if (!nb7)
>> +        return -ENOMEM;
>> +
>> +    nb7->client = client;
>> +
>> +    nb7->regmap = devm_regmap_init_i2c(client, &nb7_regmap);
>> +    if (IS_ERR(nb7->regmap)) {
>> +        dev_err(&client->dev, "Failed to allocate register map\n");
>> +        return PTR_ERR(nb7->regmap);
>> +    }
>> +
>> +    nb7->mode = TYPEC_STATE_SAFE;
>> +    nb7->orientation = TYPEC_ORIENTATION_NONE;
>> +
>> +    mutex_init(&nb7->lock);
>> +
>> +    nb7->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
>> +    if (IS_ERR(nb7->enable_gpio))
>> +        return dev_err_probe(dev, PTR_ERR(nb7->enable_gpio),
>> +                     "unable to acquire enable gpio\n");
>> +
>> +    nb7->vcc_supply = devm_regulator_get_optional(dev, "vcc");
>> +    if (IS_ERR(nb7->vcc_supply))
>> +        return PTR_ERR(nb7->vcc_supply);
>> +
>> +    nb7->typec_switch = fwnode_typec_switch_get(dev->fwnode);
>> +    if (IS_ERR(nb7->typec_switch))
>> +        return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
>> +                     "failed to acquire orientation-switch\n");
>> +
>> +    ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
>> +    if (ret)
>> +        return ret;
>> +
>> +    ret = regulator_enable(nb7->vcc_supply);
>> +    if (ret)
>> +        dev_warn(dev, "Failed to enable vcc: %d\n", ret);
>> +
>> +    gpiod_set_value(nb7->enable_gpio, 1);
>> +
>> +    ret = nb7vpq904m_register_bridge(nb7);
>> +    if (ret)
>> +        return ret;
>> +
>> +    sw_desc.drvdata = nb7;
>> +    sw_desc.fwnode = dev->fwnode;
>> +    sw_desc.set = nb7vpq904m_sw_set;
>> +
>> +    nb7->sw = typec_switch_register(dev, &sw_desc);
>> +    if (IS_ERR(nb7->sw))
>> +        return dev_err_probe(dev, PTR_ERR(nb7->sw), "Error registering typec switch\n");
>> +
>> +    mux_desc.drvdata = nb7;
>> +    mux_desc.fwnode = dev->fwnode;
>> +    mux_desc.set = nb7vpq904m_mux_set;
>> +
>> +    nb7->mux = typec_mux_register(dev, &mux_desc);
>> +    if (IS_ERR(nb7->mux)) {
>> +        typec_switch_unregister(nb7->sw);
>> +        return dev_err_probe(dev, PTR_ERR(nb7->mux), "Error registering typec mux\n");
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static void nb7vpq904m_remove(struct i2c_client *client)
>> +{
>> +    struct nb7vpq904m *nb7 = i2c_get_clientdata(client);
>> +
>> +    typec_mux_unregister(nb7->mux);
>> +    typec_switch_unregister(nb7->sw);
>> +
>> +    gpiod_set_value(nb7->enable_gpio, 0);
>> +
>> +    regulator_disable(nb7->vcc_supply);
>> +}
>> +
>> +static const struct i2c_device_id nb7vpq904m_table[] = {
>> +    { "nb7vpq904m" },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table);
>> +
>> +static const struct of_device_id nb7vpq904m_of_table[] = {
>> +    { .compatible = "onnn,nb7vpq904m" },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(of, nb7vpq904m_of_table);
>> +
>> +static struct i2c_driver nb7vpq904m_driver = {
>> +    .driver = {
>> +        .name = "nb7vpq904m",
>> +        .of_match_table = nb7vpq904m_of_table,
>> +    },
>> +    .probe_new    = nb7vpq904m_probe,
>> +    .remove        = nb7vpq904m_remove,
>> +    .id_table    = nb7vpq904m_table,
>> +};
>> +
>> +module_i2c_driver(nb7vpq904m_driver);
>> +
>> +MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
>> +MODULE_DESCRIPTION("OnSemi NB7VPQ904M Type-C driver");
>> +MODULE_LICENSE("GPL");
>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings
  2023-06-01  9:21 ` [PATCH 1/2] dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings Neil Armstrong
@ 2023-06-02 14:30   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2023-06-02 14:30 UTC (permalink / raw)
  To: Neil Armstrong, Greg Kroah-Hartman, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Heikki Krogerus, Liam Girdwood, Mark Brown, Dmitry Baryshkov
  Cc: linux-usb, devicetree, linux-kernel

On 01/06/2023 11:21, Neil Armstrong wrote:
> Document bindings for this ON Semiconductor Type-C USB SuperSpeed
> and DisplayPort ALT Mode Linear Redriver.
> 
> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
> ---


...

> +      port@2:
> +        $ref: /schemas/graph.yaml#/properties/port
> +        description:
> +          Sideband Use (SBU) AUX lines endpoint to the Type-C connector for the purpose of
> +          handling altmode muxing and orientation switching.
> +
> +required:
> +  - compatible
> +  - reg
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    i2c13 {

If there is going to be resend: i2c


> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +


Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver
  2023-06-01  9:21 ` [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver neil.armstrong
  2023-06-01  9:37   ` Dmitry Baryshkov
@ 2023-06-05  8:24   ` Heikki Krogerus
  2023-06-05  8:31     ` neil.armstrong
  1 sibling, 1 reply; 8+ messages in thread
From: Heikki Krogerus @ 2023-06-05  8:24 UTC (permalink / raw)
  To: neil.armstrong
  Cc: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Liam Girdwood, Mark Brown,
	Dmitry Baryshkov, linux-usb, devicetree, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1505 bytes --]

Hi Neil,

On Thu, Jun 01, 2023 at 11:21:13AM +0200, neil.armstrong@linaro.org wrote:
> From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> 
> Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed
> and DisplayPort ALT Mode Linear Redriver chip found on some devices
> with a Type-C port.
> 
> The redriver compensates ultra High-Speeed DisplayPort and USB
> Super Speed signal integrity losses mainly due to PCB & transmission
> cables.
> 
> The redriver doesn't support SuperSpeed lines swapping, but
> can support Type-C SBU lines swapping.
> 
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
> ---
>  drivers/usb/typec/mux/Kconfig      |   8 +
>  drivers/usb/typec/mux/Makefile     |   1 +
>  drivers/usb/typec/mux/nb7vpq904m.c | 526 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 535 insertions(+)

This looks good to me, but I think you should register a retimer
instead of a mode switch (the orientation switch is fine).

Retimers are handled just like the muxes, so this patch would not need
that many changes, but you would need to change the first patch too.
You would need to declare a "redriver-switch" instead of "mode-switch"
property in your DT (or perhaps make it just "redriver" instead of
"redriver-switch"?).

We just need to add a device type for redrivers to the retimer class -
check the attached diff. Something like that.

Let me know what you guys think.

thanks,

-- 
heikki

[-- Attachment #2: redriver_device_type.diff --]
[-- Type: text/plain, Size: 3015 bytes --]

diff --git a/drivers/usb/typec/retimer.c b/drivers/usb/typec/retimer.c
index 4a7d1b5c4d866..0eb7d7433cd63 100644
--- a/drivers/usb/typec/retimer.c
+++ b/drivers/usb/typec/retimer.c
@@ -19,7 +19,8 @@
 
 static int retimer_fwnode_match(struct device *dev, const void *fwnode)
 {
-	return is_typec_retimer(dev) && device_match_fwnode(dev, fwnode);
+	return (is_typec_retimer(dev) || is_typec_redriver(dev)) &&
+	       device_match_fwnode(dev, fwnode);
 }
 
 static void *typec_retimer_match(const struct fwnode_handle *fwnode, const char *id, void *data)
@@ -49,6 +50,8 @@ struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode)
 	struct typec_retimer *retimer;
 
 	retimer = fwnode_connection_find_match(fwnode, "retimer-switch", NULL, typec_retimer_match);
+	if (!retimer)
+		retimer = fwnode_connection_find_match(fwnode, "redriver-switch", NULL, typec_retimer_match);
 	if (!IS_ERR_OR_NULL(retimer))
 		WARN_ON(!try_module_get(retimer->dev.parent->driver->owner));
 
@@ -90,6 +93,11 @@ const struct device_type typec_retimer_dev_type = {
 	.release = typec_retimer_release,
 };
 
+const struct device_type typec_redriver_dev_type = {
+	.name = "typec_redriver",
+	.release = typec_retimer_release,
+};
+
 /**
  * typec_retimer_register - Register a retimer device.
  * @parent: Parent device.
@@ -120,10 +128,10 @@ typec_retimer_register(struct device *parent, const struct typec_retimer_desc *d
 	retimer->dev.parent = parent;
 	retimer->dev.fwnode = desc->fwnode;
 	retimer->dev.class = &retimer_class;
-	retimer->dev.type = &typec_retimer_dev_type;
+	retimer->dev.type = desc->redriver ? &typec_redriver_dev_type : &typec_retimer_dev_type;
 	retimer->dev.driver_data = desc->drvdata;
-	dev_set_name(&retimer->dev, "%s-retimer",
-		     desc->name ? desc->name : dev_name(parent));
+	dev_set_name(&retimer->dev, "%s-%s", desc->name ? desc->name : dev_name(parent),
+		     desc->redriver ? "redriver" : "retimer");
 
 	ret = device_add(&retimer->dev);
 	if (ret) {
diff --git a/drivers/usb/typec/retimer.h b/drivers/usb/typec/retimer.h
index d6a5ef9881e1f..b552cdb985724 100644
--- a/drivers/usb/typec/retimer.h
+++ b/drivers/usb/typec/retimer.h
@@ -13,7 +13,9 @@ struct typec_retimer {
 #define to_typec_retimer(_dev_) container_of(_dev_, struct typec_retimer, dev)
 
 extern const struct device_type typec_retimer_dev_type;
+extern const struct device_type typec_redriver_dev_type;
 
 #define is_typec_retimer(dev) ((dev)->type == &typec_retimer_dev_type)
+#define is_typec_redriver(dev) ((dev)->type == &typec_redriver_dev_type)
 
 #endif /* __USB_TYPEC_RETIMER__ */
diff --git a/include/linux/usb/typec_retimer.h b/include/linux/usb/typec_retimer.h
index 5e036b3360e25..b2daf8e6caeb0 100644
--- a/include/linux/usb/typec_retimer.h
+++ b/include/linux/usb/typec_retimer.h
@@ -25,6 +25,7 @@ struct typec_retimer_desc {
 	typec_retimer_set_fn_t set;
 	const char *name;
 	void *drvdata;
+	bool redriver;
 };
 
 struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode);

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver
  2023-06-05  8:24   ` Heikki Krogerus
@ 2023-06-05  8:31     ` neil.armstrong
  0 siblings, 0 replies; 8+ messages in thread
From: neil.armstrong @ 2023-06-05  8:31 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Liam Girdwood, Mark Brown,
	Dmitry Baryshkov, linux-usb, devicetree, linux-kernel

Hi,

On 05/06/2023 10:24, Heikki Krogerus wrote:
> Hi Neil,
> 
> On Thu, Jun 01, 2023 at 11:21:13AM +0200, neil.armstrong@linaro.org wrote:
>> From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>>
>> Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed
>> and DisplayPort ALT Mode Linear Redriver chip found on some devices
>> with a Type-C port.
>>
>> The redriver compensates ultra High-Speeed DisplayPort and USB
>> Super Speed signal integrity losses mainly due to PCB & transmission
>> cables.
>>
>> The redriver doesn't support SuperSpeed lines swapping, but
>> can support Type-C SBU lines swapping.
>>
>> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>> Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
>> ---
>>   drivers/usb/typec/mux/Kconfig      |   8 +
>>   drivers/usb/typec/mux/Makefile     |   1 +
>>   drivers/usb/typec/mux/nb7vpq904m.c | 526 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 535 insertions(+)
> 
> This looks good to me, but I think you should register a retimer
> instead of a mode switch (the orientation switch is fine).

Indeed, I'm not familiar with namings but by looking around it seems that
redriver and retimers for USB-C applications are exactly the same.

> 
> Retimers are handled just like the muxes, so this patch would not need
> that many changes, but you would need to change the first patch too.
> You would need to declare a "redriver-switch" instead of "mode-switch"
> property in your DT (or perhaps make it just "redriver" instead of
> "redriver-switch"?).

So it would need some additional handling to also set the retimer state
along the mux, but I was wondering, why having a separate handling
while the retimer state struct is exactly the same as the mux ?

> 
> We just need to add a device type for redrivers to the retimer class -
> check the attached diff. Something like that.
> 
> Let me know what you guys think.

I think I'll switch to retimer, thanks for pointing it to me...

Thanks,
Neil

> 
> thanks,
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-06-05  8:31 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-01  9:21 [PATCH 0/2] typec: add support for the ON Semiconductor nb7vpq904m Type-C Linear Redriver Neil Armstrong
2023-06-01  9:21 ` [PATCH 1/2] dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings Neil Armstrong
2023-06-02 14:30   ` Krzysztof Kozlowski
2023-06-01  9:21 ` [PATCH 2/2] usb: typec: add support for the nb7vpq904m Type-C Linear Redriver neil.armstrong
2023-06-01  9:37   ` Dmitry Baryshkov
2023-06-01  9:56     ` neil.armstrong
2023-06-05  8:24   ` Heikki Krogerus
2023-06-05  8:31     ` neil.armstrong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).