2022-06-09 19:09:08

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 0/7] usb: typec: Introduce typec-switch binding

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 patch introduces a change to the Type-C mux class mode-switch
matching code, while the second adds a config guard to a Type-C header.
The next couple of patches introduce the new "typec-switch" binding as
well as one user of it (the ANX7625 drm bridge).

The remaining patches 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.

v1: https://lore.kernel.org/linux-usb/[email protected]/

Changes since v1:
- Fixed function signature error in "else case" of typec_mux.h
- typec-switch.yaml: Fixed indentation, compatible, and node names in examples.
- anx7625.yaml:
+ 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".

Pin-Yen Lin (1):
drm/bridge: anx7625: Add typec_mux_set callback function

Prashant Malani (6):
usb: typec: mux: Allow muxes to specify mode-switch
usb: typec: mux: Add CONFIG guards for functions
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 | 63 ++++++++
.../devicetree/bindings/usb/typec-switch.yaml | 74 +++++++++
drivers/gpu/drm/bridge/analogix/anx7625.c | 151 ++++++++++++++++++
drivers/gpu/drm/bridge/analogix/anx7625.h | 20 +++
drivers/usb/typec/mux.c | 8 +-
include/linux/usb/typec_mux.h | 38 +++++
6 files changed, 352 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/usb/typec-switch.yaml

--
2.36.1.476.g0c4daa206d-goog


2022-06-09 19:21:09

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 6/7] drm/bridge: anx7625: Register Type-C mode switches

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: Prashant Malani <[email protected]>
---

Changes since v2:
- No changes.

drivers/gpu/drm/bridge/analogix/anx7625.c | 73 +++++++++++++++++++++++
drivers/gpu/drm/bridge/analogix/anx7625.h | 6 ++
2 files changed, 79 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index 07ed44c6b839..d41a21103bd3 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,9 +2582,59 @@ 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 = NULL;
+ struct device_node *sw;
int ret = 0;

of = of_get_child_by_name(device->of_node, "switches");
@@ -2594,6 +2645,26 @@ static int anx7625_register_typec_switches(struct device *device, struct anx7625
if (ctx->num_typec_switches <= 0)
return -ENODEV;

+ 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;
}

@@ -2759,6 +2830,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__ */
--
2.36.1.476.g0c4daa206d-goog

2022-06-09 19:22:13

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

From: Pin-Yen Lin <[email protected]>

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.

Signed-off-by: Pin-Yen Lin <[email protected]>
Signed-off-by: Prashant Malani <[email protected]>
---

Changes since v2:
- No changes.

drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
2 files changed, 71 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
+ ctx->typec_ports[1].dp_connected);
+ bool new_dp_connected;
+
+ if (ctx->num_typec_switches == 1)
+ return 0;
+
+ 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;
};
--
2.36.1.476.g0c4daa206d-goog

2022-06-09 19:23:49

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 1/7] usb: typec: mux: Allow muxes to specify mode-switch

Loosen the typec_mux_match() requirements so that searches where an
alt mode is not specified, but the target mux device lists the
"mode-switch" property, return a success.

This is helpful in Type C port drivers which would like to get a pointer
to the mux switch associated with a Type C port, but don't want to
specify a particular alt mode.

Signed-off-by: Prashant Malani <[email protected]>
---

Changes since v1:
- No changes.

drivers/usb/typec/mux.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index fd55c2c516a5..464330776cd6 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -281,9 +281,13 @@ static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id,
if (match)
goto find_mux;

- /* Accessory Mode muxes */
if (!desc) {
- match = fwnode_property_present(fwnode, "accessory");
+ /*
+ * Accessory Mode muxes & muxes which explicitly specify
+ * the required identifier can avoid SVID matching.
+ */
+ match = fwnode_property_present(fwnode, "accessory") ||
+ fwnode_property_present(fwnode, id);
if (match)
goto find_mux;
return NULL;
--
2.36.1.476.g0c4daa206d-goog

2022-06-09 19:24:21

by Prashant Malani

[permalink] [raw]
Subject: [PATCH v2 3/7] dt-bindings: usb: Add Type-C switch binding

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).

Signed-off-by: Prashant Malani <[email protected]>
---

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 <[email protected]>
+
+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>;
+ };
+ };
+ };
+ };
+ };
--
2.36.1.476.g0c4daa206d-goog

2022-06-13 21:23:47

by Prashant Malani

[permalink] [raw]
Subject: Re: [PATCH v2 3/7] dt-bindings: usb: Add Type-C switch binding

Hi Nícolas,

On Mon, Jun 13, 2022 at 1:38 PM Nícolas F. R. A. Prado
<[email protected]> wrote:
>
> Hi Prashant,
>
> thanks for the patch. Please see sugestions below.
>
> On Thu, Jun 09, 2022 at 06:09:42PM +0000, Prashant Malani wrote:
> > 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).
> >
> > Signed-off-by: Prashant Malani <[email protected]>
> > ---
> >
> > 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 <[email protected]>
> > +
> > +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.
>
> Since you have a single port, you can omit the ports node.

I've kept "ports", since it should be possible to add additional ports
which connect the switch to some other entity (and kept separate from
port@0), if the individual driver desires this.
port@0 should only contain the end-point from the 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>;
> > + };
> > + };
> > + };
>
> So instead of ports you would have just
>
> port {
> anx_ep: endpoint {
> remote-endpoint = <&typec_controller>;
> };
> };
>
> And the same simplification on patch 4, for the anx7625 dt-binding.
>
> Thanks,
> Nícolas
>
> > + };
> > + };
> > --
> > 2.36.1.476.g0c4daa206d-goog
> >

2022-06-13 21:27:19

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

Hi Prashant,

On Thu, Jun 09, 2022 at 06:09:46PM +0000, Prashant Malani wrote:
> From: Pin-Yen Lin <[email protected]>
>
> 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.
>
> Signed-off-by: Pin-Yen Lin <[email protected]>
> Signed-off-by: Prashant Malani <[email protected]>
> ---
>
> Changes since v2:
> - No changes.
>
> drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
> drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
> 2 files changed, 71 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
> + ctx->typec_ports[1].dp_connected);

Here you're assuming you have 2 switches. Given that this on its own doesn't do
anything, just move it after the

if (ctx->num_typec_switches == 1)
return 0;

check.

Thanks,
N?colas

> + bool new_dp_connected;
> +
> + if (ctx->num_typec_switches == 1)
> + return 0;
> +
> + 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;
> };
> --
> 2.36.1.476.g0c4daa206d-goog
>

2022-06-13 21:46:26

by Prashant Malani

[permalink] [raw]
Subject: Re: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

Hi Nícolas,

On Mon, Jun 13, 2022 at 1:51 PM Nícolas F. R. A. Prado
<[email protected]> wrote:
>
> Hi Prashant,
>
> On Thu, Jun 09, 2022 at 06:09:46PM +0000, Prashant Malani wrote:
> > From: Pin-Yen Lin <[email protected]>
> >
> > 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.
> >
> > Signed-off-by: Pin-Yen Lin <[email protected]>
> > Signed-off-by: Prashant Malani <[email protected]>
> > ---
> >
> > Changes since v2:
> > - No changes.
> >
> > drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
> > drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
> > 2 files changed, 71 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> > index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
> > + ctx->typec_ports[1].dp_connected);
>
> Here you're assuming you have 2 switches. Given that this on its own doesn't do
> anything, just move it after the
>
> if (ctx->num_typec_switches == 1)
> return 0;
>
> check.

Sure. I will fix this up in v3. Thanks!

>
> Thanks,
> Nícolas
>
> > + bool new_dp_connected;
> > +
> > + if (ctx->num_typec_switches == 1)
> > + return 0;
> > +
> > + 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;
> > };
> > --
> > 2.36.1.476.g0c4daa206d-goog
> >

2022-06-13 21:48:33

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v2 0/7] usb: typec: Introduce typec-switch binding

On Thu, Jun 09, 2022 at 06:09:39PM +0000, Prashant Malani wrote:
> 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 patch introduces a change to the Type-C mux class mode-switch
> matching code, while the second adds a config guard to a Type-C header.
> The next couple of patches introduce the new "typec-switch" binding as
> well as one user of it (the ANX7625 drm bridge).
>
> The remaining patches 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.
>
> v1: https://lore.kernel.org/linux-usb/[email protected]/
>
> Changes since v1:
> - Fixed function signature error in "else case" of typec_mux.h
> - typec-switch.yaml: Fixed indentation, compatible, and node names in examples.
> - anx7625.yaml:
> + 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".
>
> Pin-Yen Lin (1):
> drm/bridge: anx7625: Add typec_mux_set callback function
>
> Prashant Malani (6):
> usb: typec: mux: Allow muxes to specify mode-switch
> usb: typec: mux: Add CONFIG guards for functions
> 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

For the whole series,

Reviewed-by: N?colas F. R. A. Prado <[email protected]>
Tested-by: N?colas F. R. A. Prado <[email protected]>

Tested that external display works on both ports of mt8192-asurada-spherion.

Thanks,
N?colas

>
> .../display/bridge/analogix,anx7625.yaml | 63 ++++++++
> .../devicetree/bindings/usb/typec-switch.yaml | 74 +++++++++
> drivers/gpu/drm/bridge/analogix/anx7625.c | 151 ++++++++++++++++++
> drivers/gpu/drm/bridge/analogix/anx7625.h | 20 +++
> drivers/usb/typec/mux.c | 8 +-
> include/linux/usb/typec_mux.h | 38 +++++
> 6 files changed, 352 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/usb/typec-switch.yaml
>
> --
> 2.36.1.476.g0c4daa206d-goog
>

2022-06-13 21:55:56

by Nícolas F. R. A. Prado

[permalink] [raw]
Subject: Re: [PATCH v2 3/7] dt-bindings: usb: Add Type-C switch binding

Hi Prashant,

thanks for the patch. Please see sugestions below.

On Thu, Jun 09, 2022 at 06:09:42PM +0000, Prashant Malani wrote:
> 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).
>
> Signed-off-by: Prashant Malani <[email protected]>
> ---
>
> 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 <[email protected]>
> +
> +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.

Since you have a single port, you can omit the ports node.

> +
> + 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>;
> + };
> + };
> + };

So instead of ports you would have just

port {
anx_ep: endpoint {
remote-endpoint = <&typec_controller>;
};
};

And the same simplification on patch 4, for the anx7625 dt-binding.

Thanks,
N?colas

> + };
> + };
> --
> 2.36.1.476.g0c4daa206d-goog
>

2022-06-14 07:51:51

by Heikki Krogerus

[permalink] [raw]
Subject: Re: [PATCH v2 1/7] usb: typec: mux: Allow muxes to specify mode-switch

On Thu, Jun 09, 2022 at 06:09:40PM +0000, Prashant Malani wrote:
> Loosen the typec_mux_match() requirements so that searches where an
> alt mode is not specified, but the target mux device lists the
> "mode-switch" property, return a success.
>
> This is helpful in Type C port drivers which would like to get a pointer
> to the mux switch associated with a Type C port, but don't want to
> specify a particular alt mode.
>
> Signed-off-by: Prashant Malani <[email protected]>

Reviewed-by: Heikki Krogerus <[email protected]>

> ---
>
> Changes since v1:
> - No changes.
>
> drivers/usb/typec/mux.c | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
> index fd55c2c516a5..464330776cd6 100644
> --- a/drivers/usb/typec/mux.c
> +++ b/drivers/usb/typec/mux.c
> @@ -281,9 +281,13 @@ static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id,
> if (match)
> goto find_mux;
>
> - /* Accessory Mode muxes */
> if (!desc) {
> - match = fwnode_property_present(fwnode, "accessory");
> + /*
> + * Accessory Mode muxes & muxes which explicitly specify
> + * the required identifier can avoid SVID matching.
> + */
> + match = fwnode_property_present(fwnode, "accessory") ||
> + fwnode_property_present(fwnode, id);
> if (match)
> goto find_mux;
> return NULL;
> --
> 2.36.1.476.g0c4daa206d-goog

--
heikki

Subject: Re: [PATCH v2 1/7] usb: typec: mux: Allow muxes to specify mode-switch

Il 09/06/22 20:09, Prashant Malani ha scritto:
> Loosen the typec_mux_match() requirements so that searches where an
> alt mode is not specified, but the target mux device lists the
> "mode-switch" property, return a success.
>
> This is helpful in Type C port drivers which would like to get a pointer
> to the mux switch associated with a Type C port, but don't want to
> specify a particular alt mode.
>
> Signed-off-by: Prashant Malani <[email protected]>
> Reviewed-by: Heikki Krogerus <[email protected]>

Reviewed-by: AngeloGioacchino Del Regno <[email protected]>

Subject: Re: [PATCH v2 6/7] drm/bridge: anx7625: Register Type-C mode switches

Il 09/06/22 20:09, Prashant Malani ha scritto:
> 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: Prashant Malani <[email protected]>
> ---
>
> Changes since v2:
> - No changes.
>
> drivers/gpu/drm/bridge/analogix/anx7625.c | 73 +++++++++++++++++++++++
> drivers/gpu/drm/bridge/analogix/anx7625.h | 6 ++
> 2 files changed, 79 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> index 07ed44c6b839..d41a21103bd3 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,9 +2582,59 @@ 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);
> + }

Please return 0 at the end of this function.

if (IS_ERR(....)) {
......code......
return ret;
}

return 0;
}

> +
> + 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 = NULL;
> + struct device_node *sw;
> int ret = 0;
>
> of = of_get_child_by_name(device->of_node, "switches");
> @@ -2594,6 +2645,26 @@ static int anx7625_register_typec_switches(struct device *device, struct anx7625
> if (ctx->num_typec_switches <= 0)
> return -ENODEV;
>
> + 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;
> }
>
> @@ -2759,6 +2830,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__ */

Subject: Re: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

Il 09/06/22 20:09, Prashant Malani ha scritto:
> From: Pin-Yen Lin <[email protected]>
>
> 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.
>
> Signed-off-by: Pin-Yen Lin <[email protected]>
> Signed-off-by: Prashant Malani <[email protected]>
> ---
>
> Changes since v2:
> - No changes.
>
> drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
> drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
> 2 files changed, 71 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
> + ctx->typec_ports[1].dp_connected);

So the old connection state is "either port0 or port1 are currently connected"...

> + bool new_dp_connected;
> +
> + if (ctx->num_typec_switches == 1)
> + return 0;
> +
> + 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);

...and the new connection state is the same as the old one, because I don't see
anything that could ever modify it in this function's flow, until reaching this
assignment.

> +
> + /* dp on, power on first */
> + if (!old_dp_connected && new_dp_connected)
> + pm_runtime_get_sync(dev);

...so that will never happen...

> +
> + anx7625_typec_two_ports_update(ctx);
> +
> + /* dp off, power off last */
> + if (old_dp_connected && !new_dp_connected)
> + pm_runtime_put_sync(dev);

...and same here.

Regards,
Angelo

2022-06-14 09:22:35

by Pin-yen Lin

[permalink] [raw]
Subject: Re: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

Hi AngeloGioacchino,


On Tue, Jun 14, 2022 at 4:15 PM AngeloGioacchino Del Regno
<[email protected]> wrote:
>
> Il 09/06/22 20:09, Prashant Malani ha scritto:
> > From: Pin-Yen Lin <[email protected]>
> >
> > 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.
> >
> > Signed-off-by: Pin-Yen Lin <[email protected]>
> > Signed-off-by: Prashant Malani <[email protected]>
> > ---
> >
> > Changes since v2:
> > - No changes.
> >
> > drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
> > drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
> > 2 files changed, 71 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> > index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
> > + ctx->typec_ports[1].dp_connected);
>
> So the old connection state is "either port0 or port1 are currently connected"...
>
> > + bool new_dp_connected;
> > +
> > + if (ctx->num_typec_switches == 1)
> > + return 0;
> > +
> > + 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);
>
> ...and the new connection state is the same as the old one, because I don't see
> anything that could ever modify it in this function's flow, until reaching this
> assignment.

The typec mux driver data (`struct anx7625_port_data *data =
typec_mux_get_drvdata(mux)`) is set to one of the
`ctx->typec_ports[*]` in `anx7625_register_mode_switch` (see patch 6
of this series).

So, the `data->dp_connected = ...` assignment may change the new
connection state.

Best regards,
Pin-yen

>
> > +
> > + /* dp on, power on first */
> > + if (!old_dp_connected && new_dp_connected)
> > + pm_runtime_get_sync(dev);
>
> ...so that will never happen...
>
> > +
> > + anx7625_typec_two_ports_update(ctx);
> > +
> > + /* dp off, power off last */
> > + if (old_dp_connected && !new_dp_connected)
> > + pm_runtime_put_sync(dev);
>
> ...and same here.
>
> Regards,
> Angelo

2022-06-14 17:13:17

by Prashant Malani

[permalink] [raw]
Subject: Re: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

On Tue, Jun 14, 2022 at 2:08 AM Pin-yen Lin <[email protected]> wrote:
>
> Hi AngeloGioacchino,
>
>
> On Tue, Jun 14, 2022 at 4:15 PM AngeloGioacchino Del Regno
> <[email protected]> wrote:
> >
> > Il 09/06/22 20:09, Prashant Malani ha scritto:
> > > From: Pin-Yen Lin <[email protected]>
> > >
> > > 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.
> > >
> > > Signed-off-by: Pin-Yen Lin <[email protected]>
> > > Signed-off-by: Prashant Malani <[email protected]>
> > > ---
> > >
> > > Changes since v2:
> > > - No changes.
> > >
> > > drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
> > > drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
> > > 2 files changed, 71 insertions(+)
> > >
> > > diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> > > index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
> > > + ctx->typec_ports[1].dp_connected);
> >
> > So the old connection state is "either port0 or port1 are currently connected"...
> >
> > > + bool new_dp_connected;
> > > +
> > > + if (ctx->num_typec_switches == 1)
> > > + return 0;
> > > +
> > > + 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);
> >
> > ...and the new connection state is the same as the old one, because I don't see
> > anything that could ever modify it in this function's flow, until reaching this
> > assignment.
>
> The typec mux driver data (`struct anx7625_port_data *data =
> typec_mux_get_drvdata(mux)`) is set to one of the
> `ctx->typec_ports[*]` in `anx7625_register_mode_switch` (see patch 6
> of this series).
>
> So, the `data->dp_connected = ...` assignment may change the new
> connection state.

Angelo, I think your interpretation of this logic is not accurate..
|old_dp_connected| represents *whether* port1 or port0 has a DP
partner connected, not that *either* of them has it.

So, this logic looks OK to me.


>
> Best regards,
> Pin-yen
>
> >
> > > +
> > > + /* dp on, power on first */
> > > + if (!old_dp_connected && new_dp_connected)
> > > + pm_runtime_get_sync(dev);
> >
> > ...so that will never happen...
> >
> > > +
> > > + anx7625_typec_two_ports_update(ctx);
> > > +
> > > + /* dp off, power off last */
> > > + if (old_dp_connected && !new_dp_connected)
> > > + pm_runtime_put_sync(dev);
> >
> > ...and same here.
> >
> > Regards,
> > Angelo

2022-06-14 17:29:21

by Prashant Malani

[permalink] [raw]
Subject: Re: [PATCH v2 6/7] drm/bridge: anx7625: Register Type-C mode switches

On Tue, Jun 14, 2022 at 1:18 AM AngeloGioacchino Del Regno
<[email protected]> wrote:
>
> Il 09/06/22 20:09, Prashant Malani ha scritto:
> > 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: Prashant Malani <[email protected]>
> > ---
> >
> > Changes since v2:
> > - No changes.
> >
> > drivers/gpu/drm/bridge/analogix/anx7625.c | 73 +++++++++++++++++++++++
> > drivers/gpu/drm/bridge/analogix/anx7625.h | 6 ++
> > 2 files changed, 79 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> > index 07ed44c6b839..d41a21103bd3 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,9 +2582,59 @@ 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);
> > + }
>
> Please return 0 at the end of this function.
>
> if (IS_ERR(....)) {
> ......code......
> return ret;
> }
>
> return 0;
> }

May I ask why? We're not missing any return paths. I would rather we
keep it as is (which has the valid return value).

>
> > +
> > + 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 = NULL;
> > + struct device_node *sw;
> > int ret = 0;
> >
> > of = of_get_child_by_name(device->of_node, "switches");
> > @@ -2594,6 +2645,26 @@ static int anx7625_register_typec_switches(struct device *device, struct anx7625
> > if (ctx->num_typec_switches <= 0)
> > return -ENODEV;
> >
> > + 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;
> > }
> >
> > @@ -2759,6 +2830,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__ */
>

Subject: Re: [PATCH v2 6/7] drm/bridge: anx7625: Register Type-C mode switches

Il 14/06/22 18:57, Prashant Malani ha scritto:
> On Tue, Jun 14, 2022 at 1:18 AM AngeloGioacchino Del Regno
> <[email protected]> wrote:
>>
>> Il 09/06/22 20:09, Prashant Malani ha scritto:
>>> 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: Prashant Malani <[email protected]>
>>> ---
>>>
>>> Changes since v2:
>>> - No changes.
>>>
>>> drivers/gpu/drm/bridge/analogix/anx7625.c | 73 +++++++++++++++++++++++
>>> drivers/gpu/drm/bridge/analogix/anx7625.h | 6 ++
>>> 2 files changed, 79 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
>>> index 07ed44c6b839..d41a21103bd3 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,9 +2582,59 @@ 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);
>>> + }
>>
>> Please return 0 at the end of this function.
>>
>> if (IS_ERR(....)) {
>> ......code......
>> return ret;
>> }
>>
>> return 0;
>> }
>
> May I ask why? We're not missing any return paths. I would rather we
> keep it as is (which has the valid return value).
>

I know that you're not missing any return paths.

That's only because the proposed one is a common pattern in the kernel
and it's only for consistency.

Regards,
Angelo

Subject: Re: [PATCH v2 7/7] drm/bridge: anx7625: Add typec_mux_set callback function

Il 14/06/22 18:58, Prashant Malani ha scritto:
> On Tue, Jun 14, 2022 at 2:08 AM Pin-yen Lin <[email protected]> wrote:
>>
>> Hi AngeloGioacchino,
>>
>>
>> On Tue, Jun 14, 2022 at 4:15 PM AngeloGioacchino Del Regno
>> <[email protected]> wrote:
>>>
>>> Il 09/06/22 20:09, Prashant Malani ha scritto:
>>>> From: Pin-Yen Lin <[email protected]>
>>>>
>>>> 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.
>>>>
>>>> Signed-off-by: Pin-Yen Lin <[email protected]>
>>>> Signed-off-by: Prashant Malani <[email protected]>
>>>> ---
>>>>
>>>> Changes since v2:
>>>> - No changes.
>>>>
>>>> drivers/gpu/drm/bridge/analogix/anx7625.c | 58 +++++++++++++++++++++++
>>>> drivers/gpu/drm/bridge/analogix/anx7625.h | 13 +++++
>>>> 2 files changed, 71 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
>>>> index d41a21103bd3..2c308d12fab2 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,66 @@ 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 old_dp_connected = (ctx->typec_ports[0].dp_connected ||
>>>> + ctx->typec_ports[1].dp_connected);
>>>
>>> So the old connection state is "either port0 or port1 are currently connected"...
>>>
>>>> + bool new_dp_connected;
>>>> +
>>>> + if (ctx->num_typec_switches == 1)
>>>> + return 0;
>>>> +
>>>> + 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);
>>>
>>> ...and the new connection state is the same as the old one, because I don't see
>>> anything that could ever modify it in this function's flow, until reaching this
>>> assignment.
>>
>> The typec mux driver data (`struct anx7625_port_data *data =
>> typec_mux_get_drvdata(mux)`) is set to one of the
>> `ctx->typec_ports[*]` in `anx7625_register_mode_switch` (see patch 6
>> of this series).
>>
>> So, the `data->dp_connected = ...` assignment may change the new
>> connection state.
>
> Angelo, I think your interpretation of this logic is not accurate..
> |old_dp_connected| represents *whether* port1 or port0 has a DP
> partner connected, not that *either* of them has it.
>
> So, this logic looks OK to me.
>

Hello Prashant,

You're completely right: I've finally seen where this is happening, so yes
we don't know, nor care at that moment, whether data->dp_connected is port0
or port1, we assign and check 'em both again, which is actually smart.

I'm sorry for the misunderstandment - and thank you for your reply.

Feel free to add my

Reviewed-by: AngeloGioacchino Del Regno <[email protected]>

Regards,
Angelo

2022-06-15 17:04:24

by Prashant Malani

[permalink] [raw]
Subject: Re: [PATCH v2 6/7] drm/bridge: anx7625: Register Type-C mode switches

On Wed, Jun 15, 2022 at 1:45 AM AngeloGioacchino Del Regno
<[email protected]> wrote:
>
> Il 14/06/22 18:57, Prashant Malani ha scritto:
> > On Tue, Jun 14, 2022 at 1:18 AM AngeloGioacchino Del Regno
> > <[email protected]> wrote:
> >>
> >> Il 09/06/22 20:09, Prashant Malani ha scritto:
> >>> 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: Prashant Malani <[email protected]>
> >>> ---
> >>>
> >>> Changes since v2:
> >>> - No changes.
> >>>
> >>> drivers/gpu/drm/bridge/analogix/anx7625.c | 73 +++++++++++++++++++++++
> >>> drivers/gpu/drm/bridge/analogix/anx7625.h | 6 ++
> >>> 2 files changed, 79 insertions(+)
> >>>
> >>> diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
> >>> index 07ed44c6b839..d41a21103bd3 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,9 +2582,59 @@ 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);
> >>> + }
> >>
> >> Please return 0 at the end of this function.
> >>
> >> if (IS_ERR(....)) {
> >> ......code......
> >> return ret;
> >> }
> >>
> >> return 0;
> >> }
> >
> > May I ask why? We're not missing any return paths. I would rather we
> > keep it as is (which has the valid return value).
> >
>
> I know that you're not missing any return paths.
>
> That's only because the proposed one is a common pattern in the kernel
> and it's only for consistency.

Thanks for the additional details. Since this isn't addressing any
specific bug, and I
notice varied usages of "return ret" in this file itself [1][2], I'd
prefer keeping it as is.

[1]: https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/bridge/analogix/anx7625.c#L296
[2]: https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/bridge/analogix/anx7625.c#L436

>
> Regards,
> Angelo
>