[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