[PATCH 15/18] drm/radeon/kms: rewrite DP handling

Alex Deucher alexdeucher at gmail.com
Fri May 20 01:34:28 PDT 2011


- reorganize the functions based on use
- clean up function naming
- rework link training to better match what we use internally
- add initial support for DP 1.2 (no MST yet)

Signed-off-by: Alex Deucher <alexdeucher at gmail.com>
---
 drivers/gpu/drm/radeon/atombios_dp.c       | 1094 +++++++++++++++-------------
 drivers/gpu/drm/radeon/radeon_connectors.c |    4 +-
 drivers/gpu/drm/radeon/radeon_encoders.c   |    4 +-
 drivers/gpu/drm/radeon/radeon_mode.h       |    6 +-
 4 files changed, 584 insertions(+), 524 deletions(-)

diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 444954d..5f2ddcd 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -43,158 +43,242 @@ static char *pre_emph_names[] = {
         "0dB", "3.5dB", "6dB", "9.5dB"
 };
 
-static const int dp_clocks[] = {
-	54000,  /* 1 lane, 1.62 Ghz */
-	90000,  /* 1 lane, 2.70 Ghz */
-	108000, /* 2 lane, 1.62 Ghz */
-	180000, /* 2 lane, 2.70 Ghz */
-	216000, /* 4 lane, 1.62 Ghz */
-	360000, /* 4 lane, 2.70 Ghz */
+/***** radeon AUX functions *****/
+union aux_channel_transaction {
+	PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
+	PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
 };
 
-static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int);
+static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
+				 u8 *send, int send_bytes,
+				 u8 *recv, int recv_size,
+				 u8 delay, u8 *ack)
+{
+	struct drm_device *dev = chan->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	union aux_channel_transaction args;
+	int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
+	unsigned char *base;
+	int recv_bytes;
+
+	memset(&args, 0, sizeof(args));
 
-/* common helper functions */
-static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+	base = (unsigned char *)rdev->mode_info.atom_context->scratch;
+
+	memcpy(base, send, send_bytes);
+
+	args.v1.lpAuxRequest = 0;
+	args.v1.lpDataOut = 16;
+	args.v1.ucDataOutLen = 0;
+	args.v1.ucChannelID = chan->rec.i2c_id;
+	args.v1.ucDelay = delay / 10;
+	if (ASIC_IS_DCE4(rdev))
+		args.v2.ucHPD_ID = chan->rec.hpd;
+
+	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+	*ack = args.v1.ucReplyStatus;
+
+	/* timeout */
+	if (args.v1.ucReplyStatus == 1) {
+		DRM_DEBUG_KMS("dp_aux_ch timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	/* flags not zero */
+	if (args.v1.ucReplyStatus == 2) {
+		DRM_DEBUG_KMS("dp_aux_ch flags not zero\n");
+		return -EBUSY;
+	}
+
+	/* error */
+	if (args.v1.ucReplyStatus == 3) {
+		DRM_DEBUG_KMS("dp_aux_ch error\n");
+		return -EIO;
+	}
+
+	recv_bytes = args.v1.ucDataOutLen;
+	if (recv_bytes > recv_size)
+		recv_bytes = recv_size;
+
+	if (recv && recv_size)
+		memcpy(recv, base + 16, recv_bytes);
+
+	return recv_bytes;
+}
+
+static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector,
+				      u16 address, u8 *send, u8 send_bytes, u8 delay)
 {
-	int i;
-	u8 max_link_bw;
-	u8 max_lane_count;
+	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+	int ret;
+	u8 msg[20];
+	int msg_bytes = send_bytes + 4;
+	u8 ack;
 
-	if (!dpcd)
-		return 0;
+	if (send_bytes > 16)
+		return -1;
 
-	max_link_bw = dpcd[DP_MAX_LINK_RATE];
-	max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+	msg[0] = address;
+	msg[1] = address >> 8;
+	msg[2] = AUX_NATIVE_WRITE << 4;
+	msg[3] = (msg_bytes << 4) | (send_bytes - 1);
+	memcpy(&msg[4], send, send_bytes);
 
-	switch (max_link_bw) {
-	case DP_LINK_BW_1_62:
-	default:
-		for (i = 0; i < num_dp_clocks; i++) {
-			if (i % 2)
-				continue;
-			switch (max_lane_count) {
-			case 1:
-				if (i > 1)
-					return 0;
-				break;
-			case 2:
-				if (i > 3)
-					return 0;
-				break;
-			case 4:
-			default:
-				break;
-			}
-			if (dp_clocks[i] > mode_clock) {
-				if (i < 2)
-					return 1;
-				else if (i < 4)
-					return 2;
-				else
-					return 4;
-			}
-		}
-		break;
-	case DP_LINK_BW_2_7:
-		for (i = 0; i < num_dp_clocks; i++) {
-			switch (max_lane_count) {
-			case 1:
-				if (i > 1)
-					return 0;
-				break;
-			case 2:
-				if (i > 3)
-					return 0;
-				break;
-			case 4:
-			default:
-				break;
-			}
-			if (dp_clocks[i] > mode_clock) {
-				if (i < 2)
-					return 1;
-				else if (i < 4)
-					return 2;
-				else
-					return 4;
-			}
-		}
-		break;
+	while (1) {
+		ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
+					    msg, msg_bytes, NULL, 0, delay, &ack);
+		if (ret < 0)
+			return ret;
+		if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+			break;
+		else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+			udelay(400);
+		else
+			return -EIO;
 	}
 
-	return 0;
+	return send_bytes;
 }
 
-static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector,
+				     u16 address, u8 *recv, int recv_bytes, u8 delay)
 {
-	int i;
-	u8 max_link_bw;
-	u8 max_lane_count;
+	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+	u8 msg[4];
+	int msg_bytes = 4;
+	u8 ack;
+	int ret;
 
-	if (!dpcd)
-		return 0;
+	msg[0] = address;
+	msg[1] = address >> 8;
+	msg[2] = AUX_NATIVE_READ << 4;
+	msg[3] = (msg_bytes << 4) | (recv_bytes - 1);
 
-	max_link_bw = dpcd[DP_MAX_LINK_RATE];
-	max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+	while (1) {
+		ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
+					    msg, msg_bytes, recv, recv_bytes, delay, &ack);
+		if (ret == 0)
+			return -EPROTO;
+		if (ret < 0)
+			return ret;
+		if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+			return ret;
+		else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+			udelay(400);
+		else
+			return -EIO;
+	}
+}
 
