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