drm/radeon/kms: rewrite DP handling
authorAlex Deucher <alexdeucher@gmail.com>
Fri, 20 May 2011 08:34:28 +0000 (04:34 -0400)
committerDave Airlie <airlied@redhat.com>
Fri, 20 May 2011 10:02:32 +0000 (20:02 +1000)
- 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@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_encoders.c
drivers/gpu/drm/radeon/radeon_mode.h

index 444954d958294166f4b5abfe32469660d057c6b6..5f2ddcd5f1e4fd05eea91a6b407cb05bd49a7536 100644 (file)
@@ -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;
 }
-
index 356feea41444e0b4b09f668b850d14f1dbf71eec..6c9e17f3970ed984367468bd021f56ab59b84a22 100644 (file)
@@ -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;
 }
index 11d7b33472d3819decef631048fa88c10df3a018..73efb4e0b8cd48edb03e543f10986a96d937c7ca 100644 (file)
@@ -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);
                        }
index ec2369ee7cc79e85cc634934cd22296a28eb3010..37f57baa68f880c12f65113e2f4dc998a308e02d 100644 (file)
@@ -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);