Commit 9cf9498f authored by Wesley Chalmers's avatar Wesley Chalmers Committed by Alex Deucher
Browse files

drm/amd/display: Partition DPCD address space and break up transactions



[WHY]
SCR for DP 2.0 spec says that multiple LTTPRs must not be accessed in a
single AUX transaction.
There may be other places in future where breaking up AUX accesses is
necessary.

[HOW]
Partition the entire DPCD address space into blocks. When an incoming AUX
request spans multiple blocks, break up the request into multiple requests.

Signed-off-by: default avatarWesley Chalmers <Wesley.Chalmers@amd.com>
Reviewed-by: default avatarJun Lei <Jun.Lei@amd.com>
Acked-by: default avatarAnson Jacob <Anson.Jacob@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 95ad72f4
Loading
Loading
Loading
Loading
+85 −2
Original line number Diff line number Diff line
@@ -43,6 +43,60 @@ static enum dc_status internal_link_write_dpcd(
	return DC_OK;
}

/*
 * Partition the entire DPCD address space
 * XXX: This partitioning must cover the entire DPCD address space,
 * and must contain no gaps or overlapping address ranges.
 */
static const struct dpcd_address_range mandatory_dpcd_partitions[] = {
	{ 0, DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR1) - 1},
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR1), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR2) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR2), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR3) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR3), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR4) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR4), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR5) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR5), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR6) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR6), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR7) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR7), DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR8) - 1 },
	{ DP_TRAINING_PATTERN_SET_PHY_REPEATER(DP_PHY_LTTPR8), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR1) - 1 },
	/*
	 * The FEC registers are contiguous
	 */
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR1), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR1) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR2), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR2) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR3), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR3) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR4), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR4) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR5), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR5) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR6), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR6) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR7), DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR7) - 1 },
	{ DP_FEC_STATUS_PHY_REPEATER(DP_PHY_LTTPR8), DP_LTTPR_MAX_ADD },
	/* all remaining DPCD addresses */
	{ DP_LTTPR_MAX_ADD + 1, DP_DPCD_MAX_ADD } };

static inline bool do_addresses_intersect_with_range(
		const struct dpcd_address_range *range,
		const uint32_t start_address,
		const uint32_t end_address)
{
	return start_address <= range->end && end_address >= range->start;
}

static uint32_t dpcd_get_next_partition_size(const uint32_t address, const uint32_t size)
{
	const uint32_t end_address = END_ADDRESS(address, size);
	uint32_t partition_iterator = 0;

	/*
	 * find current partition
	 * this loop spins forever if partition map above is not surjective
	 */
	while (!do_addresses_intersect_with_range(&mandatory_dpcd_partitions[partition_iterator],
				address, end_address))
		partition_iterator++;
	if (end_address < mandatory_dpcd_partitions[partition_iterator].end)
		return size;
	return ADDRESS_RANGE_SIZE(address, mandatory_dpcd_partitions[partition_iterator].end);
}

/*
 * Ranges of DPCD addresses that must be read in a single transaction
 * XXX: Do not allow any two address ranges in this array to overlap
@@ -115,12 +169,28 @@ enum dc_status core_link_read_dpcd(
	uint32_t size)
{
	uint32_t extended_address;
	uint32_t partitioned_address;
	uint8_t *extended_data;
	uint32_t extended_size;
	/* size of the remaining partitioned address space */
	uint32_t size_left_to_read;
	enum dc_status status;
	/* size of the next partition to be read from */
	uint32_t partition_size;
	uint32_t data_index = 0;

	dpcd_extend_address_range(address, data, size, &extended_address, &extended_data, &extended_size);
	status = internal_link_read_dpcd(link, extended_address, extended_data, extended_size);
	partitioned_address = extended_address;
	size_left_to_read = extended_size;
	while (size_left_to_read) {
		partition_size = dpcd_get_next_partition_size(partitioned_address, size_left_to_read);
		status = internal_link_read_dpcd(link, partitioned_address, &extended_data[data_index], partition_size);
		if (status != DC_OK)
			break;
		partitioned_address += partition_size;
		data_index += partition_size;
		size_left_to_read -= partition_size;
	}
	dpcd_reduce_address_range(extended_address, extended_data, extended_size, address, data, size);
	return status;
}
@@ -131,5 +201,18 @@ enum dc_status core_link_write_dpcd(
	const uint8_t *data,
	uint32_t size)
{
	return internal_link_write_dpcd(link, address, data, size);
	uint32_t partition_size;
	uint32_t data_index = 0;
	enum dc_status status;

	while (size) {
		partition_size = dpcd_get_next_partition_size(address, size);
		status = internal_link_write_dpcd(link, address, &data[data_index], partition_size);
		if (status != DC_OK)
			break;
		address += partition_size;
		data_index += partition_size;
		size -= partition_size;
	}
	return status;
}
+17 −0
Original line number Diff line number Diff line
@@ -1377,10 +1377,27 @@ enum drm_dp_phy {
#define DP_SYMBOL_ERROR_COUNT_LANE1_PHY_REPEATER1	    0xf0037 /* 1.3 */
#define DP_SYMBOL_ERROR_COUNT_LANE2_PHY_REPEATER1	    0xf0039 /* 1.3 */
#define DP_SYMBOL_ERROR_COUNT_LANE3_PHY_REPEATER1	    0xf003b /* 1.3 */

#define __DP_FEC1_BASE					    0xf0290 /* 1.4 */
#define __DP_FEC2_BASE					    0xf0298 /* 1.4 */
#define DP_FEC_BASE(dp_phy) \
	(__DP_FEC1_BASE + ((__DP_FEC2_BASE - __DP_FEC1_BASE) * \
			   ((dp_phy) - DP_PHY_LTTPR1)))

#define DP_FEC_REG(dp_phy, fec1_reg) \
	(DP_FEC_BASE(dp_phy) - DP_FEC_BASE(DP_PHY_LTTPR1) + fec1_reg)

#define DP_FEC_STATUS_PHY_REPEATER1			    0xf0290 /* 1.4 */
#define DP_FEC_STATUS_PHY_REPEATER(dp_phy) \
	DP_FEC_REG(dp_phy, DP_FEC_STATUS_PHY_REPEATER1)

#define DP_FEC_ERROR_COUNT_PHY_REPEATER1                    0xf0291 /* 1.4 */
#define DP_FEC_CAPABILITY_PHY_REPEATER1                     0xf0294 /* 1.4a */

#define DP_LTTPR_MAX_ADD				    0xf02ff /* 1.4 */

#define DP_DPCD_MAX_ADD					    0xfffff /* 1.4 */

/* Repeater modes */
#define DP_PHY_REPEATER_MODE_TRANSPARENT		    0x55    /* 1.3 */
#define DP_PHY_REPEATER_MODE_NON_TRANSPARENT		    0xaa    /* 1.3 */