drm: DP update that fixes T460 external monitors through docks

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

drm: DP update that fixes T460 external monitors through docks

Paul Irofti-4
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 | Threaded
Open this post in threaded view
|

Re: drm: DP update that fixes T460 external monitors through docks

Jonathan Gray-11
On Fri, Jan 26, 2018 at 10:39:39AM +0200, Paul Irofti wrote:

> 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.

Which commits?  From which tree/branch?

Diverging from linux-4.4.y like this makes it harder to see local
changes and increases the chance of breaking things.

This does not look like the smallest possible diff.

>
> 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 | Threaded
Open this post in threaded view
|

Re: drm: DP update that fixes T460 external monitors through docks

Paul Irofti-4
Master branch. I tried trimming down the diff further but without any success.

If you want I can make a list of commits. Note that I have not applied full commit trees so that I can minimize the size of the diff and the files, areas, it touches.

Reply | Threaded
Open this post in threaded view
|

Re: drm: DP update that fixes T460 external monitors through docks

Paul Irofti-4
In reply to this post by Paul Irofti-4
On Fri, Jan 26, 2018 at 10:39:39AM +0200, Paul Irofti wrote:

> 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?

For the archives, here is a diff that fixes a locking warning with the
earlier patch. So you have to apply it after the former one.

diff --git sys/dev/pci/drm/drm_probe_helper.c sys/dev/pci/drm/drm_probe_helper.c
index 936c0c1d3a7..84a0e585d32 100644
--- sys/dev/pci/drm/drm_probe_helper.c
+++ sys/dev/pci/drm/drm_probe_helper.c
@@ -136,15 +136,27 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
  struct drm_display_mode *mode;
  const struct drm_connector_helper_funcs *connector_funcs =
  connector->helper_private;
- int count = 0;
+ int count = 0, ret;
  int mode_flags = 0;
  bool verbose_prune = true;
  enum drm_connector_status old_status;
+ struct drm_modeset_acquire_ctx ctx;
 
  WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 
  DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
  connector->name);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry:
+ ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry;
+ } else
+ WARN_ON(ret < 0);
+
  /* set all modes to the unverified state */
  list_for_each_entry(mode, &connector->modes, head)
  mode->status = MODE_UNVERIFIED;
@@ -248,6 +260,9 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
 prune:
  drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
 
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
  if (list_empty(&connector->modes))
  return 0;
 

Reply | Threaded
Open this post in threaded view
|

Re: drm: DP update that fixes T460 external monitors through docks

Jiri B-2
On Wed, Feb 21, 2018 at 03:30:13PM +0200, Paul Irofti wrote:

> For the archives, here is a diff that fixes a locking warning with the
> earlier patch. So you have to apply it after the former one.
>
> diff --git sys/dev/pci/drm/drm_probe_helper.c sys/dev/pci/drm/drm_probe_helper.c
> index 936c0c1d3a7..84a0e585d32 100644
> --- sys/dev/pci/drm/drm_probe_helper.c
> +++ sys/dev/pci/drm/drm_probe_helper.c
> @@ -136,15 +136,27 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
>   struct drm_display_mode *mode;
>   const struct drm_connector_helper_funcs *connector_funcs =
>   connector->helper_private;
> - int count = 0;
> + int count = 0, ret;
>   int mode_flags = 0;
>   bool verbose_prune = true;
>   enum drm_connector_status old_status;
> + struct drm_modeset_acquire_ctx ctx;
>  
>   WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>  
>   DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
>   connector->name);
> +
> + drm_modeset_acquire_init(&ctx, 0);
> +
> +retry:
> + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
> + if (ret == -EDEADLK) {
> + drm_modeset_backoff(&ctx);
> + goto retry;
> + } else
> + WARN_ON(ret < 0);
> +
>   /* set all modes to the unverified state */
>   list_for_each_entry(mode, &connector->modes, head)
>   mode->status = MODE_UNVERIFIED;
> @@ -248,6 +260,9 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
>  prune:
>   drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
>  
> + drm_modeset_drop_locks(&ctx);
> + drm_modeset_acquire_fini(&ctx);
> +
>   if (list_empty(&connector->modes))
>   return 0;
>  
>

