From: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
To: Imre Deak <imre.deak@intel.com>
Cc: intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Subject: Re: [Intel-gfx] [PATCH v2 4/6] drm/dp: Add LTTPR helpers
Date: Fri, 25 Sep 2020 19:02:47 +0300 [thread overview]
Message-ID: <20200925160247.GR6112@intel.com> (raw)
In-Reply-To: <20200924184805.294493-5-imre.deak@intel.com>
On Thu, Sep 24, 2020 at 09:48:03PM +0300, Imre Deak wrote:
> Add the helpers and register definitions needed to read out the common
> and per-PHY LTTPR capabilities and perform link training in the LTTPR
> non-transparent mode.
>
> v2:
> - Add drm_dp_dpcd_read_phy_link_status() and DP_PHY_LTTPR() here instead
> of adding these to i915. (Ville)
>
> Cc: dri-devel@lists.freedesktop.org
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Imre Deak <imre.deak@intel.com>
> ---
> drivers/gpu/drm/drm_dp_helper.c | 244 +++++++++++++++++++++++++++++++-
> include/drm/drm_dp_helper.h | 62 ++++++++
> 2 files changed, 302 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 478dd51f738d..6014c858b06b 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -150,11 +150,8 @@ void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
> }
> EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
>
> -void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
> +static void __drm_dp_link_train_channel_eq_delay(unsigned long rd_interval)
> {
> - unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
> - DP_TRAINING_AUX_RD_MASK;
> -
> if (rd_interval > 4)
> DRM_DEBUG_KMS("AUX interval %lu, out of range (max 4)\n",
> rd_interval);
> @@ -166,8 +163,35 @@ void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
>
> usleep_range(rd_interval, rd_interval * 2);
> }
> +
> +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
> +{
> + __drm_dp_link_train_channel_eq_delay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
> + DP_TRAINING_AUX_RD_MASK);
> +}
> EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
>
> +void drm_dp_lttpr_link_train_clock_recovery_delay(void)
> +{
> + usleep_range(100, 200);
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_link_train_clock_recovery_delay);
> +
> +static u8 dp_lttpr_phy_cap(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE], int r)
> +{
> + return phy_cap[r - DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1];
> +}
> +
> +void drm_dp_lttpr_link_train_channel_eq_delay(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + u8 interval = dp_lttpr_phy_cap(phy_cap,
> + DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) &
> + DP_TRAINING_AUX_RD_MASK;
> +
> + __drm_dp_link_train_channel_eq_delay(interval);
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_link_train_channel_eq_delay);
> +
> u8 drm_dp_link_rate_to_bw_code(int link_rate)
> {
> /* Spec says link_bw = link_rate / 0.27Gbps */
> @@ -363,6 +387,71 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
> }
> EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
>
> +/**
> + * drm_dp_dpcd_read_phy_link_status - get the link status information for a DP PHY
> + * @aux: DisplayPort AUX channel
> + * @dp_phy: the DP PHY to get the link status for
> + * @link_status: buffer to return the status in
> + *
> + * Fetch the AUX DPCD registers for the DPRX or an LTTPR PHY link status. The
> + * layout of the returned @link_status matches the DPCD register layout of the
> + * DPRX PHY link status.
> + *
> + * Returns 0 if the information was read successfully or a negative error code
> + * on failure.
> + */
> +int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 link_status[DP_LINK_STATUS_SIZE])
> +{
> + u8 lttpr_status[DP_LINK_STATUS_SIZE - 1];
> + int ret;
> +
> + if (dp_phy == DP_PHY_DPRX) {
> + ret = drm_dp_dpcd_read(aux,
> + DP_LANE0_1_STATUS,
> + link_status,
> + DP_LINK_STATUS_SIZE);
> +
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != DP_LINK_STATUS_SIZE);
> +
> + return 0;
> + }
> +
> + ret = drm_dp_dpcd_read(aux,
> + DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy),
> + lttpr_status,
> + sizeof(lttpr_status));
> +
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != sizeof(lttpr_status));
> +
> +#define link_reg(reg) link_status[(reg) - DP_LANE0_1_STATUS]
> +#define lttpr_reg(reg) lttpr_status[(reg) - DP_LANE0_1_STATUS_PHY_REPEATER1]
> +
> + /* Convert the LTTPR to the sink PHY link status layout */
> + link_reg(DP_LANE0_1_STATUS) = lttpr_reg(DP_LANE0_1_STATUS_PHY_REPEATER1);
> + link_reg(DP_LANE2_3_STATUS) = lttpr_reg(DP_LANE2_3_STATUS_PHY_REPEATER1);
> + link_reg(DP_LANE_ALIGN_STATUS_UPDATED) =
> + lttpr_reg(DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1);
> + link_reg(DP_SINK_STATUS) = 0;
> + link_reg(DP_ADJUST_REQUEST_LANE0_1) =
> + lttpr_reg(DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1);
> + link_reg(DP_ADJUST_REQUEST_LANE2_3) =
> + lttpr_reg(DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1);
> +
> +#undef link_reg
> +#undef lttpr_reg
Alternative might be to read to the target buffer directly
and just memmove() the stuff around a bit. Would avoid having
the second buffer on stack. But it's a small buffer so meh.
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dp_dpcd_read_phy_link_status);
> +
> static bool is_edid_digital_input_dp(const struct edid *edid)
> {
> return edid && edid->revision >= 4 &&
> @@ -2098,6 +2187,153 @@ int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_S
> }
> EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
>
> +/**
> + * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities
> + * @aux: DisplayPort AUX channel
> + * @caps: buffer to return the capability info in
> + *
> + * Read capabilities common to all LTTPRs.
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
> + u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + int ret;
> +
> + ret = drm_dp_dpcd_read(aux,
> + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
> + caps, DP_LTTPR_COMMON_CAP_SIZE);
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != DP_LTTPR_COMMON_CAP_SIZE);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps);
> +
> +/**
> + * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY
> + * @aux: DisplayPort AUX channel
> + * @dp_phy: LTTPR PHY to read the capabilities for
> + * @caps: buffer to return the capability info in
> + *
> + * Read the capabilities for the given LTTPR PHY.
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 caps[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + int ret;
> +
> + ret = drm_dp_dpcd_read(aux,
> + DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy),
> + caps, DP_LTTPR_PHY_CAP_SIZE);
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != DP_LTTPR_PHY_CAP_SIZE);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps);
> +
> +static u8 dp_lttpr_common_cap(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE], int r)
> +{
> + return caps[r - DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV];
> +}
> +
> +/**
> + * drm_dp_lttpr_count - get the number of detected LTTPRs
> + * @caps: LTTPR common capabilities
> + *
> + * Get the number of detected LTTPRs from the LTTPR common capabilities info.
> + *
> + * Returns:
> + * -ERANGE if more than supported number (8) of LTTPRs are detected
> + * -EINVAL if the DP_PHY_REPEATER_CNT register contains an invalid value
> + * otherwise the number of detected LTTPRs
> + */
> +int drm_dp_lttpr_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + u8 count = dp_lttpr_common_cap(caps, DP_PHY_REPEATER_CNT);
> +
> + switch (hweight8(count)) {
> + case 0:
> + return 0;
> + case 1:
> + return 8 - ilog2(count);
> + case 8:
> + return -ERANGE;
> + default:
> + return -EINVAL;
> + }
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_count);
> +
> +/**
> + * drm_dp_lttpr_max_link_rate - get the maximum link rate supported by all LTTPRs
> + * @caps: LTTPR common capabilities
> + *
> + * Returns the maximum link rate supported by all detected LTTPRs.
> + */
> +int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + u8 rate = dp_lttpr_common_cap(caps, DP_MAX_LINK_RATE_PHY_REPEATER);
> +
> + return drm_dp_bw_code_to_link_rate(rate);
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_max_link_rate);
> +
> +/**
> + * drm_dp_lttpr_max_lane_count - get the maximum lane count supported by all LTTPRs
> + * @caps: LTTPR common capabilities
> + *
> + * Returns the maximum lane count supported by all detected LTTPRs.
> + */
> +int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + u8 max_lanes = dp_lttpr_common_cap(caps, DP_MAX_LANE_COUNT_PHY_REPEATER);
> +
> + return max_lanes & DP_MAX_LANE_COUNT_MASK;
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_max_lane_count);
> +
> +/**
> + * drm_dp_lttpr_voltage_swing_level_3_supported - check for LTTPR vswing3 support
> + * @caps: LTTPR PHY capabilities
> + *
> + * Returns true if the @caps for an LTTPR TX PHY indicate support for
> + * voltage swing level 3.
> + */
> +bool
> +drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
> +
> + return txcap & DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED;
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_voltage_swing_level_3_supported);
> +
> +/**
> + * drm_dp_lttpr_pre_emphasis_level_3_supported - check for LTTPR preemph3 support
> + * @caps: LTTPR PHY capabilities
> + *
> + * Returns true if the @caps for an LTTPR TX PHY indicate support for
> + * pre-emphasis level 3.
> + */
> +bool
> +drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
> +
> + return txcap & DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED;
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_pre_emphasis_level_3_supported);
> +
> /**
> * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink.
> * @aux: DisplayPort AUX channel
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index e144b4b9d79a..42b6ab14920a 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -1118,15 +1118,58 @@ struct drm_device;
> #define DP_MAX_LANE_COUNT_PHY_REPEATER 0xf0004 /* 1.4a */
> #define DP_Repeater_FEC_CAPABILITY 0xf0004 /* 1.4 */
> #define DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT 0xf0005 /* 1.4a */
> +
> +enum drm_dp_phy {
> + DP_PHY_DPRX,
> +
> + DP_PHY_LTTPR1,
> + DP_PHY_LTTPR2,
> + DP_PHY_LTTPR3,
> + DP_PHY_LTTPR4,
> + DP_PHY_LTTPR5,
> + DP_PHY_LTTPR6,
> + DP_PHY_LTTPR7,
> + DP_PHY_LTTPR8,
> +
> + DP_MAX_LTTPR_COUNT = DP_PHY_LTTPR8,
> +};
> +
> +#define DP_PHY_LTTPR(i) (DP_PHY_LTTPR1 + (i))
> +
> +#define __DP_LTTPR1_BASE 0xf0010 /* 1.3 */
> +#define __DP_LTTPR2_BASE 0xf0060 /* 1.3 */
> +#define DP_LTTPR_BASE(dp_phy) \
> + (__DP_LTTPR1_BASE + (__DP_LTTPR2_BASE - __DP_LTTPR1_BASE) * \
> + ((dp_phy) - DP_PHY_LTTPR1))
> +
> +#define DP_LTTPR_REG(dp_phy, lttpr1_reg) \
> + (DP_LTTPR_BASE(dp_phy) - DP_LTTPR_BASE(DP_PHY_LTTPR1) + (lttpr1_reg))
> +
> #define DP_TRAINING_PATTERN_SET_PHY_REPEATER1 0xf0010 /* 1.3 */
> +#define DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy) \
> + DP_LTTPR_REG(dp_phy, DP_TRAINING_PATTERN_SET_PHY_REPEATER1)
> +
> #define DP_TRAINING_LANE0_SET_PHY_REPEATER1 0xf0011 /* 1.3 */
> +#define DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy) \
> + DP_LTTPR_REG(dp_phy, DP_TRAINING_LANE0_SET_PHY_REPEATER1)
> +
> #define DP_TRAINING_LANE1_SET_PHY_REPEATER1 0xf0012 /* 1.3 */
> #define DP_TRAINING_LANE2_SET_PHY_REPEATER1 0xf0013 /* 1.3 */
> #define DP_TRAINING_LANE3_SET_PHY_REPEATER1 0xf0014 /* 1.3 */
> #define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0020 /* 1.4a */
> +#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \
> + DP_LTTPR_REG(dp_phy, DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
> +
> #define DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1 0xf0021 /* 1.4a */
> +# define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED BIT(0)
> +# define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED BIT(1)
> +
> #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)
> +
> #define DP_LANE2_3_STATUS_PHY_REPEATER1 0xf0031 /* 1.3 */
> +
> #define DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1 0xf0032 /* 1.3 */
> #define DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1 0xf0033 /* 1.3 */
> #define DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1 0xf0034 /* 1.3 */
> @@ -1236,9 +1279,13 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ
> #define DP_DSC_RECEIVER_CAP_SIZE 0xf
> #define EDP_PSR_RECEIVER_CAP_SIZE 2
> #define EDP_DISPLAY_CTL_CAP_SIZE 3
> +#define DP_LTTPR_COMMON_CAP_SIZE 8
> +#define DP_LTTPR_PHY_CAP_SIZE 3
>
> void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
> +void drm_dp_lttpr_link_train_clock_recovery_delay(void);
> void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
> +void drm_dp_lttpr_link_train_channel_eq_delay(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
>
> u8 drm_dp_link_rate_to_bw_code(int link_rate);
> int drm_dp_bw_code_to_link_rate(u8 link_bw);
> @@ -1697,6 +1744,10 @@ int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
> int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
> u8 status[DP_LINK_STATUS_SIZE]);
>
> +int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 link_status[DP_LINK_STATUS_SIZE]);
> +
> bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
> u8 real_edid_checksum);
>
> @@ -1746,6 +1797,17 @@ bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
> const struct drm_dp_desc *desc);
> int drm_dp_read_sink_count(struct drm_dp_aux *aux);
>
> +int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
> + u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
> +int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
> +int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]);
> +int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
> +int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
> +bool drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
> +bool drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
> +
> void drm_dp_remote_aux_init(struct drm_dp_aux *aux);
> void drm_dp_aux_init(struct drm_dp_aux *aux);
> int drm_dp_aux_register(struct drm_dp_aux *aux);
> --
> 2.25.1
--
Ville Syrjälä
Intel
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
WARNING: multiple messages have this Message-ID (diff)
From: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
To: Imre Deak <imre.deak@intel.com>
Cc: intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH v2 4/6] drm/dp: Add LTTPR helpers
Date: Fri, 25 Sep 2020 19:02:47 +0300 [thread overview]
Message-ID: <20200925160247.GR6112@intel.com> (raw)
In-Reply-To: <20200924184805.294493-5-imre.deak@intel.com>
On Thu, Sep 24, 2020 at 09:48:03PM +0300, Imre Deak wrote:
> Add the helpers and register definitions needed to read out the common
> and per-PHY LTTPR capabilities and perform link training in the LTTPR
> non-transparent mode.
>
> v2:
> - Add drm_dp_dpcd_read_phy_link_status() and DP_PHY_LTTPR() here instead
> of adding these to i915. (Ville)
>
> Cc: dri-devel@lists.freedesktop.org
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Imre Deak <imre.deak@intel.com>
> ---
> drivers/gpu/drm/drm_dp_helper.c | 244 +++++++++++++++++++++++++++++++-
> include/drm/drm_dp_helper.h | 62 ++++++++
> 2 files changed, 302 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 478dd51f738d..6014c858b06b 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -150,11 +150,8 @@ void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
> }
> EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
>
> -void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
> +static void __drm_dp_link_train_channel_eq_delay(unsigned long rd_interval)
> {
> - unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
> - DP_TRAINING_AUX_RD_MASK;
> -
> if (rd_interval > 4)
> DRM_DEBUG_KMS("AUX interval %lu, out of range (max 4)\n",
> rd_interval);
> @@ -166,8 +163,35 @@ void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
>
> usleep_range(rd_interval, rd_interval * 2);
> }
> +
> +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
> +{
> + __drm_dp_link_train_channel_eq_delay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
> + DP_TRAINING_AUX_RD_MASK);
> +}
> EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
>
> +void drm_dp_lttpr_link_train_clock_recovery_delay(void)
> +{
> + usleep_range(100, 200);
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_link_train_clock_recovery_delay);
> +
> +static u8 dp_lttpr_phy_cap(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE], int r)
> +{
> + return phy_cap[r - DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1];
> +}
> +
> +void drm_dp_lttpr_link_train_channel_eq_delay(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + u8 interval = dp_lttpr_phy_cap(phy_cap,
> + DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) &
> + DP_TRAINING_AUX_RD_MASK;
> +
> + __drm_dp_link_train_channel_eq_delay(interval);
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_link_train_channel_eq_delay);
> +
> u8 drm_dp_link_rate_to_bw_code(int link_rate)
> {
> /* Spec says link_bw = link_rate / 0.27Gbps */
> @@ -363,6 +387,71 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
> }
> EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
>
> +/**
> + * drm_dp_dpcd_read_phy_link_status - get the link status information for a DP PHY
> + * @aux: DisplayPort AUX channel
> + * @dp_phy: the DP PHY to get the link status for
> + * @link_status: buffer to return the status in
> + *
> + * Fetch the AUX DPCD registers for the DPRX or an LTTPR PHY link status. The
> + * layout of the returned @link_status matches the DPCD register layout of the
> + * DPRX PHY link status.
> + *
> + * Returns 0 if the information was read successfully or a negative error code
> + * on failure.
> + */
> +int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 link_status[DP_LINK_STATUS_SIZE])
> +{
> + u8 lttpr_status[DP_LINK_STATUS_SIZE - 1];
> + int ret;
> +
> + if (dp_phy == DP_PHY_DPRX) {
> + ret = drm_dp_dpcd_read(aux,
> + DP_LANE0_1_STATUS,
> + link_status,
> + DP_LINK_STATUS_SIZE);
> +
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != DP_LINK_STATUS_SIZE);
> +
> + return 0;
> + }
> +
> + ret = drm_dp_dpcd_read(aux,
> + DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy),
> + lttpr_status,
> + sizeof(lttpr_status));
> +
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != sizeof(lttpr_status));
> +
> +#define link_reg(reg) link_status[(reg) - DP_LANE0_1_STATUS]
> +#define lttpr_reg(reg) lttpr_status[(reg) - DP_LANE0_1_STATUS_PHY_REPEATER1]
> +
> + /* Convert the LTTPR to the sink PHY link status layout */
> + link_reg(DP_LANE0_1_STATUS) = lttpr_reg(DP_LANE0_1_STATUS_PHY_REPEATER1);
> + link_reg(DP_LANE2_3_STATUS) = lttpr_reg(DP_LANE2_3_STATUS_PHY_REPEATER1);
> + link_reg(DP_LANE_ALIGN_STATUS_UPDATED) =
> + lttpr_reg(DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1);
> + link_reg(DP_SINK_STATUS) = 0;
> + link_reg(DP_ADJUST_REQUEST_LANE0_1) =
> + lttpr_reg(DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1);
> + link_reg(DP_ADJUST_REQUEST_LANE2_3) =
> + lttpr_reg(DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1);
> +
> +#undef link_reg
> +#undef lttpr_reg
Alternative might be to read to the target buffer directly
and just memmove() the stuff around a bit. Would avoid having
the second buffer on stack. But it's a small buffer so meh.
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dp_dpcd_read_phy_link_status);
> +
> static bool is_edid_digital_input_dp(const struct edid *edid)
> {
> return edid && edid->revision >= 4 &&
> @@ -2098,6 +2187,153 @@ int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_S
> }
> EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
>
> +/**
> + * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities
> + * @aux: DisplayPort AUX channel
> + * @caps: buffer to return the capability info in
> + *
> + * Read capabilities common to all LTTPRs.
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
> + u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + int ret;
> +
> + ret = drm_dp_dpcd_read(aux,
> + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
> + caps, DP_LTTPR_COMMON_CAP_SIZE);
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != DP_LTTPR_COMMON_CAP_SIZE);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps);
> +
> +/**
> + * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY
> + * @aux: DisplayPort AUX channel
> + * @dp_phy: LTTPR PHY to read the capabilities for
> + * @caps: buffer to return the capability info in
> + *
> + * Read the capabilities for the given LTTPR PHY.
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 caps[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + int ret;
> +
> + ret = drm_dp_dpcd_read(aux,
> + DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy),
> + caps, DP_LTTPR_PHY_CAP_SIZE);
> + if (ret < 0)
> + return ret;
> +
> + WARN_ON(ret != DP_LTTPR_PHY_CAP_SIZE);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps);
> +
> +static u8 dp_lttpr_common_cap(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE], int r)
> +{
> + return caps[r - DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV];
> +}
> +
> +/**
> + * drm_dp_lttpr_count - get the number of detected LTTPRs
> + * @caps: LTTPR common capabilities
> + *
> + * Get the number of detected LTTPRs from the LTTPR common capabilities info.
> + *
> + * Returns:
> + * -ERANGE if more than supported number (8) of LTTPRs are detected
> + * -EINVAL if the DP_PHY_REPEATER_CNT register contains an invalid value
> + * otherwise the number of detected LTTPRs
> + */
> +int drm_dp_lttpr_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + u8 count = dp_lttpr_common_cap(caps, DP_PHY_REPEATER_CNT);
> +
> + switch (hweight8(count)) {
> + case 0:
> + return 0;
> + case 1:
> + return 8 - ilog2(count);
> + case 8:
> + return -ERANGE;
> + default:
> + return -EINVAL;
> + }
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_count);
> +
> +/**
> + * drm_dp_lttpr_max_link_rate - get the maximum link rate supported by all LTTPRs
> + * @caps: LTTPR common capabilities
> + *
> + * Returns the maximum link rate supported by all detected LTTPRs.
> + */
> +int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + u8 rate = dp_lttpr_common_cap(caps, DP_MAX_LINK_RATE_PHY_REPEATER);
> +
> + return drm_dp_bw_code_to_link_rate(rate);
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_max_link_rate);
> +
> +/**
> + * drm_dp_lttpr_max_lane_count - get the maximum lane count supported by all LTTPRs
> + * @caps: LTTPR common capabilities
> + *
> + * Returns the maximum lane count supported by all detected LTTPRs.
> + */
> +int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
> +{
> + u8 max_lanes = dp_lttpr_common_cap(caps, DP_MAX_LANE_COUNT_PHY_REPEATER);
> +
> + return max_lanes & DP_MAX_LANE_COUNT_MASK;
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_max_lane_count);
> +
> +/**
> + * drm_dp_lttpr_voltage_swing_level_3_supported - check for LTTPR vswing3 support
> + * @caps: LTTPR PHY capabilities
> + *
> + * Returns true if the @caps for an LTTPR TX PHY indicate support for
> + * voltage swing level 3.
> + */
> +bool
> +drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
> +
> + return txcap & DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED;
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_voltage_swing_level_3_supported);
> +
> +/**
> + * drm_dp_lttpr_pre_emphasis_level_3_supported - check for LTTPR preemph3 support
> + * @caps: LTTPR PHY capabilities
> + *
> + * Returns true if the @caps for an LTTPR TX PHY indicate support for
> + * pre-emphasis level 3.
> + */
> +bool
> +drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
> +{
> + u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
> +
> + return txcap & DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED;
> +}
> +EXPORT_SYMBOL(drm_dp_lttpr_pre_emphasis_level_3_supported);
> +
> /**
> * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink.
> * @aux: DisplayPort AUX channel
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index e144b4b9d79a..42b6ab14920a 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -1118,15 +1118,58 @@ struct drm_device;
> #define DP_MAX_LANE_COUNT_PHY_REPEATER 0xf0004 /* 1.4a */
> #define DP_Repeater_FEC_CAPABILITY 0xf0004 /* 1.4 */
> #define DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT 0xf0005 /* 1.4a */
> +
> +enum drm_dp_phy {
> + DP_PHY_DPRX,
> +
> + DP_PHY_LTTPR1,
> + DP_PHY_LTTPR2,
> + DP_PHY_LTTPR3,
> + DP_PHY_LTTPR4,
> + DP_PHY_LTTPR5,
> + DP_PHY_LTTPR6,
> + DP_PHY_LTTPR7,
> + DP_PHY_LTTPR8,
> +
> + DP_MAX_LTTPR_COUNT = DP_PHY_LTTPR8,
> +};
> +
> +#define DP_PHY_LTTPR(i) (DP_PHY_LTTPR1 + (i))
> +
> +#define __DP_LTTPR1_BASE 0xf0010 /* 1.3 */
> +#define __DP_LTTPR2_BASE 0xf0060 /* 1.3 */
> +#define DP_LTTPR_BASE(dp_phy) \
> + (__DP_LTTPR1_BASE + (__DP_LTTPR2_BASE - __DP_LTTPR1_BASE) * \
> + ((dp_phy) - DP_PHY_LTTPR1))
> +
> +#define DP_LTTPR_REG(dp_phy, lttpr1_reg) \
> + (DP_LTTPR_BASE(dp_phy) - DP_LTTPR_BASE(DP_PHY_LTTPR1) + (lttpr1_reg))
> +
> #define DP_TRAINING_PATTERN_SET_PHY_REPEATER1 0xf0010 /* 1.3 */
> +#define DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy) \
> + DP_LTTPR_REG(dp_phy, DP_TRAINING_PATTERN_SET_PHY_REPEATER1)
> +
> #define DP_TRAINING_LANE0_SET_PHY_REPEATER1 0xf0011 /* 1.3 */
> +#define DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy) \
> + DP_LTTPR_REG(dp_phy, DP_TRAINING_LANE0_SET_PHY_REPEATER1)
> +
> #define DP_TRAINING_LANE1_SET_PHY_REPEATER1 0xf0012 /* 1.3 */
> #define DP_TRAINING_LANE2_SET_PHY_REPEATER1 0xf0013 /* 1.3 */
> #define DP_TRAINING_LANE3_SET_PHY_REPEATER1 0xf0014 /* 1.3 */
> #define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0020 /* 1.4a */
> +#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \
> + DP_LTTPR_REG(dp_phy, DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
> +
> #define DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1 0xf0021 /* 1.4a */
> +# define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED BIT(0)
> +# define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED BIT(1)
> +
> #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)
> +
> #define DP_LANE2_3_STATUS_PHY_REPEATER1 0xf0031 /* 1.3 */
> +
> #define DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1 0xf0032 /* 1.3 */
> #define DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1 0xf0033 /* 1.3 */
> #define DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1 0xf0034 /* 1.3 */
> @@ -1236,9 +1279,13 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ
> #define DP_DSC_RECEIVER_CAP_SIZE 0xf
> #define EDP_PSR_RECEIVER_CAP_SIZE 2
> #define EDP_DISPLAY_CTL_CAP_SIZE 3
> +#define DP_LTTPR_COMMON_CAP_SIZE 8
> +#define DP_LTTPR_PHY_CAP_SIZE 3
>
> void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
> +void drm_dp_lttpr_link_train_clock_recovery_delay(void);
> void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
> +void drm_dp_lttpr_link_train_channel_eq_delay(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
>
> u8 drm_dp_link_rate_to_bw_code(int link_rate);
> int drm_dp_bw_code_to_link_rate(u8 link_bw);
> @@ -1697,6 +1744,10 @@ int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
> int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
> u8 status[DP_LINK_STATUS_SIZE]);
>
> +int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 link_status[DP_LINK_STATUS_SIZE]);
> +
> bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
> u8 real_edid_checksum);
>
> @@ -1746,6 +1797,17 @@ bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
> const struct drm_dp_desc *desc);
> int drm_dp_read_sink_count(struct drm_dp_aux *aux);
>
> +int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
> + u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
> +int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
> + enum drm_dp_phy dp_phy,
> + u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
> +int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]);
> +int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
> +int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
> +bool drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
> +bool drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
> +
> void drm_dp_remote_aux_init(struct drm_dp_aux *aux);
> void drm_dp_aux_init(struct drm_dp_aux *aux);
> int drm_dp_aux_register(struct drm_dp_aux *aux);
> --
> 2.25.1
--
Ville Syrjälä
Intel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
next prev parent reply other threads:[~2020-09-25 16:02 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-09-24 18:47 [Intel-gfx] [PATCH v2 0/6] drm/i915: Add support for LTTPR non-transparent link training mode Imre Deak
2020-09-24 18:47 ` Imre Deak
2020-09-24 18:48 ` [Intel-gfx] [PATCH v2 1/6] drm/i915: Fix DP link training pattern mask Imre Deak
2020-09-24 18:48 ` [Intel-gfx] [PATCH v2 2/6] drm/i915: Simplify the link training functions Imre Deak
2020-09-24 18:48 ` [Intel-gfx] [PATCH v2 3/6] drm/i915: Factor out a helper to disable the DPCD training pattern Imre Deak
2020-09-24 18:57 ` Ville Syrjälä
2020-09-24 18:48 ` [Intel-gfx] [PATCH v2 4/6] drm/dp: Add LTTPR helpers Imre Deak
2020-09-24 18:48 ` Imre Deak
2020-09-25 16:02 ` Ville Syrjälä [this message]
2020-09-25 16:02 ` Ville Syrjälä
2020-09-25 16:51 ` [Intel-gfx] " Imre Deak
2020-09-25 16:51 ` Imre Deak
2020-09-24 18:48 ` [Intel-gfx] [PATCH v2 5/6] drm/i915: Switch to LTTPR transparent mode link training Imre Deak
2020-09-25 16:03 ` Ville Syrjälä
2020-09-25 16:40 ` Imre Deak
2020-09-24 18:48 ` [Intel-gfx] [PATCH v2 6/6] drm/i915: Switch to LTTPR non-transparent " Imre Deak
2020-09-24 22:01 ` [Intel-gfx] ✗ Fi.CI.SPARSE: warning for drm/i915: Add support for LTTPR non-transparent link training mode (rev2) Patchwork
2020-09-24 22:24 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
2020-09-25 3:13 ` [Intel-gfx] ✓ Fi.CI.IGT: " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200925160247.GR6112@intel.com \
--to=ville.syrjala@linux.intel.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=imre.deak@intel.com \
--cc=intel-gfx@lists.freedesktop.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.