Hi,

I have a T460 Lenovo model that, when connected to an external monitor
via a docking station, freezes the machine and produces the "snowflake"
effect on the monitor. Last time I saw this was in 2010 when working on
suspend-resume :)

After a month of fighting to fix the issue I came up with this drm
update. The code bellow includes commits taken directly from the Linux
kernel, no local modifications.

Notable changes are:
        - improved DP link training by renegotiating with different rates and
          lane values
        - caching of source, sink and common rates
        - max link rate limiting and related calculation changes
        - switch to long and short pulse interrupts

I have been running succesfully with this on both the affected model and
on the x250 that I used with a dozen monitors, projectors and the like
at uni. No problems so far, but would appreciate test repaorts on a
wider range of hardware.

Comments? Mistakes? How should we proceed to get this in the tree?

Paul

diff --git sys/dev/pci/drm/drm_crtc.c sys/dev/pci/drm/drm_crtc.c
index 874388fdd23..97ada314533 100644
--- sys/dev/pci/drm/drm_crtc.c
+++ sys/dev/pci/drm/drm_crtc.c
@@ -80,6 +80,11 @@ static const struct drm_prop_enum_list 
drm_plane_type_enum_list[] = {
        { DRM_PLANE_TYPE_CURSOR, "Cursor" },
 };
 
+static const struct drm_prop_enum_list drm_link_status_enum_list[] = {
+       { DRM_MODE_LINK_STATUS_GOOD, "Good" },
+       { DRM_MODE_LINK_STATUS_BAD, "Bad" },
+};
+
 /*
  * Optional properties
  */
@@ -1394,6 +1399,13 @@ static int drm_mode_create_standard_properties(struct 
drm_device *dev)
                return -ENOMEM;
        dev->mode_config.tile_property = prop;
 
+       prop = drm_property_create_enum(dev, 0, "link-status",
+                                       drm_link_status_enum_list,
+                                       ARRAY_SIZE(drm_link_status_enum_list));
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.link_status_property = prop;
+
        prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
                                        "type", drm_plane_type_enum_list,
                                        ARRAY_SIZE(drm_plane_type_enum_list));
@@ -4845,6 +4857,35 @@ int drm_mode_connector_property_set_ioctl(struct 
drm_device *dev,
        return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
 }
 
+/**
+ * drm_mode_connector_set_link_status_property - Set link status property of a 
connector
+ * @connector: drm connector
+ * @link_status: new value of link status property (0: Good, 1: Bad)
+ *
+ * In usual working scenario, this link status property will always be set to
+ * "GOOD". If something fails during or after a mode set, the kernel driver
+ * may set this link status property to "BAD". The caller then needs to send a
+ * hotplug uevent for userspace to re-check the valid modes through
+ * GET_CONNECTOR_IOCTL and retry modeset.
+ *
+ * Note: Drivers cannot rely on userspace to support this property and
+ * issue a modeset. As such, they may choose to handle issues (like
+ * re-training a link) without userspace's intervention.
+ *
+ * The reason for adding this property is to handle link training failures, but
+ * it is not limited to DP or link training. For example, if we implement
+ * asynchronous setcrtc, this property can be used to report any failures in 
that.
+ */
+void drm_mode_connector_set_link_status_property(struct drm_connector 
*connector,
+                                                uint64_t link_status)
+{
+       struct drm_device *dev = connector->dev;
+
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+       connector->state->link_status = link_status;
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
+}
+
 static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
                                           struct drm_property *property,
                                           uint64_t value)
diff --git sys/dev/pci/drm/drm_crtc.h sys/dev/pci/drm/drm_crtc.h
index bc5474cb422..480100d6c9e 100644
--- sys/dev/pci/drm/drm_crtc.h
+++ sys/dev/pci/drm/drm_crtc.h
@@ -126,6 +126,12 @@ enum subpixel_order {
        SubPixelNone,
 };
 
+enum drm_link_status {
+       DRM_LINK_STATUS_GOOD = DRM_MODE_LINK_STATUS_GOOD,
+       DRM_LINK_STATUS_BAD = DRM_MODE_LINK_STATUS_BAD,
+};
+
+
 #define DRM_COLOR_FORMAT_RGB444                (1<<0)
 #define DRM_COLOR_FORMAT_YCRCB444      (1<<1)
 #define DRM_COLOR_FORMAT_YCRCB422      (1<<2)
@@ -497,6 +503,8 @@ struct drm_connector_state {
 
        struct drm_encoder *best_encoder;
 
+       enum drm_link_status link_status;
+
        struct drm_atomic_state *state;
 };
 
@@ -1112,6 +1120,7 @@ struct drm_mode_config {
        struct drm_property *dpms_property;
        struct drm_property *path_property;
        struct drm_property *tile_property;
+       struct drm_property *link_status_property;
        struct drm_property *plane_type_property;
        struct drm_property *rotation_property;
        struct drm_property *prop_src_x;
@@ -1509,6 +1518,8 @@ extern struct drm_property 
*drm_mode_create_rotation_property(struct drm_device
                                                              unsigned int 
supported_rotations);
 extern unsigned int drm_rotation_simplify(unsigned int rotation,
                                          unsigned int supported_rotations);
+extern void drm_mode_connector_set_link_status_property(struct drm_connector 
*connector,
+                                                uint64_t link_status);
 
 /* Helpers */
 
diff --git sys/dev/pci/drm/drm_dp_helper.c sys/dev/pci/drm/drm_dp_helper.c
index 790bc4a194d..8b7b59089d4 100644
--- sys/dev/pci/drm/drm_dp_helper.c
+++ sys/dev/pci/drm/drm_dp_helper.c
@@ -182,8 +182,8 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 
request,
                              unsigned int offset, void *buffer, size_t size)
 {
        struct drm_dp_aux_msg msg;
-       unsigned int retry;
-       int err = 0;
+       unsigned int retry, native_reply;
+       int err = 0, ret = 0;
 
        memset(&msg, 0, sizeof(msg));
        msg.address = offset;
@@ -200,40 +200,42 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 
request,
         * sufficient, bump to 32 which makes Dell 4k monitors happier.
         */
        for (retry = 0; retry < 32; retry++) {
-
-               err = aux->transfer(aux, &msg);
-               if (err < 0) {
-                       if (err == -EBUSY)
-                               continue;
-
-                       goto unlock;
+               if (ret != 0 && ret != -ETIMEDOUT) {
+                       usleep_range(AUX_RETRY_INTERVAL,
+                                    AUX_RETRY_INTERVAL + 100);
                }
 
+               ret = aux->transfer(aux, &msg);
 
-               switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
-               case DP_AUX_NATIVE_REPLY_ACK:
-                       if (err < size)
-                               err = -EPROTO;
-                       goto unlock;
+               if (ret >= 0) {
+                       native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK;
+                       if (native_reply == DP_AUX_NATIVE_REPLY_ACK) {
+                               if (ret == size)
+                                       goto unlock;
 
-               case DP_AUX_NATIVE_REPLY_NACK:
-                       err = -EIO;
-                       goto unlock;
-
-               case DP_AUX_NATIVE_REPLY_DEFER:
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 
100);
-                       break;
+                               ret = -EPROTO;
+                       } else
+                               ret = -EIO;
                }
+
+               /*
+                * We want the error we return to be the error we received on
+                * the first transaction, since we may get a different error the
+                * next time we retry
+                */
+               if (!err)
+                       err = ret;
        }
 
-       DRM_DEBUG_KMS("too many retries, giving up\n");
-       err = -EIO;
+       DRM_DEBUG_KMS("Too many retries, giving up. First error: %d\n", err);
+       ret = err;
 
 unlock:
        mutex_unlock(&aux->hw_mutex);
-       return err;
+       return ret;
 }
 
+
 /**
  * drm_dp_dpcd_read() - read a series of bytes from the DPCD
  * @aux: DisplayPort AUX channel
diff --git sys/dev/pci/drm/drm_dp_helper.h sys/dev/pci/drm/drm_dp_helper.h
index 44e719d753d..32fb9c3816e 100644
--- sys/dev/pci/drm/drm_dp_helper.h
+++ sys/dev/pci/drm/drm_dp_helper.h
@@ -589,6 +589,7 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 
link_status[DP_LINK_STATUS_SI
 #define DP_BRANCH_OUI_HEADER_SIZE      0xc
 #define DP_RECEIVER_CAP_SIZE           0xf
 #define EDP_PSR_RECEIVER_CAP_SIZE      2
+#define EDP_DISPLAY_CTL_CAP_SIZE       3
 
 void drm_dp_link_train_clock_recovery_delay(const u8 
dpcd[DP_RECEIVER_CAP_SIZE]);
 void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
@@ -649,6 +650,12 @@ drm_dp_tps3_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
                dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED;
 }
 
+static inline bool
+drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT;
+}
+
 /*
  * DisplayPort AUX channel
  */
diff --git sys/dev/pci/drm/drm_mode.h sys/dev/pci/drm/drm_mode.h
index 772a78e0667..441146d4f0d 100644
--- sys/dev/pci/drm/drm_mode.h
+++ sys/dev/pci/drm/drm_mode.h
@@ -103,6 +103,10 @@
 #define DRM_MODE_DIRTY_ON       1
 #define DRM_MODE_DIRTY_ANNOTATE 2
 