-	switch (max_link_bw) {
-	case DP_LINK_BW_1_62:
+static void radeon_write_dpcd_reg(struct radeon_connector *radeon_connector,
+				 u16 reg, u8 val)
+{
+	radeon_dp_aux_native_write(radeon_connector, reg, &val, 1, 0);
+}
+
+static u8 radeon_read_dpcd_reg(struct radeon_connector *radeon_connector,
+			       u16 reg)
+{
+	u8 val = 0;
+
+	radeon_dp_aux_native_read(radeon_connector, reg, &val, 1, 0);
+
+	return val;
+}
+
+int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+			 u8 write_byte, u8 *read_byte)
+{
+	struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+	struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
+	u16 address = algo_data->address;
+	u8 msg[5];
+	u8 reply[2];
+	unsigned retry;
+	int msg_bytes;
+	int reply_bytes = 1;
+	int ret;
+	u8 ack;
+
+	/* Set up the command byte */
+	if (mode & MODE_I2C_READ)
+		msg[2] = AUX_I2C_READ << 4;
+	else
+		msg[2] = AUX_I2C_WRITE << 4;
+
+	if (!(mode & MODE_I2C_STOP))
+		msg[2] |= AUX_I2C_MOT << 4;
+
+	msg[0] = address;
+	msg[1] = address >> 8;
+
+	switch (mode) {
+	case MODE_I2C_WRITE:
+		msg_bytes = 5;
+		msg[3] = msg_bytes << 4;
+		msg[4] = write_byte;
+		break;
+	case MODE_I2C_READ:
+		msg_bytes = 4;
+		msg[3] = msg_bytes << 4;
+		break;
 	default:
-		for (i = 0; i < num_dp_clocks; i++) {
-			if (i % 2)
-				continue;
-			switch (max_lane_count) {
-			case 1:
-				if (i > 1)
-					return 0;
-				break;
-			case 2:
-				if (i > 3)
-					return 0;
-				break;
-			case 4:
-			default:
-				break;
-			}
-			if (dp_clocks[i] > mode_clock)
-				return 162000;
-		}
+		msg_bytes = 4;
+		msg[3] = 3 << 4;
 		break;
-	case DP_LINK_BW_2_7:
-		for (i = 0; i < num_dp_clocks; i++) {
-			switch (max_lane_count) {
-			case 1:
-				if (i > 1)
-					return 0;
-				break;
-			case 2:
-				if (i > 3)
-					return 0;
-				break;
-			case 4:
-			default:
-				break;
-			}
-			if (dp_clocks[i] > mode_clock)
-				return (i % 2) ? 270000 : 162000;
-		}
 	}
 
-	return 0;
-}
+	for (retry = 0; retry < 4; retry++) {
+		ret = radeon_process_aux_ch(auxch,
+					    msg, msg_bytes, reply, reply_bytes, 0, &ack);
+		if (ret < 0) {
+			DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
+			return ret;
+		}
 
-int dp_mode_valid(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
-{
-	int lanes = dp_lanes_for_mode_clock(dpcd, mode_clock);
-	int dp_clock = dp_link_clock_for_mode_clock(dpcd, mode_clock);
+		switch (ack & AUX_NATIVE_REPLY_MASK) {
+		case AUX_NATIVE_REPLY_ACK:
+			/* I2C-over-AUX Reply field is only valid
+			 * when paired with AUX ACK.
+			 */
+			break;
+		case AUX_NATIVE_REPLY_NACK:
+			DRM_DEBUG_KMS("aux_ch native nack\n");
+			return -EREMOTEIO;
+		case AUX_NATIVE_REPLY_DEFER:
+			DRM_DEBUG_KMS("aux_ch native defer\n");
+			udelay(400);
+			continue;
+		default:
+			DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack);
+			return -EREMOTEIO;
+		}
 
-	if ((lanes == 0) || (dp_clock == 0))
-		return MODE_CLOCK_HIGH;
+		switch (ack & AUX_I2C_REPLY_MASK) {
+		case AUX_I2C_REPLY_ACK:
+			if (mode == MODE_I2C_READ)
+				*read_byte = reply[0];
+			return ret;
+		case AUX_I2C_REPLY_NACK:
+			DRM_DEBUG_KMS("aux_i2c nack\n");
+			return -EREMOTEIO;
+		case AUX_I2C_REPLY_DEFER:
+			DRM_DEBUG_KMS("aux_i2c defer\n");
+			udelay(400);
+			break;
+		default:
+			DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack);
+			return -EREMOTEIO;
+		}
+	}
 
