2023-05-15 03:33:50

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 0/8] phy: qcom-qmp-combo: Support orientation switching

This adds support for USB and DisplayPort orientation switching to the
QMP combo PHY, as well as updating the sc8280xp devices to include the
QMP in the SuperSpeed graph.

Bjorn Andersson (8):
dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp: Add ports and
orientation-switch
phy: qcom-qmp-combo: Move phy_mutex out of com_init/exit
phy: qcom-qmp-combo: Extend phy_mutex to all phy_ops
phy: qcom-qmp-combo: Introduce orientation variable
phy: qcom-qmp-combo: Introduce orientation switching
phy: qcom-qmp-combo: Introduce drm_bridge
arm64: dts: qcom: sc8280xp-crd: Add QMP to SuperSpeed graph
arm64: dts: qcom: sc8280xp-x13s: Add QMP to SuperSpeed graph

.../phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml | 51 ++++
arch/arm64/boot/dts/qcom/sc8280xp-crd.dts | 28 +-
.../qcom/sc8280xp-lenovo-thinkpad-x13s.dts | 28 +-
arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 34 +++
drivers/phy/qualcomm/Kconfig | 3 +
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 255 ++++++++++++++----
6 files changed, 340 insertions(+), 59 deletions(-)

--
2.25.1



2023-05-15 03:39:37

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 6/8] phy: qcom-qmp-combo: Introduce drm_bridge

The QMP combo PHY sits in an of_graph connected between the DisplayPort
controller and a USB Type-C connector (or possibly a redriver).

The TCPM needs to be able to convey the HPD signal to the DisplayPort
controller, but no directly link is provided by DeviceTree so the signal
needs to "pass through" the QMP combo phy.

Handle this by introducing a drm_bridge which upon initialization finds
the next bridge (i.e. the usb-c-connector) and chain this together. This
way HPD changes in the connector will propagate to the DisplayPort
driver.

The connector bridge is resolved lazily, as the TCPM is expected to be
able to resolve the typec mux and switch at probe time, so the QMP combo
phy will probe before the TCPM.

Acked-by: Neil Armstrong <[email protected]>
Tested-by: Bryan O'Donoghue <[email protected]>
Reviewed-by: Bryan O'Donoghue <[email protected]>
Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Reviewed-by: Johan Hovold <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- Wrap DRM-related code in CONFIG_DRM guard
- Inroduce DRM-dependencies in Kconfig
- Dropped dev_err_probe() usage

drivers/phy/qualcomm/Kconfig | 2 +
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 46 +++++++++++++++++++++++
2 files changed, 48 insertions(+)

diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 45330e0f66fe..67a45d95250d 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -60,8 +60,10 @@ config PHY_QCOM_QMP_COMBO
tristate "Qualcomm QMP Combo PHY Driver"
default PHY_QCOM_QMP
depends on TYPEC || TYPEC=n
+ depends on DRM || DRM=n
select GENERIC_PHY
select MFD_SYSCON
+ select DRM_PANEL_BRIDGE if DRM
help
Enable this to support the QMP Combo PHY transceiver that is used
with USB3 and DisplayPort controllers on Qualcomm chips.
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index d1300696d3e1..33cc99d9c77d 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -22,6 +22,8 @@
#include <linux/usb/typec.h>
#include <linux/usb/typec_mux.h>

+#include <drm/drm_bridge.h>
+
#include <dt-bindings/phy/phy-qcom-qmp.h>

#include "phy-qcom-qmp.h"
@@ -1332,6 +1334,8 @@ struct qmp_combo {
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;

+ struct drm_bridge bridge;
+
struct typec_switch_dev *sw;
enum typec_orientation orientation;
};
@@ -3274,6 +3278,44 @@ static int qmp_combo_typec_switch_register(struct qmp_combo *qmp)
}
#endif

