[PATCH v3 08/12] phy: phy-mtk-dp: Add support for digital and analog calibration
Manivannan Sadhasivam
mani at kernel.org
Wed Sep 9 10:21:25 PDT 2026
On Tue, Jul 07, 2026 at 05:42:41PM +0200, AngeloGioacchino Del Regno wrote:
> Add support for reading the calibration values from eFuse: if
> present, write those - otherwise, rely on the defaults from
> SoC-specific data.
> This also adds support for writing the calibration values for
> the analog part of the PHY.
>
> Note that before this change, only default hardcoded calibration
> values were supported for the digital driving parameters.
>
> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
> ---
> drivers/phy/mediatek/phy-mtk-dp.c | 303 +++++++++++++++++++++++++++---
> 1 file changed, 281 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/phy/mediatek/phy-mtk-dp.c b/drivers/phy/mediatek/phy-mtk-dp.c
> index c87e9fa710a4..f59a415b401c 100644
> --- a/drivers/phy/mediatek/phy-mtk-dp.c
> +++ b/drivers/phy/mediatek/phy-mtk-dp.c
> @@ -14,6 +14,7 @@
> #include <linux/delay.h>
> #include <linux/io.h>
> #include <linux/mfd/syscon.h>
> +#include <linux/nvmem-consumer.h>
> #include <linux/of.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
> @@ -22,6 +23,19 @@
>
> #define MTK_DP_PHY_MAX_LANES 4
>
> +/* DP_PHYA_GLB_BIAS_GEN_0 (PHYA - Analog) */
> +#define XTP_GLB_BIAS_INT_R_CTRL GENMASK(20, 16)
> +
> +/* DP_PHYA_GLB_FORCE_CTRL_1 */
> +#define CKM_CKTX0_EN_FORCE_MODE BIT(10)
> +
> +/* DP_PHYA_GLB_DPAUX_TX */
> +#define CKM_PT0_CKTX_IMPSEL GENMASK(23, 20)
> +
> +/* DP_PHYA_LAN_LANE_TX_0 */
> +#define XTP_LN_TX_IMPSEL_PMOS GENMASK(15, 12)
> +#define XTP_LN_TX_IMPSEL_NMOS GENMASK(19, 16)
> +
> /* DP_PHYA_GLB_FORCE_CTRL_1 */
> #define CKM_CKTX0_EN_FORCE_MODE BIT(10)
>
> @@ -59,11 +73,29 @@
> #define PHYD_DP_TX_FORCE_VOLT_SWING_VAL GENMASK(2, 1)
> #define PHYD_DP_TX_FORCE_PRE_EMPH_VAL GENMASK(4, 3)
>
> +/*
> + * DRIVING_PARAM_X (PHYD - Digital)
> + *
> + * Driving param registers are split in three sets, all containing settings
> + * for Voltage Swing and Pre-Emphasis for each lane's differential pair.
> + *
> + * All three sets share the same layout, but for different physical signals;
> + * In particular:
> + * [0-2]: LC TX CM (Minus / Negative Edge)
> + * [3-5]: LC TX C (Logic State Change Point)
> + * [6-8]: LC TX CP (Plus / Positive Edge)
> + *
> + * And they contain values for:
> + * [0,3,6]: Swing 0 Pre[0-3]
> + * [1,4,7]: Swing 1 Pre[0-2] and Swing 2 Pre0
> + * [2,5,8]: Swing 2 Pre1 and Swing 3 Pre0
> + */
> +#define PHYD_DIG_NUM_DRV_PARA_REGS 9
> #define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4)
> #define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12))
> #define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19)
> #define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29)
> -#define DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
> +#define MT8195_DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT)
> @@ -72,21 +104,21 @@
> #define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9)
> #define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21))
> #define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29)
> -#define DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
> +#define MT8195_DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT)
>
> #define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5))
> #define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12)
> -#define DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
> +#define MT8195_DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \
> XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT)
>
> #define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0
> #define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10)
> #define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19)
> #define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28)
> -#define DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
> +#define MT8195_DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT)
> @@ -95,22 +127,30 @@
> #define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9)
> #define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18)
> #define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0
> -#define DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
> +#define MT8195_DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT)
>
> #define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3)
> #define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0
> -#define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
> +#define MT8195_DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \
> XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT)
>
> enum mtk_dp_phya_ana_glb_regidx {
> + DP_PHYA_GLB_BIAS_GEN_0,
> + DP_PHYA_GLB_BIAS_GEN_1,
> + DP_PHYA_GLB_DPAUX_TX,
> DP_PHYA_GLB_FORCE_CTRL_0,
> DP_PHYA_GLB_FORCE_CTRL_1,
> DP_PHYA_GLOBAL_MAX
> };
>
> +enum mtk_dp_phya_ana_lane_regidx {
> + DP_PHYA_LAN_LANE_TX_0,
> + DP_PHYA_LAN_MAX
> +};
> +
> enum mtk_dp_phyd_dig_lane_regidx {
> DP_PHYD_LAN_DRIVING_FORCE,
> DP_PHYD_LAN_DRIVING_PARAM_0,
> @@ -128,10 +168,17 @@ enum mtk_dp_phyd_dig_glb_regidx {
> };
>
> static const u8 mt8195_phy_ana_glb_regs[DP_PHYA_GLOBAL_MAX] = {
> + [DP_PHYA_GLB_BIAS_GEN_0] = 0x0,
> + [DP_PHYA_GLB_BIAS_GEN_1] = 0x4,
> + [DP_PHYA_GLB_DPAUX_TX] = 0x8,
> [DP_PHYA_GLB_FORCE_CTRL_0] = 0x30,
> [DP_PHYA_GLB_FORCE_CTRL_1] = 0x34,
> };
>
> +static const u8 mt8195_phy_ana_lane_regs[DP_PHYA_LAN_MAX] = {
> + [DP_PHYA_LAN_LANE_TX_0] = 0x4,
> +};
> +
> static const u8 mt8195_phy_dig_lane_regs[DP_PHYD_LAN_MAX] = {
> [DP_PHYD_LAN_DRIVING_FORCE] = 0x18,
> [DP_PHYD_LAN_DRIVING_PARAM_0] = 0x2c,
> @@ -146,49 +193,101 @@ static const u8 mt8195_phy_dig_glb_regs[DP_PHYD_GLOBAL_MAX] = {
> [DP_PHYD_TX_CTL_0] = 0x44,
> };
>
> +/**
> + * struct mtk_dp_phya_imp_sel - Per-Lane Impedance Selection
> + * @pmos: Impedance selection for P-Channel MOSFET
> + * @nmos: Impedance selection for N-Channel MOSFET
> + */
> +struct mtk_dp_phya_imp_sel {
> + u8 pmos : 4;
> + u8 nmos : 4;
> +};
> +
> /**
> * struct mtk_dp_phy_pdata - Platform data and defaults for MediaTek DP/eDP PHY
> * @off_ana_glb: Base offset for dptx_phyd_sifslv_ana_glb
> + * @off_ana_lane: Base offsets for dptx_phyd_sifslv_ana_lan (for each lane)
> * @off_dig_glb: Base offset for dptx_phyd_sifslv_dig_glb
> * @off_dig_lane: Base offsets for dptx_phyd_sifslv_dig_lan (for each lane)
> * @regs_ana_glb: Register (layout) offsets for ana_glb
> + * @regs_ana_lane: Register (layout) offsets for ana_lan
> * @regs_dig_glb: Register (layout) offsets for dig_glb
> * @regs_dig_lane: Register (layout) offsets for dig_lan
> + * @ana_bias_r: Internal resistance "R" Selection Settings (global)
> + * @ana_cktx_imp: TX Clock Impedance Selection Settings (global)
> + * @ana_lanes_imp: TX Impedance Selection Settings (for all lanes)
> + * @driving_params: Voltage Swing and Pre-Emphasis settings (for all lanes)
> */
> struct mtk_dp_phy_pdata {
> /* Register offsets */
> u16 off_ana_glb;
> + u16 off_ana_lane[MTK_DP_PHY_MAX_LANES];
> u16 off_dig_glb;
> u16 off_dig_lane[MTK_DP_PHY_MAX_LANES];
>
> /* Register maps */
> const u8 *regs_ana_glb;
> + const u8 *regs_ana_lane;
> const u8 *regs_dig_glb;
> const u8 *regs_dig_lane;
> +
> + /* Calibration defaults */
> + u8 ana_bias_r;
> + u8 ana_cktx_imp;
> + struct mtk_dp_phya_imp_sel ana_lanes_imp;
> + u32 driving_params[PHYD_DIG_NUM_DRV_PARA_REGS];
> };
>
> struct mtk_dp_phy {
> struct device *dev;
> struct regmap *regmap;
> const struct mtk_dp_phy_pdata *pdata;
> +
> + u8 ana_bias_r;
> + u8 ana_cktx_imp;
> + struct mtk_dp_phya_imp_sel ana_impsel[MTK_DP_PHY_MAX_LANES];
> };
>
> -static int mtk_dp_phy_init(struct phy *phy)
> +static int mtk_dp_phy_set_analog_calibration_params(struct mtk_dp_phy *dp_phy)
> +{
> + const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
> + const u8 *regs_ana_glb = pdata->regs_ana_glb;
> + const u8 *regs_ana_lane = pdata->regs_ana_lane;
> + int i, ret;
> +
> + ret = regmap_update_bits(dp_phy->regmap,
> + pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_BIAS_GEN_0],
> + XTP_GLB_BIAS_INT_R_CTRL,
> + FIELD_PREP(XTP_GLB_BIAS_INT_R_CTRL, dp_phy->ana_bias_r));
> + if (ret)
> + return ret;
Same comment on error checks.
> +
> + ret = regmap_update_bits(dp_phy->regmap,
> + pdata->off_ana_glb + regs_ana_glb[DP_PHYA_GLB_DPAUX_TX],
> + CKM_PT0_CKTX_IMPSEL,
> + FIELD_PREP(CKM_PT0_CKTX_IMPSEL, dp_phy->ana_cktx_imp));
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
> + struct mtk_dp_phya_imp_sel *ana_imp = &dp_phy->ana_impsel[i];
> + u32 val = FIELD_PREP(XTP_LN_TX_IMPSEL_PMOS, ana_imp->pmos) |
> + FIELD_PREP(XTP_LN_TX_IMPSEL_NMOS, ana_imp->nmos);
> + u32 off_ana_lane = pdata->off_ana_lane[i];
> +
> + ret = regmap_update_bits(dp_phy->regmap,
> + off_ana_lane + regs_ana_lane[DP_PHYA_LAN_LANE_TX_0],
> + XTP_LN_TX_IMPSEL_PMOS | XTP_LN_TX_IMPSEL_NMOS, val);
> + if (ret)
> + return ret;
> + }
> + return 0;
> +}
> +
> +static int mtk_dp_phy_set_digital_drv_params(struct mtk_dp_phy *dp_phy)
> {
> - struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
> const u32 reg = pdata->regs_dig_lane[DP_PHYD_LAN_DRIVING_PARAM_0];
> - static const u32 driving_params[] = {
> - DRIVING_PARAM_0_DEFAULT,
> - DRIVING_PARAM_1_DEFAULT,
> - DRIVING_PARAM_2_DEFAULT,
> - DRIVING_PARAM_3_DEFAULT,
> - DRIVING_PARAM_4_DEFAULT,
> - DRIVING_PARAM_5_DEFAULT,
> - DRIVING_PARAM_6_DEFAULT,
> - DRIVING_PARAM_7_DEFAULT,
> - DRIVING_PARAM_8_DEFAULT
> - };
> int i, ret;
>
> /*
> @@ -199,11 +298,31 @@ static int mtk_dp_phy_init(struct phy *phy)
> for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++) {
> ret = regmap_bulk_write(dp_phy->regmap,
> pdata->off_dig_lane[i] + reg,
> - driving_params,
> - ARRAY_SIZE(driving_params));
> + pdata->driving_params,
> + ARRAY_SIZE(pdata->driving_params));
> if (ret)
> return ret;
> };
> + return 0;
> +}
> +
> +static int mtk_dp_phy_init(struct phy *phy)
> +{
> + struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy);
> + struct device *dev = &phy->dev;
> + int ret;
> +
> + ret = mtk_dp_phy_set_digital_drv_params(dp_phy);
> + if (ret) {
> + dev_err(dev, "Cannot set driving params\n");
> + return ret;
> + }
> +
> + ret = mtk_dp_phy_set_analog_calibration_params(dp_phy);
> + if (ret) {
> + dev_err(dev, "Cannot set analog calibration\n");
> + return ret;
> + }
>
> return 0;
> }
> @@ -422,6 +541,111 @@ static const struct phy_ops mtk_dp_phy_dev_ops = {
> .owner = THIS_MODULE,
> };
>
> +static void mtk_dp_phy_get_default_cal_data(struct mtk_dp_phy *dp_phy)
> +{
> + const struct mtk_dp_phy_pdata *pdata = dp_phy->pdata;
> + int i;
> +
> + dp_phy->ana_bias_r = pdata->ana_bias_r;
> + dp_phy->ana_cktx_imp = pdata->ana_cktx_imp;
> +
> + /* Copy the default lane impedance settings to all lanes */
> + for (i = 0; i < MTK_DP_PHY_MAX_LANES; i++)
> + memcpy(&dp_phy->ana_impsel[i], &pdata->ana_lanes_imp,
> + sizeof(dp_phy->ana_impsel[0]));
> +
> + return;
> +}
> +
> +static int mtk_dp_phy_get_one_cal_para(struct device *dev, const char *name, u8 max_val)
> +{
> + u8 buf_byte;
> + u16 buf;
> + int ret;
> +
> + /*
> + * All of the calibrations are always max 8 bits long, but some may
> + * be split between two different 8-bits cells: handle this corner
> + * case by retrying reading as u16.
> + */
> + ret = nvmem_cell_read_u8(dev, name, &buf_byte);
> + if (ret)
> + ret = nvmem_cell_read_u16(dev, name, &buf);
> + else
> + buf = buf_byte;
> +
> + if (ret) {
> + dev_err(dev, "Cannot get calibration data for %s: %d\n", name, ret);
As Sashiko noted, this should be dev_err_probe().
- Mani
--
மணிவண்ணன் சதாசிவம்
More information about the Linux-mediatek
mailing list