-	return MODE_OK;
+	DRM_ERROR("aux i2c too many retries, giving up\n");
+	return -EREMOTEIO;
 }
 
+/***** general DP utility functions *****/
+
 static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r)
 {
 	return link_status[r - DP_LANE0_1_STATUS];
@@ -242,7 +326,7 @@ static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
 	return true;
 }
 
-static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+static u8 dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
 					int lane)
 
 {
@@ -255,7 +339,7 @@ static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE]
 	return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
 }
 
-static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+static u8 dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
 					     int lane)
 {
 	int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
@@ -267,22 +351,8 @@ static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_
 	return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
 }
 
-/* XXX fix me -- chip specific */
 #define DP_VOLTAGE_MAX         DP_TRAIN_VOLTAGE_SWING_1200
-static u8 dp_pre_emphasis_max(u8 voltage_swing)
-{
-	switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
-	case DP_TRAIN_VOLTAGE_SWING_400:
-		return DP_TRAIN_PRE_EMPHASIS_6;
-	case DP_TRAIN_VOLTAGE_SWING_600:
-		return DP_TRAIN_PRE_EMPHASIS_6;
-	case DP_TRAIN_VOLTAGE_SWING_800:
-		return DP_TRAIN_PRE_EMPHASIS_3_5;
-	case DP_TRAIN_VOLTAGE_SWING_1200:
-	default:
-		return DP_TRAIN_PRE_EMPHASIS_0;
-	}
-}
+#define DP_PRE_EMPHASIS_MAX    DP_TRAIN_PRE_EMPHASIS_9_5
 
 static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
 				int lane_count,
@@ -308,10 +378,10 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
 	}
 
 	if (v >= DP_VOLTAGE_MAX)
-		v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+		v |= DP_TRAIN_MAX_SWING_REACHED;
 
-	if (p >= dp_pre_emphasis_max(v))
-		p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+	if (p >= DP_PRE_EMPHASIS_MAX)
+		p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
 	DRM_DEBUG_KMS("using signal parameters: voltage %s pre_emph %s\n",
 		  voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
@@ -321,138 +391,109 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
 		train_set[lane] = v | p;
 }
 
-union aux_channel_transaction {
-	PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
-	PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
-};
-
-/* radeon aux chan functions */
-static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
-				 u8 *send, int send_bytes,
-				 u8 *recv, int recv_size,
-				 u8 delay, u8 *ack)
+/* convert bits per color to bits per pixel */
+/* get bpc from the EDID */
+static int convert_bpc_to_bpp(int bpc)
 {
-	struct drm_device *dev = chan->dev;
-	struct radeon_device *rdev = dev->dev_private;
-	union aux_channel_transaction args;
-	int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
-	unsigned char *base;
-	int recv_bytes;
-
-	memset(&args, 0, sizeof(args));
-
-	base = (unsigned char *)rdev->mode_info.atom_context->scratch;
-
-	memcpy(base, send, send_bytes);
-
-	args.v1.lpAuxRequest = 0;
-	args.v1.lpDataOut = 16;
-	args.v1.ucDataOutLen = 0;
-	args.v1.ucChannelID = chan->rec.i2c_id;
-	args.v1.ucDelay = delay / 10;
-	if (ASIC_IS_DCE4(rdev))
-		args.v2.ucHPD_ID = chan->rec.hpd;
-
-	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-
-	*ack = args.v1.ucReplyStatus;
-
-	/* timeout */
-	if (args.v1.ucReplyStatus == 1) {
-		DRM_DEBUG_KMS("dp_aux_ch timeout\n");
-		return -ETIMEDOUT;
-	}
+	if (bpc == 0)
+		return 24;
+	else
+		return bpc * 3;
+}
 
-	/* flags not zero */
-	if (args.v1.ucReplyStatus == 2) {
-		DRM_DEBUG_KMS("dp_aux_ch flags not zero\n");
-		return -EBUSY;
-	}
+/* get the max pix clock supported by the link rate and lane num */
+static int dp_get_max_dp_pix_clock(int link_rate,
+				   int lane_num,
+				   int bpp)
+{
+	return (link_rate * lane_num * 8) / bpp;
+}
 
