Unverified Commit c93ce6a6 authored by Maxime Ripard's avatar Maxime Ripard
Browse files

Merge tag 'topic/drm-dp-training-delay-helpers-2021-10-19' of...

Merge tag 'topic/drm-dp-training-delay-helpers-2021-10-19' of git://anongit.freedesktop.org/drm/drm-intel

 into drm-misc-next

Core Changes:
- drm dp helpers for figuring out link training delays

Signed-off-by: default avatarMaxime Ripard <maxime@cerno.tech>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/878ryps5b6.fsf@intel.com
parents 13e9e30c 02ed47aa
Loading
Loading
Loading
Loading
+135 −18
Original line number Diff line number Diff line
@@ -140,38 +140,155 @@ 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);

void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
					    const u8 dpcd[DP_RECEIVER_CAP_SIZE])
static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
{
	unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
					 DP_TRAINING_AUX_RD_MASK;

	if (rd_interval > 4)
		drm_dbg_kms(aux->drm_dev, "%s: AUX interval %lu, out of range (max 4)\n",
		drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
			    aux->name, rd_interval);

	if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
		rd_interval = 100;
	else
		rd_interval *= 4 * USEC_PER_MSEC;
	if (rd_interval == 0)
		return 100;

	usleep_range(rd_interval, rd_interval * 2);
	return rd_interval * 4 * USEC_PER_MSEC;
}
EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);

static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
						 unsigned long rd_interval)
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: AUX interval %lu, out of range (max 4)\n",
		drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
			    aux->name, rd_interval);

	if (rd_interval == 0)
		rd_interval = 400;
		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])
{
	u8 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
		DP_TRAINING_AUX_RD_MASK;
	int delay_us;

	if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
		delay_us = 100;
	else
		rd_interval *= 4 * USEC_PER_MSEC;
		delay_us = __8b10b_clock_recovery_delay_us(aux, rd_interval);

	usleep_range(delay_us, delay_us * 2);
}
EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);

static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
						 u8 rd_interval)
{
	int delay_us = __8b10b_channel_eq_delay_us(aux, rd_interval);

	usleep_range(rd_interval, rd_interval * 2);
	usleep_range(delay_us, delay_us * 2);
}

void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
+19 −2
Original line number Diff line number Diff line
@@ -1116,6 +1116,13 @@ struct drm_panel;

#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
@@ -1385,6 +1392,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)
@@ -1521,6 +1533,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);