+/* Link Status options */
+#define DRM_MODE_LINK_STATUS_GOOD      0
+#define DRM_MODE_LINK_STATUS_BAD       1
+
 struct drm_mode_modeinfo {
        __u32 clock;
        __u16 hdisplay;
diff --git sys/dev/pci/drm/i915/i915_drv.c sys/dev/pci/drm/i915/i915_drv.c
index aa3361110a1..9fe4e645758 100644
--- sys/dev/pci/drm/i915/i915_drv.c
+++ sys/dev/pci/drm/i915/i915_drv.c
@@ -389,6 +389,7 @@ static const struct intel_device_info 
intel_skylake_gt3_info = {
 static const struct intel_device_info intel_broxton_info = {
        .is_preliminary = 1,
        .is_broxton = 1,
+       .is_lp = 1,
        .gen = 9,
        .need_gfx_hws = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
diff --git sys/dev/pci/drm/i915/i915_drv.h sys/dev/pci/drm/i915/i915_drv.h
index bb8b2ddd35a..7b10520acf8 100644
--- sys/dev/pci/drm/i915/i915_drv.h
+++ sys/dev/pci/drm/i915/i915_drv.h
@@ -852,6 +852,7 @@ struct intel_csr {
        func(is_broxton) sep \
        func(is_kabylake) sep \
        func(is_preliminary) sep \
+       func(is_lp) sep \
        func(has_fbc) sep \
        func(has_pipe_cxsr) sep \
        func(has_hotplug) sep \
@@ -2685,6 +2686,9 @@ struct drm_i915_cmd_table {
 #define IS_GEN8(dev)   (INTEL_INFO(dev)->gen == 8)
 #define IS_GEN9(dev)   (INTEL_INFO(dev)->gen == 9)
 
+#define IS_LP(dev)     (INTEL_INFO(dev)->is_lp)
+#define IS_GEN9_LP(dev)        (IS_GEN9(dev) && IS_LP(dev))
+
 #define RENDER_RING            (1<<RCS)
 #define BSD_RING               (1<<VCS)
 #define BLT_RING               (1<<BCS)
diff --git sys/dev/pci/drm/i915/i915_reg.h sys/dev/pci/drm/i915/i915_reg.h
index adb3a297ce7..b564f716c4d 100644
--- sys/dev/pci/drm/i915/i915_reg.h
+++ sys/dev/pci/drm/i915/i915_reg.h
@@ -1439,6 +1439,7 @@ enum skl_disp_power_wells {
 #define BALANCE_LEG_MASK(port)         (7<<(8+3*(port)))
 /* Balance leg disable bits */
 #define BALANCE_LEG_DISABLE_SHIFT      23
+#define BALANCE_LEG_DISABLE(port)      (1 << (23 + (port)))
 
 /*
  * Fence registers
diff --git sys/dev/pci/drm/i915/intel_crt.c sys/dev/pci/drm/i915/intel_crt.c
index 3c273cf24ca..191f2de66ba 100644
--- sys/dev/pci/drm/i915/intel_crt.c
+++ sys/dev/pci/drm/i915/intel_crt.c
@@ -218,21 +218,34 @@ intel_crt_mode_valid(struct drm_connector *connector,
                     struct drm_display_mode *mode)
 {
        struct drm_device *dev = connector->dev;
+       //struct drm_i915_private *dev_priv = to_i915(dev);
+       //int max_dotclk = dev_priv->max_dotclk_freq;
+       int max_clock;
 
-       int max_clock = 0;
        if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
                return MODE_NO_DBLESCAN;
 
        if (mode->clock < 25000)
                return MODE_CLOCK_LOW;
 
-       if (IS_GEN2(dev))
-               max_clock = 350000;
-       else
+       if (HAS_PCH_LPT(dev))
+               max_clock = 180000;
+       else if (IS_VALLEYVIEW(dev))
+               /*
+                * 270 MHz due to current DPLL limits,
+                * DAC limit supposedly 355 MHz.
+                */
+               max_clock = 270000;
+       else if (IS_GEN3(dev) || IS_GEN4(dev))
                max_clock = 400000;
+       else
+               max_clock = 350000;
        if (mode->clock > max_clock)
                return MODE_CLOCK_HIGH;
 
+       //if (mode->clock > max_dotclk)
+       //      return MODE_CLOCK_HIGH;
+
        /* The FDI receiver on LPT only supports 8bpc and only has 2 lanes. */
        if (HAS_PCH_LPT(dev) &&
            (ironlake_get_lanes_required(mode->clock, 270000, 24) > 2))
diff --git sys/dev/pci/drm/i915/intel_ddi.c sys/dev/pci/drm/i915/intel_ddi.c
index 6f66b10e82f..d5512afb22d 100644
--- sys/dev/pci/drm/i915/intel_ddi.c
+++ sys/dev/pci/drm/i915/intel_ddi.c
@@ -2087,15 +2087,31 @@ void intel_ddi_disable_pipe_clock(struct intel_crtc 
*intel_crtc)
                           TRANS_CLK_SEL_DISABLED);
 }
 
-static void skl_ddi_set_iboost(struct drm_device *dev, u32 level,
-                              enum port port, int type)
+static void _skl_ddi_set_iboost(struct drm_i915_private *dev_priv,
+                               enum port port, uint8_t iboost)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 tmp;
+
+       tmp = I915_READ(DISPIO_CR_TX_BMU_CR0);
+       tmp &= ~(BALANCE_LEG_MASK(port) | BALANCE_LEG_DISABLE(port));
+       if (iboost)
+               tmp |= iboost << BALANCE_LEG_SHIFT(port);
+       else
+               tmp |= BALANCE_LEG_DISABLE(port);
+       I915_WRITE(DISPIO_CR_TX_BMU_CR0, tmp);
+}
+
+static void skl_ddi_set_iboost(struct intel_encoder *encoder,
+                              int level, enum intel_output_type type)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct intel_digital_port *intel_dig_port = 
enc_to_dig_port(&encoder->base);
+       struct drm_i915_private *dev_priv = 
to_i915(intel_dig_port->base.base.dev);
+       enum port port = intel_dig_port->port;
        const struct ddi_buf_trans *ddi_translations;
        uint8_t iboost;
        uint8_t dp_iboost, hdmi_iboost;
        int n_entries;
-       u32 reg;
 
        /* VBT may override standard boost values */
        dp_iboost = dev_priv->vbt.ddi_port_info[port].dp_boost_level;
@@ -2132,16 +2148,10 @@ static void skl_ddi_set_iboost(struct drm_device *dev, 
u32 level,
                return;
        }
 
-       reg = I915_READ(DISPIO_CR_TX_BMU_CR0);
-       reg &= ~BALANCE_LEG_MASK(port);
-       reg &= ~(1 << (BALANCE_LEG_DISABLE_SHIFT + port));
+       _skl_ddi_set_iboost(dev_priv, port, iboost);
 
-       if (iboost)
-               reg |= iboost << BALANCE_LEG_SHIFT(port);
-       else
-               reg |= 1 << (BALANCE_LEG_DISABLE_SHIFT + port);
-
-       I915_WRITE(DISPIO_CR_TX_BMU_CR0, reg);
+       if (port == PORT_A && intel_dig_port->max_lanes == 4)
+               _skl_ddi_set_iboost(dev_priv, PORT_E, iboost);
 }
 
 static void bxt_ddi_vswing_sequence(struct drm_device *dev, u32 level,
@@ -2273,7 +2283,7 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp)
        level = translate_signal_level(signal_levels);
 
        if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev))
-               skl_ddi_set_iboost(dev, level, port, encoder->type);
+               skl_ddi_set_iboost(encoder, level, encoder->type);
        else if (IS_BROXTON(dev))
                bxt_ddi_vswing_sequence(dev, level, port, encoder->type);
 
@@ -3260,6 +3270,33 @@ void intel_ddi_init(struct drm_device *dev, enum port 
port)
        struct intel_encoder *intel_encoder;
        struct drm_encoder *encoder;
        bool init_hdmi, init_dp;
+       int max_lanes;
+
+       if (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES) {
+               switch (port) {
+               case PORT_A:
+                       max_lanes = 4;
+                       break;
+               case PORT_E:
+                       max_lanes = 0;
+                       break;
+               default:
+                       max_lanes = 4;
+                       break;
+               }
+       } else {
+               switch (port) {
+               case PORT_A:
+                       max_lanes = 2;
+                       break;
+               case PORT_E:
+                       max_lanes = 2;
+                       break;
+               default:
+                       max_lanes = 4;
+                       break;
+               }
+       }
 
        init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi ||
                     dev_priv->vbt.ddi_port_info[port].supports_hdmi);
@@ -3294,6 +3331,8 @@ void intel_ddi_init(struct drm_device *dev, enum port 
port)
                                          (DDI_BUF_PORT_REVERSAL |
                                           DDI_A_4_LANES);
 
+       intel_dig_port->max_lanes = max_lanes;
+
        intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
        intel_encoder->cloneable = 0;
diff --git sys/dev/pci/drm/i915/intel_display.c 
sys/dev/pci/drm/i915/intel_display.c
index 67570ec28e1..6c5fd19b0b1 100644
--- sys/dev/pci/drm/i915/intel_display.c
+++ sys/dev/pci/drm/i915/intel_display.c
@@ -228,9 +228,12 @@ static void intel_update_czclk(struct drm_i915_private 
*dev_priv)
 }
 
 static inline u32 /* units of 100MHz */
-intel_fdi_link_freq(struct drm_device *dev)
+intel_fdi_link_freq(struct drm_device *dev,
+                   const struct intel_crtc_state *pipe_config)
 {
-       if (IS_GEN5(dev)) {
+       if (HAS_DDI(dev))
+               return pipe_config->port_clock; /* SPLL */
+       else if (IS_GEN5(dev)) {
                struct drm_i915_private *dev_priv = dev->dev_private;
                return (I915_READ(FDI_PLL_BIOS_0) & FDI_PLL_FB_CLOCK_MASK) + 2;
        } else
@@ -2099,6 +2102,18 @@ static void lpt_disable_pch_transcoder(struct 
drm_i915_private *dev_priv)
        I915_WRITE(TRANS_CHICKEN2(PIPE_A), val);
 }
 
+enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+
+       WARN_ON(!crtc->config->has_pch_encoder);
+
+       if (HAS_PCH_LPT(dev_priv))
+               return PIPE_A;
+       else
+               return crtc->pipe;
+}
+
 /**
  * intel_enable_pipe - enable a pipe, asserting requirements
  * @crtc: crtc responsible for the pipe
@@ -6591,7 +6606,7 @@ retry:
         * Hence the bw of each lane in terms of the mode signal
         * is:
         */
-       link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
+       link_bw = intel_fdi_link_freq(dev, pipe_config) * MHz(100)/KHz(1)/10;
 
        fdi_dotclock = adjusted_mode->crtc_clock;
 