-	/* error */
-	if (args.v1.ucReplyStatus == 3) {
-		DRM_DEBUG_KMS("dp_aux_ch error\n");
-		return -EIO;
+static int dp_get_max_link_rate(u8 dpcd[DP_DPCD_SIZE])
+{
+	switch (dpcd[DP_MAX_LINK_RATE]) {
+	case DP_LINK_BW_1_62:
+	default:
+		return 162000;
+	case DP_LINK_BW_2_7:
+		return 270000;
+	case DP_LINK_BW_5_4:
+		return 540000;
 	}
-
-	recv_bytes = args.v1.ucDataOutLen;
-	if (recv_bytes > recv_size)
-		recv_bytes = recv_size;
-
-	if (recv && recv_size)
-		memcpy(recv, base + 16, recv_bytes);
-
-	return recv_bytes;
 }
 
-static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector,
-				      u16 address, u8 *send, u8 send_bytes, u8 delay)
+static u8 dp_get_max_lane_number(u8 dpcd[DP_DPCD_SIZE])
 {
-	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
-	int ret;
-	u8 msg[20];
-	int msg_bytes = send_bytes + 4;
-	u8 ack;
+	return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+}
 
-	if (send_bytes > 16)
-		return -1;
+static u8 dp_get_dp_link_rate_coded(int link_rate)
+{
+	switch (link_rate) {
+	case 162000:
+	default:
+		return DP_LINK_BW_1_62;
+	case 270000:
+		return DP_LINK_BW_2_7;
+	case 540000:
+		return DP_LINK_BW_5_4;
+	}
+}
 
-	msg[0] = address;
-	msg[1] = address >> 8;
-	msg[2] = AUX_NATIVE_WRITE << 4;
-	msg[3] = (msg_bytes << 4) | (send_bytes - 1);
-	memcpy(&msg[4], send, send_bytes);
+/***** radeon specific DP functions *****/
 
-	while (1) {
-		ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
-					    msg, msg_bytes, NULL, 0, delay, &ack);
-		if (ret < 0)
-			return ret;
-		if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
-			break;
-		else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
-			udelay(400);
-		else
-			return -EIO;
+/* First get the min lane# when low rate is used according to pixel clock
+ * (prefer low rate), second check max lane# supported by DP panel,
+ * if the max lane# < low rate lane# then use max lane# instead.
+ */
+static int radeon_dp_get_dp_lane_number(struct drm_connector *connector,
+					u8 dpcd[DP_DPCD_SIZE],
+					int pix_clock)
+{
+	int bpp = convert_bpc_to_bpp(connector->display_info.bpc);
+	int max_link_rate = dp_get_max_link_rate(dpcd);
+	int max_lane_num = dp_get_max_lane_number(dpcd);
+	int lane_num;
+	int max_dp_pix_clock;
+
+	for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) {
+		max_dp_pix_clock = dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp);
+		if (pix_clock <= max_dp_pix_clock)
+			break;
 	}
 
-	return send_bytes;
+	return lane_num;
 }
 
-static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector,
-				     u16 address, u8 *recv, int recv_bytes, u8 delay)
+static int radeon_dp_get_dp_link_clock(struct drm_connector *connector,
+				       u8 dpcd[DP_DPCD_SIZE],
+				       int pix_clock)
 {
-	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
-	u8 msg[4];
-	int msg_bytes = 4;
-	u8 ack;
-	int ret;
-
-	msg[0] = address;
-	msg[1] = address >> 8;
-	msg[2] = AUX_NATIVE_READ << 4;
-	msg[3] = (msg_bytes << 4) | (recv_bytes - 1);
-
-	while (1) {
-		ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
-					    msg, msg_bytes, recv, recv_bytes, delay, &ack);
-		if (ret == 0)
-			return -EPROTO;
-		if (ret < 0)
-			return ret;
-		if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
-			return ret;
-		else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
-			udelay(400);
-		else
-			return -EIO;
+	int bpp = convert_bpc_to_bpp(connector->display_info.bpc);
+	int lane_num, max_pix_clock;
+
+	if (radeon_connector_encoder_is_dp_bridge(connector))
+		return 270000;
+
+	lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock);
+	max_pix_clock = dp_get_max_dp_pix_clock(162000, lane_num, bpp);
+	if (pix_clock <= max_pix_clock)
+		return 162000;
+	max_pix_clock = dp_get_max_dp_pix_clock(270000, lane_num, bpp);
+	if (pix_clock <= max_pix_clock)
+		return 270000;
+	if (radeon_connector_is_dp12_capable(connector)) {
+		max_pix_clock = dp_get_max_dp_pix_clock(540000, lane_num, bpp);
+		if (pix_clock <= max_pix_clock)
+			return 540000;
 	}
+
+	return dp_get_max_link_rate(dpcd);
 }
 
-/* radeon dp functions */
 static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
 				    int action, int dp_clock,
-				    uint8_t ucconfig, uint8_t lane_num)
+				    u8 ucconfig, u8 lane_num)
 {
 	DP_ENCODER_SERVICE_PARAMETERS args;
 	int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
@@ -482,55 +523,81 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
 {
 	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
 	u8 msg[25];
-	int ret;
+	int ret, i;
 
 	ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, 8, 0);
 	if (ret > 0) {
 		memcpy(dig_connector->dpcd, msg, 8);
-		{
-			int i;
-			DRM_DEBUG_KMS("DPCD: ");
-			for (i = 0; i < 8; i++)
-				DRM_DEBUG_KMS("%02x ", msg[i]);
-			DRM_DEBUG_KMS("\n");
-		}
+		DRM_DEBUG_KMS("DPCD: ");
+		for (i = 0; i < 8; i++)
+			DRM_DEBUG_KMS("%02x ", msg[i]);
+		DRM_DEBUG_KMS("\n");
 		return true;
 	}
 	dig_connector->dpcd[0] = 0;
 	return false;
 }
 
