From 1543e317f1da31b75942316931e8f491a8920811 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Thu, 04 Jan 2024 10:08:02 +0000
Subject: [PATCH] disable FB

---
 kernel/drivers/phy/rockchip/phy-rockchip-typec.c |  383 +++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 277 insertions(+), 106 deletions(-)

diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-typec.c b/kernel/drivers/phy/rockchip/phy-rockchip-typec.c
index 24adca0..f4ffb14 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
  * Author: Chris Zhong <zyw@rock-chips.com>
  *         Kever Yang <kever.yang@rock-chips.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  *
  * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock
  * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has
@@ -42,7 +34,6 @@
  * This Type-C PHY driver supports normal and flip orientation. The orientation
  * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip
  * orientation, false is normal orientation.
- *
  */
 
 #include <linux/clk.h>
@@ -60,10 +51,13 @@
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/reset.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
-#include <linux/phy/phy-rockchip-typec.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_dp.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -393,7 +387,7 @@
 };
 
 /**
- * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration.
  * @reg: the base address for usb3-phy config.
  * @typec_conn_dir: the register of type-c connector direction.
  * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
@@ -419,22 +413,31 @@
 	int pe;
 };
 
+enum {
+	TYPEC_PHY_USB,
+	TYPEC_PHY_DP,
+	TYPEC_PHY_MAX,
+};
+
 struct rockchip_typec_phy {
 	struct device *dev;
 	void __iomem *base;
-	struct extcon_dev *extcon;
+	struct typec_mux *mux;
+	struct typec_switch *sw;
 	struct regmap *grf_regs;
 	struct clk *clk_core;
 	struct clk *clk_ref;
 	struct reset_control *uphy_rst;
 	struct reset_control *pipe_rst;
 	struct reset_control *tcphy_rst;
+	struct phy *phys[TYPEC_PHY_MAX];
 	const struct rockchip_usb3phy_port_cfg *port_cfgs;
 	/* mutex to protect access to individual PHYs */
 	struct mutex lock;
 
 	bool flip;
 	u8 mode;
+	u8 new_mode;
 	struct phy_config config[3][4];
 };
 
@@ -635,7 +638,6 @@
 };
 
 enum phy_dp_power_state {
-	PHY_DP_POWER_STATE_DISABLED = -1,
 	PHY_DP_POWER_STATE_A0,
 	PHY_DP_POWER_STATE_A1,
 	PHY_DP_POWER_STATE_A2,
@@ -852,17 +854,14 @@
 	writel(0x98, tcphy->base + TX_PSC_A3(lane));
 }
 