@@ -10696,7 +10711,7 @@ static void ironlake_pch_clock_get(struct intel_crtc 
*crtc,
         * get_config() function.
         */
        pipe_config->base.adjusted_mode.crtc_clock =
-               intel_dotclock_calculate(intel_fdi_link_freq(dev) * 10000,
+               intel_dotclock_calculate(intel_fdi_link_freq(dev, pipe_config) 
* 10000,
                                         &pipe_config->fdi_m_n);
 }
 
diff --git sys/dev/pci/drm/i915/intel_dp.c sys/dev/pci/drm/i915/intel_dp.c
index ac2d714d68a..1f239cf4095 100644
--- sys/dev/pci/drm/i915/intel_dp.c
+++ sys/dev/pci/drm/i915/intel_dp.c
@@ -41,6 +41,8 @@
 #include <dev/pci/drm/i915_drm.h>
 #include "i915_drv.h"
 
+#include <sys/reboot.h>
+
 #define DP_LINK_CHECK_TIMEOUT  (10 * 1000)
 
 /* Compliance test status bits  */
@@ -99,6 +101,15 @@ static const int skl_rates[] = { 162000, 216000, 270000,
                                  324000, 432000, 540000 };
 static const int default_rates[] = { 162000, 270000, 540000 };
 
+static void
+intel_dp_dump_link_status(const uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+
+       DRM_DEBUG_KMS("ln0_1:0x%x ln2_3:0x%x align:0x%x sink:0x%x 
adj_req0_1:0x%x adj_req2_3:0x%x",
+                     link_status[0], link_status[1], link_status[2],
+                     link_status[3], link_status[4], link_status[5]);
+}
+
 /**
  * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
  * @intel_dp: DP struct
@@ -132,31 +143,28 @@ static void vlv_init_panel_power_sequencer(struct 
intel_dp *intel_dp);
 static void vlv_steal_power_sequencer(struct drm_device *dev,
                                      enum pipe pipe);
 
-static unsigned int intel_dp_unused_lane_mask(int lane_count)
+/* update sink rates from dpcd */
+static void intel_dp_set_sink_rates(struct intel_dp *intel_dp)
 {
-       return ~((1 << lane_count) - 1) & 0xf;
-}
+       int i, max_rate;
 
-static int
-intel_dp_max_link_bw(struct intel_dp  *intel_dp)
-{
-       int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
+       max_rate = 
drm_dp_bw_code_to_link_rate(intel_dp->dpcd[DP_MAX_LINK_RATE]);
 
-       switch (max_link_bw) {
-       case DP_LINK_BW_1_62:
-       case DP_LINK_BW_2_7:
-       case DP_LINK_BW_5_4:
-               break;
-       default:
-               WARN(1, "invalid max DP link bw val %x, using 1.62Gbps\n",
-                    max_link_bw);
-               max_link_bw = DP_LINK_BW_1_62;
-               break;
+       for (i = 0; i < ARRAY_SIZE(default_rates); i++) {
+               if (default_rates[i] > max_rate)
+                       break;
+               intel_dp->sink_rates[i] = default_rates[i];
        }
-       return max_link_bw;
+
+       intel_dp->num_sink_rates = i;
 }
 
-static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp)
+static unsigned int intel_dp_unused_lane_mask(int lane_count)
+{
+       return ~((1 << lane_count) - 1) & 0xf;
+}
+
+int intel_dp_max_lane_count(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct drm_device *dev = intel_dig_port->base.base.dev;
@@ -189,16 +197,113 @@ static u8 intel_dp_max_lane_count(struct intel_dp 
*intel_dp)
  * get the result in decakilobits instead of kilobits.
  */
 
-static int
+int
 intel_dp_link_required(int pixel_clock, int bpp)
 {
-       return (pixel_clock * bpp + 9) / 10;
+       /* pixel_clock is in kHz, divide bpp by 8 for bit to Byte conversion */
+       return DIV_ROUND_UP(pixel_clock * bpp, 8);
+       //return (pixel_clock * bpp + 9) / 10;
 }
 
-static int
+int
 intel_dp_max_data_rate(int max_link_clock, int max_lanes)
 {
-       return (max_link_clock * max_lanes * 8) / 10;
+       /* max_link_clock is the link symbol clock (LS_Clk) in kHz and not the
+        * link rate that is generally expressed in Gbps. Since, 8 bits of data
+        * is transmitted every LS_Clk per lane, there is no need to account for
+        * the channel encoding that is done in the PHY layer here.
+        */
+
+       return max_link_clock * max_lanes;
+       // return (max_link_clock * max_lanes * 8) / 10;
+}
+
+/* return index of rate in rates array, or -1 if not found */
+static int intel_dp_rate_index(const int *rates, int len, int rate)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               if (rate == rates[i])
+                       return i;
+
+       return -1;
+}
+
+static int intersect_rates(const int *source_rates, int source_len,
+                          const int *sink_rates, int sink_len,
+                          int *common_rates)
+{
+       int i = 0, j = 0, k = 0;
+
+       while (i < source_len && j < sink_len) {
+               if (source_rates[i] == sink_rates[j]) {
+                       if (WARN_ON(k >= DP_MAX_SUPPORTED_RATES))
+                               return k;
+                       common_rates[k] = source_rates[i];
+                       ++k;
+                       ++i;
+                       ++j;
+               } else if (source_rates[i] < sink_rates[j]) {
+                       ++i;
+               } else {
+                       ++j;
+               }
+       }
+       return k;
+}
+
+static void snprintf_int_array(char *str, size_t len,
+                              const int *array, int nelem)
+{
+       int i;
+
+       str[0] = '\0';
+
+       for (i = 0; i < nelem; i++) {
+               int r = snprintf(str, len, "%s%d", i ? ", " : "", array[i]);
+               if (r >= len)
+                       return;
+               str += r;
+               len -= r;
+       }
+}
+
+static void intel_dp_print_rates(struct intel_dp *intel_dp)
+{
+       char str[128]; /* FIXME: too big for stack? */
+
+       if ((drm_debug & DRM_UT_KMS) == 0)
+               return;
+
+       snprintf_int_array(str, sizeof(str),
+                          intel_dp->source_rates, intel_dp->num_source_rates);
+       DRM_DEBUG_KMS("source rates: %s\n", str);
+
+       snprintf_int_array(str, sizeof(str),
+                          intel_dp->sink_rates, intel_dp->num_sink_rates);
+       DRM_DEBUG_KMS("sink rates: %s\n", str);
+
+       snprintf_int_array(str, sizeof(str),
+                          intel_dp->common_rates, intel_dp->num_common_rates);
+       DRM_DEBUG_KMS("common rates: %s\n", str);
+}
+
+static void intel_dp_set_common_rates(struct intel_dp *intel_dp)
+{
+       WARN_ON(!intel_dp->num_source_rates || !intel_dp->num_sink_rates);
+
+       intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates,
+                                                    intel_dp->num_source_rates,
+                                                    intel_dp->sink_rates,
+                                                    intel_dp->num_sink_rates,
+                                                    intel_dp->common_rates);
+
+       /* Paranoia, there should always be something in common. */
+       if (WARN_ON(intel_dp->num_common_rates == 0)) {
+               intel_dp->common_rates[0] = default_rates[0];
+               intel_dp->num_common_rates = 1;
+       }
 }
 
 static enum drm_mode_status
@@ -1183,53 +1288,11 @@ hsw_dp_set_ddi_pll_sel(struct intel_crtc_state 
*pipe_config)
        }
 }
 
-static int
-intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
-{
-       if (intel_dp->num_sink_rates) {
-               *sink_rates = intel_dp->sink_rates;
-               return intel_dp->num_sink_rates;
-       }
-
-       *sink_rates = default_rates;
-
-       return (intel_dp_max_link_bw(intel_dp) >> 3) + 1;
-}
-
-static bool intel_dp_source_supports_hbr2(struct drm_device *dev)
-{
-       /* WaDisableHBR2:skl */
-       if (IS_SKL_REVID(dev, 0, SKL_REVID_B0))
-               return false;
-
-       if ((IS_HASWELL(dev) && !IS_HSW_ULX(dev)) || IS_BROADWELL(dev) ||
-           (INTEL_INFO(dev)->gen >= 9))
-               return true;
-       else
-               return false;
-}
-
-static int
-intel_dp_source_rates(struct drm_device *dev, const int **source_rates)
+static bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
 {
-       int size;
-
-       if (IS_BROXTON(dev)) {
-               *source_rates = bxt_rates;
-               size = ARRAY_SIZE(bxt_rates);
-       } else if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
-               *source_rates = skl_rates;
-               size = ARRAY_SIZE(skl_rates);
-       } else {
-               *source_rates = default_rates;
-               size = ARRAY_SIZE(default_rates);
-       }
+       int max_rate = intel_dp->source_rates[intel_dp->num_source_rates - 1];
 
-       /* This depends on the fact that 5.4 is last value in the array */
-       if (!intel_dp_source_supports_hbr2(dev))
-               size--;
-
-       return size;
+       return max_rate >= 540000;
 }
 
 static void
@@ -1265,117 +1328,135 @@ intel_dp_set_clock(struct intel_encoder *encoder,
        }
 }
 
-static int intersect_rates(const int *source_rates, int source_len,
-                          const int *sink_rates, int sink_len,
-                          int *common_rates)
+static void
+intel_dp_set_source_rates(struct intel_dp *intel_dp)
 {
-       int i = 0, j = 0, k = 0;
+       struct drm_device *dev = intel_dp_to_dev(intel_dp);
+       const int *source_rates;
+       int size;
 
-       while (i < source_len && j < sink_len) {
-               if (source_rates[i] == sink_rates[j]) {
-                       if (WARN_ON(k >= DP_MAX_SUPPORTED_RATES))
-                               return k;
-                       common_rates[k] = source_rates[i];
-                       ++k;
-                       ++i;
-                       ++j;
-               } else if (source_rates[i] < sink_rates[j]) {
-                       ++i;
-               } else {
-                       ++j;
-               }
+       /* This should only be done once */
+       WARN_ON(intel_dp->source_rates || intel_dp->num_source_rates);
+
+       if (IS_BROXTON(dev)) {
+               source_rates = bxt_rates;
+               size = ARRAY_SIZE(bxt_rates);
+       } else if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
+               source_rates = skl_rates;
+               size = ARRAY_SIZE(skl_rates);
+       } else if ((IS_HASWELL(dev) && !IS_HSW_ULX(dev)) ||
+                  IS_BROADWELL(dev)) {
+               source_rates = default_rates;
+               size = ARRAY_SIZE(default_rates);
+       } else {
+               source_rates = default_rates;
+               size = ARRAY_SIZE(default_rates) - 1;
        }
-       return k;
+
+       intel_dp->source_rates = source_rates;
+       intel_dp->num_source_rates = size;
 }
 
-static int intel_dp_common_rates(struct intel_dp *intel_dp,
-                                int *common_rates)
+/* get length of common rates potentially limited by max_rate */
+static int intel_dp_common_len_rate_limit(struct intel_dp *intel_dp,
+                                         int max_rate)
 {
-       struct drm_device *dev = intel_dp_to_dev(intel_dp);
-       const int *source_rates, *sink_rates;
-       int source_len, sink_len;
+       const int *common_rates = intel_dp->common_rates;
+       int i, common_len = intel_dp->num_common_rates;
 
-       sink_len = intel_dp_sink_rates(intel_dp, &sink_rates);
-       source_len = intel_dp_source_rates(dev, &source_rates);
+       /* Limit results by potentially reduced max rate */
+       for (i = 0; i < common_len; i++) {
+               if (common_rates[common_len - i - 1] <= max_rate)
+                       return common_len - i;
+       }
 
-       return intersect_rates(source_rates, source_len,
-                              sink_rates, sink_len,
-                              common_rates);
+       return 0;
 }
 
-static void snprintf_int_array(char *str, size_t len,
-                              const int *array, int nelem)
+static bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int 
link_rate,
+                                      uint8_t lane_count)
 {
-       int i;
+       /*
+        * FIXME: we need to synchronize the current link parameters with
+        * hardware readout. Currently fast link training doesn't work on
+        * boot-up.
+        */
+       if (link_rate == 0 ||
+           link_rate > intel_dp->max_link_rate)
+               return false;
 
-       str[0] = '\0';
+       if (lane_count == 0 ||
+           lane_count > intel_dp_max_lane_count(intel_dp))
+               return false;
 
-       for (i = 0; i < nelem; i++) {
-               int r = snprintf(str, len, "%s%d", i ? ", " : "", array[i]);
-               if (r >= len)
-                       return;
-               str += r;
-               len -= r;
-       }
+       return true;
 }
 
-static void intel_dp_print_rates(struct intel_dp *intel_dp)
+/* Theoretical max between source and sink */
+static int intel_dp_max_common_rate(struct intel_dp *intel_dp)
 {
-       struct drm_device *dev = intel_dp_to_dev(intel_dp);
-       const int *source_rates, *sink_rates;
-       int source_len, sink_len, common_len;
-       int common_rates[DP_MAX_SUPPORTED_RATES];
-       char str[128]; /* FIXME: too big for stack? */
-
-       if ((drm_debug & DRM_UT_KMS) == 0)
-               return;
-
-       source_len = intel_dp_source_rates(dev, &source_rates);
-       snprintf_int_array(str, sizeof(str), source_rates, source_len);
-       DRM_DEBUG_KMS("source rates: %s\n", str);
+       return intel_dp->common_rates[intel_dp->num_common_rates - 1];
+}
 
-       sink_len = intel_dp_sink_rates(intel_dp, &sink_rates);
-       snprintf_int_array(str, sizeof(str), sink_rates, sink_len);
-       DRM_DEBUG_KMS("sink rates: %s\n", str);
+/* Theoretical max between source and sink */
+static int intel_dp_max_common_lane_count(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       int source_max = intel_dig_port->max_lanes;
+       int sink_max = drm_dp_max_lane_count(intel_dp->dpcd);
 
-       common_len = intel_dp_common_rates(intel_dp, common_rates);
-       snprintf_int_array(str, sizeof(str), common_rates, common_len);
-       DRM_DEBUG_KMS("common rates: %s\n", str);
+       return min(source_max, sink_max);
 }
 
-static int rate_to_index(int find, const int *rates)
+int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
+                                           int link_rate, uint8_t lane_count)
 {
-       int i = 0;
+       int index;
 
-       for (i = 0; i < DP_MAX_SUPPORTED_RATES; ++i)
-               if (find == rates[i])
-                       break;
+       index = intel_dp_rate_index(intel_dp->common_rates,
+                                   intel_dp->num_common_rates,
+                                   link_rate);
+       if (index > 0) {
+               intel_dp->max_link_rate = intel_dp->common_rates[index - 1];
+               intel_dp->max_link_lane_count = lane_count;
+       } else if (lane_count > 1) {
+               intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
+               intel_dp->max_link_lane_count = lane_count >> 1;
+       } else {
+               DRM_ERROR("Link Training Unsuccessful\n");
+               return -1;
+       }
 
-       return i;
+       return 0;
 }
 
 int
 intel_dp_max_link_rate(struct intel_dp *intel_dp)
 {
-       int rates[DP_MAX_SUPPORTED_RATES] = {};
        int len;
 
-       len = intel_dp_common_rates(intel_dp, rates);
+       len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->max_link_rate);
        if (WARN_ON(len <= 0))
                return 162000;
 