+static void radeon_dp_set_panel_mode(struct drm_encoder *encoder,
+				     struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
+
+	if (!ASIC_IS_DCE4(rdev))
+		return;
+
+	if (radeon_connector_encoder_is_dp_bridge(connector))
+		panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
+
+	atombios_dig_encoder_setup(encoder,
+				   ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
+				   panel_mode);
+}
+
 void radeon_dp_set_link_config(struct drm_connector *connector,
 			       struct drm_display_mode *mode)
 {
-	struct radeon_connector *radeon_connector;
+	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 	struct radeon_connector_atom_dig *dig_connector;
 
-	if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
-	    (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
-		return;
-
-	radeon_connector = to_radeon_connector(connector);
 	if (!radeon_connector->con_priv)
 		return;
 	dig_connector = radeon_connector->con_priv;
 
-	dig_connector->dp_clock =
-		dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock);
-	dig_connector->dp_lane_count =
-		dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock);
+	if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
+	    (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
+		dig_connector->dp_clock =
+			radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock);
+		dig_connector->dp_lane_count =
+			radeon_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock);
+	}
 }
 
-int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
+int radeon_dp_mode_valid_helper(struct drm_connector *connector,
 				struct drm_display_mode *mode)
 {
-	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+	struct radeon_connector_atom_dig *dig_connector;
+	int dp_clock;
 
-	return dp_mode_valid(dig_connector->dpcd, mode->clock);
+	if (!radeon_connector->con_priv)
+		return MODE_CLOCK_HIGH;
+	dig_connector = radeon_connector->con_priv;
+
+	dp_clock =
+		radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock);
+
+	if ((dp_clock == 540000) &&
+	    (!radeon_connector_is_dp12_capable(connector)))
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
 }
 
-static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
-				    u8 link_status[DP_LINK_STATUS_SIZE])
+static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
+				      u8 link_status[DP_LINK_STATUS_SIZE])
 {
 	int ret;
 	ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS,
@@ -551,325 +618,316 @@ bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
 	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
 	u8 link_status[DP_LINK_STATUS_SIZE];
 
-	if (!atom_dp_get_link_status(radeon_connector, link_status))
+	if (!radeon_dp_get_link_status(radeon_connector, link_status))
 		return false;
 	if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
 		return false;
 	return true;
 }
 
-static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
-{
-	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
-
-	if (dig_connector->dpcd[0] >= 0x11) {
-		radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER,
-					   &power_state, 1, 0);
-	}
-}
-
-static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread)
-{
-	radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL,
-				   &downspread, 1, 0);
-}
+struct radeon_dp_link_train_info {
+	struct radeon_device *rdev;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct radeon_connector *radeon_connector;
+	int enc_id;
+	int dp_clock;
+	int dp_lane_count;
+	int rd_interval;
+	bool tp3_supported;
+	u8 dpcd[8];
+	u8 train_set[4];
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	u8 tries;
+};
 
-static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector,
-				 u8 link_configuration[DP_LINK_CONFIGURATION_SIZE])
+static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info)
 {
-	radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET,
-				   link_configuration, 2, 0);
+	/* set the initial vs/emph on the source */
+	atombios_dig_transmitter_setup(dp_info->encoder,
+				       ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
+				       0, dp_info->train_set[0]); /* sets all lanes at once */
+
+	/* set the vs/emph on the sink */
+	radeon_dp_aux_native_write(dp_info->radeon_connector, DP_TRAINING_LANE0_SET,
+				   dp_info->train_set, dp_info->dp_lane_count, 0);
 }
 
-static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector,
-				struct drm_encoder *encoder,
-				u8 train_set[4])
+static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp)
 {
-	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
-	int i;
-
-	for (i = 0; i < dig_connector->dp_lane_count; i++)
-		atombios_dig_transmitter_setup(encoder,
-					       ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
-					       i, train_set[i]);
+	int rtp = 0;
 
-	radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET,
-				   train_set, dig_connector->dp_lane_count, 0);
-}
+	/* set training pattern on the source */
+	if (ASIC_IS_DCE4(dp_info->rdev)) {
+		switch (tp) {
+		case DP_TRAINING_PATTERN_1:
+			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
+			break;
+		case DP_TRAINING_PATTERN_2:
+			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2;
+			break;
+		case DP_TRAINING_PATTERN_3:
+			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3;
+			break;
+		}
+		atombios_dig_encoder_setup(dp_info->encoder, rtp, 0);
+	} else {
+		switch (tp) {
+		case DP_TRAINING_PATTERN_1:
+			rtp = 0;
+			break;
+		case DP_TRAINING_PATTERN_2:
+			rtp = 1;
+			break;
+		}
+		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
+					  dp_info->dp_clock, dp_info->enc_id, rtp);
+	}
 
