162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2017, 2020, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2021, Linaro Ltd.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/iopoll.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/phy/phy.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1962306a36Sopenharmony_ci#include <linux/reset.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <dt-bindings/phy/phy.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "phy-qcom-qmp.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* EDP_PHY registers */
2762306a36Sopenharmony_ci#define DP_PHY_CFG                              0x0010
2862306a36Sopenharmony_ci#define DP_PHY_CFG_1                            0x0014
2962306a36Sopenharmony_ci#define DP_PHY_PD_CTL                           0x001c
3062306a36Sopenharmony_ci#define DP_PHY_MODE                             0x0020
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define DP_PHY_AUX_CFG0				0x0024
3362306a36Sopenharmony_ci#define DP_PHY_AUX_CFG1				0x0028
3462306a36Sopenharmony_ci#define DP_PHY_AUX_CFG2				0x002C
3562306a36Sopenharmony_ci#define DP_PHY_AUX_CFG3				0x0030
3662306a36Sopenharmony_ci#define DP_PHY_AUX_CFG4				0x0034
3762306a36Sopenharmony_ci#define DP_PHY_AUX_CFG5				0x0038
3862306a36Sopenharmony_ci#define DP_PHY_AUX_CFG6				0x003C
3962306a36Sopenharmony_ci#define DP_PHY_AUX_CFG7				0x0040
4062306a36Sopenharmony_ci#define DP_PHY_AUX_CFG8				0x0044
4162306a36Sopenharmony_ci#define DP_PHY_AUX_CFG9				0x0048
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define DP_PHY_AUX_INTERRUPT_MASK		0x0058
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define DP_PHY_VCO_DIV                          0x0074
4662306a36Sopenharmony_ci#define DP_PHY_TX0_TX1_LANE_CTL                 0x007c
4762306a36Sopenharmony_ci#define DP_PHY_TX2_TX3_LANE_CTL                 0x00a0
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define DP_PHY_STATUS                           0x00e0
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* LANE_TXn registers */
5262306a36Sopenharmony_ci#define TXn_CLKBUF_ENABLE                       0x0000
5362306a36Sopenharmony_ci#define TXn_TX_EMP_POST1_LVL                    0x0004
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define TXn_TX_DRV_LVL                          0x0014
5662306a36Sopenharmony_ci#define TXn_TX_DRV_LVL_OFFSET                   0x0018
5762306a36Sopenharmony_ci#define TXn_RESET_TSYNC_EN                      0x001c
5862306a36Sopenharmony_ci#define TXn_LDO_CONFIG                          0x0084
5962306a36Sopenharmony_ci#define TXn_TX_BAND                             0x0028
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define TXn_RES_CODE_LANE_OFFSET_TX0            0x0044
6262306a36Sopenharmony_ci#define TXn_RES_CODE_LANE_OFFSET_TX1            0x0048
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define TXn_TRANSCEIVER_BIAS_EN                 0x0054
6562306a36Sopenharmony_ci#define TXn_HIGHZ_DRVR_EN                       0x0058
6662306a36Sopenharmony_ci#define TXn_TX_POL_INV                          0x005c
6762306a36Sopenharmony_ci#define TXn_LANE_MODE_1                         0x0064
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define TXn_TRAN_DRVR_EMP_EN                    0x0078
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistruct qcom_edp_cfg {
7262306a36Sopenharmony_ci	bool is_dp;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* DP PHY swing and pre_emphasis tables */
7562306a36Sopenharmony_ci	const u8 (*swing_hbr_rbr)[4][4];
7662306a36Sopenharmony_ci	const u8 (*swing_hbr3_hbr2)[4][4];
7762306a36Sopenharmony_ci	const u8 (*pre_emphasis_hbr_rbr)[4][4];
7862306a36Sopenharmony_ci	const u8 (*pre_emphasis_hbr3_hbr2)[4][4];
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct qcom_edp {
8262306a36Sopenharmony_ci	struct device *dev;
8362306a36Sopenharmony_ci	const struct qcom_edp_cfg *cfg;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	struct phy *phy;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	void __iomem *edp;
8862306a36Sopenharmony_ci	void __iomem *tx0;
8962306a36Sopenharmony_ci	void __iomem *tx1;
9062306a36Sopenharmony_ci	void __iomem *pll;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	struct clk_hw dp_link_hw;
9362306a36Sopenharmony_ci	struct clk_hw dp_pixel_hw;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	struct phy_configure_opts_dp dp_opts;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	struct clk_bulk_data clks[2];
9862306a36Sopenharmony_ci	struct regulator_bulk_data supplies[2];
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const u8 dp_swing_hbr_rbr[4][4] = {
10262306a36Sopenharmony_ci	{ 0x08, 0x0f, 0x16, 0x1f },
10362306a36Sopenharmony_ci	{ 0x11, 0x1e, 0x1f, 0xff },
10462306a36Sopenharmony_ci	{ 0x16, 0x1f, 0xff, 0xff },
10562306a36Sopenharmony_ci	{ 0x1f, 0xff, 0xff, 0xff }
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic const u8 dp_pre_emp_hbr_rbr[4][4] = {
10962306a36Sopenharmony_ci	{ 0x00, 0x0d, 0x14, 0x1a },
11062306a36Sopenharmony_ci	{ 0x00, 0x0e, 0x15, 0xff },
11162306a36Sopenharmony_ci	{ 0x00, 0x0e, 0xff, 0xff },
11262306a36Sopenharmony_ci	{ 0x03, 0xff, 0xff, 0xff }
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic const u8 dp_swing_hbr2_hbr3[4][4] = {
11662306a36Sopenharmony_ci	{ 0x02, 0x12, 0x16, 0x1a },
11762306a36Sopenharmony_ci	{ 0x09, 0x19, 0x1f, 0xff },
11862306a36Sopenharmony_ci	{ 0x10, 0x1f, 0xff, 0xff },
11962306a36Sopenharmony_ci	{ 0x1f, 0xff, 0xff, 0xff }
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic const u8 dp_pre_emp_hbr2_hbr3[4][4] = {
12362306a36Sopenharmony_ci	{ 0x00, 0x0c, 0x15, 0x1b },
12462306a36Sopenharmony_ci	{ 0x02, 0x0e, 0x16, 0xff },
12562306a36Sopenharmony_ci	{ 0x02, 0x11, 0xff, 0xff },
12662306a36Sopenharmony_ci	{ 0x04, 0xff, 0xff, 0xff }
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic const struct qcom_edp_cfg dp_phy_cfg = {
13062306a36Sopenharmony_ci	.is_dp = true,
13162306a36Sopenharmony_ci	.swing_hbr_rbr = &dp_swing_hbr_rbr,
13262306a36Sopenharmony_ci	.swing_hbr3_hbr2 = &dp_swing_hbr2_hbr3,
13362306a36Sopenharmony_ci	.pre_emphasis_hbr_rbr = &dp_pre_emp_hbr_rbr,
13462306a36Sopenharmony_ci	.pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic const u8 edp_swing_hbr_rbr[4][4] = {
13862306a36Sopenharmony_ci	{ 0x07, 0x0f, 0x16, 0x1f },
13962306a36Sopenharmony_ci	{ 0x0d, 0x16, 0x1e, 0xff },
14062306a36Sopenharmony_ci	{ 0x11, 0x1b, 0xff, 0xff },
14162306a36Sopenharmony_ci	{ 0x16, 0xff, 0xff, 0xff }
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic const u8 edp_pre_emp_hbr_rbr[4][4] = {
14562306a36Sopenharmony_ci	{ 0x05, 0x12, 0x17, 0x1d },
14662306a36Sopenharmony_ci	{ 0x05, 0x11, 0x18, 0xff },
14762306a36Sopenharmony_ci	{ 0x06, 0x11, 0xff, 0xff },
14862306a36Sopenharmony_ci	{ 0x00, 0xff, 0xff, 0xff }
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic const u8 edp_swing_hbr2_hbr3[4][4] = {
15262306a36Sopenharmony_ci	{ 0x0b, 0x11, 0x17, 0x1c },
15362306a36Sopenharmony_ci	{ 0x10, 0x19, 0x1f, 0xff },
15462306a36Sopenharmony_ci	{ 0x19, 0x1f, 0xff, 0xff },
15562306a36Sopenharmony_ci	{ 0x1f, 0xff, 0xff, 0xff }
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic const u8 edp_pre_emp_hbr2_hbr3[4][4] = {
15962306a36Sopenharmony_ci	{ 0x08, 0x11, 0x17, 0x1b },
16062306a36Sopenharmony_ci	{ 0x00, 0x0c, 0x13, 0xff },
16162306a36Sopenharmony_ci	{ 0x05, 0x10, 0xff, 0xff },
16262306a36Sopenharmony_ci	{ 0x00, 0xff, 0xff, 0xff }
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic const struct qcom_edp_cfg edp_phy_cfg = {
16662306a36Sopenharmony_ci	.is_dp = false,
16762306a36Sopenharmony_ci	.swing_hbr_rbr = &edp_swing_hbr_rbr,
16862306a36Sopenharmony_ci	.swing_hbr3_hbr2 = &edp_swing_hbr2_hbr3,
16962306a36Sopenharmony_ci	.pre_emphasis_hbr_rbr = &edp_pre_emp_hbr_rbr,
17062306a36Sopenharmony_ci	.pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3,
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int qcom_edp_phy_init(struct phy *phy)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct qcom_edp *edp = phy_get_drvdata(phy);
17662306a36Sopenharmony_ci	const struct qcom_edp_cfg *cfg = edp->cfg;
17762306a36Sopenharmony_ci	int ret;
17862306a36Sopenharmony_ci	u8 cfg8;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(edp->supplies), edp->supplies);
18162306a36Sopenharmony_ci	if (ret)
18262306a36Sopenharmony_ci		return ret;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(ARRAY_SIZE(edp->clks), edp->clks);
18562306a36Sopenharmony_ci	if (ret)
18662306a36Sopenharmony_ci		goto out_disable_supplies;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
18962306a36Sopenharmony_ci	       DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
19062306a36Sopenharmony_ci	       edp->edp + DP_PHY_PD_CTL);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Turn on BIAS current for PHY/PLL */
19362306a36Sopenharmony_ci	writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
19662306a36Sopenharmony_ci	msleep(20);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
19962306a36Sopenharmony_ci	       DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
20062306a36Sopenharmony_ci	       DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
20162306a36Sopenharmony_ci	       edp->edp + DP_PHY_PD_CTL);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (cfg && cfg->is_dp)
20462306a36Sopenharmony_ci		cfg8 = 0xb7;
20562306a36Sopenharmony_ci	else
20662306a36Sopenharmony_ci		cfg8 = 0x37;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	writel(0xfc, edp->edp + DP_PHY_MODE);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	writel(0x00, edp->edp + DP_PHY_AUX_CFG0);
21162306a36Sopenharmony_ci	writel(0x13, edp->edp + DP_PHY_AUX_CFG1);
21262306a36Sopenharmony_ci	writel(0x24, edp->edp + DP_PHY_AUX_CFG2);
21362306a36Sopenharmony_ci	writel(0x00, edp->edp + DP_PHY_AUX_CFG3);
21462306a36Sopenharmony_ci	writel(0x0a, edp->edp + DP_PHY_AUX_CFG4);
21562306a36Sopenharmony_ci	writel(0x26, edp->edp + DP_PHY_AUX_CFG5);
21662306a36Sopenharmony_ci	writel(0x0a, edp->edp + DP_PHY_AUX_CFG6);
21762306a36Sopenharmony_ci	writel(0x03, edp->edp + DP_PHY_AUX_CFG7);
21862306a36Sopenharmony_ci	writel(cfg8, edp->edp + DP_PHY_AUX_CFG8);
21962306a36Sopenharmony_ci	writel(0x03, edp->edp + DP_PHY_AUX_CFG9);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK |
22262306a36Sopenharmony_ci	       PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK |
22362306a36Sopenharmony_ci	       PHY_AUX_REQ_ERR_MASK, edp->edp + DP_PHY_AUX_INTERRUPT_MASK);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	msleep(20);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ciout_disable_supplies:
23062306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return ret;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int qcom_edp_set_voltages(struct qcom_edp *edp, const struct phy_configure_opts_dp *dp_opts)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	const struct qcom_edp_cfg *cfg = edp->cfg;
23862306a36Sopenharmony_ci	unsigned int v_level = 0;
23962306a36Sopenharmony_ci	unsigned int p_level = 0;
24062306a36Sopenharmony_ci	u8 ldo_config;
24162306a36Sopenharmony_ci	u8 swing;
24262306a36Sopenharmony_ci	u8 emph;
24362306a36Sopenharmony_ci	int i;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (!cfg)
24662306a36Sopenharmony_ci		return 0;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	for (i = 0; i < dp_opts->lanes; i++) {
24962306a36Sopenharmony_ci		v_level = max(v_level, dp_opts->voltage[i]);
25062306a36Sopenharmony_ci		p_level = max(p_level, dp_opts->pre[i]);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (dp_opts->link_rate <= 2700) {
25462306a36Sopenharmony_ci		swing = (*cfg->swing_hbr_rbr)[v_level][p_level];
25562306a36Sopenharmony_ci		emph = (*cfg->pre_emphasis_hbr_rbr)[v_level][p_level];
25662306a36Sopenharmony_ci	} else {
25762306a36Sopenharmony_ci		swing = (*cfg->swing_hbr3_hbr2)[v_level][p_level];
25862306a36Sopenharmony_ci		emph = (*cfg->pre_emphasis_hbr3_hbr2)[v_level][p_level];
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (swing == 0xff || emph == 0xff)
26262306a36Sopenharmony_ci		return -EINVAL;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ldo_config = (cfg && cfg->is_dp) ? 0x1 : 0x0;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
26762306a36Sopenharmony_ci	writel(swing, edp->tx0 + TXn_TX_DRV_LVL);
26862306a36Sopenharmony_ci	writel(emph, edp->tx0 + TXn_TX_EMP_POST1_LVL);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
27162306a36Sopenharmony_ci	writel(swing, edp->tx1 + TXn_TX_DRV_LVL);
27262306a36Sopenharmony_ci	writel(emph, edp->tx1 + TXn_TX_EMP_POST1_LVL);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int qcom_edp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	const struct phy_configure_opts_dp *dp_opts = &opts->dp;
28062306a36Sopenharmony_ci	struct qcom_edp *edp = phy_get_drvdata(phy);
28162306a36Sopenharmony_ci	int ret = 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	memcpy(&edp->dp_opts, dp_opts, sizeof(*dp_opts));
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (dp_opts->set_voltages)
28662306a36Sopenharmony_ci		ret = qcom_edp_set_voltages(edp, dp_opts);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return ret;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int qcom_edp_configure_ssc(const struct qcom_edp *edp)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
29462306a36Sopenharmony_ci	u32 step1;
29562306a36Sopenharmony_ci	u32 step2;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	switch (dp_opts->link_rate) {
29862306a36Sopenharmony_ci	case 1620:
29962306a36Sopenharmony_ci	case 2700:
30062306a36Sopenharmony_ci	case 8100:
30162306a36Sopenharmony_ci		step1 = 0x45;
30262306a36Sopenharmony_ci		step2 = 0x06;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	case 5400:
30662306a36Sopenharmony_ci		step1 = 0x5c;
30762306a36Sopenharmony_ci		step2 = 0x08;
30862306a36Sopenharmony_ci		break;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	default:
31162306a36Sopenharmony_ci		/* Other link rates aren't supported */
31262306a36Sopenharmony_ci		return -EINVAL;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	writel(0x01, edp->pll + QSERDES_V4_COM_SSC_EN_CENTER);
31662306a36Sopenharmony_ci	writel(0x00, edp->pll + QSERDES_V4_COM_SSC_ADJ_PER1);
31762306a36Sopenharmony_ci	writel(0x36, edp->pll + QSERDES_V4_COM_SSC_PER1);
31862306a36Sopenharmony_ci	writel(0x01, edp->pll + QSERDES_V4_COM_SSC_PER2);
31962306a36Sopenharmony_ci	writel(step1, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0);
32062306a36Sopenharmony_ci	writel(step2, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int qcom_edp_configure_pll(const struct qcom_edp *edp)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
32862306a36Sopenharmony_ci	u32 div_frac_start2_mode0;
32962306a36Sopenharmony_ci	u32 div_frac_start3_mode0;
33062306a36Sopenharmony_ci	u32 dec_start_mode0;
33162306a36Sopenharmony_ci	u32 lock_cmp1_mode0;
33262306a36Sopenharmony_ci	u32 lock_cmp2_mode0;
33362306a36Sopenharmony_ci	u32 hsclk_sel;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	switch (dp_opts->link_rate) {
33662306a36Sopenharmony_ci	case 1620:
33762306a36Sopenharmony_ci		hsclk_sel = 0x5;
33862306a36Sopenharmony_ci		dec_start_mode0 = 0x69;
33962306a36Sopenharmony_ci		div_frac_start2_mode0 = 0x80;
34062306a36Sopenharmony_ci		div_frac_start3_mode0 = 0x07;
34162306a36Sopenharmony_ci		lock_cmp1_mode0 = 0x6f;
34262306a36Sopenharmony_ci		lock_cmp2_mode0 = 0x08;
34362306a36Sopenharmony_ci		break;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	case 2700:
34662306a36Sopenharmony_ci		hsclk_sel = 0x3;
34762306a36Sopenharmony_ci		dec_start_mode0 = 0x69;
34862306a36Sopenharmony_ci		div_frac_start2_mode0 = 0x80;
34962306a36Sopenharmony_ci		div_frac_start3_mode0 = 0x07;
35062306a36Sopenharmony_ci		lock_cmp1_mode0 = 0x0f;
35162306a36Sopenharmony_ci		lock_cmp2_mode0 = 0x0e;
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	case 5400:
35562306a36Sopenharmony_ci		hsclk_sel = 0x1;
35662306a36Sopenharmony_ci		dec_start_mode0 = 0x8c;
35762306a36Sopenharmony_ci		div_frac_start2_mode0 = 0x00;
35862306a36Sopenharmony_ci		div_frac_start3_mode0 = 0x0a;
35962306a36Sopenharmony_ci		lock_cmp1_mode0 = 0x1f;
36062306a36Sopenharmony_ci		lock_cmp2_mode0 = 0x1c;
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	case 8100:
36462306a36Sopenharmony_ci		hsclk_sel = 0x0;
36562306a36Sopenharmony_ci		dec_start_mode0 = 0x69;
36662306a36Sopenharmony_ci		div_frac_start2_mode0 = 0x80;
36762306a36Sopenharmony_ci		div_frac_start3_mode0 = 0x07;
36862306a36Sopenharmony_ci		lock_cmp1_mode0 = 0x2f;
36962306a36Sopenharmony_ci		lock_cmp2_mode0 = 0x2a;
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	default:
37362306a36Sopenharmony_ci		/* Other link rates aren't supported */
37462306a36Sopenharmony_ci		return -EINVAL;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	writel(0x01, edp->pll + QSERDES_V4_COM_SVS_MODE_CLK_SEL);
37862306a36Sopenharmony_ci	writel(0x0b, edp->pll + QSERDES_V4_COM_SYSCLK_EN_SEL);
37962306a36Sopenharmony_ci	writel(0x02, edp->pll + QSERDES_V4_COM_SYS_CLK_CTRL);
38062306a36Sopenharmony_ci	writel(0x0c, edp->pll + QSERDES_V4_COM_CLK_ENABLE1);
38162306a36Sopenharmony_ci	writel(0x06, edp->pll + QSERDES_V4_COM_SYSCLK_BUF_ENABLE);
38262306a36Sopenharmony_ci	writel(0x30, edp->pll + QSERDES_V4_COM_CLK_SELECT);
38362306a36Sopenharmony_ci	writel(hsclk_sel, edp->pll + QSERDES_V4_COM_HSCLK_SEL);
38462306a36Sopenharmony_ci	writel(0x0f, edp->pll + QSERDES_V4_COM_PLL_IVCO);
38562306a36Sopenharmony_ci	writel(0x08, edp->pll + QSERDES_V4_COM_LOCK_CMP_EN);
38662306a36Sopenharmony_ci	writel(0x36, edp->pll + QSERDES_V4_COM_PLL_CCTRL_MODE0);
38762306a36Sopenharmony_ci	writel(0x16, edp->pll + QSERDES_V4_COM_PLL_RCTRL_MODE0);
38862306a36Sopenharmony_ci	writel(0x06, edp->pll + QSERDES_V4_COM_CP_CTRL_MODE0);
38962306a36Sopenharmony_ci	writel(dec_start_mode0, edp->pll + QSERDES_V4_COM_DEC_START_MODE0);
39062306a36Sopenharmony_ci	writel(0x00, edp->pll + QSERDES_V4_COM_DIV_FRAC_START1_MODE0);
39162306a36Sopenharmony_ci	writel(div_frac_start2_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START2_MODE0);
39262306a36Sopenharmony_ci	writel(div_frac_start3_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START3_MODE0);
39362306a36Sopenharmony_ci	writel(0x02, edp->pll + QSERDES_V4_COM_CMN_CONFIG);
39462306a36Sopenharmony_ci	writel(0x3f, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0);
39562306a36Sopenharmony_ci	writel(0x00, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0);
39662306a36Sopenharmony_ci	writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_MAP);
39762306a36Sopenharmony_ci	writel(lock_cmp1_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP1_MODE0);
39862306a36Sopenharmony_ci	writel(lock_cmp2_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP2_MODE0);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	writel(0x0a, edp->pll + QSERDES_V4_COM_BG_TIMER);
40162306a36Sopenharmony_ci	writel(0x14, edp->pll + QSERDES_V4_COM_CORECLK_DIV_MODE0);
40262306a36Sopenharmony_ci	writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_CTRL);
40362306a36Sopenharmony_ci	writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
40462306a36Sopenharmony_ci	writel(0x0f, edp->pll + QSERDES_V4_COM_CORE_CLK_EN);
40562306a36Sopenharmony_ci	writel(0xa0, edp->pll + QSERDES_V4_COM_VCO_TUNE1_MODE0);
40662306a36Sopenharmony_ci	writel(0x03, edp->pll + QSERDES_V4_COM_VCO_TUNE2_MODE0);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel_freq)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
41462306a36Sopenharmony_ci	u32 vco_div;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	switch (dp_opts->link_rate) {
41762306a36Sopenharmony_ci	case 1620:
41862306a36Sopenharmony_ci		vco_div = 0x1;
41962306a36Sopenharmony_ci		*pixel_freq = 1620000000UL / 2;
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	case 2700:
42362306a36Sopenharmony_ci		vco_div = 0x1;
42462306a36Sopenharmony_ci		*pixel_freq = 2700000000UL / 2;
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	case 5400:
42862306a36Sopenharmony_ci		vco_div = 0x2;
42962306a36Sopenharmony_ci		*pixel_freq = 5400000000UL / 4;
43062306a36Sopenharmony_ci		break;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	case 8100:
43362306a36Sopenharmony_ci		vco_div = 0x0;
43462306a36Sopenharmony_ci		*pixel_freq = 8100000000UL / 6;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	default:
43862306a36Sopenharmony_ci		/* Other link rates aren't supported */
43962306a36Sopenharmony_ci		return -EINVAL;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	writel(vco_div, edp->edp + DP_PHY_VCO_DIV);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return 0;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int qcom_edp_phy_power_on(struct phy *phy)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	const struct qcom_edp *edp = phy_get_drvdata(phy);
45062306a36Sopenharmony_ci	const struct qcom_edp_cfg *cfg = edp->cfg;
45162306a36Sopenharmony_ci	u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
45262306a36Sopenharmony_ci	unsigned long pixel_freq;
45362306a36Sopenharmony_ci	u8 ldo_config;
45462306a36Sopenharmony_ci	int timeout;
45562306a36Sopenharmony_ci	int ret;
45662306a36Sopenharmony_ci	u32 val;
45762306a36Sopenharmony_ci	u8 cfg1;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
46062306a36Sopenharmony_ci	       DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
46162306a36Sopenharmony_ci	       DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
46262306a36Sopenharmony_ci	       edp->edp + DP_PHY_PD_CTL);
46362306a36Sopenharmony_ci	writel(0xfc, edp->edp + DP_PHY_MODE);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS,
46662306a36Sopenharmony_ci				     val, val & BIT(7), 5, 200);
46762306a36Sopenharmony_ci	if (timeout)
46862306a36Sopenharmony_ci		return timeout;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	ldo_config = (cfg && cfg->is_dp) ? 0x1 : 0x0;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
47462306a36Sopenharmony_ci	writel(ldo_config, edp->tx1 + TXn_LDO_CONFIG);
47562306a36Sopenharmony_ci	writel(0x00, edp->tx0 + TXn_LANE_MODE_1);
47662306a36Sopenharmony_ci	writel(0x00, edp->tx1 + TXn_LANE_MODE_1);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (edp->dp_opts.ssc) {
47962306a36Sopenharmony_ci		ret = qcom_edp_configure_ssc(edp);
48062306a36Sopenharmony_ci		if (ret)
48162306a36Sopenharmony_ci			return ret;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	ret = qcom_edp_configure_pll(edp);
48562306a36Sopenharmony_ci	if (ret)
48662306a36Sopenharmony_ci		return ret;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* TX Lane configuration */
48962306a36Sopenharmony_ci	writel(0x05, edp->edp + DP_PHY_TX0_TX1_LANE_CTL);
49062306a36Sopenharmony_ci	writel(0x05, edp->edp + DP_PHY_TX2_TX3_LANE_CTL);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* TX-0 register configuration */
49362306a36Sopenharmony_ci	writel(0x03, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN);
49462306a36Sopenharmony_ci	writel(0x0f, edp->tx0 + TXn_CLKBUF_ENABLE);
49562306a36Sopenharmony_ci	writel(0x03, edp->tx0 + TXn_RESET_TSYNC_EN);
49662306a36Sopenharmony_ci	writel(0x01, edp->tx0 + TXn_TRAN_DRVR_EMP_EN);
49762306a36Sopenharmony_ci	writel(0x04, edp->tx0 + TXn_TX_BAND);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* TX-1 register configuration */
50062306a36Sopenharmony_ci	writel(0x03, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN);
50162306a36Sopenharmony_ci	writel(0x0f, edp->tx1 + TXn_CLKBUF_ENABLE);
50262306a36Sopenharmony_ci	writel(0x03, edp->tx1 + TXn_RESET_TSYNC_EN);
50362306a36Sopenharmony_ci	writel(0x01, edp->tx1 + TXn_TRAN_DRVR_EMP_EN);
50462306a36Sopenharmony_ci	writel(0x04, edp->tx1 + TXn_TX_BAND);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ret = qcom_edp_set_vco_div(edp, &pixel_freq);
50762306a36Sopenharmony_ci	if (ret)
50862306a36Sopenharmony_ci		return ret;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	writel(0x01, edp->edp + DP_PHY_CFG);
51162306a36Sopenharmony_ci	writel(0x05, edp->edp + DP_PHY_CFG);
51262306a36Sopenharmony_ci	writel(0x01, edp->edp + DP_PHY_CFG);
51362306a36Sopenharmony_ci	writel(0x09, edp->edp + DP_PHY_CFG);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS,
51862306a36Sopenharmony_ci				     val, val & BIT(0), 500, 10000);
51962306a36Sopenharmony_ci	if (timeout)
52062306a36Sopenharmony_ci		return timeout;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	writel(0x19, edp->edp + DP_PHY_CFG);
52362306a36Sopenharmony_ci	writel(0x1f, edp->tx0 + TXn_HIGHZ_DRVR_EN);
52462306a36Sopenharmony_ci	writel(0x04, edp->tx0 + TXn_HIGHZ_DRVR_EN);
52562306a36Sopenharmony_ci	writel(0x00, edp->tx0 + TXn_TX_POL_INV);
52662306a36Sopenharmony_ci	writel(0x1f, edp->tx1 + TXn_HIGHZ_DRVR_EN);
52762306a36Sopenharmony_ci	writel(0x04, edp->tx1 + TXn_HIGHZ_DRVR_EN);
52862306a36Sopenharmony_ci	writel(0x00, edp->tx1 + TXn_TX_POL_INV);
52962306a36Sopenharmony_ci	writel(0x10, edp->tx0 + TXn_TX_DRV_LVL_OFFSET);
53062306a36Sopenharmony_ci	writel(0x10, edp->tx1 + TXn_TX_DRV_LVL_OFFSET);
53162306a36Sopenharmony_ci	writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX0);
53262306a36Sopenharmony_ci	writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX1);
53362306a36Sopenharmony_ci	writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX0);
53462306a36Sopenharmony_ci	writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX1);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	writel(0x10, edp->tx0 + TXn_TX_EMP_POST1_LVL);
53762306a36Sopenharmony_ci	writel(0x10, edp->tx1 + TXn_TX_EMP_POST1_LVL);
53862306a36Sopenharmony_ci	writel(0x1f, edp->tx0 + TXn_TX_DRV_LVL);
53962306a36Sopenharmony_ci	writel(0x1f, edp->tx1 + TXn_TX_DRV_LVL);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (edp->dp_opts.lanes == 1) {
54262306a36Sopenharmony_ci		bias0_en = 0x01;
54362306a36Sopenharmony_ci		bias1_en = 0x00;
54462306a36Sopenharmony_ci		drvr0_en = 0x06;
54562306a36Sopenharmony_ci		drvr1_en = 0x07;
54662306a36Sopenharmony_ci		cfg1 = 0x1;
54762306a36Sopenharmony_ci	} else if (edp->dp_opts.lanes == 2) {
54862306a36Sopenharmony_ci		bias0_en = 0x03;
54962306a36Sopenharmony_ci		bias1_en = 0x00;
55062306a36Sopenharmony_ci		drvr0_en = 0x04;
55162306a36Sopenharmony_ci		drvr1_en = 0x07;
55262306a36Sopenharmony_ci		cfg1 = 0x3;
55362306a36Sopenharmony_ci	} else {
55462306a36Sopenharmony_ci		bias0_en = 0x03;
55562306a36Sopenharmony_ci		bias1_en = 0x03;
55662306a36Sopenharmony_ci		drvr0_en = 0x04;
55762306a36Sopenharmony_ci		drvr1_en = 0x04;
55862306a36Sopenharmony_ci		cfg1 = 0xf;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	writel(drvr0_en, edp->tx0 + TXn_HIGHZ_DRVR_EN);
56262306a36Sopenharmony_ci	writel(bias0_en, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN);
56362306a36Sopenharmony_ci	writel(drvr1_en, edp->tx1 + TXn_HIGHZ_DRVR_EN);
56462306a36Sopenharmony_ci	writel(bias1_en, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN);
56562306a36Sopenharmony_ci	writel(cfg1, edp->edp + DP_PHY_CFG_1);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	writel(0x18, edp->edp + DP_PHY_CFG);
56862306a36Sopenharmony_ci	usleep_range(100, 1000);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	writel(0x19, edp->edp + DP_PHY_CFG);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ret = readl_poll_timeout(edp->edp + DP_PHY_STATUS,
57362306a36Sopenharmony_ci				 val, val & BIT(1), 500, 10000);
57462306a36Sopenharmony_ci	if (ret)
57562306a36Sopenharmony_ci		return ret;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	clk_set_rate(edp->dp_link_hw.clk, edp->dp_opts.link_rate * 100000);
57862306a36Sopenharmony_ci	clk_set_rate(edp->dp_pixel_hw.clk, pixel_freq);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return 0;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int qcom_edp_phy_power_off(struct phy *phy)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	const struct qcom_edp *edp = phy_get_drvdata(phy);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic int qcom_edp_phy_exit(struct phy *phy)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct qcom_edp *edp = phy_get_drvdata(phy);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	clk_bulk_disable_unprepare(ARRAY_SIZE(edp->clks), edp->clks);
59762306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	return 0;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic const struct phy_ops qcom_edp_ops = {
60362306a36Sopenharmony_ci	.init		= qcom_edp_phy_init,
60462306a36Sopenharmony_ci	.configure	= qcom_edp_phy_configure,
60562306a36Sopenharmony_ci	.power_on	= qcom_edp_phy_power_on,
60662306a36Sopenharmony_ci	.power_off	= qcom_edp_phy_power_off,
60762306a36Sopenharmony_ci	.exit		= qcom_edp_phy_exit,
60862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
60962306a36Sopenharmony_ci};
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/*
61262306a36Sopenharmony_ci * Embedded Display Port PLL driver block diagram for branch clocks
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci *              +------------------------------+
61562306a36Sopenharmony_ci *              |        EDP_VCO_CLK           |
61662306a36Sopenharmony_ci *              |                              |
61762306a36Sopenharmony_ci *              |    +-------------------+     |
61862306a36Sopenharmony_ci *              |    |  (EDP PLL/VCO)    |     |
61962306a36Sopenharmony_ci *              |    +---------+---------+     |
62062306a36Sopenharmony_ci *              |              v               |
62162306a36Sopenharmony_ci *              |   +----------+-----------+   |
62262306a36Sopenharmony_ci *              |   | hsclk_divsel_clk_src |   |
62362306a36Sopenharmony_ci *              |   +----------+-----------+   |
62462306a36Sopenharmony_ci *              +------------------------------+
62562306a36Sopenharmony_ci *                              |
62662306a36Sopenharmony_ci *          +---------<---------v------------>----------+
62762306a36Sopenharmony_ci *          |                                           |
62862306a36Sopenharmony_ci * +--------v----------------+                          |
62962306a36Sopenharmony_ci * |   edp_phy_pll_link_clk  |                          |
63062306a36Sopenharmony_ci * |     link_clk            |                          |
63162306a36Sopenharmony_ci * +--------+----------------+                          |
63262306a36Sopenharmony_ci *          |                                           |
63362306a36Sopenharmony_ci *          |                                           |
63462306a36Sopenharmony_ci *          v                                           v
63562306a36Sopenharmony_ci * Input to DISPCC block                                |
63662306a36Sopenharmony_ci * for link clk, crypto clk                             |
63762306a36Sopenharmony_ci * and interface clock                                  |
63862306a36Sopenharmony_ci *                                                      |
63962306a36Sopenharmony_ci *                                                      |
64062306a36Sopenharmony_ci *      +--------<------------+-----------------+---<---+
64162306a36Sopenharmony_ci *      |                     |                 |
64262306a36Sopenharmony_ci * +----v---------+  +--------v-----+  +--------v------+
64362306a36Sopenharmony_ci * | vco_divided  |  | vco_divided  |  | vco_divided   |
64462306a36Sopenharmony_ci * |    _clk_src  |  |    _clk_src  |  |    _clk_src   |
64562306a36Sopenharmony_ci * |              |  |              |  |               |
64662306a36Sopenharmony_ci * |divsel_six    |  |  divsel_two  |  |  divsel_four  |
64762306a36Sopenharmony_ci * +-------+------+  +-----+--------+  +--------+------+
64862306a36Sopenharmony_ci *         |                 |                  |
64962306a36Sopenharmony_ci *         v---->----------v-------------<------v
65062306a36Sopenharmony_ci *                         |
65162306a36Sopenharmony_ci *              +----------+-----------------+
65262306a36Sopenharmony_ci *              |   edp_phy_pll_vco_div_clk  |
65362306a36Sopenharmony_ci *              +---------+------------------+
65462306a36Sopenharmony_ci *                        |
65562306a36Sopenharmony_ci *                        v
65662306a36Sopenharmony_ci *              Input to DISPCC block
65762306a36Sopenharmony_ci *              for EDP pixel clock
65862306a36Sopenharmony_ci *
65962306a36Sopenharmony_ci */
66062306a36Sopenharmony_cistatic int qcom_edp_dp_pixel_clk_determine_rate(struct clk_hw *hw,
66162306a36Sopenharmony_ci						struct clk_rate_request *req)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	switch (req->rate) {
66462306a36Sopenharmony_ci	case 1620000000UL / 2:
66562306a36Sopenharmony_ci	case 2700000000UL / 2:
66662306a36Sopenharmony_ci	/* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */
66762306a36Sopenharmony_ci		return 0;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	default:
67062306a36Sopenharmony_ci		return -EINVAL;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic unsigned long
67562306a36Sopenharmony_ciqcom_edp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_pixel_hw);
67862306a36Sopenharmony_ci	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	switch (dp_opts->link_rate) {
68162306a36Sopenharmony_ci	case 1620:
68262306a36Sopenharmony_ci		return 1620000000UL / 2;
68362306a36Sopenharmony_ci	case 2700:
68462306a36Sopenharmony_ci		return 2700000000UL / 2;
68562306a36Sopenharmony_ci	case 5400:
68662306a36Sopenharmony_ci		return 5400000000UL / 4;
68762306a36Sopenharmony_ci	case 8100:
68862306a36Sopenharmony_ci		return 8100000000UL / 6;
68962306a36Sopenharmony_ci	default:
69062306a36Sopenharmony_ci		return 0;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic const struct clk_ops qcom_edp_dp_pixel_clk_ops = {
69562306a36Sopenharmony_ci	.determine_rate = qcom_edp_dp_pixel_clk_determine_rate,
69662306a36Sopenharmony_ci	.recalc_rate = qcom_edp_dp_pixel_clk_recalc_rate,
69762306a36Sopenharmony_ci};
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int qcom_edp_dp_link_clk_determine_rate(struct clk_hw *hw,
70062306a36Sopenharmony_ci					       struct clk_rate_request *req)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	switch (req->rate) {
70362306a36Sopenharmony_ci	case 162000000:
70462306a36Sopenharmony_ci	case 270000000:
70562306a36Sopenharmony_ci	case 540000000:
70662306a36Sopenharmony_ci	case 810000000:
70762306a36Sopenharmony_ci		return 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	default:
71062306a36Sopenharmony_ci		return -EINVAL;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic unsigned long
71562306a36Sopenharmony_ciqcom_edp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_link_hw);
71862306a36Sopenharmony_ci	const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	switch (dp_opts->link_rate) {
72162306a36Sopenharmony_ci	case 1620:
72262306a36Sopenharmony_ci	case 2700:
72362306a36Sopenharmony_ci	case 5400:
72462306a36Sopenharmony_ci	case 8100:
72562306a36Sopenharmony_ci		return dp_opts->link_rate * 100000;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	default:
72862306a36Sopenharmony_ci		return 0;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic const struct clk_ops qcom_edp_dp_link_clk_ops = {
73362306a36Sopenharmony_ci	.determine_rate = qcom_edp_dp_link_clk_determine_rate,
73462306a36Sopenharmony_ci	.recalc_rate = qcom_edp_dp_link_clk_recalc_rate,
73562306a36Sopenharmony_ci};
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic int qcom_edp_clks_register(struct qcom_edp *edp, struct device_node *np)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct clk_hw_onecell_data *data;
74062306a36Sopenharmony_ci	struct clk_init_data init = { };
74162306a36Sopenharmony_ci	char name[64];
74262306a36Sopenharmony_ci	int ret;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	data = devm_kzalloc(edp->dev, struct_size(data, hws, 2), GFP_KERNEL);
74562306a36Sopenharmony_ci	if (!data)
74662306a36Sopenharmony_ci		return -ENOMEM;
74762306a36Sopenharmony_ci	data->num = 2;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s::link_clk", dev_name(edp->dev));
75062306a36Sopenharmony_ci	init.ops = &qcom_edp_dp_link_clk_ops;
75162306a36Sopenharmony_ci	init.name = name;
75262306a36Sopenharmony_ci	edp->dp_link_hw.init = &init;
75362306a36Sopenharmony_ci	ret = devm_clk_hw_register(edp->dev, &edp->dp_link_hw);
75462306a36Sopenharmony_ci	if (ret)
75562306a36Sopenharmony_ci		return ret;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s::vco_div_clk", dev_name(edp->dev));
75862306a36Sopenharmony_ci	init.ops = &qcom_edp_dp_pixel_clk_ops;
75962306a36Sopenharmony_ci	init.name = name;
76062306a36Sopenharmony_ci	edp->dp_pixel_hw.init = &init;
76162306a36Sopenharmony_ci	ret = devm_clk_hw_register(edp->dev, &edp->dp_pixel_hw);
76262306a36Sopenharmony_ci	if (ret)
76362306a36Sopenharmony_ci		return ret;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	data->hws[0] = &edp->dp_link_hw;
76662306a36Sopenharmony_ci	data->hws[1] = &edp->dp_pixel_hw;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return devm_of_clk_add_hw_provider(edp->dev, of_clk_hw_onecell_get, data);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic int qcom_edp_phy_probe(struct platform_device *pdev)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct phy_provider *phy_provider;
77462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
77562306a36Sopenharmony_ci	struct qcom_edp *edp;
77662306a36Sopenharmony_ci	int ret;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL);
77962306a36Sopenharmony_ci	if (!edp)
78062306a36Sopenharmony_ci		return -ENOMEM;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	edp->dev = dev;
78362306a36Sopenharmony_ci	edp->cfg = of_device_get_match_data(&pdev->dev);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	edp->edp = devm_platform_ioremap_resource(pdev, 0);
78662306a36Sopenharmony_ci	if (IS_ERR(edp->edp))
78762306a36Sopenharmony_ci		return PTR_ERR(edp->edp);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	edp->tx0 = devm_platform_ioremap_resource(pdev, 1);
79062306a36Sopenharmony_ci	if (IS_ERR(edp->tx0))
79162306a36Sopenharmony_ci		return PTR_ERR(edp->tx0);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	edp->tx1 = devm_platform_ioremap_resource(pdev, 2);
79462306a36Sopenharmony_ci	if (IS_ERR(edp->tx1))
79562306a36Sopenharmony_ci		return PTR_ERR(edp->tx1);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	edp->pll = devm_platform_ioremap_resource(pdev, 3);
79862306a36Sopenharmony_ci	if (IS_ERR(edp->pll))
79962306a36Sopenharmony_ci		return PTR_ERR(edp->pll);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	edp->clks[0].id = "aux";
80262306a36Sopenharmony_ci	edp->clks[1].id = "cfg_ahb";
80362306a36Sopenharmony_ci	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(edp->clks), edp->clks);
80462306a36Sopenharmony_ci	if (ret)
80562306a36Sopenharmony_ci		return ret;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	edp->supplies[0].supply = "vdda-phy";
80862306a36Sopenharmony_ci	edp->supplies[1].supply = "vdda-pll";
80962306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(edp->supplies), edp->supplies);
81062306a36Sopenharmony_ci	if (ret)
81162306a36Sopenharmony_ci		return ret;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ret = regulator_set_load(edp->supplies[0].consumer, 21800); /* 1.2 V vdda-phy */
81462306a36Sopenharmony_ci	if (ret) {
81562306a36Sopenharmony_ci		dev_err(dev, "failed to set load at %s\n", edp->supplies[0].supply);
81662306a36Sopenharmony_ci		return ret;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	ret = regulator_set_load(edp->supplies[1].consumer, 36000); /* 0.9 V vdda-pll */
82062306a36Sopenharmony_ci	if (ret) {
82162306a36Sopenharmony_ci		dev_err(dev, "failed to set load at %s\n", edp->supplies[1].supply);
82262306a36Sopenharmony_ci		return ret;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	ret = qcom_edp_clks_register(edp, pdev->dev.of_node);
82662306a36Sopenharmony_ci	if (ret)
82762306a36Sopenharmony_ci		return ret;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	edp->phy = devm_phy_create(dev, pdev->dev.of_node, &qcom_edp_ops);
83062306a36Sopenharmony_ci	if (IS_ERR(edp->phy)) {
83162306a36Sopenharmony_ci		dev_err(dev, "failed to register phy\n");
83262306a36Sopenharmony_ci		return PTR_ERR(edp->phy);
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	phy_set_drvdata(edp->phy, edp);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
83862306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(phy_provider);
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic const struct of_device_id qcom_edp_phy_match_table[] = {
84262306a36Sopenharmony_ci	{ .compatible = "qcom,sc7280-edp-phy" },
84362306a36Sopenharmony_ci	{ .compatible = "qcom,sc8180x-edp-phy" },
84462306a36Sopenharmony_ci	{ .compatible = "qcom,sc8280xp-dp-phy", .data = &dp_phy_cfg },
84562306a36Sopenharmony_ci	{ .compatible = "qcom,sc8280xp-edp-phy", .data = &edp_phy_cfg },
84662306a36Sopenharmony_ci	{ }
84762306a36Sopenharmony_ci};
84862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_edp_phy_match_table);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic struct platform_driver qcom_edp_phy_driver = {
85162306a36Sopenharmony_ci	.probe		= qcom_edp_phy_probe,
85262306a36Sopenharmony_ci	.driver = {
85362306a36Sopenharmony_ci		.name	= "qcom-edp-phy",
85462306a36Sopenharmony_ci		.of_match_table = qcom_edp_phy_match_table,
85562306a36Sopenharmony_ci	},
85662306a36Sopenharmony_ci};
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cimodule_platform_driver(qcom_edp_phy_driver);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
86162306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm eDP QMP PHY driver");
86262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
863