-       return rates[rate_to_index(0, rates) - 1];
+       return intel_dp->common_rates[len - 1];
 }
 
 int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
 {
-       return rate_to_index(rate, intel_dp->sink_rates);
+       int i = intel_dp_rate_index(intel_dp->sink_rates,
+                                   intel_dp->num_sink_rates, rate);
+
+       if (WARN_ON(i < 0))
+               i = 0;
+
+       return i;
 }
 
 static void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
                                  uint8_t *link_bw, uint8_t *rate_select)
 {
-       if (intel_dp->num_sink_rates) {
+       if (intel_dp->use_rate_select) {
                *link_bw = 0;
                *rate_select =
                        intel_dp_rate_select(intel_dp, port_clock);
@@ -1385,6 +1466,23 @@ static void intel_dp_compute_rate(struct intel_dp 
*intel_dp, int port_clock,
        }
 }
 
+static bool intel_edp_compare_alt_mode(struct drm_display_mode *m1,
+                                      struct drm_display_mode *m2)
+{
+       bool bres = false;
+
+       if (m1 && m2)
+               bres = (m1->hdisplay == m2->hdisplay &&
+                       m1->hsync_start == m2->hsync_start &&
+                       m1->hsync_end == m2->hsync_end &&
+                       m1->htotal == m2->htotal &&
+                       m1->vdisplay == m2->vdisplay &&
+                       m1->vsync_start == m2->vsync_start &&
+                       m1->vsync_end == m2->vsync_end &&
+                       m1->vtotal == m2->vtotal);
+       return bres;
+}
+
 bool
 intel_dp_compute_config(struct intel_encoder *encoder,
                        struct intel_crtc_state *pipe_config)
@@ -1404,11 +1502,11 @@ intel_dp_compute_config(struct intel_encoder *encoder,
        int max_clock;
        int bpp, mode_rate;
        int link_avail, link_clock;
-       int common_rates[DP_MAX_SUPPORTED_RATES] = {};
        int common_len;
        uint8_t link_bw, rate_select;
 
-       common_len = intel_dp_common_rates(intel_dp, common_rates);
+       common_len = intel_dp_common_len_rate_limit(intel_dp,
+                                                   intel_dp->max_link_rate);
 
        /* No common link rates between source and sink */
        WARN_ON(common_len <= 0);
@@ -1423,8 +1521,16 @@ intel_dp_compute_config(struct intel_encoder *encoder,
        pipe_config->has_audio = intel_dp->has_audio && port != PORT_A;
 
        if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
-               intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
-                                      adjusted_mode);
+               struct drm_display_mode *panel_mode =
+                       intel_connector->panel.alt_fixed_mode;
+               struct drm_display_mode *req_mode = &pipe_config->base.mode;
+
+               if (!intel_edp_compare_alt_mode(req_mode, panel_mode))
+                       panel_mode = intel_connector->panel.fixed_mode;
+
+               drm_mode_debug_printmodeline(panel_mode);
+
+               intel_fixed_panel_mode(panel_mode, adjusted_mode);
 
                if (INTEL_INFO(dev)->gen >= 9) {
                        int ret;
@@ -1446,7 +1552,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 
        DRM_DEBUG_KMS("DP link computation with max lane count %i "
                      "max bw %d pixel clock %iKHz\n",
-                     max_lane_count, common_rates[max_clock],
+                     max_lane_count, intel_dp->common_rates[max_clock],
                      adjusted_mode->crtc_clock);
 
        /* Walk through all bpp values. Luckily they're all nicely spaced with 2
@@ -1482,7 +1588,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
                                lane_count <= max_lane_count;
                                lane_count <<= 1) {
 
-                               link_clock = common_rates[clock];
+                               link_clock = intel_dp->common_rates[clock];
                                link_avail = intel_dp_max_data_rate(link_clock,
                                                                    lane_count);
 
@@ -1512,7 +1618,7 @@ found:
        pipe_config->lane_count = lane_count;
 
        pipe_config->pipe_bpp = bpp;
-       pipe_config->port_clock = common_rates[clock];
+       pipe_config->port_clock = intel_dp->common_rates[clock];
 
        intel_dp_compute_rate(intel_dp, pipe_config->port_clock,
                              &link_bw, &rate_select);
@@ -2551,17 +2657,27 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
        }
 }
 
+static void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+                                      uint8_t dp_train_pat)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct drm_i915_private *dev_priv =
+               to_i915(intel_dig_port->base.base.dev);
+
+       _intel_dp_set_link_train(intel_dp, &intel_dp->DP, dp_train_pat);
+
+       I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+       POSTING_READ(intel_dp->output_reg);
+}
+
 static void intel_dp_enable_port(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* enable with pattern 1 (as per spec) */
-       _intel_dp_set_link_train(intel_dp, &intel_dp->DP,
-                                DP_TRAINING_PATTERN_1);
-
-       I915_WRITE(intel_dp->output_reg, intel_dp->DP);
-       POSTING_READ(intel_dp->output_reg);
+       intel_dp_program_link_training_pattern(intel_dp, DP_TRAINING_PATTERN_1);
 
        /*
         * Magic for VLV/CHV. We _must_ first set up the register
@@ -3559,11 +3675,12 @@ gen7_edp_signal_levels(uint8_t train_set)
 
 /* Properly updates "DP" with the correct signal levels. */
 static void
-intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
+intel_dp_set_signal_levels(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        enum port port = intel_dig_port->port;
        struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
        uint32_t signal_levels, mask = 0;
        uint8_t train_set = intel_dp->train_set[0];
 
@@ -3598,24 +3715,20 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, 
uint32_t *DP)
                (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) >>
                        DP_TRAIN_PRE_EMPHASIS_SHIFT);
 
-       *DP = (*DP & ~mask) | signal_levels;
+       intel_dp->DP = (intel_dp->DP & ~mask) | signal_levels;
+
+       I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+       POSTING_READ(intel_dp->output_reg);
 }
 
 static bool
 intel_dp_set_link_train(struct intel_dp *intel_dp,
-                       uint32_t *DP,
                        uint8_t dp_train_pat)
 {
-       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
-       struct drm_i915_private *dev_priv =
-               to_i915(intel_dig_port->base.base.dev);
        uint8_t buf[sizeof(intel_dp->train_set) + 1];
        int ret, len;
 
-       _intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
-
-       I915_WRITE(intel_dp->output_reg, *DP);
-       POSTING_READ(intel_dp->output_reg);
+       intel_dp_program_link_training_pattern(intel_dp, dp_train_pat);
 
        buf[0] = dp_train_pat;
        if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
@@ -3634,29 +3747,22 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
        return ret == len;
 }
 
+
 static bool
-intel_dp_reset_link_train(struct intel_dp *intel_dp, uint32_t *DP,
+intel_dp_reset_link_train(struct intel_dp *intel_dp,
                        uint8_t dp_train_pat)
 {
        memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
-       intel_dp_set_signal_levels(intel_dp, DP);
-       return intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
+       intel_dp_set_signal_levels(intel_dp);
+       return intel_dp_set_link_train(intel_dp, dp_train_pat);
 }
 
 static bool
-intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP,
-                          const uint8_t link_status[DP_LINK_STATUS_SIZE])
+intel_dp_update_link_train(struct intel_dp *intel_dp)
 {
-       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
-       struct drm_i915_private *dev_priv =
-               to_i915(intel_dig_port->base.base.dev);
        int ret;
 
-       intel_get_adjust_train(intel_dp, link_status);
-       intel_dp_set_signal_levels(intel_dp, DP);
-
-       I915_WRITE(intel_dp->output_reg, *DP);
-       POSTING_READ(intel_dp->output_reg);
+       intel_dp_set_signal_levels(intel_dp);
 
        ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
                                intel_dp->train_set, intel_dp->lane_count);
@@ -3695,16 +3801,26 @@ static void intel_dp_set_idle_link_train(struct 
intel_dp *intel_dp)
                DRM_ERROR("Timed out waiting for DP idle patterns\n");
 }
 
+static bool intel_dp_link_max_vswing_reached(struct intel_dp *intel_dp)
+{
+       int lane;
+
+       for (lane = 0; lane < intel_dp->lane_count; lane++)
+               if ((intel_dp->train_set[lane] &
+                    DP_TRAIN_MAX_SWING_REACHED) == 0)
+                       return false;
+
+       return true;
+}
+
 /* Enable corresponding port and start training pattern 1 */
-static void
+static bool
 intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 {
        struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base;
        struct drm_device *dev = encoder->dev;
-       int i;
        uint8_t voltage;
-       int voltage_tries, loop_tries;
-       uint32_t DP = intel_dp->DP;
+       int voltage_tries, max_vswing_tries;
        uint8_t link_config[2];
        uint8_t link_bw, rate_select;
 
@@ -3720,7 +3836,9 @@ intel_dp_link_training_clock_recovery(struct intel_dp 
*intel_dp)
        if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
                link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
        drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
-       if (intel_dp->num_sink_rates)
+
+       /* eDP 1.4 rate select method. */
+       if (!link_bw)
                drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
                                  &rate_select, 1);
 
@@ -3728,115 +3846,112 @@ intel_dp_link_training_clock_recovery(struct intel_dp 
*intel_dp)
        link_config[1] = DP_SET_ANSI_8B10B;
        drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
 
-       DP |= DP_PORT_EN;
+       intel_dp->DP |= DP_PORT_EN;
 
        /* clock recovery */
-       if (!intel_dp_reset_link_train(intel_dp, &DP,
+       if (!intel_dp_reset_link_train(intel_dp,
                                       DP_TRAINING_PATTERN_1 |
                                       DP_LINK_SCRAMBLING_DISABLE)) {
                DRM_ERROR("failed to enable link training\n");
-               return;
+               return false;
        }
 
-       voltage = 0xff;
-       voltage_tries = 0;
-       loop_tries = 0;
+       voltage_tries = 1;
+       max_vswing_tries = 0;
        for (;;) {
                uint8_t link_status[DP_LINK_STATUS_SIZE];
 
                drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
+
                if (!intel_dp_get_link_status(intel_dp, link_status)) {
                        DRM_ERROR("failed to get link status\n");
-                       break;
+                       return false;
                }
 
                if (drm_dp_clock_recovery_ok(link_status, 
intel_dp->lane_count)) {
                        DRM_DEBUG_KMS("clock recovery OK\n");
-                       break;
+                       return true;
                }
 
+               if (voltage_tries == 5) {
+                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
+                       return false;
+               }
 
-               /* Check to see if we've tried the max voltage */
-               for (i = 0; i < intel_dp->lane_count; i++)
-                       if ((intel_dp->train_set[i] & 
DP_TRAIN_MAX_SWING_REACHED) == 0)
-                               break;
-               if (i == intel_dp->lane_count) {
-                       ++loop_tries;
-                       if (loop_tries == 5) {
-                               DRM_ERROR("too many full retries, give up\n");
-                               break;
-                       }
-                       intel_dp_reset_link_train(intel_dp, &DP,
-                                                 DP_TRAINING_PATTERN_1 |
-                                                 DP_LINK_SCRAMBLING_DISABLE);
-                       voltage_tries = 0;
-                       continue;
+               if (max_vswing_tries == 1) {
+                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
+                       return false;
                }
 
-               /* Check to see if we've tried the same voltage 5 times */
-               if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == 
voltage) {
-                       ++voltage_tries;
-                       if (voltage_tries == 5) {
-                               DRM_ERROR("too many voltage retries, give 
up\n");
-                               break;
-                       }
-               } else
-                       voltage_tries = 0;
                voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
                /* Update training set as requested by target */
-               if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
+               intel_get_adjust_train(intel_dp, link_status);
+               if (!intel_dp_update_link_train(intel_dp)) {
                        DRM_ERROR("failed to update link training\n");
-                       break;
+                       return false;
                }
-       }
-
-       intel_dp->DP = DP;
-}
 
-static void
-intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
+               if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+                   voltage)
+                       ++voltage_tries;
+               else
+                       voltage_tries = 1;
+
+               if (intel_dp_link_max_vswing_reached(intel_dp))
+                       ++max_vswing_tries;
+
+       }
+}
+
+/*
+ * Pick training pattern for channel equalization. Training Pattern 3 for HBR2
+ * or 1.2 devices that support it, Training Pattern 2 otherwise.
+ */
+static u32 intel_dp_training_pattern(struct intel_dp *intel_dp)
 {
-       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
-       struct drm_device *dev = dig_port->base.base.dev;
-       bool channel_eq = false;
-       int tries, cr_tries;
-       uint32_t DP = intel_dp->DP;
-       uint32_t training_pattern = DP_TRAINING_PATTERN_2;
+       u32 training_pattern = DP_TRAINING_PATTERN_2;
+       bool source_tps3, sink_tps3;
 
        /*
-        * Training Pattern 3 for HBR2 or 1.2 devices that support it.
-        *
         * Intel platforms that support HBR2 also support TPS3. TPS3 support is
-        * also mandatory for downstream devices that support HBR2.
-        *
-        * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is
-        * supported but still not enabled.
+        * also mandatory for downstream devices that support HBR2. However, not
+        * all sinks follow the spec.
         */
-       if (intel_dp_source_supports_hbr2(dev) &&
-           drm_dp_tps3_supported(intel_dp->dpcd))
+       source_tps3 = intel_dp_source_supports_hbr2(intel_dp);
+       sink_tps3 = drm_dp_tps3_supported(intel_dp->dpcd);
+
+       if (source_tps3 && sink_tps3) {
                training_pattern = DP_TRAINING_PATTERN_3;
-       else if (intel_dp->link_rate == 540000)
-               DRM_ERROR("5.4 Gbps link rate without HBR2/TPS3 support\n");
+       } else if (intel_dp->link_rate == 540000) {
+               if (!source_tps3)
+                       DRM_DEBUG_KMS("5.4 Gbps link rate without source 
HBR2/TPS3 support\n");
+               if (!sink_tps3)
+                       DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 
support\n");
+       }
+
+       return training_pattern;
+}
+
+static bool
+intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
+{
+       int tries;
+       u32 training_pattern;
+       uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+       training_pattern = intel_dp_training_pattern(intel_dp);
 
        /* channel equalization */
-       if (!intel_dp_set_link_train(intel_dp, &DP,
+       if (!intel_dp_set_link_train(intel_dp,
                                     training_pattern |
                                     DP_LINK_SCRAMBLING_DISABLE)) {
                DRM_ERROR("failed to start channel equalization\n");
-               return;
+               return false;
        }
 
-       tries = 0;
-       cr_tries = 0;
-       channel_eq = false;
-       for (;;) {
-               uint8_t link_status[DP_LINK_STATUS_SIZE];
-
-               if (cr_tries > 5) {
-                       DRM_ERROR("failed to train DP, aborting\n");
-                       break;
-               }
+       intel_dp->channel_eq_status = false;
+       for (tries = 0; tries < 5; tries++) {
 
                drm_dp_link_train_channel_eq_delay(intel_dp->dpcd);
                if (!intel_dp_get_link_status(intel_dp, link_status)) {
@@ -3847,58 +3962,73 @@ intel_dp_link_training_channel_equalization(struct 
intel_dp *intel_dp)
                /* Make sure clock is still ok */
                if (!drm_dp_clock_recovery_ok(link_status,
                                              intel_dp->lane_count)) {
-                       intel_dp_link_training_clock_recovery(intel_dp);
-                       intel_dp_set_link_train(intel_dp, &DP,
-                                               training_pattern |
-                                               DP_LINK_SCRAMBLING_DISABLE);
-                       cr_tries++;
-                       continue;
+                       intel_dp_dump_link_status(link_status);
+                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
+                                     "continue channel equalization\n");
+                       break;
                }
 
                if (drm_dp_channel_eq_ok(link_status,
                                         intel_dp->lane_count)) {
-                       channel_eq = true;
+                       intel_dp->channel_eq_status = true;
+                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
+                                     "successful\n");
                        break;
                }
 
-               /* Try 5 times, then try clock recovery if that fails */
-               if (tries > 5) {
-                       intel_dp_link_training_clock_recovery(intel_dp);
-                       intel_dp_set_link_train(intel_dp, &DP,
-                                               training_pattern |
-                                               DP_LINK_SCRAMBLING_DISABLE);
-                       tries = 0;
-                       cr_tries++;
-                       continue;
-               }
-
                /* Update training set as requested by target */
-               if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
+               intel_get_adjust_train(intel_dp, link_status);
+               if (!intel_dp_update_link_train(intel_dp)) {
                        DRM_ERROR("failed to update link training\n");
                        break;
                }
-               ++tries;
+       }
+
+       /* Try 5 times, else fail and try at lower BW */
+       if (tries == 5) {
+               intel_dp_dump_link_status(link_status);
+               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
        }
 
        intel_dp_set_idle_link_train(intel_dp);
 
-       intel_dp->DP = DP;
+       return intel_dp->channel_eq_status;
 
-       if (channel_eq)
-               DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
 }
 
 void intel_dp_stop_link_train(struct intel_dp *intel_dp)
 {
-       intel_dp_set_link_train(intel_dp, &intel_dp->DP,
+       intel_dp_set_link_train(intel_dp,
                                DP_TRAINING_PATTERN_DISABLE);
 }
 
 void
 intel_dp_start_link_train(struct intel_dp *intel_dp)
 {
-       intel_dp_link_training_clock_recovery(intel_dp);
-       intel_dp_link_training_channel_equalization(intel_dp);
+       struct intel_connector *intel_connector = intel_dp->attached_connector;
+
+       if (!intel_dp_link_training_clock_recovery(intel_dp))
+               goto failure_handling;
+       if (!intel_dp_link_training_channel_equalization(intel_dp))
+               goto failure_handling;
+
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Link Training Passed at Link Rate = 
%d, Lane count = %d",
+                     intel_connector->base.base.id,
+                     intel_connector->base.name,
+                     intel_dp->link_rate, intel_dp->lane_count);
+       return;
+
+failure_handling:
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Link Training failed at link rate = 
%d, lane count = %d",
+                     intel_connector->base.base.id,
+                     intel_connector->base.name,
+                     intel_dp->link_rate, intel_dp->lane_count);
+       if (!intel_dp_get_link_train_fallback_values(intel_dp,
+                                                    intel_dp->link_rate,
+                                                    intel_dp->lane_count))
+               /* Schedule a Hotplug Uevent to userspace to start modeset */
+               schedule_work(&intel_connector->modeset_retry_work);
+       return;
 }
 
 static void
@@ -3960,10 +4090,7 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 static bool
 intel_dp_get_dpcd(struct intel_dp *intel_dp)
 {
-       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
-       struct drm_device *dev = dig_port->base.base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       uint8_t rev;
+       u8 sink_count;
 
        if (intel_dp_dpcd_read_wake(&intel_dp->aux, 0x000, intel_dp->dpcd,
                                    sizeof(intel_dp->dpcd)) < 0)
@@ -3974,63 +4101,24 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
        if (intel_dp->dpcd[DP_DPCD_REV] == 0)
                return false; /* DPCD not present */
 
-       /* Check if the panel supports PSR */
-       memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd));
-       if (is_edp(intel_dp)) {
-               intel_dp_dpcd_read_wake(&intel_dp->aux, DP_PSR_SUPPORT,
-                                       intel_dp->psr_dpcd,
-                                       sizeof(intel_dp->psr_dpcd));
-               if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
-                       dev_priv->psr.sink_support = true;
-                       DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
-               }
-
-               if (INTEL_INFO(dev)->gen >= 9 &&
-                       (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) {
-                       uint8_t frame_sync_cap;
-
-                       dev_priv->psr.sink_support = true;
-                       intel_dp_dpcd_read_wake(&intel_dp->aux,
-                                       DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
-                                       &frame_sync_cap, 1);
-                       dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : 
false;
-                       /* PSR2 needs frame sync as well */
-                       dev_priv->psr.psr2_support = 
dev_priv->psr.aux_frame_sync;
-                       DRM_DEBUG_KMS("PSR2 %s on sink",
-                               dev_priv->psr.psr2_support ? "supported" : "not 
supported");
-               }
+       /* Don't clobber cached eDP rates. */
+       if (!is_edp(intel_dp)) {
+               intel_dp_set_sink_rates(intel_dp);
+               intel_dp_set_common_rates(intel_dp);
        }
 
-       DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n",
-                     yesno(intel_dp_source_supports_hbr2(dev)),
-                     yesno(drm_dp_tps3_supported(intel_dp->dpcd)));
-
-       /* Intermediate frequency support */
-       if (is_edp(intel_dp) &&
-           (intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & 
DP_DPCD_DISPLAY_CONTROL_CAPABLE) &&
-           (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_EDP_DPCD_REV, &rev, 1) 
== 1) &&
-           (rev >= 0x03)) { /* eDp v1.4 or higher */
-               __le16 sink_rates[DP_MAX_SUPPORTED_RATES];
-               int i;
-
-               intel_dp_dpcd_read_wake(&intel_dp->aux,
-                               DP_SUPPORTED_LINK_RATES,
-                               sink_rates,
-                               sizeof(sink_rates));
-
-               for (i = 0; i < ARRAY_SIZE(sink_rates); i++) {
-                       int val = le16_to_cpu(sink_rates[i]);
-
-                       if (val == 0)
-                               break;
-
-                       /* Value read is in kHz while drm clock is saved in 
deca-kHz */
-                       intel_dp->sink_rates[i] = (val * 200) / 10;
-               }
-               intel_dp->num_sink_rates = i;
-       }
+       if (drm_dp_dpcd_readb(&intel_dp->aux, DP_SINK_COUNT, &sink_count) <= 0)
+               return false;
 
-       intel_dp_print_rates(intel_dp);
+       /*
+        * SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that
+        * a dongle is present but no display. Unless we require to know
+        * if a dongle is present or not, we don't need to update
+        * downstream port information. So, an early return here saves
+        * time from performing other operations which are not required.
+        */
+       if (!is_edp(intel_dp) && !DP_GET_SINK_COUNT(sink_count))
+               return false;
 
        if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
              DP_DWN_STRM_PORT_PRESENT))