-static void dp_set_training(struct radeon_connector *radeon_connector,
-			    u8 training)
-{
-	radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET,
-				   &training, 1, 0);
+	/* enable training pattern on the sink */
+	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp);
 }
 
-void dp_link_train(struct drm_encoder *encoder,
-		   struct drm_connector *connector)
+static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
 {
-	struct drm_device *dev = encoder->dev;
-	struct radeon_device *rdev = dev->dev_private;
-	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
-	struct radeon_encoder_atom_dig *dig;
-	struct radeon_connector *radeon_connector;
-	struct radeon_connector_atom_dig *dig_connector;
-	int enc_id = 0;
-	bool clock_recovery, channel_eq;
-	u8 link_status[DP_LINK_STATUS_SIZE];
-	u8 link_configuration[DP_LINK_CONFIGURATION_SIZE];
-	u8 tries, voltage;
-	u8 train_set[4];
-	int i;
+	u8 tmp;
 
-	if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
-	    (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
-		return;
+	/* power up the sink */
+	if (dp_info->dpcd[0] >= 0x11)
+		radeon_write_dpcd_reg(dp_info->radeon_connector,
+				      DP_SET_POWER, DP_SET_POWER_D0);
+
+	/* possibly enable downspread on the sink */
+	if (dp_info->dpcd[3] & 0x1)
+		radeon_write_dpcd_reg(dp_info->radeon_connector,
+				      DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
+	else
+		radeon_write_dpcd_reg(dp_info->radeon_connector,
+				      DP_DOWNSPREAD_CTRL, 0);
 
-	if (!radeon_encoder->enc_priv)
-		return;
-	dig = radeon_encoder->enc_priv;
+	radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector);
 
-	radeon_connector = to_radeon_connector(connector);
-	if (!radeon_connector->con_priv)
-		return;
-	dig_connector = radeon_connector->con_priv;
+	/* set the lane count on the sink */
+	tmp = dp_info->dp_lane_count;
+	if (dp_info->dpcd[0] >= 0x11)
+		tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);
 
-	if (dig->dig_encoder)
-		enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
-	else
-		enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
-	if (dig->linkb)
-		enc_id |= ATOM_DP_CONFIG_LINK_B;
-	else
-		enc_id |= ATOM_DP_CONFIG_LINK_A;
+	/* set the link rate on the sink */
+	tmp = dp_get_dp_link_rate_coded(dp_info->dp_clock);
+	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);
 
-	memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
-	if (dig_connector->dp_clock == 270000)
-		link_configuration[0] = DP_LINK_BW_2_7;
+	/* start training on the source */
+	if (ASIC_IS_DCE4(dp_info->rdev))
+		atombios_dig_encoder_setup(dp_info->encoder,
+					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
 	else
-		link_configuration[0] = DP_LINK_BW_1_62;
-	link_configuration[1] = dig_connector->dp_lane_count;
-	if (dig_connector->dpcd[0] >= 0x11)
-		link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_START,
+					  dp_info->dp_clock, dp_info->enc_id, 0);
 
-	/* power up the sink */
-	dp_set_power(radeon_connector, DP_SET_POWER_D0);
 	/* disable the training pattern on the sink */
-	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
-	/* set link bw and lanes on the sink */
-	dp_set_link_bw_lanes(radeon_connector, link_configuration);
-	/* disable downspread on the sink */
-	dp_set_downspread(radeon_connector, 0);
-	if (ASIC_IS_DCE4(rdev)) {
-		/* start training on the source */
-		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
-		/* set training pattern 1 on the source */
-		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1, 0);
-	} else {
-		/* start training on the source */
-		radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
-					  dig_connector->dp_clock, enc_id, 0);
-		/* set training pattern 1 on the source */
-		radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
-					  dig_connector->dp_clock, enc_id, 0);
-	}
+	radeon_write_dpcd_reg(dp_info->radeon_connector,
+			      DP_TRAINING_PATTERN_SET,
+			      DP_TRAINING_PATTERN_DISABLE);
+
+	return 0;
+}
 
-	/* set initial vs/emph */
-	memset(train_set, 0, 4);
+static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info)
+{
 	udelay(400);
-	/* set training pattern 1 on the sink */
-	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1);
 
-	dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+	/* disable the training pattern on the sink */
+	radeon_write_dpcd_reg(dp_info->radeon_connector,
+			      DP_TRAINING_PATTERN_SET,
+			      DP_TRAINING_PATTERN_DISABLE);
+
+	/* disable the training pattern on the source */
+	if (ASIC_IS_DCE4(dp_info->rdev))
+		atombios_dig_encoder_setup(dp_info->encoder,
+					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
+	else
+		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
+					  dp_info->dp_clock, dp_info->enc_id, 0);
+
+	return 0;
+}
+
+static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info)
+{
+	bool clock_recovery;
+ 	u8 voltage;
+	int i;
+
+	radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1);
+	memset(dp_info->train_set, 0, 4);
+	radeon_dp_update_vs_emph(dp_info);
+
+	udelay(400);
 
 	/* clock recovery loop */
 	clock_recovery = false;
-	tries = 0;
+	dp_info->tries = 0;
 	voltage = 0xff;
