Re: [Intel-gfx] [PATCH 1/3] drm/dp: add helpers to read link training delays

Jani Nikula
2021-10-15T15:21:50Z

On Thu, 14 Oct 2021, Jani Nikula <jani.nikula@intel.com> 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@linux.intel.com>
> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Jani Nikula <jani.nikula@intel.com>

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