@@ -4047,46 +4135,38 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
        return true;
 }
 
-static void
-intel_dp_probe_oui(struct intel_dp *intel_dp)
+static bool
+intel_dp_can_mst(struct intel_dp *intel_dp)
 {
-       u8 buf[3];
+       u8 mstm_cap;
 
-       if (!(intel_dp->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
-               return;
+       if (!intel_dp->can_mst)
+               return false;
+
+       if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
+               return false;
 
-       if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_SINK_OUI, buf, 3) == 3)
-               DRM_DEBUG_KMS("Sink OUI: %02x%02x%02x\n",
-                             buf[0], buf[1], buf[2]);
+       if (drm_dp_dpcd_readb(&intel_dp->aux, DP_MSTM_CAP, &mstm_cap) != 1)
+               return false;
 
-       if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_BRANCH_OUI, buf, 3) == 3)
-               DRM_DEBUG_KMS("Branch OUI: %02x%02x%02x\n",
-                             buf[0], buf[1], buf[2]);
+       return mstm_cap & DP_MST_CAP;
 }
 
-static bool
-intel_dp_probe_mst(struct intel_dp *intel_dp)
+static void
+intel_dp_configure_mst(struct intel_dp *intel_dp)
 {
-       u8 buf[1];
-
        if (!intel_dp->can_mst)
-               return false;
+               return;
 
-       if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
-               return false;
+       intel_dp->is_mst = intel_dp_can_mst(intel_dp);
 
-       if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) {
-               if (buf[0] & DP_MST_CAP) {
-                       DRM_DEBUG_KMS("Sink is MST capable\n");
-                       intel_dp->is_mst = true;
-               } else {
-                       DRM_DEBUG_KMS("Sink is not MST capable\n");
-                       intel_dp->is_mst = false;
-               }
-       }
+       if (intel_dp->is_mst)
+               DRM_DEBUG_KMS("Sink is MST capable\n");
+       else
+               DRM_DEBUG_KMS("Sink is not MST capable\n");
 
