@@ -3312,6 +3312,8 @@ static void intel_disable_ddi(struct intel_atomic_state *state,
const struct intel_crtc_state *old_crtc_state,
const struct drm_connector_state *old_conn_state)
{
+ cancel_delayed_work(&enc_to_dig_port(encoder)->reset_link_work);
+
intel_hdcp_disable(to_intel_connector(old_conn_state->connector));
if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
@@ -4220,9 +4222,29 @@ static void intel_ddi_encoder_reset(struct drm_encoder *encoder)
intel_tc_port_init_mode(dig_port);
}
+static bool intel_ddi_tc_port_reset_link(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (!intel_tc_port_link_needs_reset(dig_port))
+ return false;
+
+ queue_delayed_work(system_unbound_wq, &dig_port->reset_link_work, msecs_to_jiffies(2000));
+
+ return true;
+}
+
+static int intel_ddi_encoder_late_register(struct drm_encoder *_encoder)
+{
+ intel_ddi_tc_port_reset_link(to_intel_encoder(_encoder));
+
+ return 0;
+}
+
static const struct drm_encoder_funcs intel_ddi_funcs = {
.reset = intel_ddi_encoder_reset,
.destroy = intel_ddi_encoder_destroy,
+ .late_register = intel_ddi_encoder_late_register,
};
static struct intel_connector *
@@ -4397,6 +4419,25 @@ static void call_with_modeset_ctx(int (*fn)(struct intel_encoder *encoder,
"Acquiring modeset locks failed with %i\n", ret);
}
+static void intel_ddi_tc_link_reset_work(struct work_struct *work)
+{
+ struct intel_digital_port *dig_port =
+ container_of(work, struct intel_digital_port, reset_link_work.work);
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_encoder *encoder = &dig_port->base;
+
+ mutex_lock(&i915->drm.mode_config.mutex);
+
+ if (intel_tc_port_link_needs_reset(dig_port)) {
+ drm_dbg_kms(&i915->drm,
+ "[ENCODER:%d:%s] TypeC DP-alt sink disconnected, resetting link\n",
+ encoder->base.base.id, encoder->base.name);
+ call_with_modeset_ctx(intel_dp_reset_link, &dig_port->base);
+ }
+
+ mutex_unlock(&i915->drm.mode_config.mutex);
+}
+
static enum intel_hotplug_state
intel_ddi_hotplug(struct intel_encoder *encoder,
struct intel_connector *connector)
@@ -4417,10 +4458,12 @@ intel_ddi_hotplug(struct intel_encoder *encoder,
state = intel_encoder_hotplug(encoder, connector);
- if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA)
- call_with_modeset_ctx(intel_hdmi_reset_link, encoder);
- else
- call_with_modeset_ctx(intel_dp_retrain_link, encoder);
+ if (!intel_ddi_tc_port_reset_link(encoder)) {
+ if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA)
+ call_with_modeset_ctx(intel_hdmi_reset_link, encoder);
+ else
+ call_with_modeset_ctx(intel_dp_retrain_link, encoder);
+ }
/*
* Unpowered type-c dongles can take some time to boot and be
@@ -4956,6 +4999,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
encoder->pipe_mask = intel_ddi_splitter_pipe_mask(dev_priv);
}
+ INIT_DELAYED_WORK(&dig_port->reset_link_work, intel_ddi_tc_link_reset_work);
+
/*
* In theory we don't need the encoder->type check,
* but leave it just in case we have some really bad VBTs...
@@ -1803,6 +1803,8 @@ struct intel_digital_port {
struct intel_tc_port *tc;
+ struct delayed_work reset_link_work;
+
/* protects num_hdcp_streams reference count, hdcp_port_data and hdcp_auth_status */
struct mutex hdcp_mutex;
/* the number of pipes using HDCP signalling out of this port */
@@ -4250,6 +4250,55 @@ int intel_dp_retrain_link(struct intel_encoder *encoder,
return 0;
}
+int intel_dp_reset_link(struct intel_encoder *encoder,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ struct drm_atomic_state *_state;
+ struct intel_atomic_state *state;
+ struct intel_crtc *crtc;
+ u8 pipe_mask;
+ int ret = 0;
+
+ ret = drm_modeset_lock(&i915->drm.mode_config.connection_mutex,
+ ctx);
+ if (ret)
+ return ret;
+
+ ret = intel_dp_get_active_pipes(enc_to_intel_dp(encoder), ctx, &pipe_mask);
+ if (ret)
+ return ret;
+
+ if (!pipe_mask)
+ return 0;
+
+ _state = drm_atomic_state_alloc(&i915->drm);
+ if (!_state)
+ return -ENOMEM;
+ state = to_intel_atomic_state(_state);
+
+ state->base.acquire_ctx = ctx;
+ state->internal = true;
+
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, pipe_mask) {
+ struct intel_crtc_state *crtc_state;
+
+ crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ break;
+ }
+
+ crtc_state->uapi.connectors_changed = true;
+ }
+
+ ret = drm_atomic_commit(&state->base);
+
+ drm_atomic_state_put(&state->base);
+
+ return ret;
+}
+
static int intel_dp_prep_phy_test(struct intel_dp *intel_dp,
struct drm_modeset_acquire_ctx *ctx,
u8 *pipe_mask)
@@ -45,6 +45,8 @@ int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
bool intel_dp_is_connected(struct intel_dp *intel_dp);
int intel_dp_retrain_link(struct intel_encoder *encoder,
struct drm_modeset_acquire_ctx *ctx);
+int intel_dp_reset_link(struct intel_encoder *encoder,
+ struct drm_modeset_acquire_ctx *ctx);
void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode);
void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
@@ -26,6 +26,7 @@
#include "intel_fifo_underrun.h"
#include "intel_modeset_setup.h"
#include "intel_pch_display.h"
+#include "intel_tc.h"
#include "intel_vblank.h"
#include "intel_wm.h"
#include "skl_watermark.h"
@@ -253,17 +254,6 @@ intel_sanitize_plane_mapping(struct drm_i915_private *i915)
}
}
-static bool intel_crtc_has_encoders(struct intel_crtc *crtc)
-{
- struct drm_device *dev = crtc->base.dev;
- struct intel_encoder *encoder;
-
- for_each_encoder_on_crtc(dev, &crtc->base, encoder)
- return true;
-
- return false;
-}
-
static struct intel_connector *intel_encoder_find_connector(struct intel_encoder *encoder)
{
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
@@ -382,6 +372,9 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc,
{
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state);
+ struct intel_encoder *encoder;
+ bool needs_link_reset = false;
+ bool has_encoder = false;
if (crtc_state->hw.active) {
struct intel_plane *plane;
@@ -401,13 +394,28 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc,
intel_color_commit_arm(crtc_state);
}
+ if (!crtc_state->hw.active ||
+ intel_crtc_is_bigjoiner_slave(crtc_state))
+ return;
+
+ for_each_encoder_on_crtc(&i915->drm, &crtc->base, encoder) {
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ has_encoder = true;
+
+ if (!dig_port || !intel_tc_port_link_needs_reset(dig_port))
+ continue;
+
+ needs_link_reset = true;
+ break;
+ }
+
/*
* Adjust the state of the output pipe according to whether we have
* active connectors/encoders.
* TODO: Add support for MST
*/
- if (crtc_state->hw.active && !intel_crtc_has_encoders(crtc) &&
- !intel_crtc_is_bigjoiner_slave(crtc_state))
+ if (!has_encoder || needs_link_reset)
disable_crtc_with_slaves(crtc, ctx);
}
@@ -1335,6 +1335,27 @@ bool intel_tc_port_connected(struct intel_encoder *encoder)
return is_connected;
}
+bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+ bool ret;
+
+ if (!intel_phy_is_tc(i915, phy))
+ return false;
+
+ mutex_lock(&tc->lock);
+
+ ret = tc->link_refcount &&
+ intel_tc_port_in_dp_alt_mode(dig_port) &&
+ intel_tc_port_needs_reset(tc);
+
+ mutex_unlock(&tc->lock);
+
+ return ret;
+}
+
static void __intel_tc_port_lock(struct intel_tc_port *tc,
int required_lanes)
{
@@ -34,6 +34,7 @@ void intel_tc_port_flush_work(struct intel_digital_port *dig_port);
void intel_tc_port_get_link(struct intel_digital_port *dig_port,
int required_lanes);
void intel_tc_port_put_link(struct intel_digital_port *dig_port);
+bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port);
bool intel_tc_port_ref_held(struct intel_digital_port *dig_port);
int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);