-	for (;;) {
-		udelay(100);
-		if (!atom_dp_get_link_status(radeon_connector, link_status))
+	while (1) {
+		if (dp_info->rd_interval == 0)
+			udelay(100);
+		else
+			mdelay(dp_info->rd_interval * 4);
+
+		if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status))
 			break;
 
-		if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) {
+		if (dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) {
 			clock_recovery = true;
 			break;
 		}
 
-		for (i = 0; i < dig_connector->dp_lane_count; i++) {
-			if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+		for (i = 0; i < dp_info->dp_lane_count; i++) {
+			if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
 				break;
 		}
-		if (i == dig_connector->dp_lane_count) {
+		if (i == dp_info->dp_lane_count) {
 			DRM_ERROR("clock recovery reached max voltage\n");
 			break;
 		}
 
-		if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
-			++tries;
-			if (tries == 5) {
+		if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+			++dp_info->tries;
+			if (dp_info->tries == 5) {
 				DRM_ERROR("clock recovery tried 5 times\n");
 				break;
 			}
 		} else
-			tries = 0;
+			dp_info->tries = 0;
 
-		voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+		voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
 		/* Compute new train_set as requested by sink */
-		dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
-		dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+		dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set);
+
+		radeon_dp_update_vs_emph(dp_info);
 	}
-	if (!clock_recovery)
+	if (!clock_recovery) {
 		DRM_ERROR("clock recovery failed\n");
-	else
+		return -1;
+	} else {
 		DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n",
-			  train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
-			  (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+			  dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+			  (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
 			  DP_TRAIN_PRE_EMPHASIS_SHIFT);
+		return 0;
+	}
+}
 
+static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info)
+{
+	bool channel_eq;
 
-	/* set training pattern 2 on the sink */
-	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
-	/* set training pattern 2 on the source */
-	if (ASIC_IS_DCE4(rdev))
-		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2, 0);
+	if (dp_info->tp3_supported)
+		radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_3);
 	else
-		radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
-					  dig_connector->dp_clock, enc_id, 1);
+		radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_2);
 
 	/* channel equalization loop */
-	tries = 0;
+	dp_info->tries = 0;
 	channel_eq = false;
-	for (;;) {
-		udelay(400);
-		if (!atom_dp_get_link_status(radeon_connector, link_status))
+	while (1) {
+		if (dp_info->rd_interval == 0)
+			udelay(400);
+		else
+			mdelay(dp_info->rd_interval * 4);
+
+		if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status))
 			break;
 
-		if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) {
+		if (dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) {
 			channel_eq = true;
 			break;
 		}
 
 		/* Try 5 times */
-		if (tries > 5) {
+		if (dp_info->tries > 5) {
 			DRM_ERROR("channel eq failed: 5 tries\n");
 			break;
 		}
 
 		/* Compute new train_set as requested by sink */
-		dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
-		dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+		dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set);
 
-		tries++;
+		radeon_dp_update_vs_emph(dp_info);
+		dp_info->tries++;
 	}
 
-	if (!channel_eq)
+	if (!channel_eq) {
 		DRM_ERROR("channel eq failed\n");
-	else
+		return -1;
+	} else {
 		DRM_DEBUG_KMS("channel eq at voltage %d pre-emphasis %d\n",
-			  train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
-			  (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+			  dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+			  (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
 			  >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
-
-	/* disable the training pattern on the sink */
-	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
-
-	/* disable the training pattern on the source */
-	if (ASIC_IS_DCE4(rdev))
-		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
-	else
-		radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
-					  dig_connector->dp_clock, enc_id, 0);
+		return 0;
+	}
 }
 
-int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
-			 u8 write_byte, u8 *read_byte)
+void radeon_dp_link_train(struct drm_encoder *encoder,
+			  struct drm_connector *connector)
 {
-	struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
-	struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
-	u16 address = algo_data->address;
-	u8 msg[5];
-	u8 reply[2];
-	unsigned retry;
-	int msg_bytes;
-	int reply_bytes = 1;
-	int ret;
-	u8 ack;
-
-	/* Set up the command byte */
-	if (mode & MODE_I2C_READ)
-		msg[2] = AUX_I2C_READ << 4;
-	else
-		msg[2] = AUX_I2C_WRITE << 4;
-
-	if (!(mode & MODE_I2C_STOP))
-		msg[2] |= AUX_I2C_MOT << 4;
-
-	msg[0] = address;
-	msg[1] = address >> 8;
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct radeon_encoder_atom_dig *dig;
+	struct radeon_connector *radeon_connector;
+	struct radeon_connector_atom_dig *dig_connector;
+	struct radeon_dp_link_train_info dp_info;
+ 	u8 tmp;
 
-	switch (mode) {
-	case MODE_I2C_WRITE:
-		msg_bytes = 5;
-		msg[3] = msg_bytes << 4;
-		msg[4] = write_byte;
-		break;
-	case MODE_I2C_READ:
-		msg_bytes = 4;
-		msg[3] = msg_bytes << 4;
-		break;
-	default:
-		msg_bytes = 4;
-		msg[3] = 3 << 4;
-		break;
-	}
+	if (!radeon_encoder->enc_priv)
+		return;
+	dig = radeon_encoder->enc_priv;
 
-	for (retry = 0; retry < 4; retry++) {
-		ret = radeon_process_aux_ch(auxch,
-					    msg, msg_bytes, reply, reply_bytes, 0, &ack);
-		if (ret < 0) {
-			DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
-			return ret;
-		}
+	radeon_connector = to_radeon_connector(connector);
+	if (!radeon_connector->con_priv)
+		return;
+	dig_connector = radeon_connector->con_priv;
 
-		switch (ack & AUX_NATIVE_REPLY_MASK) {
-		case AUX_NATIVE_REPLY_ACK:
-			/* I2C-over-AUX Reply field is only valid
-			 * when paired with AUX ACK.
-			 */
-			break;
-		case AUX_NATIVE_REPLY_NACK:
-			DRM_DEBUG_KMS("aux_ch native nack\n");
-			return -EREMOTEIO;
-		case AUX_NATIVE_REPLY_DEFER:
-			DRM_DEBUG_KMS("aux_ch native defer\n");
-			udelay(400);
-			continue;
-		default:
-			DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack);
-			return -EREMOTEIO;
-		}
+	if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) &&
+	    (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP))
+		return;
 
