On Mon, Feb 10, 2025 at 10:49:53PM +0800, Yongbang Shi wrote: > From: Baihan Li <libaihan@xxxxxxxxxx> > > This serdes is inited and configured for dp, and integrating them into dp > init and dp link training process. > For rate changing, we can change from 1.62-8.2Gpbs by cfg reg. > For voltage and pre-emphasis levels changing, we can cfg different > serdes ffe value. > > Signed-off-by: Baihan Li <libaihan@xxxxxxxxxx> > Signed-off-by: Yongbang Shi <shiyongbang@xxxxxxxxxx> > --- > ChangeLog: > v1 -> v2: > - splittting the patch and add more detailed the changes in the commit message, suggested by Dmitry Baryshkov. > - changing all names of dp phy to dp serdes. Nit: subject still mentions phy. Nit #2: The s/phy/serdes/ doesn't change the fact that you are still doing PHY-like programming. As such, please mention in the commit message that using PHY subsystem is impossible for you. > --- > drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +- > drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 1 + > drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 4 + > .../gpu/drm/hisilicon/hibmc/dp/dp_serdes.c | 74 +++++++++++++++++++ > .../gpu/drm/hisilicon/hibmc/dp/dp_serdes.h | 36 +++++++++ > 5 files changed, 116 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c > create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.h > > diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile > index 95a4ed599d98..43de077d6769 100644 > --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile > +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile > @@ -1,5 +1,5 @@ > # SPDX-License-Identifier: GPL-2.0-only > hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ > - dp/dp_aux.o dp/dp_link.o dp/dp_hw.o hibmc_drm_dp.o > + dp/dp_aux.o dp/dp_link.o dp/dp_hw.o dp/dp_serdes.o hibmc_drm_dp.o > > obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o > diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h > index 2c52a4476c4d..e7746bc4b592 100644 > --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h > +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h > @@ -38,6 +38,7 @@ struct hibmc_dp_dev { > struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */ > struct hibmc_dp_link link; > u8 dpcd[DP_RECEIVER_CAP_SIZE]; > + void __iomem *serdes_base; > }; > > #define dp_field_modify(reg_value, mask, val) \ > diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c > index a8d543881c09..39fd3687efca 100644 > --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c > +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c > @@ -7,6 +7,7 @@ > #include "dp_comm.h" > #include "dp_reg.h" > #include "dp_hw.h" > +#include "dp_serdes.h" > > static void hibmc_dp_set_tu(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) > { > @@ -165,6 +166,9 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) > > hibmc_dp_aux_init(dp_dev); > > + if (hibmc_dp_serdes_init(dp_dev)) > + return -EAGAIN; Can you simply propagate the return value here? > + > dp_dev->link.cap.lanes = 0x2; > dp_dev->link.cap.link_rate = DP_LINK_BW_2_7; > > diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c > new file mode 100644 > index 000000000000..66586db2268a > --- /dev/null > +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c > @@ -0,0 +1,74 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +// Copyright (c) 2024 Hisilicon Limited. > + > +#include <linux/delay.h> > +#include <drm/drm_device.h> > +#include <drm/drm_print.h> > +#include "dp_comm.h" > +#include "dp_config.h" > +#include "dp_reg.h" > +#include "dp_serdes.h" > + > +int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]) > +{ > + u32 serdes_tx_cfg[4][4] = { {DP_SERDES_VOL0_PRE0, DP_SERDES_VOL0_PRE1, static const > + DP_SERDES_VOL0_PRE2, DP_SERDES_VOL0_PRE3}, > + {DP_SERDES_VOL1_PRE0, DP_SERDES_VOL1_PRE1, > + DP_SERDES_VOL1_PRE2}, {DP_SERDES_VOL2_PRE0, > + DP_SERDES_VOL2_PRE1}, {DP_SERDES_VOL3_PRE0}}; > + int cfg[2]; > + int i; > + > + for (i = 0; i < HIBMC_DP_LANE_NUM_MAX; i++) { > + cfg[i] = serdes_tx_cfg[(train_set[i] & 0x3)] > + [(train_set[i] << DP_TRAIN_PRE_EMPHASIS_SHIFT & 0x3)]; > + if (!cfg[i]) { > + cfg[i] = DP_SERDES_VOL3_PRE0; > + drm_warn(dp->dev, "dp serdes cfg beyonds the allowable range\n"); Is it something that you should be mitigating? Can you return -EINVAL instead? > + } > + > + /* lane1 offset is 4 */ > + writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, cfg[i]), > + dp->serdes_base + HIBMC_DP_PMA_LANE0_OFFSET + i * 4); > + } > + > + usleep_range(300, 500); > + > + if (readl(dp->serdes_base + HIBMC_DP_LANE_STATUS_OFFSET) != DP_SERDES_DONE) { > + drm_err(dp->dev, "dp serdes cfg failed\n"); drm_dbg? > + return -EAGAIN; > + } > + > + return 0; > +} > + > +int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp) > +{ > + writel(rate, dp->serdes_base + HIBMC_DP_LANE0_RATE_OFFSET); > + writel(rate, dp->serdes_base + HIBMC_DP_LANE1_RATE_OFFSET); > + > + usleep_range(300, 500); > + > + if (readl(dp->serdes_base + HIBMC_DP_LANE_STATUS_OFFSET) != DP_SERDES_DONE) { > + drm_err(dp->dev, "dp serdes rate switching failed\n"); > + return -EAGAIN; > + } > + > + if (rate < DP_SERDES_BW_8_1) > + drm_warn(dp->dev, "reducing serdes rate to :%d\n", > + rate ? rate * HIBMC_DP_LINK_RATE_CAL * 10 : 162); drm_dbg > + > + return 0; > +} > + > +int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp) > +{ > + dp->serdes_base = dp->base + HIBMC_DP_HOST_OFFSET; > + > + writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, DP_SERDES_VOL0_PRE0), > + dp->serdes_base + HIBMC_DP_PMA_LANE0_OFFSET); > + writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, DP_SERDES_VOL0_PRE0), > + dp->serdes_base + HIBMC_DP_PMA_LANE1_OFFSET); > + > + return hibmc_dp_serdes_rate_switch(DP_SERDES_BW_8_1, dp); > +} > diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.h > new file mode 100644 > index 000000000000..57f7f054f2b7 > --- /dev/null > +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.h > @@ -0,0 +1,36 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* Copyright (c) 2025 Hisilicon Limited. */ > + > +#ifndef DP_SERDES_H > +#define DP_SERDES_H > + > +#define HIBMC_DP_HOST_OFFSET 0x10000 > +#define HIBMC_DP_LANE0_RATE_OFFSET 0x4 > +#define HIBMC_DP_LANE1_RATE_OFFSET 0xc > +#define HIBMC_DP_LANE_STATUS_OFFSET 0x10 > +#define HIBMC_DP_PMA_LANE0_OFFSET 0x18 > +#define HIBMC_DP_PMA_LANE1_OFFSET 0x1c > +#define HIBMC_DP_PMA_TXDEEMPH GENMASK(18, 1) > + > +#define DP_SERDES_VOL0_PRE0 0x280 > +#define DP_SERDES_VOL0_PRE1 0x2300 > +#define DP_SERDES_VOL0_PRE2 0x53c0 > +#define DP_SERDES_VOL0_PRE3 0x8400 > +#define DP_SERDES_VOL1_PRE0 0x380 > +#define DP_SERDES_VOL1_PRE1 0x3440 > +#define DP_SERDES_VOL1_PRE2 0x6480 > +#define DP_SERDES_VOL2_PRE0 0x500 > +#define DP_SERDES_VOL2_PRE1 0x4500 > +#define DP_SERDES_VOL3_PRE0 0x600 These need some explanation. > +#define DP_SERDES_BW_8_1 0x3 > +#define DP_SERDES_BW_5_4 0x2 > +#define DP_SERDES_BW_2_7 0x1 > +#define DP_SERDES_BW_1_62 0x0 > + > +#define DP_SERDES_DONE 0x3 > + > +int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp); > +int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); > +int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]); > + > +#endif > -- > 2.33.0 > -- With best wishes Dmitry