[PATCH 2/2] phy: qcom: edp: update v8 power-on programming sequence
Konrad Dybcio
konrad.dybcio at oss.qualcomm.com
Tue Jun 23 02:10:52 PDT 2026
On 6/23/26 1:29 AM, Bjorn Andersson wrote:
> While the introduction of the v8 programming sequence brought functional
> eDP support for the 4-lane 8.1Gbps case, it doesn't entirely match the
> documented sequences from the programming guide. Further 5.4Gbps,
> 2.7Gbps and 1.62Gbps, and 2-lane support are both incorrect and
> non-functional.
This largely looks good - I painstakingly compared it to the HPG and got
the following diff, most of which shouldn't matter (ordering of config
writes and duplicates), although there are 1 or 2 things that may actually
matter, please take a look:
diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c
index c5d1fa0ae6cb..6504c52b33b5 100644
--- a/drivers/phy/qualcomm/phy-qcom-edp.c
+++ b/drivers/phy/qualcomm/phy-qcom-edp.c
@@ -385,6 +385,7 @@ static int qcom_edp_phy_init(struct phy *phy)
if (ret)
return ret;
+ // hpg just powers on all the lanes here already
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
edp->edp + DP_PHY_PD_CTL);
@@ -393,8 +394,8 @@ static int qcom_edp_phy_init(struct phy *phy)
if (ret)
return ret;
- writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
- msleep(20);
+ // not in HPG
+ // writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
+ // msleep(20);
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
@@ -404,8 +405,10 @@ static int qcom_edp_phy_init(struct phy *phy)
if (!edp->is_edp)
aux_cfg[8] = 0xb7;
+ // HPG only turns this on in mainlink sequence
writel(0xfc, edp->edp + DP_PHY_MODE);
+ // there's probably less registers on older platforms
for (int i = 0; i < DP_AUX_CFG_SIZE; i++)
writel(aux_cfg[i], edp->edp + DP_PHY_AUX_CFG(i));
@@ -516,6 +519,11 @@ static int qcom_edp_set_link_rate_aux_cfg2(const struct qcom_edp *edp)
static void qcom_edp_configure_tx_pre_pll_v8_lane(void __iomem *tx, u32 interface_select)
{
+ // 3 times in total? I would imagine once should be good
+ writel(0x12, tx + TXn_TX_DRV_LVL);
+ writel(0x12, tx + TXn_TX_DRV_LVL);
+ // auxless_cfg1
+
writel(0x0f, tx + TXn_CLKBUF_ENABLE);
writel(0x00, tx + TXn_PRE_EMPH);
writel(0x00, tx + TXn_VMODE_CTRL1);
@@ -533,8 +541,13 @@ static void qcom_edp_configure_tx_pre_pll_v8_lane(void __iomem *tx, u32 interfac
writel(0x03, tx + TXn_RESET_TSYNC_EN);
writel(0x04, tx + TXn_TX_BAND);
writel(0x00, tx + TXn_SLEW_CNTL);
+ // ldo_config - already done in ldo_config_v8()
writel(0x60, tx + TXn_RES_CODE_LANE_TX);
writel(0x60, tx + TXn_RES_CODE_LANE_TX1);
+
+ // if tx1 (&& is_edp? unsure)
+ // writel(0x1f, tx + TXn_RES_CODE_LANE_OFFSET_TX0);
+ // writel(0x1f, tx + TXn_RES_CODE_LANE_OFFSET_TX1);
}
static void qcom_edp_configure_lanes_after_pll_v8(const struct qcom_edp *edp)
@@ -586,7 +599,7 @@ static int qcom_edp_configure_tx_pre_pll_v8_lanes(const struct qcom_edp *edp)
return 0;
}
-static int qcom_edp_configure_pcs_v8(const struct qcom_edp *edp)
+static int qcom_edp_configure_auxless_v8(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 auxless_setup_cyc;
@@ -618,14 +631,18 @@ static int qcom_edp_configure_pcs_v8(const struct qcom_edp *edp)
return -EINVAL;
}
- writel(edp->is_edp ? 0x03 : 0x00, edp->edp + DP_PHY_LDO_CFG);
+ // shouldnt be called here
writel(0x0f, edp->edp + DP_PHY_CFG_1);
- writel(0x00, edp->edp + DP_PHY_AUXLESS_CFG1);
+
+ // ???
+ // writel(0x00, edp->edp + DP_PHY_AUXLESS_CFG1);
writel(auxless_setup_cyc, edp->edp + DP_PHY_AUXLESS_SETUP_CYC);
writel(auxless_silence_cyc, edp->edp + DP_PHY_AUXLESS_SILENCE_CYC);
writel(0x08, edp->edp + DP_PHY_LFPS_CYC);
writel(lfps_period, edp->edp + DP_PHY_LFPS_PERIOD);
- writel(0x2f, edp->edp + DP_PHY_CFG_1);
+
+ // bit(5) is for built-in selftest only
+ writel(0xf, edp->edp + DP_PHY_CFG_1);
return 0;
}
@@ -1117,15 +1134,33 @@ static int qcom_edp_ldo_config_v6(const struct qcom_edp *edp)
static int qcom_edp_ldo_config_v8(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
- u32 ldo_config;
+ u32 ldo_lane_config = 0x00;
+ u32 ldo_cfg;
- if (edp->is_edp)
- ldo_config = 0xd0;
- else
- ldo_config = 0x00;
+ if (edp->is_edp) {
+ switch (dp_opts->link_rate) {
+ case 8100:
+ case 5400:
+ ldo_lane_config = 0x91;
+ break;
+ case 2700:
+ case 1620:
+ ldo_lane_config = 0x51;
+ break;
+ default:
+ WARN_ON(1);
+ }
- writel(ldo_config, edp->tx0 + TXn_LDO_CONFIG);
- writel(dp_opts->lanes > 2 ? ldo_config : 0x00, edp->tx1 + TXn_LDO_CONFIG);
+ ldo_cfg = 0x03;
+ } else {
+ ldo_lane_config = 0x00;
+ ldo_cfg = 0x00;
+ }
+
+ writel(ldo_lane_config, edp->tx0 + TXn_LDO_CONFIG);
+ writel(dp_opts->lanes > 2 ? ldo_lane_config : 0x00, edp->tx1 + TXn_LDO_CONFIG);
+
+ writel(ldo_cfg, edp->edp + DP_PHY_LDO_CFG);
return 0;
}
@@ -1184,6 +1219,8 @@ static int qcom_edp_com_configure_ssc_v8(const struct qcom_edp *edp)
writel(0x01, edp->pll + DP_QSERDES_V8_COM_SSC_EN_CENTER);
writel(0x00, edp->pll + DP_QSERDES_V8_COM_SSC_ADJ_PER1);
+
+ // these should be set later
writel(0x6b, edp->pll + DP_QSERDES_V8_COM_SSC_PER1);
writel(0x02, edp->pll + DP_QSERDES_V8_COM_SSC_PER2);
writel(step1, edp->pll + DP_QSERDES_V8_COM_SSC_STEP_SIZE1_MODE0);
@@ -1296,28 +1333,41 @@ static int qcom_edp_com_configure_pll_v8(const struct qcom_edp *edp)
writel(0x36, edp->pll + DP_QSERDES_V8_COM_PLL_CCTRL_MODE0);
writel(0x16, edp->pll + DP_QSERDES_V8_COM_PLL_RCTRL_MODE0);
writel(0x06, edp->pll + DP_QSERDES_V8_COM_CP_CTRL_MODE0);
+ // hsclk_sel_1 (duplicate)
+ // (ssc_step_size1_mode0)
+ // (ssc_step_size2_mode0)
+ // cp_ctrl_mode0 (duplicate)
+ // rctrl_mode0 (duplicate)
+ // cctrl_mode0 (duplicate)
+ writel(coreclk_div_mode0, edp->pll + DP_QSERDES_V8_COM_CORECLK_DIV_MODE0);
+ writel(lock_cmp1_mode0, edp->pll + DP_QSERDES_V8_COM_LOCK_CMP1_MODE0);
+ writel(lock_cmp2_mode0, edp->pll + DP_QSERDES_V8_COM_LOCK_CMP2_MODE0);
writel(dec_start_mode0, edp->pll + DP_QSERDES_V8_COM_DEC_START_MODE0);
writel(0x00, edp->pll + DP_QSERDES_V8_COM_DIV_FRAC_START1_MODE0);
writel(div_frac_start2_mode0, edp->pll + DP_QSERDES_V8_COM_DIV_FRAC_START2_MODE0);
writel(div_frac_start3_mode0, edp->pll + DP_QSERDES_V8_COM_DIV_FRAC_START3_MODE0);
- writel(cmn_config_1, edp->pll + DP_QSERDES_V8_COM_CMN_CONFIG_1);
writel(0x3f, edp->pll + DP_QSERDES_V8_COM_INTEGLOOP_GAIN0_MODE0);
writel(0x00, edp->pll + DP_QSERDES_V8_COM_INTEGLOOP_GAIN1_MODE0);
- writel(0x00, edp->pll + DP_QSERDES_V8_COM_VCO_TUNE_MAP);
- writel(lock_cmp1_mode0, edp->pll + DP_QSERDES_V8_COM_LOCK_CMP1_MODE0);
- writel(lock_cmp2_mode0, edp->pll + DP_QSERDES_V8_COM_LOCK_CMP2_MODE0);
-
- writel(0x0a, edp->pll + DP_QSERDES_V8_COM_BG_TIMER);
- writel(coreclk_div_mode0, edp->pll + DP_QSERDES_V8_COM_CORECLK_DIV_MODE0);
- writel(0x00, edp->pll + DP_QSERDES_V8_COM_VCO_TUNE_CTRL);
- writel(0x1f, edp->pll + DP_QSERDES_V8_COM_BIAS_EN_CLKBUFLR_EN);
- writel(core_clk_en, edp->pll + DP_QSERDES_V8_COM_CORE_CLK_EN);
writel(vco_tune1_mode0, edp->pll + DP_QSERDES_V8_COM_VCO_TUNE1_MODE0);
writel(vco_tune2_mode0, edp->pll + DP_QSERDES_V8_COM_VCO_TUNE2_MODE0);
-
+ writel(0x0a, edp->pll + DP_QSERDES_V8_COM_BG_TIMER);
+ // ssc_en_center
+ // ssc_per1
+ // ssc_per2
+ // clk_enable1 (duplicate)
+ // sysclk_en_sel (duplicate)
+ writel(0x00, edp->pll + DP_QSERDES_V8_COM_VCO_TUNE_MAP);
+ // clk_select (duplicate)
+ writel(core_clk_en, edp->pll + DP_QSERDES_V8_COM_CORE_CLK_EN);
+ writel(cmn_config_1, edp->pll + DP_QSERDES_V8_COM_CMN_CONFIG_1);
+ // clk_fwd_config_1 (already set in phy_init)
+ writel(0x00, edp->pll + DP_QSERDES_V8_COM_VCO_TUNE_CTRL);
writel(code1_mode0, edp->pll + DP_QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE1_MODE0);
writel(code2_mode0, edp->pll + DP_QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE2_MODE0);
+ // already enabled in phy_init()
+ // writel(0x1f, edp->pll + DP_QSERDES_V8_COM_BIAS_EN_CLKBUFLR_EN);
+
return 0;
}
@@ -1540,11 +1590,7 @@ static int qcom_edp_configure_rate_pcs_v8(const struct qcom_edp *edp,
if (ret)
return ret;
- ret = qcom_edp_set_link_rate_aux_cfg2(edp);
- if (ret)
- return ret;
-
- return qcom_edp_configure_pcs_v8(edp);
+ return qcom_edp_set_link_rate_aux_cfg2(edp);
}
static int qcom_edp_start_pll(const struct qcom_edp *edp)
@@ -1609,6 +1655,8 @@ static int qcom_edp_phy_power_on(struct phy *phy)
if (ret)
return ret;
+ // bias_en_clkbuflr enabled here in mainlink sequence
+
ret = edp->cfg->ver_ops->com_ldo_config(edp);
if (ret)
return ret;
@@ -1616,6 +1664,17 @@ static int qcom_edp_phy_power_on(struct phy *phy)
writel(0x00, edp->tx0 + TXn_LANE_MODE_1);
writel(0x00, edp->tx1 + TXn_LANE_MODE_1);
+ // should be called here
+ // writel(0x0f, edp->edp + DP_PHY_CFG_1);
+
+ ret = qcom_edp_configure_auxless_v8(edp);
+ if (ret)
+ return ret;
+
+ //>com_power_on()
+
+ // cmn_config_1
+
if (edp->dp_opts.ssc) {
ret = qcom_edp_configure_ssc(edp);
if (ret)
More information about the linux-phy
mailing list