All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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.