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.39.2
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.
Signed-off-by: Bjorn Andersson <[email protected]>
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
---
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 52aa2735b841..d500cb8d2eec 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.39.2
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.
Signed-off-by: Bjorn Andersson <[email protected]>
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
---
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.39.2
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.
Signed-off-by: Bjorn Andersson <[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
---
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..e0d246e7086d 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;
}
@@ -2235,7 +2236,7 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)
{
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
+ bool reverse = qmp->orientation == TYPEC_ORIENTATION_REVERSE;
u32 status;
int ret;
@@ -2299,7 +2300,7 @@ static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp)
{
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
+ bool reverse = qmp->orientation == TYPEC_ORIENTATION_REVERSE;
u32 status;
int ret;
@@ -2358,7 +2359,7 @@ static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp)
{
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
+ bool reverse = qmp->orientation == TYPEC_ORIENTATION_REVERSE;
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.39.2
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]>
Signed-off-by: Bjorn Andersson <[email protected]>
---
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.39.2
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]/
Signed-off-by: Bjorn Andersson <[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
---
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 e0d246e7086d..52aa2735b841 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.39.2
Following the CRD, connect the two QMP phys inbetween the USB Type-C
connectors and the DisplayPort controller, to handle orientation
switching.
Signed-off-by: Bjorn Andersson <[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
---
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 5ef3f4c07d75..382f27946468 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>;
};
};
@@ -554,7 +554,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 {
@@ -563,7 +563,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 {
@@ -1140,9 +1140,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>;
};
@@ -1167,9 +1177,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.39.2
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.
Signed-off-by: Bjorn Andersson <[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
---
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.39.2
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.
Signed-off-by: Bjorn Andersson <[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
---
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 547277924ea3..33c973661fa5 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>;
};
};
@@ -412,7 +412,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 {
@@ -421,7 +421,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 {
@@ -670,9 +670,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>;
};
@@ -697,9 +707,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 0e691bb0120c..19686557efd0 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
@@ -3006,6 +3006,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 {
@@ -3042,6 +3059,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.39.2
On 10/05/2023 05:19, Bjorn Andersson wrote:
> 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.
>
> Signed-off-by: Bjorn Andersson <[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
> ---
>
> Changes since v1:
> - Corrected port $ref
>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
Best regards,
Krzysztof
On Tue, May 09, 2023 at 08:19:25PM -0700, Bjorn Andersson wrote:
> 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]>
> Signed-off-by: Bjorn Andersson <[email protected]>
Reviewed-by: Johan Hovold <[email protected]>
Johan
On Tue, May 09, 2023 at 08:19:26PM -0700, Bjorn Andersson wrote:
> 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.
>
> Signed-off-by: Bjorn Andersson <[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
> ---
>
> 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(-)
> static bool qmp_combo_configure_dp_mode(struct qmp_combo *qmp)
> {
> + bool reverse = qmp->orientation == TYPEC_ORIENTATION_REVERSE;
Nit: I still think parentheses around the right-hand side would improve
readability.
> + const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
> u32 val;
> - bool reverse = false;
> @@ -2235,7 +2236,7 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)
> {
> const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
> u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
> - bool reverse = false;
> + bool reverse = qmp->orientation == TYPEC_ORIENTATION_REVERSE;
Same here and below.
And maintaining reverse xmas style throughout the driver would be nice
for consistency too.
> u32 status;
> int ret;
Looks good otherwise:
Reviewed-by: Johan Hovold <[email protected]>
Johan
On Tue, May 09, 2023 at 08:19:28PM -0700, Bjorn Andersson wrote:
> 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.
>
> Signed-off-by: Bjorn Andersson <[email protected]>
> 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
> ---
>
> Changes since v1:
> - Wrap DRM-related code in CONFIG_DRM guard
> - Inroduce DRM-dependencies in Kconfig
> - Dropped dev_err_probe() usage
Reviewed-by: Johan Hovold <[email protected]>
On Tue, May 09, 2023 at 08:19:30PM -0700, Bjorn Andersson wrote:
> Following the CRD, connect the two QMP phys inbetween the USB Type-C
> connectors and the DisplayPort controller, to handle orientation
> switching.
>
> Signed-off-by: Bjorn Andersson <[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
> ---
>
> Changes since v1:
> - None
Reviewed-by: Johan Hovold <[email protected]>
On Tue, May 09, 2023 at 08:19:29PM -0700, Bjorn Andersson wrote:
> 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.
>
> Signed-off-by: Bjorn Andersson <[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
> ---
>
> Changes since v1:
> - DP input is port@2
Reviewed-by: Johan Hovold <[email protected]>