-       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
-       return intel_dp->is_mst;
+       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                       intel_dp->is_mst);
 }
 
 static int intel_dp_sink_crc_stop(struct intel_dp *intel_dp)
@@ -4407,60 +4487,126 @@ go_again:
        return -EINVAL;
 }
 
-/*
- * According to DP spec
- * 5.1.2:
- *  1. Read DPCD
- *  2. Configure link according to Receiver Capabilities
- *  3. Use Link Training from 2.5.3.3 and 3.5.1.3
- *  4. Check link status on receipt of hot-plug interrupt
- */
 static void
-intel_dp_check_link_status(struct intel_dp *intel_dp)
+intel_dp_retrain_link(struct intel_dp *intel_dp)
 {
+       struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
+
+       /* Suppress underruns caused by re-training */
+       intel_set_cpu_fifo_underrun_reporting(dev_priv, crtc->pipe, false);
+       if (crtc->config->has_pch_encoder)
+               intel_set_pch_fifo_underrun_reporting(dev_priv,
+                                                     
intel_crtc_pch_transcoder(crtc), false);
+
+       intel_dp_start_link_train(intel_dp);
+       intel_dp_stop_link_train(intel_dp);
+
+       /* Keep underrun reporting disabled until things are stable */
+       intel_wait_for_vblank(dev, crtc->pipe);
+
+       intel_set_cpu_fifo_underrun_reporting(dev_priv, crtc->pipe, true);
+       if (crtc->config->has_pch_encoder)
+               intel_set_pch_fifo_underrun_reporting(dev_priv,
+                                                     
intel_crtc_pch_transcoder(crtc), true);
+}
+
+
+static void
+intel_dp_check_link_status(struct intel_dp *intel_dp)
+{
        struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
-       u8 sink_irq_vector;
+       struct drm_device *dev = intel_dp_to_dev(intel_dp);
        u8 link_status[DP_LINK_STATUS_SIZE];
 
        WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
+       if (!intel_dp_get_link_status(intel_dp, link_status)) {
+               DRM_ERROR("Failed to get link status\n");
+               return;
+       }
+
        if (!intel_encoder->base.crtc)
                return;
 
        if (!to_intel_crtc(intel_encoder->base.crtc)->active)
                return;
 
-       /* Try to read receiver status if the link appears to be up */
-       if (!intel_dp_get_link_status(intel_dp, link_status)) {
+       /*
+        * Validate the cached values of intel_dp->link_rate and
+        * intel_dp->lane_count before attempting to retrain.
+        */
+       if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate,
+                                       intel_dp->lane_count))
                return;
+
+       /* Retrain if Channel EQ or CR not ok */
+       if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) {
+               DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
+                             intel_encoder->base.name);
+
+               intel_dp_retrain_link(intel_dp);
        }
+}
 
-       /* Now read the DPCD to see if it's actually running */
-       if (!intel_dp_get_dpcd(intel_dp)) {
-               return;
+/*
+ * According to DP spec
+ * 5.1.2:
+ *  1. Read DPCD
+ *  2. Configure link according to Receiver Capabilities
+ *  3. Use Link Training from 2.5.3.3 and 3.5.1.3
+ *  4. Check link status on receipt of hot-plug interrupt
+ *
+ * intel_dp_short_pulse -  handles short pulse interrupts
+ * when full detection is not required.
+ * Returns %true if short pulse is handled and full detection
+ * is NOT required and %false otherwise.
+ */
+static bool
+intel_dp_short_pulse(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp_to_dev(intel_dp);
+       u8 sink_irq_vector = 0;
+       //u8 old_sink_count = intel_dp->sink_count;
+       bool ret;
+
+       /*
+        * Now read the DPCD to see if it's actually running
+        * If the current value of sink count doesn't match with
+        * the value that was stored earlier or dpcd read failed
+        * we need to do full detection
+        */
+       ret = intel_dp_get_dpcd(intel_dp);
+
+#if 0
+       if ((old_sink_count != intel_dp->sink_count) || !ret) {
+               /* No need to proceed if we are going to do full detect */
+               return false;
        }
+#endif
 
        /* Try to read the source of the interrupt */
        if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
-           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) {
+           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector) &&
+           sink_irq_vector != 0) {
                /* Clear interrupt source */
                drm_dp_dpcd_writeb(&intel_dp->aux,
                                   DP_DEVICE_SERVICE_IRQ_VECTOR,
                                   sink_irq_vector);
 
                if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST)
-                       DRM_DEBUG_DRIVER("Test request in short pulse not 
handled\n");
+                       intel_dp_handle_test_request(intel_dp);
                if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ))
                        DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n");
        }
 
-       if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) {
-               DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
-                             intel_encoder->base.name);
-               intel_dp_start_link_train(intel_dp);
-               intel_dp_stop_link_train(intel_dp);
-       }
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+       intel_dp_check_link_status(intel_dp);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+       return true;
 }
 
 /* XXX this is probably wrong for multiple downstream ports */
@@ -4473,6 +4619,9 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
        if (!intel_dp_get_dpcd(intel_dp))
                return connector_status_disconnected;
 
+       if (is_edp(intel_dp))
+               return connector_status_connected;
+
        /* if there's no downstream port, we're done */
        if (!(dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT))
                return connector_status_connected;
@@ -4673,6 +4822,7 @@ static bool intel_digital_port_connected(struct 
drm_i915_private *dev_priv,
                return g4x_digital_port_connected(dev_priv, port);
 }
 
+#if 0
 static enum drm_connector_status
 ironlake_dp_detect(struct intel_dp *intel_dp)
 {
@@ -4707,6 +4857,7 @@ g4x_dp_detect(struct intel_dp *intel_dp)
 
        return intel_dp_detect_dpcd(intel_dp);
 }
+#endif
 
 static struct edid *
 intel_dp_get_edid(struct intel_dp *intel_dp)
@@ -4751,28 +4902,19 @@ intel_dp_unset_edid(struct intel_dp *intel_dp)
        intel_dp->has_audio = false;
 }
 
-static enum drm_connector_status
-intel_dp_detect(struct drm_connector *connector, bool force)
+static int
+intel_dp_long_pulse(struct intel_connector *intel_connector)
 {
+       struct drm_connector *connector = &intel_connector->base;
        struct intel_dp *intel_dp = intel_attached_dp(connector);
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct intel_encoder *intel_encoder = &intel_dig_port->base;
        struct drm_device *dev = connector->dev;
        enum drm_connector_status status;
+       u8 sink_irq_vector = 0;
        enum intel_display_power_domain power_domain;
-       bool ret;
-       u8 sink_irq_vector;
-
-       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, connector->name);
-       intel_dp_unset_edid(intel_dp);
 
-       if (intel_dp->is_mst) {
-               /* MST devices are disconnected from a monitor POV */
-               if (intel_encoder->type != INTEL_OUTPUT_EDP)
-                       intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
-               return connector_status_disconnected;
-       }
+       
WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex));
 
        power_domain = intel_display_port_aux_power_domain(intel_encoder);
        intel_display_power_get(to_i915(dev), power_domain);
@@ -4780,34 +4922,87 @@ intel_dp_detect(struct drm_connector *connector, bool 
force)
        /* Can't disconnect eDP, but you can close the lid... */
        if (is_edp(intel_dp))
                status = edp_detect(intel_dp);
-       else if (HAS_PCH_SPLIT(dev))
-               status = ironlake_dp_detect(intel_dp);
+       else if (intel_digital_port_connected(to_i915(dev),
+                                             dp_to_dig_port(intel_dp)))
+               status = intel_dp_detect_dpcd(intel_dp);
        else
-               status = g4x_dp_detect(intel_dp);
-       if (status != connector_status_connected)
+               status = connector_status_disconnected;
+
+       if (status == connector_status_disconnected) {
+               if (intel_dp->is_mst) {
+                       DRM_DEBUG_KMS("MST device may have disappeared %d vs 
%d\n",
+                                     intel_dp->is_mst,
+                                     intel_dp->mst_mgr.mst_state);
+                       intel_dp->is_mst = false;
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                                       intel_dp->is_mst);
+               }
+
                goto out;
+       }
+
+       if (intel_encoder->type != INTEL_OUTPUT_EDP)
+               intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
 
-       intel_dp_probe_oui(intel_dp);
+       if (intel_dp->reset_link_params) {
+               /* Initial max link lane count */
+               intel_dp->max_link_lane_count = 
intel_dp_max_common_lane_count(intel_dp);
 
-       ret = intel_dp_probe_mst(intel_dp);
-       if (ret) {
-               /* if we are in MST mode then this connector
-                  won't appear connected or have anything with EDID on it */
-               if (intel_encoder->type != INTEL_OUTPUT_EDP)
-                       intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+               /* Initial max link rate */
+               intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
+
+               intel_dp->reset_link_params = false;
+       }
+
+       intel_dp_print_rates(intel_dp);
+
+       // XXX
+       //drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc,
+                        //drm_dp_is_branch(intel_dp->dpcd));
+
+       intel_dp_configure_mst(intel_dp);
+
+       if (intel_dp->is_mst) {
+               /*
+                * If we are in MST mode then this connector
+                * won't appear connected or have anything
+                * with EDID on it
+                */
                status = connector_status_disconnected;
                goto out;
+       } else {
+               /*
+                * If display is now connected check links status,
+                * there has been known issues of link loss triggerring
+                * long pulse.
+                *
+                * Some sinks (eg. ASUS PB287Q) seem to perform some
+                * weird HPD ping pong during modesets. So we can apparently
+                * end up with HPD going low during a modeset, and then
+                * going back up soon after. And once that happens we must
+                * retrain the link to get a picture. That's in case no
+                * userspace component reacted to intermittent HPD dip.
+                */
+               intel_dp_check_link_status(intel_dp);
        }
 
-       intel_dp_set_edid(intel_dp);
+       /*
+        * Clearing NACK and defer counts to get their exact values
+        * while reading EDID which are required by Compliance tests
+        * 4.2.2.4 and 4.2.2.5
+        */
+       intel_dp->aux.i2c_nack_count = 0;
+       intel_dp->aux.i2c_defer_count = 0;
 
-       if (intel_encoder->type != INTEL_OUTPUT_EDP)
-               intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
-       status = connector_status_connected;
+       intel_dp_set_edid(intel_dp);
+       if (is_edp(intel_dp) || intel_connector->detect_edid)
+               status = connector_status_connected;
+       intel_dp->detect_done = true;
 
        /* Try to read the source of the interrupt */
        if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
-           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) {
+           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector) &&
+           sink_irq_vector != 0) {
                /* Clear interrupt source */
                drm_dp_dpcd_writeb(&intel_dp->aux,
                                   DP_DEVICE_SERVICE_IRQ_VECTOR,
@@ -4820,10 +5015,31 @@ intel_dp_detect(struct drm_connector *connector, bool 
force)
        }
 
 out:
+       if (status != connector_status_connected && !intel_dp->is_mst)
+               intel_dp_unset_edid(intel_dp);
+
        intel_display_power_put(to_i915(dev), power_domain);
        return status;
 }
 
