Commit 357c0ae9 authored by Imre Deak's avatar Imre Deak

drm/i915/lspcon: Wait for expected LSPCON mode to settle

Some LSPCON adaptors may return an incorrect LSPCON mode right after
waking from DP Sleep state. This is the case at least for the ParadTech
PS175 adaptor, both when waking because of exiting the DP Sleep to
active state, or due to any other AUX CH transfer. We can determine the
current expected mode based on whether the DPCD area is accessible,
since according to the LSPCON spec this area is only accesible
in PCON mode.

This wait will avoid us trying to change the mode, while the current
expected mode hasn't settled yet and start link training before the
adaptor thinks it's in PCON mode after waking from DP Sleep state.

Cc: Shashank Sharma <shashank.sharma@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: default avatarImre Deak <imre.deak@intel.com>
Reviewed-by: default avatarShashank Sharma <shashank.sharma@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1479755707-29596-4-git-send-email-imre.deak@intel.com
parent dd75f6dd
......@@ -2401,6 +2401,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER,
DP_SET_POWER_D3);
} else {
struct intel_lspcon *lspcon = dp_to_lspcon(intel_dp);
/*
* When turning on, we need to retry for 1ms to give the sink
* time to wake up.
......@@ -2412,6 +2414,9 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
break;
msleep(1);
}
if (ret == 1 && lspcon->active)
lspcon_wait_pcon_mode(lspcon);
}
if (ret != 1)
......
......@@ -1830,4 +1830,5 @@ void intel_color_load_luts(struct drm_crtc_state *crtc_state);
/* intel_lspcon.c */
bool lspcon_init(struct intel_digital_port *intel_dig_port);
void lspcon_resume(struct intel_lspcon *lspcon);
void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon);
#endif /* __INTEL_DRV_H__ */
......@@ -35,16 +35,54 @@ static struct intel_dp *lspcon_to_intel_dp(struct intel_lspcon *lspcon)
return &dig_port->dp;
}
static const char *lspcon_mode_name(enum drm_lspcon_mode mode)
{
switch (mode) {
case DRM_LSPCON_MODE_PCON:
return "PCON";
case DRM_LSPCON_MODE_LS:
return "LS";
case DRM_LSPCON_MODE_INVALID:
return "INVALID";
default:
MISSING_CASE(mode);
return "INVALID";
}
}
static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon)
{
enum drm_lspcon_mode current_mode = DRM_LSPCON_MODE_INVALID;
enum drm_lspcon_mode current_mode;
struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc;
if (drm_lspcon_get_mode(adapter, &current_mode))
if (drm_lspcon_get_mode(adapter, &current_mode)) {
DRM_ERROR("Error reading LSPCON mode\n");
else
return DRM_LSPCON_MODE_INVALID;
}
return current_mode;
}
static enum drm_lspcon_mode lspcon_wait_mode(struct intel_lspcon *lspcon,
enum drm_lspcon_mode mode)
{
enum drm_lspcon_mode current_mode;
current_mode = lspcon_get_current_mode(lspcon);
if (current_mode == mode || current_mode == DRM_LSPCON_MODE_INVALID)
goto out;
DRM_DEBUG_KMS("Waiting for LSPCON mode %s to settle\n",
lspcon_mode_name(mode));
wait_for((current_mode = lspcon_get_current_mode(lspcon)) == mode ||
current_mode == DRM_LSPCON_MODE_INVALID, 100);
if (current_mode != mode)
DRM_DEBUG_KMS("LSPCON mode hasn't settled\n");
out:
DRM_DEBUG_KMS("Current LSPCON mode %s\n",
current_mode == DRM_LSPCON_MODE_PCON ? "PCON" : "LS");
lspcon_mode_name(current_mode));
return current_mode;
}
......@@ -97,8 +135,10 @@ static bool lspcon_probe(struct intel_lspcon *lspcon)
{
enum drm_dp_dual_mode_type adaptor_type;
struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc;
enum drm_lspcon_mode expected_mode;
lspcon_wake_native_aux_ch(lspcon);
expected_mode = lspcon_wake_native_aux_ch(lspcon) ?
DRM_LSPCON_MODE_PCON : DRM_LSPCON_MODE_LS;
/* Lets probe the adaptor and check its type */
adaptor_type = drm_dp_dual_mode_detect(adapter);
......@@ -110,7 +150,7 @@ static bool lspcon_probe(struct intel_lspcon *lspcon)
/* Yay ... got a LSPCON device */
DRM_DEBUG_KMS("LSPCON detected\n");
lspcon->mode = lspcon_get_current_mode(lspcon);
lspcon->mode = lspcon_wait_mode(lspcon, expected_mode);
lspcon->active = true;
return true;
}
......@@ -150,8 +190,17 @@ static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon)
void lspcon_resume(struct intel_lspcon *lspcon)
{
if (lspcon_wake_native_aux_ch(lspcon))
enum drm_lspcon_mode expected_mode;
if (lspcon_wake_native_aux_ch(lspcon)) {
expected_mode = DRM_LSPCON_MODE_PCON;
lspcon_resume_in_pcon_wa(lspcon);
} else {
expected_mode = DRM_LSPCON_MODE_LS;
}
if (lspcon_wait_mode(lspcon, expected_mode) == DRM_LSPCON_MODE_PCON)
return;
if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON, true))
DRM_ERROR("LSPCON resume failed\n");
......@@ -159,6 +208,11 @@ void lspcon_resume(struct intel_lspcon *lspcon)
DRM_DEBUG_KMS("LSPCON resume success\n");
}
void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon)
{
lspcon_wait_mode(lspcon, DRM_LSPCON_MODE_PCON);
}
bool lspcon_init(struct intel_digital_port *intel_dig_port)
{
struct intel_dp *dp = &intel_dig_port->dp;
......
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