+#if IS_ENABLED(CONFIG_DRM)
+static int qmp_combo_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct qmp_combo *qmp = container_of(bridge, struct qmp_combo, bridge);
+ struct drm_bridge *next_bridge;
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+ return -EINVAL;
+
+ next_bridge = devm_drm_of_get_bridge(qmp->dev, qmp->dev->of_node, 0, 0);
+ if (IS_ERR(next_bridge)) {
+ dev_err(qmp->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
+ return PTR_ERR(next_bridge);
+ }
+
+ return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static const struct drm_bridge_funcs qmp_combo_bridge_funcs = {
+ .attach = qmp_combo_bridge_attach,
+};
+
+static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp)
+{
+ qmp->bridge.funcs = &qmp_combo_bridge_funcs;
+ qmp->bridge.of_node = qmp->dev->of_node;
+
+ return devm_drm_bridge_add(qmp->dev, &qmp->bridge);
+}
+#else
+static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp)
+{
+ return 0;
+}
+#endif
+
static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_node *np)
{
struct device *dev = qmp->dev;
@@ -3478,6 +3520,10 @@ static int qmp_combo_probe(struct platform_device *pdev)
if (ret)
return ret;

+ ret = qmp_combo_dp_register_bridge(qmp);
+ if (ret)
+ return ret;
+
/* Check for legacy binding with child nodes. */
usb_np = of_get_child_by_name(dev->of_node, "usb3-phy");
if (usb_np) {
--
2.25.1


2023-05-15 03:40:19

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 4/8] phy: qcom-qmp-combo: Introduce orientation variable

In multiple places throughout the driver code has been written in
prepration for handling of orientation switching.

Introduce a typec_orientation in qmp_combo and fill out the various
"placeholders" with the associated logic. By initializing the
orientation to "normal" this change has no functional impact, but
reduces the size of the upcoming introduction of dynamic orientation
switching.

Reviewed-by: Neil Armstrong <[email protected]>
Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Reviewed-by: Johan Hovold <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- Parenthesis around equality check
- Reordered all reverse-checks in X-mas style

Changes since v1:
- X-mas in qmp_combo_configure_dp_mode()

drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 54 +++++++++++++----------
1 file changed, 30 insertions(+), 24 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 6baacdf3a4cb..b44c029ef23f 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -19,6 +19,7 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/usb/typec.h>

#include <dt-bindings/phy/phy-qcom-qmp.h>

@@ -63,6 +64,10 @@
/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
#define CLAMP_EN BIT(0) /* enables i/o clamp_n */

+/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */
+#define SW_PORTSELECT_VAL BIT(0)
+#define SW_PORTSELECT_MUX BIT(1)
+
#define PHY_INIT_COMPLETE_TIMEOUT 10000

struct qmp_phy_init_tbl {
@@ -1323,6 +1328,8 @@ struct qmp_combo {
struct clk_fixed_rate pipe_clk_fixed;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
+
+ enum typec_orientation orientation;
};

static void qmp_v3_dp_aux_init(struct qmp_combo *qmp);
@@ -1954,30 +1961,24 @@ static void qmp_v3_configure_dp_tx(struct qmp_combo *qmp)

static bool qmp_combo_configure_dp_mode(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
+ const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 val;
- bool reverse = false;

val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN;

- /*
- * TODO: Assume orientation is CC1 for now and two lanes, need to
- * use type-c connector to understand orientation and lanes.
- *
- * Otherwise val changes to be like below if this code understood
- * the orientation of the type-c cable.
- *
- * if (lane_cnt == 4 || orientation == ORIENTATION_CC2)
- * val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN;
- * if (lane_cnt == 4 || orientation == ORIENTATION_CC1)
- * val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
- * if (orientation == ORIENTATION_CC2)
- * writel(0x4c, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_MODE);
- */
- val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
+ if (dp_opts->lanes == 4 || reverse)
+ val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN;
+ if (dp_opts->lanes == 4 || !reverse)
+ val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
+
writel(val, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);

- writel(0x5c, qmp->dp_dp_phy + QSERDES_DP_PHY_MODE);
+ if (reverse)
+ writel(0x4c, qmp->pcs + QSERDES_DP_PHY_MODE);
+ else
+ writel(0x5c, qmp->pcs + QSERDES_DP_PHY_MODE);

return reverse;
}
@@ -2233,9 +2234,9 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp,

static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
u32 status;
int ret;

@@ -2297,9 +2298,9 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)

static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
u32 status;
int ret;

@@ -2356,9 +2357,9 @@ static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp)

static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
u32 status;
int ret;

@@ -2471,6 +2472,7 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *com = qmp->com;
int ret;
+ u32 val;

if (qmp->init_count++)
return 0;
@@ -2504,10 +2506,12 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);

- /* Default type-c orientation, i.e CC1 */
- qphy_setbits(com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02);
-
- qphy_setbits(com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE);
+ /* Use software based port select and switch on typec orientation */
+ val = SW_PORTSELECT_MUX;
+ if (qmp->orientation == TYPEC_ORIENTATION_REVERSE)
+ val |= SW_PORTSELECT_VAL;
+ writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL);
+ writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL);

/* bring both QMP USB and QMP DP PHYs PCS block out of reset */
qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
@@ -3379,6 +3383,8 @@ static int qmp_combo_probe(struct platform_device *pdev)

qmp->dev = dev;

+ qmp->orientation = TYPEC_ORIENTATION_NORMAL;
+
qmp->cfg = of_device_get_match_data(dev);
if (!qmp->cfg)
return -EINVAL;
--
2.25.1


2023-05-15 03:52:33

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 2/8] phy: qcom-qmp-combo: Move phy_mutex out of com_init/exit

With the upcoming introduction of USB Type-C orientation switching the
region of mutual exclusion needs to be extended to cover both the common
init/exit as well as the individual functions.

So move the phy_mutex one step up the stack.

Reviewed-by: Johan Hovold <[email protected]>
Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- Rebased on Johan's init_count fixes

drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 47 ++++++++++++++---------
1 file changed, 28 insertions(+), 19 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 87b17e5877ab..8918ba2b18a4 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -2463,11 +2463,8 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
void __iomem *com = qmp->com;
int ret;

- mutex_lock(&qmp->phy_mutex);
- if (qmp->init_count++) {
- mutex_unlock(&qmp->phy_mutex);
+ if (qmp->init_count++)
return 0;
- }

ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
@@ -2514,8 +2511,6 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);

- mutex_unlock(&qmp->phy_mutex);
-
return 0;

err_assert_reset:
@@ -2524,7 +2519,6 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
err_decrement_count:
qmp->init_count--;
- mutex_unlock(&qmp->phy_mutex);

return ret;
}
@@ -2533,11 +2527,8 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;

- mutex_lock(&qmp->phy_mutex);
- if (--qmp->init_count) {
- mutex_unlock(&qmp->phy_mutex);
+ if (--qmp->init_count)
return 0;
- }

reset_control_bulk_assert(cfg->num_resets, qmp->resets);

@@ -2545,8 +2536,6 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp)

regulator_bulk_disable(cfg->num_vregs, qmp->vregs);

- mutex_unlock(&qmp->phy_mutex);
-
return 0;
}

@@ -2556,21 +2545,29 @@ static int qmp_combo_dp_init(struct phy *phy)
const struct qmp_phy_cfg *cfg = qmp->cfg;
int ret;

+ mutex_lock(&qmp->phy_mutex);
+
ret = qmp_combo_com_init(qmp);
if (ret)
- return ret;
+ goto out_unlock;

cfg->dp_aux_init(qmp);

- return 0;
+out_unlock:
+ mutex_unlock(&qmp->phy_mutex);
+ return ret;
}

static int qmp_combo_dp_exit(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);

+ mutex_lock(&qmp->phy_mutex);
+
qmp_combo_com_exit(qmp);

+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}

@@ -2687,14 +2684,19 @@ static int qmp_combo_usb_init(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;

+ mutex_lock(&qmp->phy_mutex);
ret = qmp_combo_com_init(qmp);
if (ret)
- return ret;
+ goto out_unlock;

ret = qmp_combo_usb_power_on(phy);
- if (ret)
+ if (ret) {
qmp_combo_com_exit(qmp);
+ goto out_unlock;
+ }

+out_unlock:
+ mutex_unlock(&qmp->phy_mutex);
return ret;
}

@@ -2703,11 +2705,18 @@ static int qmp_combo_usb_exit(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;

+ mutex_lock(&qmp->phy_mutex);
ret = qmp_combo_usb_power_off(phy);
if (ret)
- return ret;
+ goto out_unlock;

- return qmp_combo_com_exit(qmp);
+ ret = qmp_combo_com_exit(qmp);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ mutex_unlock(&qmp->phy_mutex);
+ return ret;
}

static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode)
--
2.25.1


2023-05-15 03:53:42

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 1/8] dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp: Add ports and orientation-switch

The QMP combo phy can be connected to a TCPM, a USB controller and a
DisplayPort controller for handling USB Type-C orientation switching
and propagating HPD signals.

Extend the binding to allow these connections to be described.

Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Reviewed-by: Krzysztof Kozlowski <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- Corrected port $ref

.../phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
index 3cd5fc3e8fab..ef1c02d8ac88 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml
@@ -60,6 +60,26 @@ properties:
description:
See include/dt-bindings/dt-bindings/phy/phy-qcom-qmp.h

+ orientation-switch:
+ description:
+ Flag the PHY as possible handler of USB Type-C orientation switching
+ type: boolean
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Output endpoint of the PHY
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Incoming endpoint from the USB controller
+
+ port@2:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Incoming endpoint from the DisplayPort controller
+
required:
- compatible
- reg
@@ -98,6 +118,37 @@ examples:
vdda-phy-supply = <&vreg_l9d>;
vdda-pll-supply = <&vreg_l4d>;

+ orientation-switch;
+
#clock-cells = <1>;
#phy-cells = <1>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&typec_connector_ss>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&dwc3_ss_out>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ endpoint {
+ remote-endpoint = <&mdss_dp_out>;
+ };
+ };
+ };
};
--
2.25.1


2023-05-15 04:04:24

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 7/8] arm64: dts: qcom: sc8280xp-crd: Add QMP to SuperSpeed graph

With support for the QMP combo phy to react to USB Type-C switch events,
introduce it as the next hop for the SuperSpeed lanes of the two USB
Type-C connectors, and connect the output of the DisplayPort controller
to the QMP combo phy.

This allows the TCPM to perform orientation switching of both USB and
DisplayPort signals.

Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Reviewed-by: Johan Hovold <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- DP input is port@2

arch/arm64/boot/dts/qcom/sc8280xp-crd.dts | 28 ++++++++++++++++---
arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 34 +++++++++++++++++++++++
2 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts
index 5b25d54b9591..e22f9b65b7b6 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts
+++ b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts
@@ -64,7 +64,7 @@ port@1 {
reg = <1>;

pmic_glink_con0_ss: endpoint {
- remote-endpoint = <&mdss0_dp0_out>;
+ remote-endpoint = <&usb_0_qmpphy_out>;
};
};

@@ -99,7 +99,7 @@ port@1 {
reg = <1>;

pmic_glink_con1_ss: endpoint {
- remote-endpoint = <&mdss0_dp1_out>;
+ remote-endpoint = <&usb_1_qmpphy_out>;
};
};

@@ -386,7 +386,7 @@ &mdss0_dp0 {

&mdss0_dp0_out {
data-lanes = <0 1>;
- remote-endpoint = <&pmic_glink_con0_ss>;
+ remote-endpoint = <&usb_0_qmpphy_dp_in>;
};

&mdss0_dp1 {
@@ -395,7 +395,7 @@ &mdss0_dp1 {

&mdss0_dp1_out {
data-lanes = <0 1>;
- remote-endpoint = <&pmic_glink_con1_ss>;
+ remote-endpoint = <&usb_1_qmpphy_dp_in>;
};

&mdss0_dp3 {
@@ -644,9 +644,19 @@ &usb_0_qmpphy {
vdda-phy-supply = <&vreg_l9d>;
vdda-pll-supply = <&vreg_l4d>;

+ orientation-switch;
+
status = "okay";
};

+&usb_0_qmpphy_dp_in {
+ remote-endpoint = <&mdss0_dp0_out>;
+};
+
+&usb_0_qmpphy_out {
+ remote-endpoint = <&pmic_glink_con0_ss>;
+};
+
&usb_0_role_switch {
remote-endpoint = <&pmic_glink_con0_hs>;
};
@@ -671,9 +681,19 @@ &usb_1_qmpphy {
vdda-phy-supply = <&vreg_l4b>;
vdda-pll-supply = <&vreg_l3b>;

+ orientation-switch;
+
status = "okay";
};

+&usb_1_qmpphy_dp_in {
+ remote-endpoint = <&mdss0_dp1_out>;
+};
+
+&usb_1_qmpphy_out {
+ remote-endpoint = <&pmic_glink_con1_ss>;
+};
+
&usb_1_role_switch {
remote-endpoint = <&pmic_glink_con1_hs>;
};
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
index 8fa9fbfe5d00..1fb42067d0d1 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
@@ -2835,6 +2835,23 @@ usb_0_qmpphy: phy@88eb000 {
#phy-cells = <1>;

status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ usb_0_qmpphy_out: endpoint {};
+ };
+
+ port@2 {
+ reg = <2>;
+
+ usb_0_qmpphy_dp_in: endpoint {};
+ };
+ };
};

usb_1_hsphy: phy@8902000 {
@@ -2871,6 +2888,23 @@ usb_1_qmpphy: phy@8903000 {
#phy-cells = <1>;

status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ usb_1_qmpphy_out: endpoint {};
+ };
+
+ port@2 {
+ reg = <2>;
+
+ usb_1_qmpphy_dp_in: endpoint {};
+ };
+ };
};

mdss1_dp0_phy: phy@8909a00 {
--
2.25.1


2023-05-15 04:05:29

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 5/8] phy: qcom-qmp-combo: Introduce orientation switching

The data lanes of the QMP PHY is swapped in order to handle changing
orientation of the USB Type-C cable. Register a typec_switch device to
allow a TCPM to configure the orientation.

The newly introduced orientation variable is adjusted based on the
request, and the initialized components are brought down and up again.
To keep track of what parts needs to be cycled new variables to keep
track of the individual init_count is introduced.

Both the USB and the DisplayPort altmode signals are properly switched.
For DisplayPort the controller will after the TCPM having established
orientation power on the PHY, so this is not done implicitly, but for
USB the PHY typically is kept initialized across the switch, and must
therefore then be reinitialized.

This is based on initial work by Wesley Cheng.

Link: https://lore.kernel.org/r/[email protected]/
Reviewed-by: Johan Hovold <[email protected]>
Reviewed-by: Neil Armstrong <[email protected]>
Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- Add TYPEC dependency in Kconfig
- Whitespace changes

drivers/phy/qualcomm/Kconfig | 1 +
drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 93 ++++++++++++++++++++---
2 files changed, 85 insertions(+), 9 deletions(-)

diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 4850d48f31fa..45330e0f66fe 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -59,6 +59,7 @@ if PHY_QCOM_QMP
config PHY_QCOM_QMP_COMBO
tristate "Qualcomm QMP Combo PHY Driver"
default PHY_QCOM_QMP
+ depends on TYPEC || TYPEC=n
select GENERIC_PHY
select MFD_SYSCON
help
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index b44c029ef23f..d1300696d3e1 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -20,6 +20,7 @@
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/usb/typec.h>
+#include <linux/usb/typec_mux.h>

#include <dt-bindings/phy/phy-qcom-qmp.h>

@@ -1320,15 +1321,18 @@ struct qmp_combo {

struct phy *usb_phy;
enum phy_mode mode;
+ unsigned int usb_init_count;

struct phy *dp_phy;
unsigned int dp_aux_cfg;
struct phy_configure_opts_dp dp_opts;
+ unsigned int dp_init_count;

struct clk_fixed_rate pipe_clk_fixed;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;

+ struct typec_switch_dev *sw;
enum typec_orientation orientation;
};

@@ -2467,14 +2471,14 @@ static int qmp_combo_dp_calibrate(struct phy *phy)
return ret;
}

-static int qmp_combo_com_init(struct qmp_combo *qmp)
+static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *com = qmp->com;
int ret;
u32 val;