-		switch (ack & AUX_I2C_REPLY_MASK) {
-		case AUX_I2C_REPLY_ACK:
-			if (mode == MODE_I2C_READ)
-				*read_byte = reply[0];
-			return ret;
-		case AUX_I2C_REPLY_NACK:
-			DRM_DEBUG_KMS("aux_i2c nack\n");
-			return -EREMOTEIO;
-		case AUX_I2C_REPLY_DEFER:
-			DRM_DEBUG_KMS("aux_i2c defer\n");
-			udelay(400);
-			break;
-		default:
-			DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack);
-			return -EREMOTEIO;
-		}
-	}
+	dp_info.enc_id = 0;
+	if (dig->dig_encoder)
+		dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
+	else
+		dp_info.enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
+	if (dig->linkb)
+		dp_info.enc_id |= ATOM_DP_CONFIG_LINK_B;
+	else
+		dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A;
 
-	DRM_ERROR("aux i2c too many retries, giving up\n");
-	return -EREMOTEIO;
+	dp_info.rd_interval = radeon_read_dpcd_reg(radeon_connector, DP_TRAINING_AUX_RD_INTERVAL);
+	tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT);
+	if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED))
+		dp_info.tp3_supported = true;
+	else
+		dp_info.tp3_supported = false;
+
+	memcpy(dp_info.dpcd, dig_connector->dpcd, 8);
+	dp_info.rdev = rdev;
+	dp_info.encoder = encoder;
+	dp_info.connector = connector;
+	dp_info.radeon_connector = radeon_connector;
+	dp_info.dp_lane_count = dig_connector->dp_lane_count;
+	dp_info.dp_clock = dig_connector->dp_clock;
+
+	if (radeon_dp_link_train_init(&dp_info))
+		goto done;
+	if (radeon_dp_link_train_cr(&dp_info))
+		goto done;
+	if (radeon_dp_link_train_ce(&dp_info))
+		goto done;
+done:
+	if (radeon_dp_link_train_finish(&dp_info))
+		return;
 }
-
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 356feea..6c9e17f 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -59,7 +59,7 @@ void radeon_connector_hotplug(struct drm_connector *connector)
 		    (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_eDP)) {
 			if (radeon_dp_needs_link_train(radeon_connector)) {
 				if (connector->encoder)
-					dp_link_train(connector->encoder, connector);
+					radeon_dp_link_train(connector->encoder, connector);
 			}
 		}
 	}
@@ -1195,7 +1195,7 @@ static int radeon_dp_mode_valid(struct drm_connector *connector,
 
 	if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
 	    (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
-		return radeon_dp_mode_valid_helper(radeon_connector, mode);
+		return radeon_dp_mode_valid_helper(connector, mode);
 	else
 		return MODE_OK;
 }
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index 11d7b33..73efb4e 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -1417,7 +1417,9 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
 								     ATOM_TRANSMITTER_ACTION_POWER_ON);
 					radeon_dig_connector->edp_on = true;
 				}
-				dp_link_train(encoder, connector);
+				if (ASIC_IS_DCE4(rdev))
+					atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
+				radeon_dp_link_train(encoder, connector);
 				if (ASIC_IS_DCE4(rdev))
 					atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
 			}
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index ec2369e..37f57ba 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -471,12 +471,12 @@ extern bool radeon_connector_is_dp12_capable(struct drm_connector *connector);
 
 extern void radeon_connector_hotplug(struct drm_connector *connector);
 extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
-extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
+extern int radeon_dp_mode_valid_helper(struct drm_connector *connector,
 				       struct drm_display_mode *mode);
 extern void radeon_dp_set_link_config(struct drm_connector *connector,
 				      struct drm_display_mode *mode);
-extern void dp_link_train(struct drm_encoder *encoder,
-			  struct drm_connector *connector);
+extern void radeon_dp_link_train(struct drm_encoder *encoder,
+				 struct drm_connector *connector);
 extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
 extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
 extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode);
-- 
1.7.1.1



More information about the dri-devel mailing list