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 via email to