- if (qmp->init_count++)
+ if (!force && qmp->init_count++)
return 0;

ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
@@ -2536,11 +2540,11 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
return ret;
}

-static int qmp_combo_com_exit(struct qmp_combo *qmp)
+static int qmp_combo_com_exit(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;

- if (--qmp->init_count)
+ if (!force && --qmp->init_count)
return 0;

reset_control_bulk_assert(cfg->num_resets, qmp->resets);
@@ -2560,12 +2564,14 @@ static int qmp_combo_dp_init(struct phy *phy)

mutex_lock(&qmp->phy_mutex);

- ret = qmp_combo_com_init(qmp);
+ ret = qmp_combo_com_init(qmp, false);
if (ret)
goto out_unlock;

cfg->dp_aux_init(qmp);

+ qmp->dp_init_count++;
+
out_unlock:
mutex_unlock(&qmp->phy_mutex);
return ret;
@@ -2577,7 +2583,9 @@ static int qmp_combo_dp_exit(struct phy *phy)

mutex_lock(&qmp->phy_mutex);

- qmp_combo_com_exit(qmp);
+ qmp_combo_com_exit(qmp, false);
+
+ qmp->dp_init_count--;

mutex_unlock(&qmp->phy_mutex);

@@ -2706,16 +2714,18 @@ static int qmp_combo_usb_init(struct phy *phy)
int ret;

mutex_lock(&qmp->phy_mutex);
- ret = qmp_combo_com_init(qmp);
+ ret = qmp_combo_com_init(qmp, false);
if (ret)
goto out_unlock;

ret = qmp_combo_usb_power_on(phy);
if (ret) {
- qmp_combo_com_exit(qmp);
+ qmp_combo_com_exit(qmp, false);
goto out_unlock;
}

+ qmp->usb_init_count++;
+
out_unlock:
mutex_unlock(&qmp->phy_mutex);
return ret;
@@ -2731,10 +2741,12 @@ static int qmp_combo_usb_exit(struct phy *phy)
if (ret)
goto out_unlock;

- ret = qmp_combo_com_exit(qmp);
+ ret = qmp_combo_com_exit(qmp, false);
if (ret)
goto out_unlock;

+ qmp->usb_init_count--;
+
out_unlock:
mutex_unlock(&qmp->phy_mutex);
return ret;
@@ -3203,6 +3215,65 @@ static int qmp_combo_register_clocks(struct qmp_combo *qmp, struct device_node *
return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, dp_np);
}

