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;
> }
>
>