+static enum drm_connector_status
+intel_dp_detect(struct drm_connector *connector, bool force)
+{
+       struct intel_dp *intel_dp = intel_attached_dp(connector);
+       int status = connector->status;
+
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                     connector->base.id, connector->name);
+
+       /* If full detect is not performed yet, do a full detect */
+       if (!intel_dp->detect_done)
+               status = intel_dp_long_pulse(intel_dp->attached_connector);
+
+       intel_dp->detect_done = false;
+
+       return status;
+}
+
 static void
 intel_dp_force(struct drm_connector *connector)
 {
@@ -5074,8 +5290,7 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder)
        if (!HAS_DDI(dev_priv))
                intel_dp->DP = I915_READ(intel_dp->output_reg);
 
-       if (to_intel_encoder(encoder)->type != INTEL_OUTPUT_EDP)
-               return;
+       intel_dp->reset_link_params = true;
 
        pps_lock(intel_dp);
 
@@ -5086,7 +5301,8 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder)
        if (IS_VALLEYVIEW(encoder->dev))
                vlv_initial_power_sequencer_setup(intel_dp);
 
-       intel_edp_panel_vdd_sanitize(intel_dp);
+       if (is_edp(intel_dp))
+               intel_edp_panel_vdd_sanitize(intel_dp);
 
        pps_unlock(intel_dp);
 }
@@ -5144,48 +5360,41 @@ intel_dp_hpd_pulse(struct intel_digital_port 
*intel_dig_port, bool long_hpd)
                      port_name(intel_dig_port->port),
                      long_hpd ? "long" : "short");
 
-       power_domain = intel_display_port_aux_power_domain(intel_encoder);
-       intel_display_power_get(dev_priv, power_domain);
-
        if (long_hpd) {
-               if (!intel_digital_port_connected(dev_priv, intel_dig_port))
-                       goto mst_fail;
+               intel_dp->reset_link_params = true;
+               intel_dp->detect_done = false;
+               return IRQ_NONE;
+       }
 
-               if (!intel_dp_get_dpcd(intel_dp)) {
-                       goto mst_fail;
-               }
+       power_domain = intel_display_port_aux_power_domain(intel_encoder);
+       intel_display_power_get(dev_priv, power_domain);
 
-               intel_dp_probe_oui(intel_dp);
 
