[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