On Thu, 14 Oct 2021, Jani Nikula <jani.nikula@xxxxxxxxx> wrote: > The link training delays are different and/or available in different > DPCD offsets depending on: > > - Clock recovery vs. channel equalization > - DPRX vs. LTTPR > - 128b/132b vs. 8b/10b > - DPCD 1.4+ vs. earlier > > Add helpers to get the correct delays in us, reading DPCD if > necessary. This is more straightforward than trying to retrofit the > existing helpers to take 128b/132b into account. > > Having to pass in the DPCD receiver cap field seems unavoidable, because > reading it involves checking the revision and reading extended receiver > cap. So unfortunately the interface is mixed cached and read as needed. > > v2: Remove delay_us < 0 check and the whole local var (Ville) > > Cc: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> > Reviewed-by: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> > Signed-off-by: Jani Nikula <jani.nikula@xxxxxxxxx> Maarten, Maxime, Thomas - Ack on the first two patches in this series? Should we merge them via a topic branch to both drm-misc-next and drm-intel-next, or is it fine to merge them all via drm-intel-next? We might be at a point in the development cycle that it takes a while to get the branches in sync again. BR, Jani. > --- > drivers/gpu/drm/drm_dp_helper.c | 127 ++++++++++++++++++++++++++++++++ > include/drm/drm_dp_helper.h | 21 +++++- > 2 files changed, 146 insertions(+), 2 deletions(-) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c > index 4d0d1e8e51fa..f7ebf5974fa7 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -154,6 +154,133 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ > } > EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor); > > +static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) > +{ > + if (rd_interval > 4) > + drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n", > + aux->name, rd_interval); > + > + if (rd_interval == 0) > + return 100; > + > + return rd_interval * 4 * USEC_PER_MSEC; > +} > + > +static int __8b10b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) > +{ > + if (rd_interval > 4) > + drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n", > + aux->name, rd_interval); > + > + if (rd_interval == 0) > + return 400; > + > + return rd_interval * 4 * USEC_PER_MSEC; > +} > + > +static int __128b132b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval) > +{ > + switch (rd_interval) { > + default: > + drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x\n", > + aux->name, rd_interval); > + fallthrough; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US: > + return 400; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS: > + return 4000; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS: > + return 8000; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS: > + return 12000; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS: > + return 16000; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS: > + return 32000; > + case DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS: > + return 64000; > + } > +} > + > +/* > + * The link training delays are different for: > + * > + * - Clock recovery vs. channel equalization > + * - DPRX vs. LTTPR > + * - 128b/132b vs. 8b/10b > + * - DPCD rev 1.3 vs. later > + * > + * Get the correct delay in us, reading DPCD if necessary. > + */ > +static int __read_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + enum drm_dp_phy dp_phy, bool uhbr, bool cr) > +{ > + int (*parse)(const struct drm_dp_aux *aux, u8 rd_interval); > + unsigned int offset; > + u8 rd_interval, mask; > + > + if (dp_phy == DP_PHY_DPRX) { > + if (uhbr) { > + if (cr) > + return 100; > + > + offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL; > + mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; > + parse = __128b132b_channel_eq_delay_us; > + } else { > + if (cr && dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) > + return 100; > + > + offset = DP_TRAINING_AUX_RD_INTERVAL; > + mask = DP_TRAINING_AUX_RD_MASK; > + if (cr) > + parse = __8b10b_clock_recovery_delay_us; > + else > + parse = __8b10b_channel_eq_delay_us; > + } > + } else { > + if (uhbr) { > + offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy); > + mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK; > + parse = __128b132b_channel_eq_delay_us; > + } else { > + if (cr) > + return 100; > + > + offset = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy); > + mask = DP_TRAINING_AUX_RD_MASK; > + parse = __8b10b_channel_eq_delay_us; > + } > + } > + > + if (offset < DP_RECEIVER_CAP_SIZE) { > + rd_interval = dpcd[offset]; > + } else { > + if (drm_dp_dpcd_readb(aux, offset, &rd_interval) != 1) { > + drm_dbg_kms(aux->drm_dev, "%s: failed rd interval read\n", > + aux->name); > + /* arbitrary default delay */ > + return 400; > + } > + } > + > + return parse(aux, rd_interval & mask); > +} > + > +int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + enum drm_dp_phy dp_phy, bool uhbr) > +{ > + return __read_delay(aux, dpcd, dp_phy, uhbr, true); > +} > +EXPORT_SYMBOL(drm_dp_read_clock_recovery_delay); > + > +int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + enum drm_dp_phy dp_phy, bool uhbr) > +{ > + return __read_delay(aux, dpcd, dp_phy, uhbr, false); > +} > +EXPORT_SYMBOL(drm_dp_read_channel_eq_delay); > + > void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, > const u8 dpcd[DP_RECEIVER_CAP_SIZE]) > { > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index b52df4db3e8f..afdf7f4183f9 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -1114,8 +1114,15 @@ struct drm_panel; > # define DP_UHBR20 (1 << 1) > # define DP_UHBR13_5 (1 << 2) > > -#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */ > -# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f > +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */ > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US 0x00 > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS 0x01 > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS 0x02 > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS 0x03 > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS 0x04 > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS 0x05 > +# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS 0x06 > > #define DP_TEST_264BIT_CUSTOM_PATTERN_7_0 0x2230 > #define DP_TEST_264BIT_CUSTOM_PATTERN_263_256 0x2250 > @@ -1389,6 +1396,11 @@ enum drm_dp_phy { > # define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED BIT(0) > # define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED BIT(1) > > +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0022 /* 2.0 */ > +#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \ > + DP_LTTPR_REG(dp_phy, DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) > +/* see DP_128B132B_TRAINING_AUX_RD_INTERVAL for values */ > + > #define DP_LANE0_1_STATUS_PHY_REPEATER1 0xf0030 /* 1.3 */ > #define DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy) \ > DP_LTTPR_REG(dp_phy, DP_LANE0_1_STATUS_PHY_REPEATER1) > @@ -1527,6 +1539,11 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ > #define DP_LTTPR_COMMON_CAP_SIZE 8 > #define DP_LTTPR_PHY_CAP_SIZE 3 > > +int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + enum drm_dp_phy dp_phy, bool uhbr); > +int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + enum drm_dp_phy dp_phy, bool uhbr); > + > void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux, > const u8 dpcd[DP_RECEIVER_CAP_SIZE]); > void drm_dp_lttpr_link_train_clock_recovery_delay(void); -- Jani Nikula, Intel Open Source Graphics Center