-int tcphy_dp_set_phy_config(struct phy *phy, int link_rate,
-			    int lane_count, u8 swing, u8 pre_emp)
+static int rockchip_dp_phy_set_voltages(struct rockchip_typec_phy *tcphy,
+					struct phy_configure_opts_dp *dp)
 {
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
 	u8 i, j, lane;
 	u32 val;
 
-	if (!phy->power_count)
-		return -EPERM;
 
-	if (lane_count == 4) {
+	if (dp->lanes == 4) {
 		i = 0;
 		j = 3;
 	} else {
@@ -876,12 +875,12 @@
 	}
 
 	for (lane = i; lane <= j; lane++) {
-		writel(tcphy->config[swing][pre_emp].swing,
+		writel(tcphy->config[dp->voltage[lane]][dp->pre[lane]].swing,
 		       tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
-		writel(tcphy->config[swing][pre_emp].pe,
+		writel(tcphy->config[dp->voltage[lane]][dp->pre[lane]].pe,
 		       tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
 
-		if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
+		if (dp->voltage[lane] == 2 && dp->pre[lane] == 0 && dp->link_rate != 540000) {
 			writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
 			writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
 		} else {
@@ -891,21 +890,17 @@
 
 		val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 		val &= ~GENMASK(14, 12);
-		val |= ((link_rate == 540000) ? 0x5 : 0x6) << 12;
+		val |= ((dp->link_rate == 540000) ? 0x5 : 0x6) << 12;
 		writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL(tcphy_dp_set_phy_config);
 
-int tcphy_dp_set_lane_count(struct phy *phy, u8 lane_count)
+static int rockchip_dp_phy_set_lanes(struct rockchip_typec_phy *tcphy,
+				     struct phy_configure_opts_dp *dp)
 {
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
 	u32 reg;
-
-	if (!phy->power_count)
-		return -EPERM;
 
 	/*
 	 * In cases where fewer than the configured number of DP lanes are
@@ -917,7 +912,7 @@
 	reg = readl(tcphy->base + PHY_DP_MODE_CTL);
 	reg |= PHY_DP_LANE_DISABLE;
 
-	switch (lane_count) {
+	switch (dp->lanes) {
 	case 4:
 		reg &= ~(PHY_DP_LANE_3_DISABLE | PHY_DP_LANE_2_DISABLE |
 			 PHY_DP_LANE_1_DISABLE | PHY_DP_LANE_0_DISABLE);
@@ -936,18 +931,14 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(tcphy_dp_set_lane_count);
 
-int tcphy_dp_set_link_rate(struct phy *phy, int link_rate, bool ssc_on)
+static int rockchip_dp_phy_set_rate(struct rockchip_typec_phy *tcphy,
+				    struct phy_configure_opts_dp *dp)
 {
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
 	const struct phy_reg *phy_cfg;
 	u32 cmn_diag_hsclk_sel, phy_dp_clk_ctl, reg;
 	u32 i, cfg_size;
 	int ret;
-
-	if (!phy->power_count)
-		return -EPERM;
 
 	/* Place the PHY lanes in the A3 power state. */
 	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A3);
@@ -991,29 +982,29 @@
 	phy_dp_clk_ctl = readl(tcphy->base + PHY_DP_CLK_CTL);
 	phy_dp_clk_ctl &= ~(GENMASK(15, 12) | GENMASK(11, 8));
 
-	switch (link_rate) {
-	case 162000:
+	switch (dp->link_rate) {
+	case 1620:
 		cmn_diag_hsclk_sel |= (3 << 4) | (0 << 0);
 		phy_dp_clk_ctl |= (2 << 12) | (4 << 8);
 
-		phy_cfg = ssc_on ? dp_pll_rbr_ssc_cfg : dp_pll_rbr_cfg;
-		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_rbr_ssc_cfg) :
+		phy_cfg = dp->ssc ? dp_pll_rbr_ssc_cfg : dp_pll_rbr_cfg;
+		cfg_size = dp->ssc ? ARRAY_SIZE(dp_pll_rbr_ssc_cfg) :
 				    ARRAY_SIZE(dp_pll_rbr_cfg);
 		break;
-	case 270000:
+	case 2700:
 		cmn_diag_hsclk_sel |= (3 << 4) | (0 << 0);
 		phy_dp_clk_ctl |= (2 << 12) | (4 << 8);
 
-		phy_cfg = ssc_on ? dp_pll_hbr_ssc_cfg : dp_pll_hbr_cfg;
-		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_hbr_ssc_cfg) :
+		phy_cfg = dp->ssc ? dp_pll_hbr_ssc_cfg : dp_pll_hbr_cfg;
+		cfg_size = dp->ssc ? ARRAY_SIZE(dp_pll_hbr_ssc_cfg) :
 				    ARRAY_SIZE(dp_pll_hbr_cfg);
 		break;
-	case 540000:
+	case 5400:
 		cmn_diag_hsclk_sel |= (2 << 4) | (0 << 0);
 		phy_dp_clk_ctl |= (1 << 12) | (2 << 8);
 
-		phy_cfg = ssc_on ? dp_pll_hbr2_ssc_cfg : dp_pll_hbr2_cfg;
-		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_hbr2_ssc_cfg) :
+		phy_cfg = dp->ssc ? dp_pll_hbr2_ssc_cfg : dp_pll_hbr2_cfg;
+		cfg_size = dp->ssc ? ARRAY_SIZE(dp_pll_hbr2_ssc_cfg) :
 				    ARRAY_SIZE(dp_pll_hbr2_cfg);
 		break;
 	default:
@@ -1071,7 +1062,6 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(tcphy_dp_set_link_rate);
 
 static inline int property_enable(struct rockchip_typec_phy *tcphy,
 				  const struct usb3phy_reg *reg, bool en)
@@ -1334,50 +1324,52 @@
 
 static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 {
-	struct extcon_dev *edev = tcphy->extcon;
-	union extcon_property_value property;
-	unsigned int id;
-	bool ufp, dp;
-	u8 mode;
-	int ret;
+	return tcphy->new_mode;
+}
 
-	if (!edev)
-		return MODE_DFP_USB;
+static int tcphy_orien_sw_set(struct typec_switch *sw,
+			      enum typec_orientation orien)
+{
+	struct rockchip_typec_phy *tcphy = typec_switch_get_drvdata(sw);
 
-	ufp = extcon_get_state(edev, EXTCON_USB);
-	dp = extcon_get_state(edev, EXTCON_DISP_DP);
+	mutex_lock(&tcphy->lock);
 
-	mode = MODE_DFP_USB;
-	id = EXTCON_USB_HOST;
-
-	if (ufp) {
-		mode = MODE_UFP_USB;
-		id = EXTCON_USB;
-	} else if (dp) {
-		mode = MODE_DFP_DP;
-		id = EXTCON_DISP_DP;
-
-		ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS,
-					  &property);
-		if (ret) {
-			dev_err(tcphy->dev, "get superspeed property failed\n");
-			return ret;
-		}
-
-		if (property.intval)
-			mode |= MODE_DFP_USB;
+	if (orien == TYPEC_ORIENTATION_NONE) {
+		tcphy->new_mode = MODE_DISCONNECT;
+		goto unlock_ret;
 	}
 
-	ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY,
-				  &property);
-	if (ret) {
-		dev_err(tcphy->dev, "get polarity property failed\n");
-		return ret;
+	tcphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
+	tcphy->new_mode = MODE_DFP_USB;
+
+unlock_ret:
+	mutex_unlock(&tcphy->lock);
+	return 0;
+}
+
+static int tcphy_setup_orien_switch(struct rockchip_typec_phy *tcphy)
+{
+	struct typec_switch_desc sw_desc = { };
+
+	sw_desc.drvdata = tcphy;
+	sw_desc.fwnode = dev_fwnode(tcphy->dev);
+	sw_desc.set = tcphy_orien_sw_set;
+
+	tcphy->sw = typec_switch_register(tcphy->dev, &sw_desc);
+	if (IS_ERR(tcphy->sw)) {
+		dev_err(tcphy->dev, "Error register typec orientation switch: %ld\n",
+			PTR_ERR(tcphy->sw));
+		return PTR_ERR(tcphy->sw);
 	}
 
-	tcphy->flip = property.intval ? 1 : 0;
+	return 0;
+}
 
-	return mode;
+static void udphy_orien_switch_unregister(void *data)
+{
+	struct rockchip_typec_phy *tcphy = data;
+
+	typec_switch_unregister(tcphy->sw);
 }
 
 static int _rockchip_usb3_phy_power_on(struct rockchip_typec_phy *tcphy)
@@ -1415,7 +1407,6 @@
 		regmap_read(tcphy->grf_regs, reg->offset, &val);
 		if (!(val & BIT(reg->enable_bit))) {
 			tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB);
-
 			/* enable usb3 host */
 			tcphy_cfg_usb3_to_usb2_only(tcphy, false);
 			goto unlock_ret;
@@ -1567,11 +1558,166 @@
 	return 0;
 }
 
+static int rockchip_dp_phy_verify_config(struct rockchip_typec_phy *tcphy,
+					 struct phy_configure_opts_dp *dp)
+{
+	u8 i;
+
+	/* If changing link rate was required, verify it's supported. */
+	if (dp->set_rate) {
+		switch (dp->link_rate) {
+		case 1620:
+		case 2700:
+		case 5400:
+			/* valid bit rate */
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Verify lane count. */
+	switch (dp->lanes) {
+	case 1:
+	case 2:
+	case 4:
+		/* valid lane count. */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * If changing voltages is required, check swing and pre-emphasis
+	 * levels, per-lane.
+	 */
+	if (dp->set_voltages) {
+		/* Lane count verified previously. */
+		for (i = 0; i < dp->lanes; i++) {
+			if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+				return -EINVAL;
+
+			/* Sum of voltage swing and pre-emphasis levels cannot
+			 * exceed 3.
+			 */
+			if (dp->voltage[i] + dp->pre[i] > 3)
+				return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int rockchip_dp_phy_configure(struct phy *phy,
+					union phy_configure_opts *opts)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	int ret;
+
+	if (!phy->power_count)
+		return -EPERM;
+
+	ret = rockchip_dp_phy_verify_config(tcphy, &opts->dp);
+	if (ret) {
+		dev_err(&phy->dev, "invalid params for phy configure\n");
+		return ret;
+	}
+
+	if (opts->dp.set_lanes) {
+		ret = rockchip_dp_phy_set_lanes(tcphy, &opts->dp);
+		if (ret) {
+			dev_err(&phy->dev, "rockchip_dp_phy_set_lanes failed\n");
+			return ret;
+		}
+	}
+
+	if (opts->dp.set_rate) {
+		ret = rockchip_dp_phy_set_rate(tcphy, &opts->dp);
+		if (ret) {
+			dev_err(&phy->dev, "rockchip_dp_phy_set_rate failed\n");
+			return ret;
+		}
+	}
+
+	if (opts->dp.set_voltages) {
+		ret = rockchip_dp_phy_set_voltages(tcphy, &opts->dp);
+		if (ret) {
+			dev_err(&phy->dev, "rockchip_dp_phy_set_voltages failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static const struct phy_ops rockchip_dp_phy_ops = {
 	.power_on	= rockchip_dp_phy_power_on,
 	.power_off	= rockchip_dp_phy_power_off,
+	.configure	= rockchip_dp_phy_configure,
 	.owner		= THIS_MODULE,
 };
+
+static int tcphy_typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+{
+	struct rockchip_typec_phy *tcphy = typec_mux_get_drvdata(mux);
+	struct typec_displayport_data *data;
+	int hpd = 0;
+
+	mutex_lock(&tcphy->lock);
+
+	switch (state->mode) {
+	case TYPEC_STATE_SAFE:
+		fallthrough;
+	case TYPEC_STATE_USB:
+		tcphy->new_mode = MODE_DFP_USB;
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], 0);
+		break;
+	case TYPEC_DP_STATE_C:
+	case TYPEC_DP_STATE_E:
+		tcphy->new_mode = MODE_DFP_DP;
+		data = state->data;
+		hpd = !!(data->status & DP_STATUS_HPD_STATE);
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], hpd ? 4 : 0);
+		break;
+	case TYPEC_DP_STATE_D:
+		tcphy->new_mode = MODE_DFP_DP | MODE_DFP_USB;
+		data = state->data;
+		hpd = !!(data->status & DP_STATUS_HPD_STATE);
+		phy_set_bus_width(tcphy->phys[TYPEC_PHY_DP], hpd ? 2 : 0);
+		break;
+	default:
+		break;
+	}
+
+	mutex_unlock(&tcphy->lock);
+
+	return 0;
+}
+
+static int tcphy_setup_typec_mux(struct rockchip_typec_phy *tcphy)
+{
+	struct typec_mux_desc mux_desc = {};
+
+	mux_desc.drvdata = tcphy;
+	mux_desc.fwnode = dev_fwnode(tcphy->dev);
+	mux_desc.set = tcphy_typec_mux_set;
+
+	tcphy->mux = typec_mux_register(tcphy->dev, &mux_desc);
+	if (IS_ERR(tcphy->mux)) {
+		dev_err(tcphy->dev, "Error register typec mux: %ld\n",
+			PTR_ERR(tcphy->mux));
+		return PTR_ERR(tcphy->mux);
+	}
+
+	return 0;
+}
+
+static void tcphy_typec_mux_unregister(void *data)
+{
+	struct rockchip_typec_phy *tcphy = data;
+
+	typec_mux_unregister(tcphy->mux);
+}
 
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
@@ -1683,8 +1829,8 @@
 	}
 
 	if (!tcphy->port_cfgs) {
-		dev_err(dev, "no phy-config can be matched with %s node\n",
-			np->name);
+		dev_err(dev, "no phy-config can be matched with %pOFn node\n",
+			np);
 		return -EINVAL;
 	}
 
@@ -1693,20 +1839,30 @@
 		return ret;
 
 	tcphy->dev = dev;
+	tcphy->new_mode = MODE_DFP_USB;
 	platform_set_drvdata(pdev, tcphy);
 	mutex_init(&tcphy->lock);
 
 	typec_phy_pre_init(tcphy);
 
-	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
-	if (IS_ERR(tcphy->extcon)) {
-		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
-			tcphy->extcon = NULL;
-		} else {
-			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
-				dev_err(dev, "Invalid or missing extcon\n");
-			return PTR_ERR(tcphy->extcon);
-		}
+	if (device_property_present(dev, "orientation-switch")) {
+		ret = tcphy_setup_orien_switch(tcphy);
+		if (ret)
+			return ret;
+		ret = devm_add_action_or_reset(dev, udphy_orien_switch_unregister,
+					       tcphy);
+		if (ret)
+			return ret;
+	}
+
+	if (device_property_present(dev, "svid")) {
+		ret = tcphy_setup_typec_mux(tcphy);
+		if (ret)
+			return ret;
+
+		ret = devm_add_action_or_reset(dev, tcphy_typec_mux_unregister, tcphy);
+		if (ret)
+			return ret;
 	}
 
 	pm_runtime_enable(dev);
@@ -1714,20 +1870,31 @@
 	for_each_available_child_of_node(np, child_np) {
 		struct phy *phy;
 
-		if (!of_node_cmp(child_np->name, "dp-port"))
+		if (!of_node_cmp(child_np->name, "dp-port")) {
 			phy = devm_phy_create(dev, child_np,
 					      &rockchip_dp_phy_ops);
-		else if (!of_node_cmp(child_np->name, "usb3-port"))
+			if (IS_ERR(phy)) {
+				dev_err(dev, "failed to create phy: %s\n",
+					child_np->name);
+				of_node_put(child_np);
+				ret = PTR_ERR(phy);
+				goto error;
+			}
+			tcphy->phys[TYPEC_PHY_DP] = phy;
+		} else if (!of_node_cmp(child_np->name, "usb3-port")) {
 			phy = devm_phy_create(dev, child_np,
 					      &rockchip_usb3_phy_ops);
-		else
+			if (IS_ERR(phy)) {
+				dev_err(dev, "failed to create phy: %s\n",
+					child_np->name);
+				of_node_put(child_np);
+				ret = PTR_ERR(phy);
+				goto error;
+			}
+			tcphy->phys[TYPEC_PHY_USB] = phy;
+		} else {
 			continue;
 
-		if (IS_ERR(phy)) {
-			dev_err(dev, "failed to create phy: %s\n",
-				child_np->name);
-			pm_runtime_disable(dev);
-			return PTR_ERR(phy);
 		}
 
 		phy_set_drvdata(phy, tcphy);
@@ -1736,11 +1903,15 @@
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 	if (IS_ERR(phy_provider)) {
 		dev_err(dev, "Failed to register phy provider\n");
-		pm_runtime_disable(dev);
-		return PTR_ERR(phy_provider);
+		ret = PTR_ERR(phy_provider);
+		goto error;
 	}
 
 	return 0;
+
+error:
+	pm_runtime_disable(dev);
+	return ret;
 }
 
 static int rockchip_typec_phy_remove(struct platform_device *pdev)

--
Gitblit v1.6.2