+#if IS_ENABLED(CONFIG_TYPEC)
+static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct qmp_combo *qmp = typec_switch_get_drvdata(sw);
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+
+ if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE)
+ return 0;
+
+ mutex_lock(&qmp->phy_mutex);
+ qmp->orientation = orientation;
+
+ if (qmp->init_count) {
+ if (qmp->usb_init_count)
+ qmp_combo_usb_power_off(qmp->usb_phy);
+ qmp_combo_com_exit(qmp, true);
+
+ qmp_combo_com_init(qmp, true);
+ if (qmp->usb_init_count)
+ qmp_combo_usb_power_on(qmp->usb_phy);
+ if (qmp->dp_init_count)
+ cfg->dp_aux_init(qmp);
+ }
+ mutex_unlock(&qmp->phy_mutex);
+
+ return 0;
+}
+
+static void qmp_combo_typec_unregister(void *data)
+{
+ struct qmp_combo *qmp = data;
+
+ typec_switch_unregister(qmp->sw);
+}
+
+static int qmp_combo_typec_switch_register(struct qmp_combo *qmp)
+{
+ struct typec_switch_desc sw_desc = {};
+ struct device *dev = qmp->dev;
+
+ sw_desc.drvdata = qmp;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = qmp_combo_typec_switch_set;
+ qmp->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(qmp->sw)) {
+ dev_err(dev, "Unable to register typec switch: %pe\n", qmp->sw);
+ return PTR_ERR(qmp->sw);
+ }
+
+ return devm_add_action_or_reset(dev, qmp_combo_typec_unregister, qmp);
+}
+#else
+static int qmp_combo_typec_switch_register(struct qmp_combo *qmp)
+{
+ return 0;
+}
+#endif
+
static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_node *np)
{
struct device *dev = qmp->dev;
@@ -3403,6 +3474,10 @@ static int qmp_combo_probe(struct platform_device *pdev)
if (ret)
return ret;

+ ret = qmp_combo_typec_switch_register(qmp);
+ if (ret)
+ return ret;
+
/* Check for legacy binding with child nodes. */
usb_np = of_get_child_by_name(dev->of_node, "usb3-phy");
if (usb_np) {
--
2.25.1


2023-05-15 04:08:42

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 3/8] phy: qcom-qmp-combo: Extend phy_mutex to all phy_ops

The phy core ensures mutual exclusion across the ops for a given phy,
but the upcoming introduction of USB Type-C orientation switching might
race with the DisplayPort phy operations. So extend the mutual exclusion
to cover the remaining ops as well, to avoid concurrent reconfiguration
of the hardware.

Reported-by: Johan Hovold <[email protected]>
Reviewed-by: Johan Hovold <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- New patch

drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 8918ba2b18a4..6baacdf3a4cb 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -2437,12 +2437,16 @@ static int qmp_combo_dp_configure(struct phy *phy, union phy_configure_opts *opt
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;

+ mutex_lock(&qmp->phy_mutex);
+
memcpy(&qmp->dp_opts, dp_opts, sizeof(*dp_opts));
if (qmp->dp_opts.set_voltages) {
cfg->configure_dp_tx(qmp);
qmp->dp_opts.set_voltages = 0;
}

+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}

@@ -2450,11 +2454,16 @@ static int qmp_combo_dp_calibrate(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ int ret = 0;
+
+ mutex_lock(&qmp->phy_mutex);

if (cfg->calibrate_dp_phy)
- return cfg->calibrate_dp_phy(qmp);
+ ret = cfg->calibrate_dp_phy(qmp);

- return 0;
+ mutex_unlock(&qmp->phy_mutex);
+
+ return ret;
}

static int qmp_combo_com_init(struct qmp_combo *qmp)
@@ -2578,6 +2587,8 @@ static int qmp_combo_dp_power_on(struct phy *phy)
void __iomem *tx = qmp->dp_tx;
void __iomem *tx2 = qmp->dp_tx2;

+ mutex_lock(&qmp->phy_mutex);
+
qmp_combo_dp_serdes_init(qmp);

qmp_combo_configure_lane(tx, cfg->dp_tx_tbl, cfg->dp_tx_tbl_num, 1);
@@ -2589,6 +2600,8 @@ static int qmp_combo_dp_power_on(struct phy *phy)
/* Configure link rate, swing, etc. */
cfg->configure_dp_phy(qmp);

+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}

@@ -2596,9 +2609,13 @@ static int qmp_combo_dp_power_off(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);

+ mutex_lock(&qmp->phy_mutex);
+
/* Assert DP PHY power down */
writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);

+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}

--
2.25.1


2023-05-15 04:17:08

by Bjorn Andersson

[permalink] [raw]
Subject: [PATCH v3 8/8] arm64: dts: qcom: sc8280xp-x13s: Add QMP to SuperSpeed graph

Following the CRD, connect the two QMP phys inbetween the USB Type-C
connectors and the DisplayPort controller, to handle orientation
switching.

Tested-by: Abel Vesa <[email protected]>
Tested-by: Steev Klimaszewski <[email protected]>
Tested-by: Neil Armstrong <[email protected]> # on HDK8450
Tested-by: Johan Hovold <[email protected]> # X13s
Reviewed-by: Johan Hovold <[email protected]>
Signed-off-by: Bjorn Andersson <[email protected]>
---

Changes since v2:
- None

Changes since v1:
- None

.../qcom/sc8280xp-lenovo-thinkpad-x13s.dts | 28 ++++++++++++++++---
1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts
index bdcba719fc38..5ae057ad6438 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts
+++ b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts
@@ -106,7 +106,7 @@ port@1 {
reg = <1>;

pmic_glink_con0_ss: endpoint {
- remote-endpoint = <&mdss0_dp0_out>;
+ remote-endpoint = <&usb_0_qmpphy_out>;
};
};

