This series introduces a binding for Type-C data lane switches. These control the routing and operating modes of USB Type-C data lanes based on the PD messaging from the Type-C port driver regarding connected peripherals.
The first 2 patches introduce the new "typec-switch" binding as well as one user of it (the ANX7625 drm bridge).
Patches 3-5 add functionality to the anx7625 driver to register the mode-switches, as well as program its crosspoint switch depending on which Type-C port has a DisplayPort (DP) peripheral connected to it.
Patch 6-9 add similar bindings update and Type-C switch support to the it6505 driver.
v4: https://lore.kernel.org/linux-usb/20220615172129.1314056-8-pmalani@chromium....
Changes in v5: - Rebased on usb-next, so removed Patch v4 1/7 and Patch v4 2/7 from this version (v5) since they are already in usb-next. - Added newer Reviewed-by tags. - Added new patches (6-9) in this version for a 2nd example (it6505) of a binding of the user.
Patch submission suggestions: Option 1: - Bindings patches 1/9 and 2/9 can go through the USB repo (since they are already reviewed from v4 [1]). - Bindings patch 6/9 can go through the USB repo, and the remaining patches (3-5,7-9) can go through the DRM repo. <or> - Patches 3-9 can all go through the DRM repo.
Option 2: - All patches (1-9) go through the USB repo.
(My apologies if I've made this confusing, and I appreciate any suggestions for better submission strategy).
[1]: https://lore.kernel.org/linux-usb/YrMxFeMc0tk%2FK1qL@kroah.com/
Pin-Yen Lin (5): drm/bridge: anx7625: Add typec_mux_set callback function dt/bindings: drm/bridge: it6505: Add mode-switch support drm/bridge: it6505: Register number of Type C switches drm/bridge: it6505: Register Type-C mode switches drm/bridge: it6505: Add typec_mux_set callback function
Prashant Malani (4): dt-bindings: usb: Add Type-C switch binding dt-bindings: drm/bridge: anx7625: Add mode-switch support drm/bridge: anx7625: Register number of Type C switches drm/bridge: anx7625: Register Type-C mode switches
.../display/bridge/analogix,anx7625.yaml | 64 +++++++ .../bindings/display/bridge/ite,it6505.yaml | 97 +++++++++- .../devicetree/bindings/usb/typec-switch.yaml | 74 ++++++++ drivers/gpu/drm/bridge/analogix/anx7625.c | 148 +++++++++++++++ drivers/gpu/drm/bridge/analogix/anx7625.h | 20 ++ drivers/gpu/drm/bridge/ite-it6505.c | 171 +++++++++++++++++- 6 files changed, 569 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/typec-switch.yaml
Introduce a binding which represents a component that can control the routing of USB Type-C data lines as well as address data line orientation (based on CC lines' orientation).
Reviewed-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Reviewed-by: AngeloGioacchino Del Regno angelogioacchino.delregno@collabora.com Reviewed-by: Nícolas F. R. A. Prado nfraprado@collabora.com Tested-by: Nícolas F. R. A. Prado nfraprado@collabora.com Signed-off-by: Prashant Malani pmalani@chromium.org ---
Changes since v4: - Added Reviewed-by tags. - Patch moved to 1/9 position (since Patch v4 1/7 and 2/7 were applied to usb-next)
Changes since v3: - No changes.
Changes since v2: - Added Reviewed-by and Tested-by tags.
Changes since v1: - Removed "items" from compatible. - Fixed indentation in example.
.../devicetree/bindings/usb/typec-switch.yaml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/typec-switch.yaml
diff --git a/Documentation/devicetree/bindings/usb/typec-switch.yaml b/Documentation/devicetree/bindings/usb/typec-switch.yaml new file mode 100644 index 000000000000..78b0190c8543 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-switch.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/typec-switch.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: USB Type-C Switch + +maintainers: + - Prashant Malani pmalani@chromium.org + +description: + A USB Type-C switch represents a component which routes USB Type-C data + lines to various protocol host controllers (e.g USB, VESA DisplayPort, + Thunderbolt etc.) depending on which mode the Type-C port, port partner + and cable are operating in. It can also modify lane routing based on + the orientation of a connected Type-C peripheral. + +properties: + compatible: + const: typec-switch + + mode-switch: + type: boolean + description: Specify that this switch can handle alternate mode switching. + + orientation-switch: + type: boolean + description: Specify that this switch can handle orientation switching. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + description: OF graph binding modelling data lines to the Type-C switch. + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Link between the switch and a Type-C connector. + + required: + - port@0 + +required: + - compatible + - ports + +anyOf: + - required: + - mode-switch + - required: + - orientation-switch + +additionalProperties: true + +examples: + - | + drm-bridge { + usb-switch { + compatible = "typec-switch"; + mode-switch; + orientation-switch; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + anx_ep: endpoint { + remote-endpoint = <&typec_controller>; + }; + }; + }; + }; + };
Quoting Prashant Malani (2022-06-22 10:34:30)
diff --git a/Documentation/devicetree/bindings/usb/typec-switch.yaml b/Documentation/devicetree/bindings/usb/typec-switch.yaml new file mode 100644 index 000000000000..78b0190c8543 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-switch.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/typec-switch.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: USB Type-C Switch
+maintainers:
- Prashant Malani pmalani@chromium.org
+description:
- A USB Type-C switch represents a component which routes USB Type-C data
- lines to various protocol host controllers (e.g USB, VESA DisplayPort,
- Thunderbolt etc.) depending on which mode the Type-C port, port partner
- and cable are operating in. It can also modify lane routing based on
- the orientation of a connected Type-C peripheral.
+properties:
- compatible:
- const: typec-switch
- mode-switch:
- type: boolean
- description: Specify that this switch can handle alternate mode switching.
- orientation-switch:
- type: boolean
- description: Specify that this switch can handle orientation switching.
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- description: OF graph binding modelling data lines to the Type-C switch.
- properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Link between the switch and a Type-C connector.
Is there an update to the usb-c-connector binding to accept this port connection?
- required:
- port@0
+required:
- compatible
- ports
+anyOf:
- required:
- mode-switch
- required:
- orientation-switch
+additionalProperties: true
+examples:
- |
- drm-bridge {
usb-switch {
compatible = "typec-switch";
I still don't understand the subnode design here. usb-switch as a container node indicates to me that this is a bus, but in earlier rounds of this series it was stated this isn't a bus. Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
mode-switch;
orientation-switch;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
anx_ep: endpoint {
remote-endpoint = <&typec_controller>;
};
};
};
};
On Thu, Jun 23, 2022 at 11:30 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:30)
diff --git a/Documentation/devicetree/bindings/usb/typec-switch.yaml b/Documentation/devicetree/bindings/usb/typec-switch.yaml new file mode 100644 index 000000000000..78b0190c8543 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-switch.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/typec-switch.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: USB Type-C Switch
+maintainers:
- Prashant Malani pmalani@chromium.org
+description:
- A USB Type-C switch represents a component which routes USB Type-C data
- lines to various protocol host controllers (e.g USB, VESA DisplayPort,
- Thunderbolt etc.) depending on which mode the Type-C port, port partner
- and cable are operating in. It can also modify lane routing based on
- the orientation of a connected Type-C peripheral.
+properties:
- compatible:
- const: typec-switch
- mode-switch:
- type: boolean
- description: Specify that this switch can handle alternate mode switching.
- orientation-switch:
- type: boolean
- description: Specify that this switch can handle orientation switching.
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- description: OF graph binding modelling data lines to the Type-C switch.
- properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Link between the switch and a Type-C connector.
Is there an update to the usb-c-connector binding to accept this port connection?
Not at this time. I don't think we should enforce that either. (Type-C data-lines could theoretically be routed through intermediate hardware like retimers/repeaters)
- required:
- port@0
+required:
- compatible
- ports
+anyOf:
- required:
- mode-switch
- required:
- orientation-switch
+additionalProperties: true
+examples:
- |
- drm-bridge {
usb-switch {
compatible = "typec-switch";
I still don't understand the subnode design here. usb-switch as a container node indicates to me that this is a bus, but in earlier rounds of this series it was stated this isn't a bus.
I am not aware of this as a requirement. Can you please point me to the documentation that states this needs to be the case?
Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
I attempted to explain the rationale in the previous version [1], but using a dedicated sub-node means the driver doesn't haven't to inspect individual ports to determine which of them need switches registered for them. If it sees a `typec-switch`, it registers a mode-switch and/or orientation-switch. IMO it simplifies the hardware device binding too.
It also maps with the internal block diagram for these hardware components (for ex. the anx7625 crosspoint switch is a separate sub-block within anx7625).
[1] https://lore.kernel.org/linux-usb/CACeCKaeH6qTTdG_huC4yw0xxG8TYEOtfPW3tiVNwY...
mode-switch;
orientation-switch;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
anx_ep: endpoint {
remote-endpoint = <&typec_controller>;
};
};
};
};
Quoting Prashant Malani (2022-06-23 12:08:21)
On Thu, Jun 23, 2022 at 11:30 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:30)
diff --git a/Documentation/devicetree/bindings/usb/typec-switch.yaml b/Documentation/devicetree/bindings/usb/typec-switch.yaml new file mode 100644 index 000000000000..78b0190c8543 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-switch.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2
[...]
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- description: OF graph binding modelling data lines to the Type-C switch.
- properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Link between the switch and a Type-C connector.
Is there an update to the usb-c-connector binding to accept this port connection?
Not at this time. I don't think we should enforce that either. (Type-C data-lines could theoretically be routed through intermediate hardware like retimers/repeaters)
I'm mostly wondering if having such a connection to the usb-c-connector, or even through some retimer/repeater, would be sufficient to detect how many type-c ports are connected to the device. If the type-c pin assignments only support two or four lanes for DP then it seems like we should describe the two lanes or four lanes as one graph endpoint "output" and then have some 'data-lanes' property in case the DP lanes are flipped while being sent to the retimer or usb-c-connector. This would of course depend on the capability of the device, i.e. if it can remap DP lanes or only has 2 lanes of DP, etc.
- |
- drm-bridge {
usb-switch {
compatible = "typec-switch";
I still don't understand the subnode design here. usb-switch as a container node indicates to me that this is a bus, but in earlier rounds of this series it was stated this isn't a bus.
I am not aware of this as a requirement. Can you please point me to the documentation that states this needs to be the case?
I'm not aware of any documentation for the dos and don'ts here. Are there any examples in the bindings directory that split up a device into subnodes that isn't in bindings/mfd? I just know from experience that any time I try to make a child node of an existing node that I'm supposed to be describing a bus, unless I'm adding some sort of exception node like a graph binding or an opp table. Typically a node corresponds 1:1 with a device in the kernel. I'll defer to Rob for any citations.
Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
I attempted to explain the rationale in the previous version [1], but using a dedicated sub-node means the driver doesn't haven't to inspect individual ports to determine which of them need switches registered for them. If it sees a `typec-switch`, it registers a mode-switch and/or orientation-switch. IMO it simplifies the hardware device binding too.
How is that any harder than hard-coding that detail into the driver about which port and endpoint is possibly connected to the usb-c-connector (or retimer)? All of that logic could be behind some API that registers a typec-switch based on a graph port number that's passed in, ala drm_of_find_panel_or_bridge()'s design.
Coming from a DT writer's perspective, I just want to go through the list of output pins in the datasheet and match them up to the ports binding for this device. If it's a pure DP bridge, where USB hardware isn't an input or an output like the ITE chip, then I don't want to have to describe a port graph binding for the case when it's connected to a dp-connector (see dp-connector.yaml) in the top-level node and then have to make an entirely different subnode for the usb-c-connector case with a whole other set of graph ports.
How would I even know which two differential pairs correspond to port0 or port1 in this binding in the ITE case? Ideally we make the graph binding more strict for devices by enforcing that their graph ports exist. Otherwise we're not fully describing the connections between devices and our dtb checkers are going to let things through where the driver most likely will fail because it can't figure out what to do, e.g. display DP on 4 lanes or play some DP lane rerouting games to act as a mux.
It also maps with the internal block diagram for these hardware components (for ex. the anx7625 crosspoint switch is a separate sub-block within anx7625).
We don't make DT bindings for sub-components like this very often. It would make more sense to me to have a subnode if a typec switch was some sort of off the shelf hard macro that the hardware engineer placed down inside the IC that they delivered. Then we could have a completely generic driver that binds to the generic binding that knows how to drive the hardware, because it's an unchangeable hard macro with a well defined programming interface.
[1] https://lore.kernel.org/linux-usb/CACeCKaeH6qTTdG_huC4yw0xxG8TYEOtfPW3tiVNwY...
I looked at the fsa4480 driver and the device has a publicly available datasheet[2]. That device is designed for "audio accessory mode" but I guess it's being used to simply mux SBU lines? There isn't an upstream user of the binding so far, but it also doesn't look like a complete binding. I'd expect to see DN_L/R as a graph output connected to the usb-c-connector and probably have a usb2.0 input port and a 'sound-dai' property to represent the input audio path.
Finally, simply connecting to the typec controller node isn't sufficient because a typec controller can be controlling many usb-c-connectors so I don't see how the graph binding would be able to figure out how many usb-c-connectors are connected to a mux like device, unless we took the approach of this patch. Is that why you're proposing this binding? To avoid describing a graph binding in the usb-c-connector and effectively "pushing" the port count up to the mux?
On Thu, Jun 23, 2022 at 4:14 PM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-23 12:08:21)
On Thu, Jun 23, 2022 at 11:30 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:30)
diff --git a/Documentation/devicetree/bindings/usb/typec-switch.yaml b/Documentation/devicetree/bindings/usb/typec-switch.yaml new file mode 100644 index 000000000000..78b0190c8543 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-switch.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2
[...]
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- description: OF graph binding modelling data lines to the Type-C switch.
- properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Link between the switch and a Type-C connector.
Is there an update to the usb-c-connector binding to accept this port connection?
Not at this time. I don't think we should enforce that either. (Type-C data-lines could theoretically be routed through intermediate hardware like retimers/repeaters)
I'm mostly wondering if having such a connection to the usb-c-connector, or even through some retimer/repeater, would be sufficient to detect how many type-c ports are connected to the device. If the type-c pin assignments only support two or four lanes for DP then it seems like we should describe the two lanes or four lanes as one graph endpoint "output" and then have some 'data-lanes' property in case the DP lanes are flipped while being sent to the retimer or usb-c-connector. This would of course depend on the capability of the device, i.e. if it can remap DP lanes or only has 2 lanes of DP, etc.
- |
- drm-bridge {
usb-switch {
compatible = "typec-switch";
I still don't understand the subnode design here. usb-switch as a container node indicates to me that this is a bus, but in earlier rounds of this series it was stated this isn't a bus.
I am not aware of this as a requirement. Can you please point me to the documentation that states this needs to be the case?
I'm not aware of any documentation for the dos and don'ts here. Are there any examples in the bindings directory that split up a device into subnodes that isn't in bindings/mfd?
usb-c-connector [3] and its users is an example.
I just know from experience that any time I try to make a child node of an existing node that I'm supposed to be describing a bus, unless I'm adding some sort of exception node like a graph binding or an opp table. Typically a node corresponds 1:1 with a device in the kernel. I'll defer to Rob for any citations.
Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
I attempted to explain the rationale in the previous version [1], but using a dedicated sub-node means the driver doesn't haven't to inspect individual ports to determine which of them need switches registered for them. If it sees a `typec-switch`, it registers a mode-switch and/or orientation-switch. IMO it simplifies the hardware device binding too.
How is that any harder than hard-coding that detail into the driver about which port and endpoint is possibly connected to the usb-c-connector (or retimer)? All of that logic could be behind some API that registers a typec-switch based on a graph port number that's passed in, ala drm_of_find_panel_or_bridge()'s design.
If each driver has to do it (and the port specifics vary for each driver), it becomes an avoidable overhead for each of them. I prefer hard-coding such details if avoidable. I suppose both approaches require modifications to the binding and the driver code.
Coming from a DT writer's perspective, I just want to go through the list of output pins in the datasheet and match them up to the ports binding for this device. If it's a pure DP bridge, where USB hardware isn't an input or an output like the ITE chip, then I don't want to have to describe a port graph binding for the case when it's connected to a dp-connector (see dp-connector.yaml) in the top-level node and then have to make an entirely different subnode for the usb-c-connector case with a whole other set of graph ports.
This approach still allows for that, if the driver has any use for it (AFAICT these drivers don't). Iff that driver uses it, one can (optionally) route their output (top-level) ports through the "typec-switch" sub-node (and onwards as required). If it's being used in a "pure-DP" configuration, the "typec-switch" just goes away (the top level ports can be routed as desired by the driver). (Again, I must reiterate that neither this driver or the anx driver utilizes this)
How would I even know which two differential pairs correspond to port0 or port1 in this binding in the ITE case?
Why do we need to know that? It doesn't affect this or the other driver or hardware's functioning in a perceivable way.
Ideally we make the graph binding more strict for devices by enforcing that their graph ports exist. Otherwise we're not fully describing the connections between devices and our dtb checkers are going to let things through where the driver most likely will fail because it can't figure out what to do, e.g. display DP on 4 lanes or play some DP lane rerouting games to act as a mux.
How is the current binding enforcing this? The typec-switch binding as a first step ensures that the DT is connecting the hardware(anx,ite etc) to something that at least "claims" to be a Type-C switch.
It also maps with the internal block diagram for these hardware components (for ex. the anx7625 crosspoint switch is a separate sub-block within anx7625).
We don't make DT bindings for sub-components like this very often. It would make more sense to me to have a subnode if a typec switch was some sort of off the shelf hard macro that the hardware engineer placed down inside the IC that they delivered. Then we could have a completely generic driver that binds to the generic binding that knows how to drive the hardware, because it's an unchangeable hard macro with a well defined programming interface.
[1] https://lore.kernel.org/linux-usb/CACeCKaeH6qTTdG_huC4yw0xxG8TYEOtfPW3tiVNwY...
I looked at the fsa4480 driver and the device has a publicly available datasheet[2]. That device is designed for "audio accessory mode" but I guess it's being used to simply mux SBU lines? There isn't an upstream user of the binding so far, but it also doesn't look like a complete binding. I'd expect to see DN_L/R as a graph output connected to the usb-c-connector and probably have a usb2.0 input port and a 'sound-dai' property to represent the input audio path.
Finally, simply connecting to the typec controller node isn't sufficient because a typec controller can be controlling many usb-c-connectors so I don't see how the graph binding would be able to figure out how many usb-c-connectors are connected to a mux like device, unless we took the approach of this patch.
It can follow the endpoint of the typec-switch port (the port parent of the remote end-point would be a 'usb-c-connector'). That is if the graph binding (I'm assuming you mean the switch device here) wants to figure this out in the first place.
Is that why you're proposing this binding? To avoid describing a graph binding in the usb-c-connector and effectively "pushing" the port count up to the mux?
No, that is not the intention behind this series. The 'usb-c-connector' still needs the graph binding to the `typec-switch`. SBU, HS and SS lanes might have different muxes altogether (usb-c-connect has separate ports for SBU, HS and SS lanes)
[3] https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bind...
On Thu, Jun 23, 2022 at 5:35 PM Prashant Malani pmalani@chromium.org wrote:
On Thu, Jun 23, 2022 at 4:14 PM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-23 12:08:21)
On Thu, Jun 23, 2022 at 11:30 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:30)
diff --git a/Documentation/devicetree/bindings/usb/typec-switch.yaml b/Documentation/devicetree/bindings/usb/typec-switch.yaml new file mode 100644 index 000000000000..78b0190c8543 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/typec-switch.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2
[...]
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- description: OF graph binding modelling data lines to the Type-C switch.
- properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Link between the switch and a Type-C connector.
Is there an update to the usb-c-connector binding to accept this port connection?
Not at this time. I don't think we should enforce that either. (Type-C data-lines could theoretically be routed through intermediate hardware like retimers/repeaters)
I'm mostly wondering if having such a connection to the usb-c-connector, or even through some retimer/repeater, would be sufficient to detect how many type-c ports are connected to the device. If the type-c pin assignments only support two or four lanes for DP then it seems like we should describe the two lanes or four lanes as one graph endpoint "output" and then have some 'data-lanes' property in case the DP lanes are flipped while being sent to the retimer or usb-c-connector. This would of course depend on the capability of the device, i.e. if it can remap DP lanes or only has 2 lanes of DP, etc.
- |
- drm-bridge {
usb-switch {
compatible = "typec-switch";
I still don't understand the subnode design here. usb-switch as a container node indicates to me that this is a bus, but in earlier rounds of this series it was stated this isn't a bus.
I am not aware of this as a requirement. Can you please point me to the documentation that states this needs to be the case?
I'm not aware of any documentation for the dos and don'ts here. Are there any examples in the bindings directory that split up a device into subnodes that isn't in bindings/mfd?
usb-c-connector [3] and its users is an example.
I just know from experience that any time I try to make a child node of an existing node that I'm supposed to be describing a bus, unless I'm adding some sort of exception node like a graph binding or an opp table. Typically a node corresponds 1:1 with a device in the kernel. I'll defer to Rob for any citations.
Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
I attempted to explain the rationale in the previous version [1], but using a dedicated sub-node means the driver doesn't haven't to inspect individual ports to determine which of them need switches registered for them. If it sees a `typec-switch`, it registers a mode-switch and/or orientation-switch. IMO it simplifies the hardware device binding too.
How is that any harder than hard-coding that detail into the driver about which port and endpoint is possibly connected to the usb-c-connector (or retimer)? All of that logic could be behind some API that registers a typec-switch based on a graph port number that's passed in, ala drm_of_find_panel_or_bridge()'s design.
If each driver has to do it (and the port specifics vary for each driver), it becomes an avoidable overhead for each of them. I prefer hard-coding such details if avoidable. I suppose both approaches
Sorry, I meant "I prefer not hard-coding such details..."
require modifications to the binding and the driver code.
Coming from a DT writer's perspective, I just want to go through the list of output pins in the datasheet and match them up to the ports binding for this device. If it's a pure DP bridge, where USB hardware isn't an input or an output like the ITE chip, then I don't want to have to describe a port graph binding for the case when it's connected to a dp-connector (see dp-connector.yaml) in the top-level node and then have to make an entirely different subnode for the usb-c-connector case with a whole other set of graph ports.
This approach still allows for that, if the driver has any use for it (AFAICT these drivers don't). Iff that driver uses it, one can (optionally) route their output (top-level) ports through the "typec-switch" sub-node (and onwards as required). If it's being used in a "pure-DP" configuration, the "typec-switch" just goes away (the top level ports can be routed as desired by the driver). (Again, I must reiterate that neither this driver or the anx driver utilizes this)
How would I even know which two differential pairs correspond to port0 or port1 in this binding in the ITE case?
Why do we need to know that? It doesn't affect this or the other driver or hardware's functioning in a perceivable way.
Ideally we make the graph binding more strict for devices by enforcing that their graph ports exist. Otherwise we're not fully describing the connections between devices and our dtb checkers are going to let things through where the driver most likely will fail because it can't figure out what to do, e.g. display DP on 4 lanes or play some DP lane rerouting games to act as a mux.
How is the current binding enforcing this? The typec-switch binding as a first step ensures that the DT is connecting the hardware(anx,ite etc) to something that at least "claims" to be a Type-C switch.
It also maps with the internal block diagram for these hardware components (for ex. the anx7625 crosspoint switch is a separate sub-block within anx7625).
We don't make DT bindings for sub-components like this very often. It would make more sense to me to have a subnode if a typec switch was some sort of off the shelf hard macro that the hardware engineer placed down inside the IC that they delivered. Then we could have a completely generic driver that binds to the generic binding that knows how to drive the hardware, because it's an unchangeable hard macro with a well defined programming interface.
[1] https://lore.kernel.org/linux-usb/CACeCKaeH6qTTdG_huC4yw0xxG8TYEOtfPW3tiVNwY...
I looked at the fsa4480 driver and the device has a publicly available datasheet[2]. That device is designed for "audio accessory mode" but I guess it's being used to simply mux SBU lines? There isn't an upstream user of the binding so far, but it also doesn't look like a complete binding. I'd expect to see DN_L/R as a graph output connected to the usb-c-connector and probably have a usb2.0 input port and a 'sound-dai' property to represent the input audio path.
Finally, simply connecting to the typec controller node isn't sufficient because a typec controller can be controlling many usb-c-connectors so I don't see how the graph binding would be able to figure out how many usb-c-connectors are connected to a mux like device, unless we took the approach of this patch.
It can follow the endpoint of the typec-switch port (the port parent of the remote end-point would be a 'usb-c-connector'). That is if the graph binding (I'm assuming you mean the switch device here) wants to figure this out in the first place.
Is that why you're proposing this binding? To avoid describing a graph binding in the usb-c-connector and effectively "pushing" the port count up to the mux?
No, that is not the intention behind this series. The 'usb-c-connector' still needs the graph binding to the `typec-switch`. SBU, HS and SS lanes might have different muxes altogether (usb-c-connect has separate ports for SBU, HS and SS lanes)
[3] https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bind...
Quoting Prashant Malani (2022-06-23 17:35:38)
On Thu, Jun 23, 2022 at 4:14 PM Stephen Boyd swboyd@chromium.org wrote:
I'm not aware of any documentation for the dos and don'ts here. Are there any examples in the bindings directory that split up a device into subnodes that isn't in bindings/mfd?
usb-c-connector [3] and its users is an example.
What are the subnodes? The graph ports? That is not what I meant. I meant splitting up a device functionality, like type-c and display bridge, into subnodes. Composition of devices through DT bindings isn't how it's done. Instead, we dump all the different functionality into the same node. For example, look at the number of bindings that have both #clock-cells and #reset-cells, when those are distinct frameworks in the kernel and also different properties. We don't make subnodes to contain the different functionality of a device.
And in this case I still don't see the point to making a subnode. The API can simply setup a type-c switch based on a graph binding for the toplevel node, e.g. the drm-bridge, and the driver can tell the API which port+endpoint to use to search the graph for the usb-c-connector to associate with the switch. We don't need to connect the graph within the drm-bridge node to the graph within the typec-switch node to do that. That's an internal detail of the drm-bridge that we don't expose to DT, because the driver knows the detail. It also aligns the graph binding for the top-level node with non-typec bindings, like drm, which don't use a second level of graph binding to achieve essentially the same thing when the output is connected to a DP connector.
Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
I attempted to explain the rationale in the previous version [1], but using a dedicated sub-node means the driver doesn't haven't to inspect individual ports to determine which of them need switches registered for them. If it sees a `typec-switch`, it registers a mode-switch and/or orientation-switch. IMO it simplifies the hardware device binding too.
How is that any harder than hard-coding that detail into the driver about which port and endpoint is possibly connected to the usb-c-connector (or retimer)? All of that logic could be behind some API that registers a typec-switch based on a graph port number that's passed in, ala drm_of_find_panel_or_bridge()'s design.
If each driver has to do it (and the port specifics vary for each driver), it becomes an avoidable overhead for each of them. I prefer hard-coding such details if avoidable. I suppose both approaches require modifications to the binding and the driver code.
Ok, sounds like it is not any harder.
Coming from a DT writer's perspective, I just want to go through the list of output pins in the datasheet and match them up to the ports binding for this device. If it's a pure DP bridge, where USB hardware isn't an input or an output like the ITE chip, then I don't want to have to describe a port graph binding for the case when it's connected to a dp-connector (see dp-connector.yaml) in the top-level node and then have to make an entirely different subnode for the usb-c-connector case with a whole other set of graph ports.
This approach still allows for that, if the driver has any use for it (AFAICT these drivers don't). Iff that driver uses it, one can (optionally) route their output (top-level) ports through the "typec-switch" sub-node (and onwards as required). If it's being used in a "pure-DP" configuration, the "typec-switch" just goes away (the top level ports can be routed as desired by the driver). (Again, I must reiterate that neither this driver or the anx driver utilizes this)
How would I even know which two differential pairs correspond to port0 or port1 in this binding in the ITE case?
Why do we need to know that? It doesn't affect this or the other driver or hardware's functioning in a perceivable way.
If the device registers allow control of the DP lane to physical pin mapping, so that DP lane0 and DP lane1 can be swapped logically, then we'll want to know which DP lanes we need to swap by writing some lane remapping register in the device. Sometimes for routing purposes devices support this lane remapping feature so the PCB can route the lines directly to the connector instead of going in circles and destroying the signal integrity.
Ideally we make the graph binding more strict for devices by enforcing that their graph ports exist. Otherwise we're not fully describing the connections between devices and our dtb checkers are going to let things through where the driver most likely will fail because it can't figure out what to do, e.g. display DP on 4 lanes or play some DP lane rerouting games to act as a mux.
How is the current binding enforcing this? The typec-switch binding as a first step ensures that the DT is connecting the hardware(anx,ite etc) to something that at least "claims" to be a Type-C switch.
I'm simply saying that we can extend existing bindings like anx or ite to have required properties for ports so that we know the driver will find something on the other end of the graph. A binding that doesn't have any ports will be invalid. I don't know if that's possible to do in the schema.
Is that why you're proposing this binding? To avoid describing a graph binding in the usb-c-connector and effectively "pushing" the port count up to the mux?
No, that is not the intention behind this series. The 'usb-c-connector' still needs the graph binding to the `typec-switch`. SBU, HS and SS lanes might have different muxes altogether (usb-c-connect has separate ports for SBU, HS and SS lanes)
If the usb-c-connector still needs a graph binding to the typec-switch then why isn't that part of this series?
On Thu, Jun 23, 2022 at 7:13 PM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-23 17:35:38)
On Thu, Jun 23, 2022 at 4:14 PM Stephen Boyd swboyd@chromium.org wrote:
I'm not aware of any documentation for the dos and don'ts here. Are there any examples in the bindings directory that split up a device into subnodes that isn't in bindings/mfd?
usb-c-connector [3] and its users is an example.
What are the subnodes? The graph ports? That is not what I meant.
cros-ec-typec [4] uses subnodes of usb-c-connector. Chrome OS DTs use the ports from the included usb-c-connector to switching hardware.
I meant splitting up a device functionality, like type-c and display bridge, into subnodes. Composition of devices through DT bindings isn't how it's done. Instead, we dump all the different functionality into the same node. For example, look at the number of bindings that have both #clock-cells and #reset-cells, when those are distinct frameworks in the kernel and also different properties. We don't make subnodes to contain the different functionality of a device.
And in this case I still don't see the point to making a subnode.
I've already provided my best effort at explaining the rationale.
The API can simply setup a type-c switch based on a graph binding for the toplevel node, e.g. the drm-bridge, and the driver can tell the API which port+endpoint to use to search the graph for the usb-c-connector to associate with the switch.
OK, drm-bridge uses that approach. This is another approach. I didn't fully understand why we *have* to follow what drm-bridge is doing.
We don't need to connect the graph within the drm-bridge node to the graph within the typec-switch node to do that. That's an internal detail of the drm-bridge that we don't expose to DT, because the driver knows the detail.
I still don't understand why we can't do that. These devices have actual hardware blocks that represent the Type-C switch functionality.
It also aligns the graph binding for the top-level node with non-typec bindings, like drm, which don't use a second level of graph binding to achieve essentially the same thing when the output is connected to a DP connector.
Why doesn't it work to merge everything inside usb-switch directly into the drm-bridge node?
I attempted to explain the rationale in the previous version [1], but using a dedicated sub-node means the driver doesn't haven't to inspect individual ports to determine which of them need switches registered for them. If it sees a `typec-switch`, it registers a mode-switch and/or orientation-switch. IMO it simplifies the hardware device binding too.
How is that any harder than hard-coding that detail into the driver about which port and endpoint is possibly connected to the usb-c-connector (or retimer)? All of that logic could be behind some API that registers a typec-switch based on a graph port number that's passed in, ala drm_of_find_panel_or_bridge()'s design.
If each driver has to do it (and the port specifics vary for each driver), it becomes an avoidable overhead for each of them. I prefer hard-coding such details if avoidable. I suppose both approaches require modifications to the binding and the driver code.
Ok, sounds like it is not any harder.
I feel this approach is easier :)
Coming from a DT writer's perspective, I just want to go through the list of output pins in the datasheet and match them up to the ports binding for this device. If it's a pure DP bridge, where USB hardware isn't an input or an output like the ITE chip, then I don't want to have to describe a port graph binding for the case when it's connected to a dp-connector (see dp-connector.yaml) in the top-level node and then have to make an entirely different subnode for the usb-c-connector case with a whole other set of graph ports.
This approach still allows for that, if the driver has any use for it (AFAICT these drivers don't). Iff that driver uses it, one can (optionally) route their output (top-level) ports through the "typec-switch" sub-node (and onwards as required). If it's being used in a "pure-DP" configuration, the "typec-switch" just goes away (the top level ports can be routed as desired by the driver). (Again, I must reiterate that neither this driver or the anx driver utilizes this)
How would I even know which two differential pairs correspond to port0 or port1 in this binding in the ITE case?
Why do we need to know that? It doesn't affect this or the other driver or hardware's functioning in a perceivable way.
If the device registers allow control of the DP lane to physical pin mapping, so that DP lane0 and DP lane1 can be swapped logically, then we'll want to know which DP lanes we need to swap by writing some lane remapping register in the device. Sometimes for routing purposes devices support this lane remapping feature so the PCB can route the lines directly to the connector instead of going in circles and destroying the signal integrity.
Then add more end-points to port@1 (for each differential pair you want to describe) of the usb-c-connector and route them to the typec-switch accordingly. FWIW I'm not aware of h/w *that supports DP alt mode* that uses the functionality you're referring to.
Ideally we make the graph binding more strict for devices by enforcing that their graph ports exist. Otherwise we're not fully describing the connections between devices and our dtb checkers are going to let things through where the driver most likely will fail because it can't figure out what to do, e.g. display DP on 4 lanes or play some DP lane rerouting games to act as a mux.
How is the current binding enforcing this? The typec-switch binding as a first step ensures that the DT is connecting the hardware(anx,ite etc) to something that at least "claims" to be a Type-C switch.
I'm simply saying that we can extend existing bindings like anx or ite to have required properties for ports so that we know the driver will find something on the other end of the graph. A binding that doesn't have any ports will be invalid.
typec-switch requires a port.
I don't know if that's possible to do
in the schema.
Is that why you're proposing this binding? To avoid describing a graph binding in the usb-c-connector and effectively "pushing" the port count up to the mux?
No, that is not the intention behind this series. The 'usb-c-connector' still needs the graph binding to the `typec-switch`. SBU, HS and SS lanes might have different muxes altogether (usb-c-connect has separate ports for SBU, HS and SS lanes)
If the usb-c-connector still needs a graph binding to the typec-switch then why isn't that part of this series?
That's not what I meant (what I meant earlier is the intention is not what you stated). I simply meant that the usb-c-connectors ports should be connected to the typec-switch ports. There isn't any binding update required for this.
[4] https://www.kernel.org/doc/Documentation/devicetree/bindings/chrome/google%2...
Analogix 7625 can be used in systems to switch USB Type-C DisplayPort alternate mode lane traffic between 2 Type-C ports.
Update the binding to accommodate this usage by introducing a switch property.
Reviewed-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org Reviewed-by: AngeloGioacchino Del Regno angelogioacchino.delregno@collabora.com Reviewed-by: Nícolas F. R. A. Prado nfraprado@collabora.com Tested-by: Nícolas F. R. A. Prado nfraprado@collabora.com Signed-off-by: Prashant Malani pmalani@chromium.org ---
Changes since v4: - Added Reviewed-by tags. - Patch moved to 2/9 position (since Patch v4 1/7 and 2/7 were applied to usb-next).
Changes since v3: - Fix unevaluatedProperties usage. - Add additionalProperties to top level "switches" nodes. - Make quotes consistent. - Add '^..$' to regex. (All suggested by Krzysztof Kozlowski)
Changes since v2: - Added Reviewed-by and Tested-by tags.
Changes since v1: - Introduced patternProperties for "switch" children (suggested by Krzysztof Kozlowski). - Added unevaluatedProperties descriptor (suggested by Krzysztof Kozlowski). - Added "address-cells" and "size-cells" properties to "switches".
.../display/bridge/analogix,anx7625.yaml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml b/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml index 35a48515836e..bc6f7644db31 100644 --- a/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml +++ b/Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml @@ -105,6 +105,34 @@ properties: - port@0 - port@1
+ switches: + type: object + description: Set of switches controlling DisplayPort traffic on + outgoing RX/TX lanes to Type C ports. + additionalProperties: false + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^switch@[01]$': + $ref: /schemas/usb/typec-switch.yaml# + unevaluatedProperties: false + + properties: + reg: + maxItems: 1 + + required: + - reg + + required: + - switch@0 + required: - compatible - reg @@ -167,5 +195,41 @@ examples: }; }; }; + switches { + #address-cells = <1>; + #size-cells = <0>; + switch@0 { + compatible = "typec-switch"; + reg = <0>; + mode-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + anx_typec0: endpoint { + remote-endpoint = <&typec_port0>; + }; + }; + }; + }; + switch@1 { + compatible = "typec-switch"; + reg = <1>; + mode-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + anx_typec1: endpoint { + remote-endpoint = <&typec_port1>; + }; + }; + }; + }; + }; }; };
Parse the "switches" node, if available, and count and store the number of Type-C switches within it. Since we currently don't do anything with this info, no functional changes are expected from this change.
This patch sets a foundation for the actual registering of Type-C switches with the Type-C connector class framework.
Reviewed-by: AngeloGioacchino Del Regno angelogioacchino.delregno@collabora.com Reviewed-by: Nícolas F. R. A. Prado nfraprado@collabora.com Tested-by: Nícolas F. R. A. Prado nfraprado@collabora.com Signed-off-by: Prashant Malani pmalani@chromium.org ---
Changes since v4: - Added Reviewed-by tags. - Patch moved to 3/9 position (since Patch v4 1/7 and 2/7 were applied to usb-next).
Changes since v3: - No changes.
Changes since v2: - Move ret variable to Patch v3 6/7. - Make error print a dev_dbg, since it is noisy. - Added Reviewed-by and Tested-by tags.
Changes since v1: - No changes.
drivers/gpu/drm/bridge/analogix/anx7625.c | 18 ++++++++++++++++++ drivers/gpu/drm/bridge/analogix/anx7625.h | 1 + 2 files changed, 19 insertions(+)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 53a5da6c49dd..e3d4c2738b8c 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2581,6 +2581,20 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); }
+static int anx7625_register_typec_switches(struct device *device, struct anx7625_data *ctx) +{ + struct device_node *of = of_get_child_by_name(device->of_node, "switches"); + + if (!of) + return -ENODEV; + + ctx->num_typec_switches = of_get_child_count(of); + if (ctx->num_typec_switches <= 0) + return -ENODEV; + + return 0; +} + static int anx7625_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2686,6 +2700,10 @@ static int anx7625_i2c_probe(struct i2c_client *client, if (platform->pdata.intp_irq) queue_work(platform->workqueue, &platform->work);
+ ret = anx7625_register_typec_switches(dev, platform); + if (ret) + dev_dbg(dev, "Didn't register Type C switches, err: %d\n", ret); + platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node; if (!anx7625_of_panel_on_aux_bus(&client->dev)) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index e257a84db962..d5cbca708842 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -473,6 +473,7 @@ struct anx7625_data { struct drm_connector *connector; struct mipi_dsi_device *dsi; struct drm_dp_aux aux; + int num_typec_switches; };
#endif /* __ANX7625_H__ */
When the DT node has "switches" available, register a Type-C mode-switch for each listed "switch". This allows the driver to receive state information about what operating mode a Type-C port and its connected peripherals are in, as well as status information (like VDOs) related to that state.
The callback function is currently a stub, but subsequent patches will implement the required functionality.
Reviewed-by: AngeloGioacchino Del Regno angelogioacchino.delregno@collabora.com Reviewed-by: Nícolas F. R. A. Prado nfraprado@collabora.com Tested-by: Nícolas F. R. A. Prado nfraprado@collabora.com Signed-off-by: Prashant Malani pmalani@chromium.org ---
Changes since v4: - Added Reviewed-by tags. - Patch moved to 4/9 position (since Patch v4 1/7 and 2/7 were applied to usb-next).
Changes since v3: - No changes.
Changes since v2: - Updated dev_info() to dev_warn() print, but added a check to ensure it only triggers on non -ENODEV errors. - Made conflict resolutions resulting from changes introduced in Patch v3 5/7 (add ret variable here instead of in Patch v3 5/7). - Added Reviewed-by and Tested-by tags.
Changes since v1: - No changes.
drivers/gpu/drm/bridge/analogix/anx7625.c | 82 +++++++++++++++++++++-- drivers/gpu/drm/bridge/analogix/anx7625.h | 6 ++ 2 files changed, 84 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index e3d4c2738b8c..bd21f159b973 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -15,6 +15,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/usb/typec_mux.h> #include <linux/workqueue.h>
#include <linux/of_gpio.h> @@ -2581,10 +2582,61 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); }
+static int anx7625_typec_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + return 0; +} + +static int anx7625_register_mode_switch(struct device *dev, struct device_node *node, + struct anx7625_data *ctx) +{ + struct anx7625_port_data *port_data; + struct typec_mux_desc mux_desc = {}; + char name[32]; + u32 port_num; + int ret; + + ret = of_property_read_u32(node, "reg", &port_num); + if (ret) + return ret; + + if (port_num >= ctx->num_typec_switches) { + dev_err(dev, "Invalid port number specified: %d\n", port_num); + return -EINVAL; + } + + port_data = &ctx->typec_ports[port_num]; + port_data->ctx = ctx; + mux_desc.fwnode = &node->fwnode; + mux_desc.drvdata = port_data; + snprintf(name, sizeof(name), "%s-%u", node->name, port_num); + mux_desc.name = name; + mux_desc.set = anx7625_typec_mux_set; + + port_data->typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(port_data->typec_mux)) { + ret = PTR_ERR(port_data->typec_mux); + dev_err(dev, "Mode switch register for port %d failed: %d", port_num, ret); + } + + return ret; +} + +static void anx7625_unregister_typec_switches(struct anx7625_data *ctx) +{ + int i; + + for (i = 0; i < ctx->num_typec_switches; i++) + typec_mux_unregister(ctx->typec_ports[i].typec_mux); +} + static int anx7625_register_typec_switches(struct device *device, struct anx7625_data *ctx) { - struct device_node *of = of_get_child_by_name(device->of_node, "switches"); + struct device_node *of, *sw; + int ret = 0;
+ of = of_get_child_by_name(device->of_node, "switches"); if (!of) return -ENODEV;
@@ -2592,7 +2644,27 @@ static int anx7625_register_typec_switches(struct device *device, struct anx7625 if (ctx->num_typec_switches <= 0) return -ENODEV;
- return 0; + ctx->typec_ports = devm_kzalloc(device, + ctx->num_typec_switches * sizeof(struct anx7625_port_data), + GFP_KERNEL); + if (!ctx->typec_ports) + return -ENOMEM; + + /* Register switches for each connector. */ + for_each_available_child_of_node(of, sw) { + if (!of_property_read_bool(sw, "mode-switch")) + continue; + ret = anx7625_register_mode_switch(device, sw, ctx); + if (ret) { + dev_err(device, "Failed to register mode switch: %d\n", ret); + break; + } + } + + if (ret) + anx7625_unregister_typec_switches(ctx); + + return ret; }
static int anx7625_i2c_probe(struct i2c_client *client, @@ -2701,8 +2773,8 @@ static int anx7625_i2c_probe(struct i2c_client *client, queue_work(platform->workqueue, &platform->work);
ret = anx7625_register_typec_switches(dev, platform); - if (ret) - dev_dbg(dev, "Didn't register Type C switches, err: %d\n", ret); + if (ret && ret != -ENODEV) + dev_warn(dev, "Didn't register Type C switches, err: %d\n", ret);
platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = client->dev.of_node; @@ -2757,6 +2829,8 @@ static int anx7625_i2c_remove(struct i2c_client *client)
drm_bridge_remove(&platform->bridge);
+ anx7625_unregister_typec_switches(platform); + if (platform->pdata.intp_irq) destroy_workqueue(platform->workqueue);
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index d5cbca708842..76cfc64f7574 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -443,6 +443,11 @@ struct anx7625_i2c_client { struct i2c_client *tcpc_client; };
+struct anx7625_port_data { + struct typec_mux_dev *typec_mux; + struct anx7625_data *ctx; +}; + struct anx7625_data { struct anx7625_platform_data pdata; struct platform_device *audio_pdev; @@ -474,6 +479,7 @@ struct anx7625_data { struct mipi_dsi_device *dsi; struct drm_dp_aux aux; int num_typec_switches; + struct anx7625_port_data *typec_ports; };
#endif /* __ANX7625_H__ */
From: Pin-Yen Lin treapking@chromium.org
Add the callback function when the driver receives state changes of the Type-C port. The callback function configures the crosspoint switch of the anx7625 bridge chip, which can change the output pins of the signals according to the port state.
Reviewed-by: AngeloGioacchino Del Regno angelogioacchino.delregno@collabora.com Reviewed-by: Nícolas F. R. A. Prado nfraprado@collabora.com Tested-by: Nícolas F. R. A. Prado nfraprado@collabora.com Signed-off-by: Pin-Yen Lin treapking@chromium.org Signed-off-by: Prashant Malani pmalani@chromium.org ---
Changes since v4: - Patch moved to 5/9 position (since Patch v4 1/7 and 2/7 were applied to usb-next).
Changes since v3: - Added Reviewed-by tag from Angelo.
Changes since v2: - Moved num_typec_switches check to beginning of function - Made dp_connected assignments fit on one line (and removed unnecessary parentheses) - Added Reviewed-by and Tested-by tags.
Changes since v1: - No changes.
drivers/gpu/drm/bridge/analogix/anx7625.c | 56 +++++++++++++++++++++++ drivers/gpu/drm/bridge/analogix/anx7625.h | 13 ++++++ 2 files changed, 69 insertions(+)
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index bd21f159b973..5992fc8beeeb 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -15,6 +15,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/usb/typec_dp.h> #include <linux/usb/typec_mux.h> #include <linux/workqueue.h>
@@ -2582,9 +2583,64 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); }
+static void anx7625_set_crosspoint_switch(struct anx7625_data *ctx, + enum typec_orientation orientation) +{ + if (orientation == TYPEC_ORIENTATION_NORMAL) { + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_0, + SW_SEL1_SSRX_RX1 | SW_SEL1_DPTX0_RX2); + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_1, + SW_SEL2_SSTX_TX1 | SW_SEL2_DPTX1_TX2); + } else if (orientation == TYPEC_ORIENTATION_REVERSE) { + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_0, + SW_SEL1_SSRX_RX2 | SW_SEL1_DPTX0_RX1); + anx7625_reg_write(ctx, ctx->i2c.tcpc_client, TCPC_SWITCH_1, + SW_SEL2_SSTX_TX2 | SW_SEL2_DPTX1_TX1); + } +} + +static void anx7625_typec_two_ports_update(struct anx7625_data *ctx) +{ + if (ctx->typec_ports[0].dp_connected && ctx->typec_ports[1].dp_connected) + /* Both ports available, do nothing to retain the current one. */ + return; + else if (ctx->typec_ports[0].dp_connected) + anx7625_set_crosspoint_switch(ctx, TYPEC_ORIENTATION_NORMAL); + else if (ctx->typec_ports[1].dp_connected) + anx7625_set_crosspoint_switch(ctx, TYPEC_ORIENTATION_REVERSE); +} + static int anx7625_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) { + struct anx7625_port_data *data = typec_mux_get_drvdata(mux); + struct anx7625_data *ctx = data->ctx; + struct device *dev = &ctx->client->dev; + bool new_dp_connected, old_dp_connected; + + if (ctx->num_typec_switches == 1) + return 0; + + old_dp_connected = ctx->typec_ports[0].dp_connected || ctx->typec_ports[1].dp_connected; + + dev_dbg(dev, "mux_set dp_connected: c0=%d, c1=%d\n", + ctx->typec_ports[0].dp_connected, ctx->typec_ports[1].dp_connected); + + data->dp_connected = (state->alt && state->alt->svid == USB_TYPEC_DP_SID && + state->alt->mode == USB_TYPEC_DP_MODE); + + new_dp_connected = ctx->typec_ports[0].dp_connected || ctx->typec_ports[1].dp_connected; + + /* dp on, power on first */ + if (!old_dp_connected && new_dp_connected) + pm_runtime_get_sync(dev); + + anx7625_typec_two_ports_update(ctx); + + /* dp off, power off last */ + if (old_dp_connected && !new_dp_connected) + pm_runtime_put_sync(dev); + return 0; }
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h index 76cfc64f7574..7d6c6fdf9a3a 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.h +++ b/drivers/gpu/drm/bridge/analogix/anx7625.h @@ -55,6 +55,18 @@ #define HPD_STATUS_CHANGE 0x80 #define HPD_STATUS 0x80
+#define TCPC_SWITCH_0 0xB4 +#define SW_SEL1_DPTX0_RX2 BIT(0) +#define SW_SEL1_DPTX0_RX1 BIT(1) +#define SW_SEL1_SSRX_RX2 BIT(4) +#define SW_SEL1_SSRX_RX1 BIT(5) + +#define TCPC_SWITCH_1 0xB5 +#define SW_SEL2_DPTX1_TX2 BIT(0) +#define SW_SEL2_DPTX1_TX1 BIT(1) +#define SW_SEL2_SSTX_TX2 BIT(4) +#define SW_SEL2_SSTX_TX1 BIT(5) + /******** END of I2C Address 0x58 ********/
/***************************************************************/ @@ -444,6 +456,7 @@ struct anx7625_i2c_client { };
struct anx7625_port_data { + bool dp_connected; struct typec_mux_dev *typec_mux; struct anx7625_data *ctx; };
From: Pin-Yen Lin treapking@chromium.org
ITE IT6505 can be used in systems to switch USB Type-C DisplayPort alternate mode lane traffic between 2 Type-C ports.
Update the binding to accommodate this usage by introducing a switch property.
Signed-off-by: Pin-Yen Lin treapking@chromium.org Signed-off-by: Prashant Malani pmalani@chromium.org ---
v5 is the first version for this patch.
.../bindings/display/bridge/ite,it6505.yaml | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml index 833d11b2303a..86bb6dc5ae6f 100644 --- a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml @@ -56,13 +56,46 @@ properties: $ref: /schemas/graph.yaml#/properties/port description: A port node pointing to DPI host port node
+ switches: + type: object + description: Set of switches controlling DisplayPort traffic on + outgoing RX/TX lanes to Type C ports. + additionalProperties: false + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^switch@[01]$': + $ref: /schemas/usb/typec-switch.yaml# + unevaluatedProperties: false + + properties: + reg: + maxItems: 1 + + required: + - reg + + required: + - switch@0 + required: - compatible - ovdd-supply - pwr18-supply - interrupts - reset-gpios - - extcon + +oneOf: + - required: + - extcon + - required: + - switches
additionalProperties: false
@@ -92,3 +125,65 @@ examples: }; }; }; + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c3 { + #address-cells = <1>; + #size-cells = <0>; + + it6505dptx: it6505dptx@5c { + compatible = "ite,it6505"; + interrupts = <8 IRQ_TYPE_LEVEL_LOW 8 0>; + reg = <0x5c>; + pinctrl-names = "default"; + pinctrl-0 = <&it6505_pins>; + ovdd-supply = <&mt6366_vsim2_reg>; + pwr18-supply = <&pp1800_dpbrdg_dx>; + reset-gpios = <&pio 177 0>; + + port { + it6505_dp_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + + switches { + #address-cells = <1>; + #size-cells = <0>; + switch@0 { + compatible = "typec-switch"; + reg = <0>; + mode-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + ite_typec0: endpoint { + remote-endpoint = <&typec_port0>; + }; + }; + }; + }; + + switch@1 { + compatible = "typec-switch"; + reg = <1>; + mode-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + ite_typec1: endpoint { + remote-endpoint = <&typec_port1>; + }; + }; + }; + }; + }; + }; + };
Quoting Prashant Malani (2022-06-22 10:34:35)
From: Pin-Yen Lin treapking@chromium.org
ITE IT6505 can be used in systems to switch USB Type-C DisplayPort alternate mode lane traffic between 2 Type-C ports.
How does it work? From what I can tell from the information I find when googling this part[1] and looking at the existing binding doc is that this device is a DPI to DP bridge, and it outputs DP (probably 4 lanes of it?). Does the 2 type-c port design work by transmitting DP on two lanes of DP for one type-c port and another two lanes of DP for the other type-c port?
DP could be one lane, so if this device is able to output one lane on any output differential pair then I suspect it could support 4 type-c ports if the hardware engineer connected it that way. Can you confirm my suspicion?
On Thu, Jun 23, 2022 at 11:24 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:35)
From: Pin-Yen Lin treapking@chromium.org
ITE IT6505 can be used in systems to switch USB Type-C DisplayPort alternate mode lane traffic between 2 Type-C ports.
How does it work? From what I can tell from the information I find when googling this part[1] and looking at the existing binding doc is that this device is a DPI to DP bridge, and it outputs DP (probably 4 lanes of it?). Does the 2 type-c port design work by transmitting DP on two lanes of DP for one type-c port and another two lanes of DP for the other type-c port?
DP could be one lane, so if this device is able to output one lane on any output differential pair then I suspect it could support 4 type-c ports if the hardware engineer connected it that way. Can you confirm my suspicion?
I will let Pin-Yen comment re: this hardware, but 1-lane DP is not a supported Type-C Pin assignment (as per VESA DP Alternate Mode Spec version 2.0 [2]), so the H/W configuration you are suggesting shouldn't be possible.
Quoting Prashant Malani (2022-06-23 11:37:08)
On Thu, Jun 23, 2022 at 11:24 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:35)
From: Pin-Yen Lin treapking@chromium.org
ITE IT6505 can be used in systems to switch USB Type-C DisplayPort alternate mode lane traffic between 2 Type-C ports.
How does it work? From what I can tell from the information I find when googling this part[1] and looking at the existing binding doc is that this device is a DPI to DP bridge, and it outputs DP (probably 4 lanes of it?). Does the 2 type-c port design work by transmitting DP on two lanes of DP for one type-c port and another two lanes of DP for the other type-c port?
DP could be one lane, so if this device is able to output one lane on any output differential pair then I suspect it could support 4 type-c ports if the hardware engineer connected it that way. Can you confirm my suspicion?
I will let Pin-Yen comment re: this hardware, but 1-lane DP is not a supported Type-C Pin assignment (as per VESA DP Alternate Mode Spec version 2.0 [2]), so the H/W
Some missing link?
configuration you are suggesting shouldn't be possible.
Alright, cool. But it is possible in the DP spec. So it seems like if this is connected to 4 DP connectors it could be used to mux between DP on 4 DP ports.
On Thu, Jun 23, 2022 at 12:08 PM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-23 11:37:08)
On Thu, Jun 23, 2022 at 11:24 AM Stephen Boyd swboyd@chromium.org wrote:
Quoting Prashant Malani (2022-06-22 10:34:35)
From: Pin-Yen Lin treapking@chromium.org
ITE IT6505 can be used in systems to switch USB Type-C DisplayPort alternate mode lane traffic between 2 Type-C ports.
How does it work? From what I can tell from the information I find when googling this part[1] and looking at the existing binding doc is that this device is a DPI to DP bridge, and it outputs DP (probably 4 lanes of it?). Does the 2 type-c port design work by transmitting DP on two lanes of DP for one type-c port and another two lanes of DP for the other type-c port?
DP could be one lane, so if this device is able to output one lane on any output differential pair then I suspect it could support 4 type-c ports if the hardware engineer connected it that way. Can you confirm my suspicion?
I will let Pin-Yen comment re: this hardware, but 1-lane DP is not a supported Type-C Pin assignment (as per VESA DP Alternate Mode Spec version 2.0 [2]), so the H/W
Some missing link?
My bad. I tried to find a publicly accessible link to the DP altmode spec, but it seems like one needs to be a VESA member to access it :/
configuration you are suggesting shouldn't be possible.
Alright, cool. But it is possible in the DP spec. So it seems like if this is connected to 4 DP connectors it could be used to mux between DP on 4 DP ports.
Ack. In that case, no "typec-switches" should be added to the DT.
From: Pin-Yen Lin treapking@chromium.org
Parse the "switches" node, if available, and count and store the number of Type-C switches within it. The extcon registration is still supported, but we don't expect both extcon and typec-switch be registered at the same time.
This patch sets a foundation for the actual registering of Type-C switches with the Type-C connector class framework.
Signed-off-by: Pin-Yen Lin treapking@chromium.org Signed-off-by: Prashant Malani pmalani@chromium.org ---
v5 is the first version for this patch.
drivers/gpu/drm/bridge/ite-it6505.c | 34 +++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 4b673c4792d7..b259f9f367f6 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -452,6 +452,7 @@ struct it6505 { struct delayed_work delayed_audio; struct it6505_audio_data audio; struct dentry *debugfs; + int num_typec_switches;
/* it6505 driver hold option */ bool enable_drv_hold; @@ -3229,13 +3230,28 @@ static void it6505_shutdown(struct i2c_client *client) it6505_lane_off(it6505); }
+static int it6505_register_typec_switches(struct device *device, struct it6505 *it6505) +{ + struct device_node *of; + + of = of_get_child_by_name(device->of_node, "switches"); + if (!of) + return -ENODEV; + + it6505->num_typec_switches = of_get_child_count(of); + if (it6505->num_typec_switches <= 0) + return -ENODEV; + + return 0; +} + static int it6505_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct it6505 *it6505; struct device *dev = &client->dev; struct extcon_dev *extcon; - int err, intp_irq; + int err, intp_irq, ret;
it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL); if (!it6505) @@ -3255,11 +3271,21 @@ static int it6505_i2c_probe(struct i2c_client *client, if (PTR_ERR(extcon) == -EPROBE_DEFER) return -EPROBE_DEFER; if (IS_ERR(extcon)) { - dev_err(dev, "can not get extcon device!"); - return PTR_ERR(extcon); + if (PTR_ERR(extcon) != -ENODEV) + dev_warn(dev, "Cannot get extcon device: %ld", PTR_ERR(extcon)); + it6505->extcon = NULL; + } else { + it6505->extcon = extcon; }
- it6505->extcon = extcon; + ret = it6505_register_typec_switches(dev, it6505); + if (ret) { + dev_dbg(dev, "Didn't register Type C switches, err: %d", ret); + if (!it6505->extcon) { + dev_err(dev, "Both extcon and typec-switch are not registered."); + return -EINVAL; + } + }
it6505->regmap = devm_regmap_init_i2c(client, &it6505_regmap_config); if (IS_ERR(it6505->regmap)) {
From: Pin-Yen Lin treapking@chromium.org
When the DT node has "switches" available, register a Type-C mode-switch for each listed "switch". This allows the driver to receive state information about what operating mode a Type-C port and its connected peripherals are in, as well as status information (like VDOs) related to that state.
The callback function is currently a stub, but subsequent patches will implement the required functionality.
Signed-off-by: Pin-Yen Lin treapking@chromium.org Signed-off-by: Prashant Malani pmalani@chromium.org ---
v5 is the first version for this patch.
drivers/gpu/drm/bridge/ite-it6505.c | 85 ++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index b259f9f367f6..cb1dd4cbd33b 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -17,6 +17,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> +#include <linux/usb/typec_mux.h> #include <linux/wait.h>
#include <crypto/hash.h> @@ -402,6 +403,11 @@ struct debugfs_entries { const struct file_operations *fops; };
+struct it6505_port_data { + struct typec_mux_dev *typec_mux; + struct it6505 *it6505; +}; + struct it6505 { struct drm_dp_aux aux; struct drm_bridge bridge; @@ -453,6 +459,7 @@ struct it6505 { struct it6505_audio_data audio; struct dentry *debugfs; int num_typec_switches; + struct it6505_port_data *typec_ports;
/* it6505 driver hold option */ bool enable_drv_hold; @@ -3230,9 +3237,59 @@ static void it6505_shutdown(struct i2c_client *client) it6505_lane_off(it6505); }
+static int it6505_typec_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + return 0; +} + +static int it6505_register_mode_switch(struct device *dev, struct device_node *node, + struct it6505 *it6505) +{ + struct it6505_port_data *port_data; + struct typec_mux_desc mux_desc = {}; + char name[32]; + u32 port_num; + int ret; + + ret = of_property_read_u32(node, "reg", &port_num); + if (ret) + return ret; + + if (port_num >= it6505->num_typec_switches) { + dev_err(dev, "Invalid port number specified: %d\n", port_num); + return -EINVAL; + } + + port_data = &it6505->typec_ports[port_num]; + port_data->it6505 = it6505; + mux_desc.fwnode = &node->fwnode; + mux_desc.drvdata = port_data; + snprintf(name, sizeof(name), "%s-%u", node->name, port_num); + mux_desc.name = name; + mux_desc.set = it6505_typec_mux_set; + + port_data->typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(port_data->typec_mux)) { + ret = PTR_ERR(port_data->typec_mux); + dev_err(dev, "Mode switch register for port %d failed: %d", port_num, ret); + } + + return ret; +} + +static void it6505_unregister_typec_switches(struct it6505 *it6505) +{ + int i; + + for (i = 0; i < it6505->num_typec_switches; i++) + typec_mux_unregister(it6505->typec_ports[i].typec_mux); +} + static int it6505_register_typec_switches(struct device *device, struct it6505 *it6505) { - struct device_node *of; + struct device_node *of, *sw; + int ret = 0;
of = of_get_child_by_name(device->of_node, "switches"); if (!of) @@ -3241,8 +3298,28 @@ static int it6505_register_typec_switches(struct device *device, struct it6505 * it6505->num_typec_switches = of_get_child_count(of); if (it6505->num_typec_switches <= 0) return -ENODEV; + it6505->typec_ports = devm_kzalloc(device, + it6505->num_typec_switches * + sizeof(struct it6505_port_data), + GFP_KERNEL); + if (!it6505->typec_ports) + return -ENOMEM;
- return 0; + /* Register switches for each connector. */ + for_each_available_child_of_node(of, sw) { + if (!of_property_read_bool(sw, "mode-switch")) + continue; + ret = it6505_register_mode_switch(device, sw, it6505); + if (ret) { + dev_err(device, "Failed to register mode switch: %d\n", ret); + break; + } + } + + if (ret) + it6505_unregister_typec_switches(it6505); + + return ret; }
static int it6505_i2c_probe(struct i2c_client *client, @@ -3280,7 +3357,8 @@ static int it6505_i2c_probe(struct i2c_client *client,
ret = it6505_register_typec_switches(dev, it6505); if (ret) { - dev_dbg(dev, "Didn't register Type C switches, err: %d", ret); + if (ret != -ENODEV) + dev_warn(dev, "Didn't register Type C switches, err: %d", ret); if (!it6505->extcon) { dev_err(dev, "Both extcon and typec-switch are not registered."); return -EINVAL; @@ -3350,6 +3428,7 @@ static int it6505_i2c_remove(struct i2c_client *client) drm_dp_aux_unregister(&it6505->aux); it6505_debugfs_remove(it6505); it6505_poweroff(it6505); + it6505_unregister_typec_switches(it6505);
return 0; }
From: Pin-Yen Lin treapking@chromium.org
Add the callback function when the driver receives state changes of the Type-C ports. The callback function configures the lane_swap state and ends up updating the lane swap registers of it6505 bridge chip.
Signed-off-by: Pin-Yen Lin treapking@chromium.org Signed-off-by: Prashant Malani pmalani@chromium.org ---
v5 is the first version for this patch.
drivers/gpu/drm/bridge/ite-it6505.c | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index cb1dd4cbd33b..87b9bd742b52 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -17,6 +17,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> +#include <linux/usb/typec_dp.h> #include <linux/usb/typec_mux.h> #include <linux/wait.h>
@@ -404,6 +405,7 @@ struct debugfs_entries { };
struct it6505_port_data { + bool dp_connected; struct typec_mux_dev *typec_mux; struct it6505 *it6505; }; @@ -3237,9 +3239,65 @@ static void it6505_shutdown(struct i2c_client *client) it6505_lane_off(it6505); }
+static void it6505_typec_ports_update(struct it6505 *it6505) +{ + usleep_range(3000, 4000); + + if (it6505->typec_ports[0].dp_connected && it6505->typec_ports[1].dp_connected) + /* Both ports available, do nothing to retain the current one. */ + return; + else if (it6505->typec_ports[0].dp_connected) + it6505->lane_swap = false; + else if (it6505->typec_ports[1].dp_connected) + it6505->lane_swap = true; + + usleep_range(3000, 4000); +} + static int it6505_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) { + struct it6505_port_data *data = typec_mux_get_drvdata(mux); + struct it6505 *it6505 = data->it6505; + struct device *dev = &it6505->client->dev; + bool old_dp_connected, new_dp_connected; + + if (it6505->num_typec_switches == 1) + return 0; + + mutex_lock(&it6505->extcon_lock); + + old_dp_connected = it6505->typec_ports[0].dp_connected || + it6505->typec_ports[1].dp_connected; + + dev_dbg(dev, "mux_set dp_connected: c0=%d, c1=%d\n", + it6505->typec_ports[0].dp_connected, it6505->typec_ports[1].dp_connected); + + data->dp_connected = (state->alt && state->alt->svid == USB_TYPEC_DP_SID && + state->alt->mode == USB_TYPEC_DP_MODE); + + new_dp_connected = it6505->typec_ports[0].dp_connected || + it6505->typec_ports[1].dp_connected; + + if (it6505->enable_drv_hold) { + dev_dbg(dev, "enable driver hold"); + goto unlock; + } + + it6505_typec_ports_update(it6505); + + if (!old_dp_connected && new_dp_connected) + pm_runtime_get_sync(dev); + + if (old_dp_connected && !new_dp_connected) { + pm_runtime_put_sync(dev); + if (it6505->bridge.dev) + drm_helper_hpd_irq_event(it6505->bridge.dev); + memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + } + +unlock: + mutex_unlock(&it6505->extcon_lock); return 0; }
dri-devel@lists.freedesktop.org