-               if (!intel_dp_probe_mst(intel_dp)) {
-                       drm_modeset_lock(&dev->mode_config.connection_mutex, 
NULL);
-                       intel_dp_check_link_status(intel_dp);
-                       drm_modeset_unlock(&dev->mode_config.connection_mutex);
-                       goto mst_fail;
-               }
-       } else {
-               if (intel_dp->is_mst) {
-                       if (intel_dp_check_mst_status(intel_dp) == -EINVAL)
-                               goto mst_fail;
+       if (intel_dp->is_mst) {
+               if (intel_dp_check_mst_status(intel_dp) == -EINVAL) {
+                       /*
+                        * If we were in MST mode, and device is not
+                        * there, get out of MST mode
+                        */
+                       DRM_DEBUG_KMS("MST device may have disappeared %d vs 
%d\n",
+                                     intel_dp->is_mst, 
intel_dp->mst_mgr.mst_state);
+                       intel_dp->is_mst = false;
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                                       intel_dp->is_mst);
+                       intel_dp->detect_done = false;
+                       goto put_power;
                }
+       }
 
-               if (!intel_dp->is_mst) {
-                       drm_modeset_lock(&dev->mode_config.connection_mutex, 
NULL);
-                       intel_dp_check_link_status(intel_dp);
-                       drm_modeset_unlock(&dev->mode_config.connection_mutex);
+       if (!intel_dp->is_mst) {
+               if (!intel_dp_short_pulse(intel_dp)) {
+                       intel_dp->detect_done = false;
+                       goto put_power;
                }
        }
 
        ret = IRQ_HANDLED;
 
-       goto put_power;
-mst_fail:
-       /* if we were in MST mode, and device is not there get out of MST mode 
*/
-       if (intel_dp->is_mst) {
-               DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n", 
intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
-               intel_dp->is_mst = false;
-               drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, 
intel_dp->is_mst);
-       }
 put_power:
        intel_display_power_put(dev_priv, power_domain);
 
@@ -5867,6 +6076,113 @@ intel_dp_drrs_init(struct intel_connector 
*intel_connector,
        return downclock_mode;
 }
 
+bool
+intel_dp_read_dpcd(struct intel_dp *intel_dp)
+{
+       if (drm_dp_dpcd_read(&intel_dp->aux, 0x000, intel_dp->dpcd,
+                            sizeof(intel_dp->dpcd)) < 0)
+               return false; /* aux transfer failed */
+
+       DRM_DEBUG_KMS("DPCD: %*ph\n", (int) sizeof(intel_dp->dpcd), 
intel_dp->dpcd);
+
+       return intel_dp->dpcd[DP_DPCD_REV] != 0;
+}
+
+static bool
+intel_edp_init_dpcd(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct drm_device *dev = dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* this function is meant to be called only once */
+       WARN_ON(intel_dp->dpcd[DP_DPCD_REV] != 0);
+
+       if (!intel_dp_read_dpcd(intel_dp))
+               return false;
+
+       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
+               dev_priv->no_aux_handshake = intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
+                       DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
+
+       /* Check if the panel supports PSR */
+       memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd));
+       if (is_edp(intel_dp)) {
+               intel_dp_dpcd_read_wake(&intel_dp->aux, DP_PSR_SUPPORT,
+                                       intel_dp->psr_dpcd,
+                                       sizeof(intel_dp->psr_dpcd));
+               if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
+                       dev_priv->psr.sink_support = true;
+                       DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
+               }
+
+               if (INTEL_INFO(dev)->gen >= 9 &&
+                       (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) {
+                       uint8_t frame_sync_cap;
+
+                       dev_priv->psr.sink_support = true;
+                       intel_dp_dpcd_read_wake(&intel_dp->aux,
+                                       DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
+                                       &frame_sync_cap, 1);
+                       dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : 
false;
+                       /* PSR2 needs frame sync as well */
+                       dev_priv->psr.psr2_support = 
dev_priv->psr.aux_frame_sync;
+                       DRM_DEBUG_KMS("PSR2 %s on sink",
+                               dev_priv->psr.psr2_support ? "supported" : "not 
supported");
+               }
+       }
+
+       DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n",
+                     yesno(intel_dp_source_supports_hbr2(intel_dp)),
+                     yesno(drm_dp_tps3_supported(intel_dp->dpcd)));
+
+       /*
+        * Read the eDP display control registers.
+        *
+        * Do this independent of DP_DPCD_DISPLAY_CONTROL_CAPABLE bit in
+        * DP_EDP_CONFIGURATION_CAP, because some buggy displays do not have it
+        * set, but require eDP 1.4+ detection (e.g. for supported link rates
+        * method). The display control registers should read zero if they're
+        * not supported anyway.
+        */
+       if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV,
+                            intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd)) ==
+                            sizeof(intel_dp->edp_dpcd))
+               DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) 
sizeof(intel_dp->edp_dpcd),
+                             intel_dp->edp_dpcd);
+
+       /* Intermediate frequency support */
+       if (intel_dp->edp_dpcd[0] >= 0x03) { /* eDp v1.4 or higher */
+               __le16 sink_rates[DP_MAX_SUPPORTED_RATES];
+               int i;
+
+               intel_dp_dpcd_read_wake(&intel_dp->aux, DP_SUPPORTED_LINK_RATES,
+                                       sink_rates, sizeof(sink_rates));
+
+               for (i = 0; i < ARRAY_SIZE(sink_rates); i++) {
+                       int val = le16_to_cpu(sink_rates[i]);
+
+                       if (val == 0)
+                               break;
+
+                       /* Value read is in kHz while drm clock is saved in 
deca-kHz */
+                       intel_dp->sink_rates[i] = (val * 200) / 10;
+               }
+               intel_dp->num_sink_rates = i;
+       }
+
+       intel_dp_print_rates(intel_dp);
+       if (intel_dp->num_sink_rates)
+               intel_dp->use_rate_select = true;
+       else
+               intel_dp_set_sink_rates(intel_dp);
+
+       intel_dp_set_common_rates(intel_dp);
+       intel_dp_print_rates(intel_dp);
+
+       return true;
+}
+
 static bool intel_edp_init_connector(struct intel_dp *intel_dp,
                                     struct intel_connector *intel_connector)
 {
@@ -5876,6 +6192,7 @@ static bool intel_edp_init_connector(struct intel_dp 
*intel_dp,
        struct drm_device *dev = intel_encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_display_mode *fixed_mode = NULL;
+       struct drm_display_mode *alt_fixed_mode = NULL;
        struct drm_display_mode *downclock_mode = NULL;
        bool has_dpcd;
        struct drm_display_mode *scan;
@@ -5890,13 +6207,15 @@ static bool intel_edp_init_connector(struct intel_dp 
*intel_dp,
        pps_unlock(intel_dp);
 
        /* Cache DPCD and EDID for edp. */
-       has_dpcd = intel_dp_get_dpcd(intel_dp);
+       has_dpcd = intel_edp_init_dpcd(intel_dp);
+
+       if (!has_dpcd) {
+               /* if this fails, presume the device is a ghost */
+               DRM_INFO("failed to retrieve link info, disabling eDP\n");
+               goto out_vdd_off;
+       }
 
        if (has_dpcd) {
-               if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
-                       dev_priv->no_aux_handshake =
-                               intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
-                               DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
        } else {
                /* if this fails, presume the device is a ghost */
                DRM_INFO("failed to retrieve link info, disabling eDP\n");
@@ -5924,13 +6243,15 @@ static bool intel_edp_init_connector(struct intel_dp 
*intel_dp,
        }
        intel_connector->edid = edid;
 
-       /* prefer fixed mode from EDID if available */
+       /* prefer fixed mode from EDID if available, save an alt mode also */
        list_for_each_entry(scan, &connector->probed_modes, head) {
                if ((scan->type & DRM_MODE_TYPE_PREFERRED)) {
                        fixed_mode = drm_mode_duplicate(dev, scan);
                        downclock_mode = intel_dp_drrs_init(
                                                intel_connector, fixed_mode);
                        break;
+               } else if (!alt_fixed_mode) {
+                       alt_fixed_mode = drm_mode_duplicate(dev, scan);
                }
        }
 
@@ -5967,11 +6288,47 @@ static bool intel_edp_init_connector(struct intel_dp 
*intel_dp,
                              pipe_name(pipe));
        }
 
-       intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
+       intel_panel_init(&intel_connector->panel, fixed_mode, alt_fixed_mode,
+                        downclock_mode);
        intel_connector->panel.backlight.power = intel_edp_backlight_power;
        intel_panel_setup_backlight(connector, pipe);
 
        return true;
+
+out_vdd_off:
+       cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
+       /*
+        * vdd might still be enabled do to the delayed vdd off.
+        * Make sure vdd is actually turned off here.
+        */
+       pps_lock(intel_dp);
+       edp_panel_vdd_off_sync(intel_dp);
+       pps_unlock(intel_dp);
+
+       return false;
+}
+
+static void intel_dp_modeset_retry_work_fn(struct work_struct *work)
+{
+       struct intel_connector *intel_connector;
+       struct drm_connector *connector;
+
+       intel_connector = container_of(work, typeof(*intel_connector),
+                                      modeset_retry_work);
+       connector = &intel_connector->base;
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
+                     connector->name);
+
+       /* Grab the locks before changing connector property*/
+       mutex_lock(&connector->dev->mode_config.mutex);
+       /* Set connector link status to BAD and send a Uevent to notify
+        * userspace to do a modeset.
+        */
+       drm_mode_connector_set_link_status_property(connector,
+                                                   DRM_MODE_LINK_STATUS_BAD);
+       mutex_unlock(&connector->dev->mode_config.mutex);
+       /* Send Hotplug uevent so userspace can reprobe */
+       drm_kms_helper_hotplug_event(connector->dev);
 }
 
 bool
@@ -5986,6 +6343,18 @@ intel_dp_init_connector(struct intel_digital_port 
*intel_dig_port,
        enum port port = intel_dig_port->port;
        int type;
 
+       /* Initialize the work for modeset in case of link train failure */
+       INIT_WORK(&intel_connector->modeset_retry_work,
+                 intel_dp_modeset_retry_work_fn);
+
+       if (WARN(intel_dig_port->max_lanes < 1,
+                "Not enough lanes (%d) for DP on port %c\n",
+                intel_dig_port->max_lanes, port_name(port)))
+               return false;
+
+       intel_dp_set_source_rates(intel_dp);
+
+       intel_dp->reset_link_params = true;
        intel_dp->pps_pipe = INVALID_PIPE;
 
        /* intel_dp vfuncs */
@@ -6172,6 +6541,7 @@ bool intel_dp_init(struct drm_device *dev,
 
        intel_dig_port->port = port;
        intel_dig_port->dp.output_reg = output_reg;
+       intel_dig_port->max_lanes = 4;
 
        intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
        if (IS_CHERRYVIEW(dev)) {
diff --git sys/dev/pci/drm/i915/intel_dp_mst.c 
sys/dev/pci/drm/i915/intel_dp_mst.c
index 6a0abef0b53..98a231f07a3 100644
--- sys/dev/pci/drm/i915/intel_dp_mst.c
+++ sys/dev/pci/drm/i915/intel_dp_mst.c
@@ -54,12 +54,12 @@ static bool intel_dp_mst_compute_config(struct 
intel_encoder *encoder,
         * for MST we always configure max link bw - the spec doesn't
         * seem to suggest we should do otherwise.
         */
-       lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
+       lane_count = intel_dp_max_lane_count(intel_dp);
 
 
        pipe_config->lane_count = lane_count;
 
-       pipe_config->pipe_bpp = 24;
+       pipe_config->pipe_bpp = bpp;
        pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
 
        state = pipe_config->base.state;
@@ -177,10 +177,23 @@ static void intel_mst_pre_enable_dp(struct intel_encoder 
*encoder)
 
                intel_dp_set_link_params(intel_dp, intel_crtc->config);
 
-               /* FIXME: add support for SKL */
                if (INTEL_INFO(dev)->gen < 9)
                        I915_WRITE(PORT_CLK_SEL(port),
                                   intel_crtc->config->ddi_pll_sel);
+               else if (IS_GEN9(dev)) {
+                       uint32_t dpll = intel_crtc->config->ddi_pll_sel;
+                       uint32_t val;
+                       /* DDI -> PLL mapping  */
+                       val = I915_READ(DPLL_CTRL2);
+
+                       val &= ~(DPLL_CTRL2_DDI_CLK_OFF(port) |
+                               DPLL_CTRL2_DDI_CLK_SEL_MASK(port));
+                       val |= (DPLL_CTRL2_DDI_CLK_SEL(dpll, port) |
+                               DPLL_CTRL2_DDI_SEL_OVERRIDE(port));
+
+                       I915_WRITE(DPLL_CTRL2, val);
+               }
+
 
                intel_ddi_init_dp_buf_reg(&intel_dig_port->base);
 
@@ -353,6 +366,21 @@ static enum drm_mode_status
 intel_dp_mst_mode_valid(struct drm_connector *connector,
                        struct drm_display_mode *mode)
 {
+       struct intel_connector *intel_connector = to_intel_connector(connector);
+       struct intel_dp *intel_dp = intel_connector->mst_port;
+       int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
+       int bpp = 24; /* MST uses fixed bpp */
+       int max_rate, mode_rate, max_lanes, max_link_clock;
+
+       if (!intel_dp)
+               return MODE_ERROR;
+
+       max_link_clock = intel_dp_max_link_rate(intel_dp);
+       max_lanes = intel_dp_max_lane_count(intel_dp);
+
+       max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes);
+       mode_rate = intel_dp_link_required(mode->clock, bpp);
+
        /* TODO - validate mode against available PBN for link */
        if (mode->clock < 10000)
                return MODE_CLOCK_LOW;
@@ -360,6 +388,9 @@ intel_dp_mst_mode_valid(struct drm_connector *connector,
        if (mode->flags & DRM_MODE_FLAG_DBLCLK)
                return MODE_H_ILLEGAL;
 
+       if (mode_rate > max_rate || mode->clock > max_dotclk)
+               return MODE_CLOCK_HIGH;
+
        return MODE_OK;
 }
 
diff --git sys/dev/pci/drm/i915/intel_drv.h sys/dev/pci/drm/i915/intel_drv.h
index 28559379dfe..c4e813733c2 100644
--- sys/dev/pci/drm/i915/intel_drv.h
+++ sys/dev/pci/drm/i915/intel_drv.h
@@ -169,6 +169,7 @@ struct intel_encoder {
 
 struct intel_panel {
        struct drm_display_mode *fixed_mode;
+       struct drm_display_mode *alt_fixed_mode;
        struct drm_display_mode *downclock_mode;
        int fitting_mode;
 
@@ -235,6 +236,9 @@ struct intel_connector {
        void *port; /* store this opaque as its illegal to dereference it */
 
        struct intel_dp *mst_port;
+
+       /* Work struct to schedule a uevent on link train failure */
+       struct work_struct modeset_retry_work;
 };
 
 typedef struct dpll {
@@ -736,16 +740,31 @@ struct intel_dp {
        uint32_t DP;
        int link_rate;
        uint8_t lane_count;
+       bool detect_done;
        bool has_audio;
        enum hdmi_force_audio force_audio;
        bool limited_color_range;
        bool color_range_auto;
+       bool channel_eq_status;
+       bool reset_link_params;
        uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
        uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
        uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
+       uint8_t edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
+       /* source rates */
+       int num_source_rates;
+       const int *source_rates;
        /* sink rates as reported by DP_SUPPORTED_LINK_RATES */
        uint8_t num_sink_rates;
        int sink_rates[DP_MAX_SUPPORTED_RATES];
+       bool use_rate_select;
+       /* intersection of source and sink rates */
+       int num_common_rates;
+       int common_rates[DP_MAX_SUPPORTED_RATES];
+       /* Max lane count for the current link */
+       int max_link_lane_count;
+       /* Max rate for the current link */
+       int max_link_rate;
        struct sink_crc sink_crc;
        struct drm_dp_aux aux;
        uint8_t train_set[4];
@@ -803,6 +822,7 @@ struct intel_digital_port {
        struct intel_hdmi hdmi;
        enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool);
        bool release_cl2_override;
+       u8 max_lanes;
 };
 
 struct intel_dp_mst_encoder {
@@ -1032,6 +1052,7 @@ void i915_audio_component_cleanup(struct drm_i915_private 
*dev_priv);
 
 /* intel_display.c */
 extern const struct drm_plane_funcs intel_plane_funcs;
+enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc);
 bool intel_has_pending_fb_unpin(struct drm_device *dev);
 int intel_pch_rawclk(struct drm_device *dev);
 int intel_hrawclk(struct drm_device *dev);
@@ -1226,6 +1247,7 @@ void intel_dp_add_properties(struct intel_dp *intel_dp, 
struct drm_connector *co
 void intel_dp_mst_suspend(struct drm_device *dev);
 void intel_dp_mst_resume(struct drm_device *dev);
 int intel_dp_max_link_rate(struct intel_dp *intel_dp);
+int intel_dp_max_lane_count(struct intel_dp *intel_dp);
 int intel_dp_rate_select(struct intel_dp *intel_dp, int rate);
 void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
 void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv);
@@ -1237,6 +1259,8 @@ void intel_edp_drrs_invalidate(struct drm_device *dev,
                unsigned frontbuffer_bits);
 void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits);
 void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config);
+int intel_dp_link_required(int pixel_clock, int bpp);
+int intel_dp_max_data_rate(int max_link_clock, int max_lanes);
 
 /* intel_dp_mst.c */
 int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int 
conn_id);
@@ -1331,6 +1355,7 @@ void intel_overlay_reset(struct drm_i915_private 
*dev_priv);
 /* intel_panel.c */
 int intel_panel_init(struct intel_panel *panel,
                     struct drm_display_mode *fixed_mode,
+                    struct drm_display_mode *alt_fixed_mode,
                     struct drm_display_mode *downclock_mode);
 void intel_panel_fini(struct intel_panel *panel);
 void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
diff --git sys/dev/pci/drm/i915/intel_dsi.c sys/dev/pci/drm/i915/intel_dsi.c
index 3d06538e4e9..548cb3678b8 100644
--- sys/dev/pci/drm/i915/intel_dsi.c
+++ sys/dev/pci/drm/i915/intel_dsi.c
@@ -1257,7 +1257,7 @@ void intel_dsi_init(struct drm_device *dev)
                goto err;
        }
 
-       intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
+       intel_panel_init(&intel_connector->panel, fixed_mode, NULL, NULL);
        intel_panel_setup_backlight(connector, INVALID_PIPE);
 
        return;
diff --git sys/dev/pci/drm/i915/intel_dvo.c sys/dev/pci/drm/i915/intel_dvo.c
index c897711978d..c2f2accaf00 100644
--- sys/dev/pci/drm/i915/intel_dvo.c
+++ sys/dev/pci/drm/i915/intel_dvo.c
@@ -540,7 +540,7 @@ void intel_dvo_init(struct drm_device *dev)
                         */
                        intel_panel_init(&intel_connector->panel,
                                         intel_dvo_get_current_mode(connector),
-                                        NULL);
+                                        NULL, NULL);
                        intel_dvo->panel_wants_dither = true;
                }
 
diff --git sys/dev/pci/drm/i915/intel_lvds.c sys/dev/pci/drm/i915/intel_lvds.c
index 14840ed2f97..a529c07f185 100644
--- sys/dev/pci/drm/i915/intel_lvds.c
+++ sys/dev/pci/drm/i915/intel_lvds.c
@@ -1165,7 +1165,8 @@ void intel_lvds_init(struct drm_device *dev)
 out:
        mutex_unlock(&dev->mode_config.mutex);
 
-       intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
+       intel_panel_init(&intel_connector->panel, fixed_mode, NULL,
+                        downclock_mode);
 
        lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
        DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
diff --git sys/dev/pci/drm/i915/intel_panel.c sys/dev/pci/drm/i915/intel_panel.c
index 263977047d2..a37fabfd2e4 100644
--- sys/dev/pci/drm/i915/intel_panel.c
+++ sys/dev/pci/drm/i915/intel_panel.c
@@ -1831,11 +1831,13 @@ intel_panel_init_backlight_funcs(struct intel_panel 
*panel)
 
 int intel_panel_init(struct intel_panel *panel,
                     struct drm_display_mode *fixed_mode,
+                    struct drm_display_mode *alt_fixed_mode,
                     struct drm_display_mode *downclock_mode)
 {
        intel_panel_init_backlight_funcs(panel);
 
        panel->fixed_mode = fixed_mode;
+       panel->alt_fixed_mode = alt_fixed_mode;
        panel->downclock_mode = downclock_mode;
 
        return 0;
@@ -1849,6 +1851,10 @@ void intel_panel_fini(struct intel_panel *panel)
        if (panel->fixed_mode)
                drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);
 
+       if (panel->alt_fixed_mode)
+               drm_mode_destroy(intel_connector->base.dev,
+                               panel->alt_fixed_mode);
+
        if (panel->downclock_mode)
                drm_mode_destroy(intel_connector->base.dev,
                                panel->downclock_mode);
diff --git sys/dev/pci/drm/i915/intel_psr.c sys/dev/pci/drm/i915/intel_psr.c
index 5ff5221f574..4be100e0d43 100644
--- sys/dev/pci/drm/i915/intel_psr.c
+++ sys/dev/pci/drm/i915/intel_psr.c
@@ -58,6 +58,9 @@
 
 static bool is_edp_psr(struct intel_dp *intel_dp)
 {
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       if (intel_dig_port->base.type != INTEL_OUTPUT_EDP)
+               return false;
        return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
 }
 

Reply via email to