I'm using it daily for couple of days, FDE and hibernating.

Although I still see some 'error's and 'WARNING's, see below:

OpenBSD 6.2-current (GENERIC.MP) #0: Fri Feb 23 22:40:24 CET 2018
    [hidden email]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 17032802304 (16243MB)
avail mem = 16509616128 (15744MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 3.0 @ 0x9a2bb000 (62 entries)
bios0: vendor LENOVO version "N1WET41W (1.20 )" date 10/17/2017
bios0: LENOVO 20HGS22D0W
acpi0 at bios0: rev 2
acpi0: sleep states S0 S3 S4 S5
acpi0: tables DSDT FACP SSDT TPM2 UEFI SSDT SSDT HPET APIC MCFG ECDT SSDT BOOT BATB SSDT SSDT SSDT WSMT SSDT SSDT DBGP DBG2 POAT DMAR ASF! FPDT UEFI
acpi0: wakeup devices GLAN(S4) XHC_(S3) XDCI(S4) HDAS(S4) RP01(S4) RP02(S4) RP04(S4) RP05(S4) RP06(S4) RP07(S4) RP08(S4) RP09(S4) RP10(S4) RP11(S4) RP12(S4) RP13(S4) [...]
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpihpet0 at acpi0: 23999999 Hz
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz, 2694.93 MHz
cpu0: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,SGX,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,MPX,RDSEED,ADX,SMAP,CLFLUSHOPT,PT,SENSOR,ARAT
cpu0: 256KB 64b/line 8-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 10 var ranges, 88 fixed ranges
cpu0: apic clock running at 24MHz
cpu0: mwait min=64, max=64, C-substates=0.2.1.2.4.1.1.1, IBE
cpu1 at mainbus0: apid 2 (application processor)
cpu1: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz, 2693.74 MHz
cpu1: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,SGX,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,MPX,RDSEED,ADX,SMAP,CLFLUSHOPT,PT,SENSOR,ARAT
cpu1: 256KB 64b/line 8-way L2 cache
cpu1: smt 0, core 1, package 0
cpu2 at mainbus0: apid 1 (application processor)
cpu2: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz, 2693.73 MHz
cpu2: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,SGX,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,MPX,RDSEED,ADX,SMAP,CLFLUSHOPT,PT,SENSOR,ARAT
cpu2: 256KB 64b/line 8-way L2 cache
cpu2: smt 1, core 0, package 0
cpu3 at mainbus0: apid 3 (application processor)
cpu3: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz, 2693.74 MHz
cpu3: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,SGX,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,MPX,RDSEED,ADX,SMAP,CLFLUSHOPT,PT,SENSOR,ARAT
cpu3: 256KB 64b/line 8-way L2 cache
cpu3: smt 1, core 1, package 0
ioapic0 at mainbus0: apid 2 pa 0xfec00000, version 20, 120 pins
acpimcfg0 at acpi0 addr 0xf8000000, bus 0-63
acpiec0 at acpi0
acpiprt0 at acpi0: bus 0 (PCI0)
acpiprt1 at acpi0: bus 1 (RP01)
acpiprt2 at acpi0: bus -1 (RP02)
acpiprt3 at acpi0: bus 58 (RP03)
acpiprt4 at acpi0: bus -1 (RP04)
acpiprt5 at acpi0: bus -1 (RP05)
acpiprt6 at acpi0: bus -1 (RP06)
acpiprt7 at acpi0: bus -1 (RP07)
acpiprt8 at acpi0: bus -1 (RP08)
acpiprt9 at acpi0: bus 60 (RP09)
acpiprt10 at acpi0: bus -1 (RP10)
acpiprt11 at acpi0: bus -1 (RP11)
acpiprt12 at acpi0: bus -1 (RP12)
acpiprt13 at acpi0: bus -1 (RP13)
acpiprt14 at acpi0: bus -1 (RP14)
acpiprt15 at acpi0: bus -1 (RP15)
acpiprt16 at acpi0: bus -1 (RP16)
acpiprt17 at acpi0: bus -1 (RP17)
acpiprt18 at acpi0: bus -1 (RP18)
acpiprt19 at acpi0: bus -1 (RP19)
acpiprt20 at acpi0: bus -1 (RP20)
acpiprt21 at acpi0: bus -1 (RP21)
acpiprt22 at acpi0: bus -1 (RP22)
acpiprt23 at acpi0: bus -1 (RP23)
acpiprt24 at acpi0: bus -1 (RP24)
acpicpu0 at acpi0: C3(200@1034 mwait.1@0x60), C2(200@151 mwait.1@0x33), C1(1000@1 mwait.1), PSS
acpicpu1 at acpi0: C3(200@1034 mwait.1@0x60), C2(200@151 mwait.1@0x33), C1(1000@1 mwait.1), PSS
acpicpu2 at acpi0: C3(200@1034 mwait.1@0x60), C2(200@151 mwait.1@0x33), C1(1000@1 mwait.1), PSS
acpicpu3 at acpi0: C3(200@1034 mwait.1@0x60), C2(200@151 mwait.1@0x33), C1(1000@1 mwait.1), PSS
acpipwrres0 at acpi0: PUBS, resource for XHC_
acpipwrres1 at acpi0: WRST
acpipwrres2 at acpi0: WRST
acpitz0 at acpi0: critical temperature is 128 degC
acpithinkpad0 at acpi0
acpiac0 at acpi0: AC unit online
acpibat0 at acpi0: BAT0 model "00HW022" serial  2680 type LiP oem "SANYO"
acpibat1 at acpi0: BAT1 model "01AV406" serial  4538 type LiP oem "SMP"
"INT3F0D" at acpi0 not configured
"LEN0071" at acpi0 not configured
"LEN006C" at acpi0 not configured
acpibtn0 at acpi0: SLPB
"PNP0C14" at acpi0 not configured
acpibtn1 at acpi0: LID_
"PNP0C14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
"MSFT0101" at acpi0 not configured
"INT3394" at acpi0 not configured
"USBC000" at acpi0 not configured
acpivideo0 at acpi0: GFX0
acpivout at acpivideo0 not configured
cpu0: Enhanced SpeedStep 2694 MHz: speeds: 2801, 2800, 2700, 2500, 2400, 2200, 2000, 1800, 1600, 1500, 1300, 1100, 800, 700, 600, 400 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel Core 7G Host" rev 0x02
inteldrm0 at pci0 dev 2 function 0 "Intel HD Graphics 620" rev 0x02
drm0 at inteldrm0
inteldrm0: msi
error: [drm:pid0:i915_firmware_load_error_print] *ERROR* failed to load firmware i915/kbl_dmc_ver1.bin (-22)
inteldrm0: 1920x1080, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
xhci0 at pci0 dev 20 function 0 "Intel 100 Series xHCI" rev 0x21: msi
usb0 at xhci0: USB revision 3.0
uhub0 at usb0 configuration 1 interface 0 "Intel xHCI root hub" rev 3.00/1.00 addr 1
pchtemp0 at pci0 dev 20 function 2 "Intel 100 Series Thermal" rev 0x21
"Intel 100 Series MEI" rev 0x21 at pci0 dev 22 function 0 not configured
ppb0 at pci0 dev 28 function 0 "Intel 100 Series PCIE" rev 0xf1: msi
pci1 at ppb0 bus 1
ppb1 at pci0 dev 28 function 2 "Intel 100 Series PCIE" rev 0xf1: msi
pci2 at ppb1 bus 58
iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless-AC 8265" rev 0x78, msi
ppb2 at pci0 dev 29 function 0 "Intel 100 Series PCIE" rev 0xf1: msi
pci3 at ppb2 bus 60
nvme0 at pci3 dev 0 function 0 "Toshiba NVMe" rev 0x01: msi, NVMe 1.1
nvme0: THNSF5256GPUK TOSHIBA, firmware 51045KLA, serial X7PS10Y9TR3T
scsibus1 at nvme0: 1 targets
sd0 at scsibus1 targ 0 lun 0: <NVMe, THNSF5256GPUK TO, 5104> SCSI4 0/direct fixed
sd0: 244198MB, 512 bytes/sector, 500118192 sectors
pcib0 at pci0 dev 31 function 0 "Intel 200 Series LPC" rev 0x21
"Intel 100 Series PMC" rev 0x21 at pci0 dev 31 function 2 not configured
azalia0 at pci0 dev 31 function 3 "Intel 200 Series HD Audio" rev 0x21: msi
azalia0: codecs: Realtek/0x0298, Intel/0x280b, using Realtek/0x0298
audio0 at azalia0
ichiic0 at pci0 dev 31 function 4 "Intel 100 Series SMBus" rev 0x21: apic 2 int 16
iic0 at ichiic0
em0 at pci0 dev 31 function 6 "Intel I219-LM" rev 0x21: msi, address 54:e1:ad:d9:d5:5a
isa0 at pcib0
isadma0 at isa0
com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
wsmouse1 at pms0 mux 0
pms0: Synaptics clickpad, firmware 8.2, 0x1e2b1 0x943300
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
vmm0 at mainbus0: VMX/EPT
efifb at mainbus0 not configured
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
uhub1 at uhub0 port 4 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" rev 2.10/50.40 addr 2
uplcom0 at uhub1 port 1 configuration 1 interface 0 "Prolific Technology Inc. USB-Serial Controller" rev 1.10/3.00 addr 3
ucom0 at uplcom0
uhub2 at uhub1 port 4 configuration 1 interface 0 "Lenovo Lenovo ThinkPad Dock" rev 2.00/0.01 addr 4
uhidev0 at uhub2 port 3 configuration 1 interface 0 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev0: iclass 3/1
ukbd0 at uhidev0: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
wskbd1: connecting to wsdisplay0
uhidev1 at uhub2 port 3 configuration 1 interface 1 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev1: iclass 3/1, 26 report ids
uhid0 at uhidev1 reportid 18: input=0, output=0, feature=1
uhid1 at uhidev1 reportid 23: input=0, output=0, feature=1
ums0 at uhidev1 reportid 26: 5 buttons, Z and W dir
wsmouse2 at ums0 mux 0
uhidev2 at uhub2 port 3 configuration 1 interface 2 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev2: iclass 3/0, 39 report ids
uhid2 at uhidev2 reportid 3: input=1, output=0, feature=0
uhid3 at uhidev2 reportid 7: input=7, output=0, feature=0
uhid4 at uhidev2 reportid 32: input=0, output=0, feature=18
uhid5 at uhidev2 reportid 33: input=2, output=0, feature=0
uhid6 at uhidev2 reportid 34: input=0, output=0, feature=26
uhid7 at uhidev2 reportid 35: input=0, output=0, feature=26
uhid8 at uhidev2 reportid 36: input=0, output=0, feature=31
uhid9 at uhidev2 reportid 37: input=0, output=0, feature=31
uhid10 at uhidev2 reportid 38: input=0, output=0, feature=31
uhid11 at uhidev2 reportid 39: input=31, output=0, feature=0
ugen0 at uhub0 port 7 "Intel Bluetooth" rev 2.00/0.10 addr 6
uvideo0 at uhub0 port 8 configuration 1 interface 0 "Bison Integrated Camera" rev 2.00/37.27 addr 7
video0 at uvideo0
umass0 at uhub0 port 15 configuration 1 interface 0 "Generic USB3.0-CRW" rev 3.00/2.04 addr 8
umass0: using SCSI over Bulk-Only
scsibus2 at umass0: 2 targets, initiator 0
sd1 at scsibus2 targ 1 lun 0: <Generic-, SD/MMC, 1.00> SCSI4 0/direct removable serial.0bda0316501030900000
uhub3 at uhub0 port 16 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" rev 3.00/50.41 addr 9
vscsi0 at root
scsibus3 at vscsi0: 256 targets
softraid0 at root
scsibus4 at softraid0: 256 targets
sd2 at scsibus4 targ 1 lun 0: <OPENBSD, SR CRYPTO, 006> SCSI2 0/direct fixed
sd2: 174079MB, 512 bytes/sector, 356515312 sectors
root on sd2a (e562b0aa528a77c5.a) swap on sd2b dump on sd2b
iwm0: hw rev 0x230, fw ver 22.361476.0, address 40:a3:cc:28:68:0b
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
acpivideo0: unknown event 0x00
error: [drm:pid52261:intel_pipe_update_start] *ERROR* Potential atomic update failure on pipe B
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
vblank not available on crtc 1, ret=-22
ucom0 detached
uplcom0 detached
wskbd1: disconnecting from wsdisplay0
wskbd1 detached
ukbd0 detached
uhidev0 detached
uhid0 detached
uhid1 detached
wsmouse2 detached
ums0 detached
uhidev1 detached
uhid2 detached
uhid3 detached
uhid4 detached
uhid5 detached
uhid6 detached
uhid7 detached
uhid8 detached
uhid9 detached
uhid10 detached
uhid11 detached
uhidev2 detached
uhub2 detached
uhub1 detached
ugen0 detached
video0 detached
uvideo0 detached
sd1 detached
scsibus2 detached
umass0 detached
uhub3 detached
uhub0 detached
/var force dirty (dangling 4 inflight 0)
/usr/local force dirty (dangling 4 inflight 0)
/tmp force dirty (dangling 32 inflight 0)
/home force dirty (dangling 3 inflight 0)
uhub0 at usb0 configuration 1 interface 0 "Intel xHCI root hub" rev 3.00/1.00 addr 1
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
WARNING !drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4917
WARNING !drm_modeset_is_locked(&dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4524
WARNING !drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4917
WARNING !drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4917
uhub1 at uhub0 port 4 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" rev 2.10/50.40 addr 2
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
uplcom0 at uhub1 port 1 configuration 1 interface 0 "Prolific Technology Inc. USB-Serial Controller" rev 1.10/3.00 addr 3
ucom0 at uplcom0
uhub2 at uhub1 port 4 configuration 1 interface 0 "Lenovo Lenovo ThinkPad Dock" rev 2.00/0.01 addr 4
uhidev0 at uhub2 port 3 configuration 1 interface 0 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev0: iclass 3/1
ukbd0 at uhidev0: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
wskbd1: connecting to wsdisplay0
uhidev1 at uhub2 port 3 configuration 1 interface 1 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev1: iclass 3/1, 26 report ids
uhid0 at uhidev1 reportid 18: input=0, output=0, feature=1
uhid1 at uhidev1 reportid 23: input=0, output=0, feature=1
ums0 at uhidev1 reportid 26: 5 buttons, Z and W dir
wsmouse2 at ums0 mux 0
uhidev2 at uhub2 port 3 configuration 1 interface 2 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev2: iclass 3/0, 39 report ids
uhid2 at uhidev2 reportid 3: input=1, output=0, feature=0
uhid3 at uhidev2 reportid 7: input=7, output=0, feature=0
uhid4 at uhidev2 reportid 32: input=0, output=0, feature=18
uhid5 at uhidev2 reportid 33: input=2, output=0, feature=0
uhid6 at uhidev2 reportid 34: input=0, output=0, feature=26
uhid7 at uhidev2 reportid 35: input=0, output=0, feature=26
uhid8 at uhidev2 reportid 36: input=0, output=0, feature=31
uhid9 at uhidev2 reportid 37: input=0, output=0, feature=31
uhid10 at uhidev2 reportid 38: input=0, output=0, feature=31
uhid11 at uhidev2 reportid 39: input=31, output=0, feature=0
ugen0 at uhub0 port 7 "Intel Bluetooth" rev 2.00/0.10 addr 6
uvideo0 at uhub0 port 8 configuration 1 interface 0 "Bison Integrated Camera" rev 2.00/37.27 addr 7
video0 at uvideo0
umass0 at uhub0 port 15 configuration 1 interface 0 "Generic USB3.0-CRW" rev 3.00/2.04 addr 8
umass0: using SCSI over Bulk-Only
scsibus2 at umass0: 2 targets, initiator 0
sd1 at scsibus2 targ 1 lun 0: <Generic-, SD/MMC, 1.00> SCSI4 0/direct removable serial.0bda0316501030900000
uhub3 at uhub0 port 16 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" rev 3.00/50.41 addr 9
acpivideo0: unknown event 0x00
error: [drm:pid2231:intel_cpu_fifo_underrun_irq_handler] *ERROR* CPU pipe B FIFO underrun
error: [drm:pid52261:intel_pipe_update_start] *ERROR* Potential atomic update failure on pipe B
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
vblank not available on crtc 1, ret=-22
ucom0 detached
uplcom0 detached
wskbd1: disconnecting from wsdisplay0
wskbd1 detached
ukbd0 detached
uhidev0 detached
uhid0 detached
uhid1 detached
wsmouse2 detached
ums0 detached
uhidev1 detached
uhid2 detached
uhid3 detached
uhid4 detached
uhid5 detached
uhid6 detached
uhid7 detached
uhid8 detached
uhid9 detached
uhid10 detached
uhid11 detached
uhidev2 detached
uhub2 detached
uhub1 detached
ugen0 detached
video0 detached
uvideo0 detached
sd1 detached
scsibus2 detached
umass0 detached
uhub3 detached
uhub0 detached
/var force dirty (dangling 4 inflight 0)
/usr/local force dirty (dangling 5 inflight 0)
/tmp force dirty (dangling 23 inflight 0)
/home force dirty (dangling 6 inflight 0)
uhub0 at usb0 configuration 1 interface 0 "Intel xHCI root hub" rev 3.00/1.00 addr 1
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
WARNING !drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4917
WARNING !drm_modeset_is_locked(&dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4524
WARNING !drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4917
WARNING !drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex) failed at /usr/src/sys/dev/pci/drm/i915/intel_dp.c:4917
uhub1 at uhub0 port 4 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" rev 2.10/50.40 addr 2
WARNING !wm_changed failed at /usr/src/sys/dev/pci/drm/i915/intel_pm.c:3609
uplcom0 at uhub1 port 1 configuration 1 interface 0 "Prolific Technology Inc. USB-Serial Controller" rev 1.10/3.00 addr 3
ucom0 at uplcom0
uhub2 at uhub1 port 4 configuration 1 interface 0 "Lenovo Lenovo ThinkPad Dock" rev 2.00/0.01 addr 4
uhidev0 at uhub2 port 3 configuration 1 interface 0 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev0: iclass 3/1
ukbd0 at uhidev0: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
wskbd1: connecting to wsdisplay0
uhidev1 at uhub2 port 3 configuration 1 interface 1 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev1: iclass 3/1, 26 report ids
uhid0 at uhidev1 reportid 18: input=0, output=0, feature=1
uhid1 at uhidev1 reportid 23: input=0, output=0, feature=1
ums0 at uhidev1 reportid 26: 5 buttons, Z and W dir
wsmouse2 at ums0 mux 0
uhidev2 at uhub2 port 3 configuration 1 interface 2 "Microsoft Microsoft\M-. 2.4GHz Transceiver v9.0" rev 2.00/7.97 addr 5
uhidev2: iclass 3/0, 39 report ids
uhid2 at uhidev2 reportid 3: input=1, output=0, feature=0
uhid3 at uhidev2 reportid 7: input=7, output=0, feature=0
uhid4 at uhidev2 reportid 32: input=0, output=0, feature=18
uhid5 at uhidev2 reportid 33: input=2, output=0, feature=0
uhid6 at uhidev2 reportid 34: input=0, output=0, feature=26
uhid7 at uhidev2 reportid 35: input=0, output=0, feature=26
uhid8 at uhidev2 reportid 36: input=0, output=0, feature=31
uhid9 at uhidev2 reportid 37: input=0, output=0, feature=31
uhid10 at uhidev2 reportid 38: input=0, output=0, feature=31
uhid11 at uhidev2 reportid 39: input=31, output=0, feature=0
ugen0 at uhub0 port 7 "Intel Bluetooth" rev 2.00/0.10 addr 6
uvideo0 at uhub0 port 8 configuration 1 interface 0 "Bison Integrated Camera" rev 2.00/37.27 addr 7
video0 at uvideo0
umass0 at uhub0 port 15 configuration 1 interface 0 "Generic USB3.0-CRW" rev 3.00/2.04 addr 8
umass0: using SCSI over Bulk-Only
scsibus2 at umass0: 2 targets, initiator 0
sd1 at scsibus2 targ 1 lun 0: <Generic-, SD/MMC, 1.00> SCSI4 0/direct removable serial.0bda0316501030900000
uhub3 at uhub0 port 16 configuration 1 interface 0 "LENOVO Lenovo ThinkPad Dock" rev 3.00/50.41 addr 9
acpivideo0: unknown event 0x00