@@ -141,7 +141,7 @@ port@1 {
reg = <1>;

pmic_glink_con1_ss: endpoint {
- remote-endpoint = <&mdss0_dp1_out>;
+ remote-endpoint = <&usb_1_qmpphy_out>;
};
};

@@ -528,7 +528,7 @@ &mdss0_dp0 {

&mdss0_dp0_out {
data-lanes = <0 1>;
- remote-endpoint = <&pmic_glink_con0_ss>;
+ remote-endpoint = <&usb_0_qmpphy_dp_in>;
};

&mdss0_dp1 {
@@ -537,7 +537,7 @@ &mdss0_dp1 {

&mdss0_dp1_out {
data-lanes = <0 1>;
- remote-endpoint = <&pmic_glink_con1_ss>;
+ remote-endpoint = <&usb_1_qmpphy_dp_in>;
};

&mdss0_dp3 {
@@ -1114,9 +1114,19 @@ &usb_0_qmpphy {
vdda-phy-supply = <&vreg_l9d>;
vdda-pll-supply = <&vreg_l4d>;

+ orientation-switch;
+
status = "okay";
};

+&usb_0_qmpphy_dp_in {
+ remote-endpoint = <&mdss0_dp0_out>;
+};
+
+&usb_0_qmpphy_out {
+ remote-endpoint = <&pmic_glink_con0_ss>;
+};
+
&usb_0_role_switch {
remote-endpoint = <&pmic_glink_con0_hs>;
};
@@ -1141,9 +1151,19 @@ &usb_1_qmpphy {
vdda-phy-supply = <&vreg_l4b>;
vdda-pll-supply = <&vreg_l3b>;

+ orientation-switch;
+
status = "okay";
};

+&usb_1_qmpphy_dp_in {
+ remote-endpoint = <&mdss0_dp1_out>;
+};
+
+&usb_1_qmpphy_out {
+ remote-endpoint = <&pmic_glink_con1_ss>;
+};
+
&usb_1_role_switch {
remote-endpoint = <&pmic_glink_con1_hs>;
};
--
2.25.1


2023-05-19 18:21:58

by Vinod Koul

[permalink] [raw]
Subject: Re: [PATCH v3 0/8] phy: qcom-qmp-combo: Support orientation switching

On 14-05-23, 20:27, Bjorn Andersson wrote:
> This adds support for USB and DisplayPort orientation switching to the
> QMP combo PHY, as well as updating the sc8280xp devices to include the
> QMP in the SuperSpeed graph.

Applied phy patches, thanks

--
~Vinod

2023-05-23 03:02:40

by Bjorn Andersson

[permalink] [raw]
Subject: Re: (subset) [PATCH v3 0/8] phy: qcom-qmp-combo: Support orientation switching

On Sun, 14 May 2023 20:27:35 -0700, Bjorn Andersson wrote:
> This adds support for USB and DisplayPort orientation switching to the
> QMP combo PHY, as well as updating the sc8280xp devices to include the
> QMP in the SuperSpeed graph.
>
> Bjorn Andersson (8):
> dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp: Add ports and
> orientation-switch
> phy: qcom-qmp-combo: Move phy_mutex out of com_init/exit
> phy: qcom-qmp-combo: Extend phy_mutex to all phy_ops
> phy: qcom-qmp-combo: Introduce orientation variable
> phy: qcom-qmp-combo: Introduce orientation switching
> phy: qcom-qmp-combo: Introduce drm_bridge
> arm64: dts: qcom: sc8280xp-crd: Add QMP to SuperSpeed graph
> arm64: dts: qcom: sc8280xp-x13s: Add QMP to SuperSpeed graph
>
> [...]

Applied, thanks!

[7/8] arm64: dts: qcom: sc8280xp-crd: Add QMP to SuperSpeed graph
commit: 507ceaa5ca9fac0d9fe2521c29d7d6237c1214f4
[8/8] arm64: dts: qcom: sc8280xp-x13s: Add QMP to SuperSpeed graph
commit: 42b08375498e74f094425fad10d10c338fd29858

Best regards,
--
Bjorn Andersson <[email protected]>