Commit 3b3be899 authored by Imre Deak's avatar Imre Deak

drm/i915/dp: Recheck link state after modeset

Recheck the link state after a passing link training, with a 2 sec delay
to account for cases where the link goes bad following the link training
and the sink doesn't report this via an HPD IRQ.

The delayed work added here will be also used by a later patch after a
failed link training to try to retrain the link with unchanged link
params before reducing the link params.

v2: Don't flush an uninitialized delayed work (on HDMI-only DDI ports).
v3:
- Move the helpers to a new intel_encoder.c file, rename them
  accordingly. (Ville)
- Add the work to intel_encoder instead of intel_digital_port.
- Call the encoder specific link check function via an encoder hook.
- Flush the link check work during encoder destroy from
  intel_dp_encoder_flush_work().
- Flush the link check work during encoder suspend as well.
v4: Call intel_encoder_link_check_init() with a valid encoder pointer.

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarImre Deak <imre.deak@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240610181428.2955658-1-imre.deak@intel.com
parent 4ce7cff5
...@@ -329,6 +329,7 @@ i915-y += \ ...@@ -329,6 +329,7 @@ i915-y += \
display/intel_dsi_dcs_backlight.o \ display/intel_dsi_dcs_backlight.o \
display/intel_dsi_vbt.o \ display/intel_dsi_vbt.o \
display/intel_dvo.o \ display/intel_dvo.o \
display/intel_encoder.o \
display/intel_gmbus.o \ display/intel_gmbus.o \
display/intel_hdmi.o \ display/intel_hdmi.o \
display/intel_lspcon.o \ display/intel_lspcon.o \
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "intel_dp_aux.h" #include "intel_dp_aux.h"
#include "intel_dp_link_training.h" #include "intel_dp_link_training.h"
#include "intel_dpio_phy.h" #include "intel_dpio_phy.h"
#include "intel_encoder.h"
#include "intel_fifo_underrun.h" #include "intel_fifo_underrun.h"
#include "intel_hdmi.h" #include "intel_hdmi.h"
#include "intel_hotplug.h" #include "intel_hotplug.h"
...@@ -1239,6 +1240,15 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder) ...@@ -1239,6 +1240,15 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder)
return intel_de_read(dev_priv, DEISR) & bit; return intel_de_read(dev_priv, DEISR) & bit;
} }
static void g4x_dp_suspend_complete(struct intel_encoder *encoder)
{
/*
* TODO: Move this to intel_dp_encoder_suspend(),
* once modeset locking around that is removed.
*/
intel_encoder_link_check_flush_work(encoder);
}
static void intel_dp_encoder_destroy(struct drm_encoder *encoder) static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
{ {
intel_dp_encoder_flush_work(encoder); intel_dp_encoder_flush_work(encoder);
...@@ -1325,6 +1335,8 @@ bool g4x_dp_init(struct drm_i915_private *dev_priv, ...@@ -1325,6 +1335,8 @@ bool g4x_dp_init(struct drm_i915_private *dev_priv,
"DP %c", port_name(port))) "DP %c", port_name(port)))
goto err_encoder_init; goto err_encoder_init;
intel_encoder_link_check_init(intel_encoder, intel_dp_link_check);
intel_encoder->hotplug = intel_dp_hotplug; intel_encoder->hotplug = intel_dp_hotplug;
intel_encoder->compute_config = intel_dp_compute_config; intel_encoder->compute_config = intel_dp_compute_config;
intel_encoder->get_hw_state = intel_dp_get_hw_state; intel_encoder->get_hw_state = intel_dp_get_hw_state;
...@@ -1333,6 +1345,7 @@ bool g4x_dp_init(struct drm_i915_private *dev_priv, ...@@ -1333,6 +1345,7 @@ bool g4x_dp_init(struct drm_i915_private *dev_priv,
intel_encoder->initial_fastset_check = intel_dp_initial_fastset_check; intel_encoder->initial_fastset_check = intel_dp_initial_fastset_check;
intel_encoder->update_pipe = intel_backlight_update; intel_encoder->update_pipe = intel_backlight_update;
intel_encoder->suspend = intel_dp_encoder_suspend; intel_encoder->suspend = intel_dp_encoder_suspend;
intel_encoder->suspend_complete = g4x_dp_suspend_complete;
intel_encoder->shutdown = intel_dp_encoder_shutdown; intel_encoder->shutdown = intel_dp_encoder_shutdown;
if (IS_CHERRYVIEW(dev_priv)) { if (IS_CHERRYVIEW(dev_priv)) {
intel_encoder->pre_pll_enable = chv_dp_pre_pll_enable; intel_encoder->pre_pll_enable = chv_dp_pre_pll_enable;
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#include "intel_dp_tunnel.h" #include "intel_dp_tunnel.h"
#include "intel_dpio_phy.h" #include "intel_dpio_phy.h"
#include "intel_dsi.h" #include "intel_dsi.h"
#include "intel_encoder.h"
#include "intel_fdi.h" #include "intel_fdi.h"
#include "intel_fifo_underrun.h" #include "intel_fifo_underrun.h"
#include "intel_gmbus.h" #include "intel_gmbus.h"
...@@ -4525,6 +4526,17 @@ static int intel_hdmi_reset_link(struct intel_encoder *encoder, ...@@ -4525,6 +4526,17 @@ static int intel_hdmi_reset_link(struct intel_encoder *encoder,
return intel_modeset_commit_pipes(dev_priv, BIT(crtc->pipe), ctx); return intel_modeset_commit_pipes(dev_priv, BIT(crtc->pipe), ctx);
} }
static void intel_ddi_link_check(struct intel_encoder *encoder)
{
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
/* TODO: Move checking the HDMI link state here as well. */
drm_WARN_ON(&i915->drm, !dig_port->dp.attached_connector);
intel_dp_link_check(encoder);
}
static enum intel_hotplug_state static enum intel_hotplug_state
intel_ddi_hotplug(struct intel_encoder *encoder, intel_ddi_hotplug(struct intel_encoder *encoder,
struct intel_connector *connector) struct intel_connector *connector)
...@@ -4768,6 +4780,11 @@ static void intel_ddi_tc_encoder_suspend_complete(struct intel_encoder *encoder) ...@@ -4768,6 +4780,11 @@ static void intel_ddi_tc_encoder_suspend_complete(struct intel_encoder *encoder)
struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
/*
* TODO: Move this to intel_dp_encoder_suspend(),
* once modeset locking around that is removed.
*/
intel_encoder_link_check_flush_work(encoder);
intel_tc_port_suspend(dig_port); intel_tc_port_suspend(dig_port);
} }
...@@ -4958,6 +4975,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, ...@@ -4958,6 +4975,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv,
"DDI %c/PHY %c", port_name(port), phy_name(phy)); "DDI %c/PHY %c", port_name(port), phy_name(phy));
} }
intel_encoder_link_check_init(encoder, intel_ddi_link_check);
mutex_init(&dig_port->hdcp_mutex); mutex_init(&dig_port->hdcp_mutex);
dig_port->num_hdcp_streams = 0; dig_port->num_hdcp_streams = 0;
......
...@@ -161,6 +161,11 @@ struct intel_encoder { ...@@ -161,6 +161,11 @@ struct intel_encoder {
enum port port; enum port port;
u16 cloneable; u16 cloneable;
u8 pipe_mask; u8 pipe_mask;
/* Check and recover a bad link state. */
struct delayed_work link_check_work;
void (*link_check)(struct intel_encoder *encoder);
enum intel_hotplug_state (*hotplug)(struct intel_encoder *encoder, enum intel_hotplug_state (*hotplug)(struct intel_encoder *encoder,
struct intel_connector *connector); struct intel_connector *connector);
enum intel_output_type (*compute_output_type)(struct intel_encoder *, enum intel_output_type (*compute_output_type)(struct intel_encoder *,
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
#include "intel_dpio_phy.h" #include "intel_dpio_phy.h"
#include "intel_dpll.h" #include "intel_dpll.h"
#include "intel_drrs.h" #include "intel_drrs.h"
#include "intel_encoder.h"
#include "intel_fifo_underrun.h" #include "intel_fifo_underrun.h"
#include "intel_hdcp.h" #include "intel_hdcp.h"
#include "intel_hdmi.h" #include "intel_hdmi.h"
...@@ -76,6 +77,7 @@ ...@@ -76,6 +77,7 @@
#include "intel_hotplug_irq.h" #include "intel_hotplug_irq.h"
#include "intel_lspcon.h" #include "intel_lspcon.h"
#include "intel_lvds.h" #include "intel_lvds.h"
#include "intel_modeset_lock.h"
#include "intel_panel.h" #include "intel_panel.h"
#include "intel_pch_display.h" #include "intel_pch_display.h"
#include "intel_pps.h" #include "intel_pps.h"
...@@ -5248,6 +5250,18 @@ int intel_dp_retrain_link(struct intel_encoder *encoder, ...@@ -5248,6 +5250,18 @@ int intel_dp_retrain_link(struct intel_encoder *encoder,
return 0; return 0;
} }
void intel_dp_link_check(struct intel_encoder *encoder)
{
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
struct drm_modeset_acquire_ctx ctx;
int ret;
intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret)
ret = intel_dp_retrain_link(encoder, &ctx);
drm_WARN_ON(&i915->drm, ret);
}
static int intel_dp_prep_phy_test(struct intel_dp *intel_dp, static int intel_dp_prep_phy_test(struct intel_dp *intel_dp,
struct drm_modeset_acquire_ctx *ctx, struct drm_modeset_acquire_ctx *ctx,
u8 *pipe_mask) u8 *pipe_mask)
...@@ -6014,11 +6028,14 @@ void intel_dp_connector_sync_state(struct intel_connector *connector, ...@@ -6014,11 +6028,14 @@ void intel_dp_connector_sync_state(struct intel_connector *connector,
} }
} }
void intel_dp_encoder_flush_work(struct drm_encoder *encoder) void intel_dp_encoder_flush_work(struct drm_encoder *_encoder)
{ {
struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder)); struct intel_encoder *encoder = to_intel_encoder(_encoder);
struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
struct intel_dp *intel_dp = &dig_port->dp; struct intel_dp *intel_dp = &dig_port->dp;
intel_encoder_link_check_flush_work(encoder);
intel_dp_mst_encoder_cleanup(dig_port); intel_dp_mst_encoder_cleanup(dig_port);
intel_dp_tunnel_destroy(intel_dp); intel_dp_tunnel_destroy(intel_dp);
......
...@@ -60,6 +60,7 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp, ...@@ -60,6 +60,7 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp,
u8 *pipe_mask); u8 *pipe_mask);
int intel_dp_retrain_link(struct intel_encoder *encoder, int intel_dp_retrain_link(struct intel_encoder *encoder,
struct drm_modeset_acquire_ctx *ctx); struct drm_modeset_acquire_ctx *ctx);
void intel_dp_link_check(struct intel_encoder *encoder);
void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode); void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode);
void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "intel_display_types.h" #include "intel_display_types.h"
#include "intel_dp.h" #include "intel_dp.h"
#include "intel_dp_link_training.h" #include "intel_dp_link_training.h"
#include "intel_encoder.h"
#include "intel_panel.h" #include "intel_panel.h"
#define LT_MSG_PREFIX "[CONNECTOR:%d:%s][ENCODER:%d:%s][%s] " #define LT_MSG_PREFIX "[CONNECTOR:%d:%s][ENCODER:%d:%s][%s] "
...@@ -1464,6 +1465,8 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp, ...@@ -1464,6 +1465,8 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state) const struct intel_crtc_state *crtc_state)
{ {
struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct drm_i915_private *i915 = dp_to_i915(intel_dp);
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
struct intel_encoder *encoder = &dig_port->base;
bool passed; bool passed;
/* /*
...@@ -1483,6 +1486,11 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp, ...@@ -1483,6 +1486,11 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp,
else else
passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count); passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count);
if (passed) {
intel_encoder_link_check_queue_work(encoder, 2000);
return;
}
/* /*
* Ignore the link failure in CI * Ignore the link failure in CI
* *
...@@ -1495,13 +1503,12 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp, ...@@ -1495,13 +1503,12 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp,
* For test cases which rely on the link training or processing of HPDs * For test cases which rely on the link training or processing of HPDs
* ignore_long_hpd flag can unset from the testcase. * ignore_long_hpd flag can unset from the testcase.
*/ */
if (!passed && i915->display.hotplug.ignore_long_hpd) { if (i915->display.hotplug.ignore_long_hpd) {
lt_dbg(intel_dp, DP_PHY_DPRX, "Ignore the link failure\n"); lt_dbg(intel_dp, DP_PHY_DPRX, "Ignore the link failure\n");
return; return;
} }
if (!passed) intel_dp_schedule_fallback_link_training(intel_dp, crtc_state);
intel_dp_schedule_fallback_link_training(intel_dp, crtc_state);
} }
void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp,
......
// SPDX-License-Identifier: MIT
/*
* Copyright © 2024 Intel Corporation
*/
#include <linux/workqueue.h>
#include "i915_drv.h"
#include "intel_display_types.h"
#include "intel_encoder.h"
static void intel_encoder_link_check_work_fn(struct work_struct *work)
{
struct intel_encoder *encoder =
container_of(work, typeof(*encoder), link_check_work.work);
encoder->link_check(encoder);
}
void intel_encoder_link_check_init(struct intel_encoder *encoder,
void (*callback)(struct intel_encoder *encoder))
{
INIT_DELAYED_WORK(&encoder->link_check_work, intel_encoder_link_check_work_fn);
encoder->link_check = callback;
}
void intel_encoder_link_check_flush_work(struct intel_encoder *encoder)
{
cancel_delayed_work_sync(&encoder->link_check_work);
}
void intel_encoder_link_check_queue_work(struct intel_encoder *encoder, int delay_ms)
{
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
mod_delayed_work(i915->unordered_wq,
&encoder->link_check_work, msecs_to_jiffies(delay_ms));
}
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2024 Intel Corporation
*/
#ifndef __INTEL_ENCODER_H__
#define __INTEL_ENCODER_H__
struct intel_encoder;
void intel_encoder_link_check_init(struct intel_encoder *encoder,
void (*callback)(struct intel_encoder *encoder));
void intel_encoder_link_check_queue_work(struct intel_encoder *encoder, int delay_ms);
void intel_encoder_link_check_flush_work(struct intel_encoder *encoder);
#endif /* __INTEL_ENCODER_H__ */
...@@ -258,6 +258,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ ...@@ -258,6 +258,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_dsi.o \ i915-display/intel_dsi.o \
i915-display/intel_dsi_dcs_backlight.o \ i915-display/intel_dsi_dcs_backlight.o \
i915-display/intel_dsi_vbt.o \ i915-display/intel_dsi_vbt.o \
i915-display/intel_encoder.o \
i915-display/intel_fb.o \ i915-display/intel_fb.o \
i915-display/intel_fbc.o \ i915-display/intel_fbc.o \
i915-display/intel_fdi.o \ i915-display/intel_fdi.o \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment