2015-10-23 13:01:48 +03:00
/*
* Copyright © 2008 - 2015 Intel Corporation
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE .
*/
2019-08-06 14:39:33 +03:00
# include "intel_display_types.h"
2019-04-05 14:00:17 +03:00
# include "intel_dp.h"
2019-04-29 15:29:25 +03:00
# include "intel_dp_link_training.h"
2015-10-23 13:01:48 +03:00
2016-08-04 13:48:36 -07:00
static void
2019-01-16 11:15:27 +02:00
intel_dp_dump_link_status ( const u8 link_status [ DP_LINK_STATUS_SIZE ] )
2016-08-04 13:48:36 -07:00
{
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 ] ) ;
}
2020-05-12 20:41:43 +03:00
static u8 dp_voltage_max ( u8 preemph )
2020-05-12 20:41:42 +03:00
{
2020-05-12 20:41:43 +03:00
switch ( preemph & DP_TRAIN_PRE_EMPHASIS_MASK ) {
case DP_TRAIN_PRE_EMPH_LEVEL_0 :
return DP_TRAIN_VOLTAGE_SWING_LEVEL_3 ;
case DP_TRAIN_PRE_EMPH_LEVEL_1 :
return DP_TRAIN_VOLTAGE_SWING_LEVEL_2 ;
case DP_TRAIN_PRE_EMPH_LEVEL_2 :
return DP_TRAIN_VOLTAGE_SWING_LEVEL_1 ;
case DP_TRAIN_PRE_EMPH_LEVEL_3 :
2020-05-12 20:41:42 +03:00
default :
2020-05-12 20:41:43 +03:00
return DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ;
2020-05-12 20:41:42 +03:00
}
}
2020-03-16 16:07:55 +05:30
void intel_dp_get_adjust_train ( struct intel_dp * intel_dp ,
const u8 link_status [ DP_LINK_STATUS_SIZE ] )
2015-10-23 13:01:48 +03:00
{
2020-07-09 17:58:45 +03:00
struct drm_i915_private * i915 = dp_to_i915 ( intel_dp ) ;
2019-01-16 11:15:27 +02:00
u8 v = 0 ;
u8 p = 0 ;
2015-10-23 13:01:48 +03:00
int lane ;
2019-01-16 11:15:27 +02:00
u8 voltage_max ;
u8 preemph_max ;
2015-10-23 13:01:48 +03:00
for ( lane = 0 ; lane < intel_dp - > lane_count ; lane + + ) {
2020-05-12 20:41:45 +03:00
v = max ( v , drm_dp_get_adjust_request_voltage ( link_status , lane ) ) ;
p = max ( p , drm_dp_get_adjust_request_pre_emphasis ( link_status , lane ) ) ;
2015-10-23 13:01:48 +03:00
}
2020-05-12 20:41:43 +03:00
preemph_max = intel_dp - > preemph_max ( intel_dp ) ;
2020-07-09 17:58:45 +03:00
drm_WARN_ON_ONCE ( & i915 - > drm ,
preemph_max ! = DP_TRAIN_PRE_EMPH_LEVEL_2 & &
preemph_max ! = DP_TRAIN_PRE_EMPH_LEVEL_3 ) ;
2015-10-23 13:01:48 +03:00
if ( p > = preemph_max )
p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED ;
2020-05-12 20:41:44 +03:00
v = min ( v , dp_voltage_max ( p ) ) ;
voltage_max = intel_dp - > voltage_max ( intel_dp ) ;
2020-07-09 17:58:45 +03:00
drm_WARN_ON_ONCE ( & i915 - > drm ,
voltage_max ! = DP_TRAIN_VOLTAGE_SWING_LEVEL_2 & &
voltage_max ! = DP_TRAIN_VOLTAGE_SWING_LEVEL_3 ) ;
2020-05-12 20:41:43 +03:00
if ( v > = voltage_max )
v = voltage_max | DP_TRAIN_MAX_SWING_REACHED ;
2015-10-23 13:01:48 +03:00
for ( lane = 0 ; lane < 4 ; lane + + )
intel_dp - > train_set [ lane ] = v | p ;
}
static bool
intel_dp_set_link_train ( struct intel_dp * intel_dp ,
2019-01-16 11:15:27 +02:00
u8 dp_train_pat )
2015-10-23 13:01:48 +03:00
{
2019-01-16 11:15:27 +02:00
u8 buf [ sizeof ( intel_dp - > train_set ) + 1 ] ;
2015-10-23 13:01:48 +03:00
int ret , len ;
intel_dp_program_link_training_pattern ( intel_dp , dp_train_pat ) ;
buf [ 0 ] = dp_train_pat ;
if ( ( dp_train_pat & DP_TRAINING_PATTERN_MASK ) = =
DP_TRAINING_PATTERN_DISABLE ) {
/* don't write DP_TRAINING_LANEx_SET on disable */
len = 1 ;
} else {
/* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
memcpy ( buf + 1 , intel_dp - > train_set , intel_dp - > lane_count ) ;
len = intel_dp - > lane_count + 1 ;
}
ret = drm_dp_dpcd_write ( & intel_dp - > aux , DP_TRAINING_PATTERN_SET ,
buf , len ) ;
return ret = = len ;
}
static bool
intel_dp_reset_link_train ( struct intel_dp * intel_dp ,
2019-01-16 11:15:27 +02:00
u8 dp_train_pat )
2015-10-23 13:01:48 +03:00
{
2016-06-20 11:10:26 +03:00
memset ( intel_dp - > train_set , 0 , sizeof ( intel_dp - > train_set ) ) ;
2015-10-23 13:01:48 +03:00
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 )
{
int ret ;
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 ) ;
return ret = = intel_dp - > lane_count ;
}
2016-09-07 11:28:00 -07:00
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 ;
}
2015-10-23 13:01:48 +03:00
/* Enable corresponding port and start training pattern 1 */
2016-09-07 11:28:01 -07:00
static bool
2015-10-23 13:01:48 +03:00
intel_dp_link_training_clock_recovery ( struct intel_dp * intel_dp )
{
2020-02-06 11:00:03 +03:00
struct drm_i915_private * i915 = dp_to_i915 ( intel_dp ) ;
2019-01-16 11:15:27 +02:00
u8 voltage ;
2018-07-20 14:44:13 -07:00
int voltage_tries , cr_tries , max_cr_tries ;
bool max_vswing_reached = false ;
2019-01-16 11:15:27 +02:00
u8 link_config [ 2 ] ;
u8 link_bw , rate_select ;
2015-10-23 13:01:48 +03:00
2015-10-23 13:01:49 +03:00
if ( intel_dp - > prepare_link_retrain )
intel_dp - > prepare_link_retrain ( intel_dp ) ;
2015-10-23 13:01:48 +03:00
intel_dp_compute_rate ( intel_dp , intel_dp - > link_rate ,
& link_bw , & rate_select ) ;
2018-04-26 11:25:25 +03:00
if ( link_bw )
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm ,
" Using LINK_BW_SET value %02x \n " , link_bw ) ;
2018-04-26 11:25:25 +03:00
else
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm ,
" Using LINK_RATE_SET value %02x \n " , rate_select ) ;
2018-04-26 11:25:25 +03:00
2015-10-23 13:01:48 +03:00
/* Write the link configuration data */
link_config [ 0 ] = link_bw ;
link_config [ 1 ] = intel_dp - > lane_count ;
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 ) ;
2016-09-07 11:28:01 -07:00
2017-03-28 17:59:05 +03:00
/* eDP 1.4 rate select method. */
if ( ! link_bw )
2015-10-23 13:01:48 +03:00
drm_dp_dpcd_write ( & intel_dp - > aux , DP_LINK_RATE_SET ,
& rate_select , 1 ) ;
link_config [ 0 ] = 0 ;
link_config [ 1 ] = DP_SET_ANSI_8B10B ;
drm_dp_dpcd_write ( & intel_dp - > aux , DP_DOWNSPREAD_CTRL , link_config , 2 ) ;
intel_dp - > DP | = DP_PORT_EN ;
/* clock recovery */
if ( ! intel_dp_reset_link_train ( intel_dp ,
DP_TRAINING_PATTERN_1 |
DP_LINK_SCRAMBLING_DISABLE ) ) {
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm , " failed to enable link training \n " ) ;
2016-09-07 11:28:01 -07:00
return false ;
2015-10-23 13:01:48 +03:00
}
2018-07-20 14:44:12 -07:00
/*
2018-07-24 15:33:32 -07:00
* The DP 1.4 spec defines the max clock recovery retries value
* as 10 but for pre - DP 1.4 devices we set a very tolerant
* retry limit of 80 ( 4 voltage levels x 4 preemphasis levels x
* x 5 identical voltage retries ) . Since the previous specs didn ' t
* define a limit and created the possibility of an infinite loop
* we want to prevent any sync from triggering that corner case .
2018-07-20 14:44:12 -07:00
*/
if ( intel_dp - > dpcd [ DP_DPCD_REV ] > = DP_DPCD_REV_14 )
max_cr_tries = 10 ;
else
max_cr_tries = 80 ;
2016-09-07 11:28:01 -07:00
voltage_tries = 1 ;
2018-07-20 14:44:12 -07:00
for ( cr_tries = 0 ; cr_tries < max_cr_tries ; + + cr_tries ) {
2019-01-16 11:15:27 +02:00
u8 link_status [ DP_LINK_STATUS_SIZE ] ;
2015-10-23 13:01:48 +03:00
drm_dp_link_train_clock_recovery_delay ( intel_dp - > dpcd ) ;
2016-09-07 11:28:01 -07:00
2015-10-23 13:01:48 +03:00
if ( ! intel_dp_get_link_status ( intel_dp , link_status ) ) {
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm , " failed to get link status \n " ) ;
2016-09-07 11:28:01 -07:00
return false ;
2015-10-23 13:01:48 +03:00
}
if ( drm_dp_clock_recovery_ok ( link_status , intel_dp - > lane_count ) ) {
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm , " clock recovery OK \n " ) ;
2016-09-07 11:28:01 -07:00
return true ;
2015-10-23 13:01:48 +03:00
}
2016-09-07 11:28:01 -07:00
if ( voltage_tries = = 5 ) {
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm ,
" Same voltage tried 5 times \n " ) ;
2016-09-07 11:28:01 -07:00
return false ;
}
2018-07-20 14:44:13 -07:00
if ( max_vswing_reached ) {
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm , " Max Voltage Swing reached \n " ) ;
2016-09-07 11:28:01 -07:00
return false ;
2015-10-23 13:01:48 +03:00
}
voltage = intel_dp - > train_set [ 0 ] & DP_TRAIN_VOLTAGE_SWING_MASK ;
/* Update training set as requested by target */
2020-03-16 16:07:55 +05:30
intel_dp_get_adjust_train ( intel_dp , link_status ) ;
2015-10-23 13:01:48 +03:00
if ( ! intel_dp_update_link_train ( intel_dp ) ) {
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm ,
" failed to update link training \n " ) ;
2016-09-07 11:28:01 -07:00
return false ;
2015-10-23 13:01:48 +03:00
}
2016-09-07 11:28:01 -07:00
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 ) )
2018-07-20 14:44:13 -07:00
max_vswing_reached = true ;
2016-09-07 11:28:01 -07:00
2015-10-23 13:01:48 +03:00
}
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm ,
" Failed clock recovery %d times, giving up! \n " , max_cr_tries ) ;
2018-07-20 14:44:12 -07:00
return false ;
2015-10-23 13:01:48 +03:00
}
2016-02-05 12:16:09 +02:00
/*
2018-06-11 15:26:55 -07:00
* Pick training pattern for channel equalization . Training pattern 4 for HBR3
* or for 1.4 devices that support it , training Pattern 3 for HBR2
2016-02-05 12:16:09 +02:00
* or 1.2 devices that support it , Training Pattern 2 otherwise .
*/
static u32 intel_dp_training_pattern ( struct intel_dp * intel_dp )
2015-10-23 13:01:48 +03:00
{
2018-06-11 15:26:55 -07:00
bool source_tps3 , sink_tps3 , source_tps4 , sink_tps4 ;
2015-10-23 13:01:48 +03:00
2018-06-11 15:26:55 -07:00
/*
* Intel platforms that support HBR3 also support TPS4 . It is mandatory
* for all downstream devices that support HBR3 . There are no known eDP
* panels that support TPS4 as of Feb 2018 as per VESA eDP_v1 .4 b_E1
* specification .
*/
source_tps4 = intel_dp_source_supports_hbr3 ( intel_dp ) ;
sink_tps4 = drm_dp_tps4_supported ( intel_dp - > dpcd ) ;
if ( source_tps4 & & sink_tps4 ) {
return DP_TRAINING_PATTERN_4 ;
} else if ( intel_dp - > link_rate = = 810000 ) {
if ( ! source_tps4 )
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" 8.1 Gbps link rate without source HBR3/TPS4 support \n " ) ;
2018-06-11 15:26:55 -07:00
if ( ! sink_tps4 )
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" 8.1 Gbps link rate without sink TPS4 support \n " ) ;
2018-06-11 15:26:55 -07:00
}
2015-10-23 13:01:48 +03:00
/*
* Intel platforms that support HBR2 also support TPS3 . TPS3 support is
2016-02-05 12:16:10 +02:00
* also mandatory for downstream devices that support HBR2 . However , not
* all sinks follow the spec .
2015-10-23 13:01:48 +03:00
*/
2016-02-05 12:16:10 +02:00
source_tps3 = intel_dp_source_supports_hbr2 ( intel_dp ) ;
sink_tps3 = drm_dp_tps3_supported ( intel_dp - > dpcd ) ;
if ( source_tps3 & & sink_tps3 ) {
2018-06-11 15:26:55 -07:00
return DP_TRAINING_PATTERN_3 ;
} else if ( intel_dp - > link_rate > = 540000 ) {
2016-02-05 12:16:10 +02:00
if ( ! source_tps3 )
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" >=5.4/6.48 Gbps link rate without source HBR2/TPS3 support \n " ) ;
2016-02-05 12:16:10 +02:00
if ( ! sink_tps3 )
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" >=5.4/6.48 Gbps link rate without sink TPS3 support \n " ) ;
2016-02-05 12:16:10 +02:00
}
2015-10-23 13:01:48 +03:00
2018-06-11 15:26:55 -07:00
return DP_TRAINING_PATTERN_2 ;
2016-02-05 12:16:09 +02:00
}
2016-09-01 15:08:15 -07:00
static bool
2016-02-05 12:16:09 +02:00
intel_dp_link_training_channel_equalization ( struct intel_dp * intel_dp )
{
2020-02-06 11:00:03 +03:00
struct drm_i915_private * i915 = dp_to_i915 ( intel_dp ) ;
2016-09-01 15:08:15 -07:00
int tries ;
2016-02-05 12:16:09 +02:00
u32 training_pattern ;
2019-01-16 11:15:27 +02:00
u8 link_status [ DP_LINK_STATUS_SIZE ] ;
2018-01-17 21:21:48 +02:00
bool channel_eq = false ;
2016-02-05 12:16:09 +02:00
training_pattern = intel_dp_training_pattern ( intel_dp ) ;
2018-06-11 15:26:55 -07:00
/* Scrambling is disabled for TPS2/3 and enabled for TPS4 */
if ( training_pattern ! = DP_TRAINING_PATTERN_4 )
training_pattern | = DP_LINK_SCRAMBLING_DISABLE ;
2016-02-05 12:16:09 +02:00
2015-10-23 13:01:48 +03:00
/* channel equalization */
if ( ! intel_dp_set_link_train ( intel_dp ,
2018-06-11 15:26:55 -07:00
training_pattern ) ) {
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm , " failed to start channel equalization \n " ) ;
2016-09-01 15:08:15 -07:00
return false ;
2015-10-23 13:01:48 +03:00
}
2016-09-01 15:08:15 -07:00
for ( tries = 0 ; tries < 5 ; tries + + ) {
2015-10-23 13:01:48 +03:00
drm_dp_link_train_channel_eq_delay ( intel_dp - > dpcd ) ;
if ( ! intel_dp_get_link_status ( intel_dp , link_status ) ) {
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm ,
" failed to get link status \n " ) ;
2015-10-23 13:01:48 +03:00
break ;
}
/* Make sure clock is still ok */
if ( ! drm_dp_clock_recovery_ok ( link_status ,
intel_dp - > lane_count ) ) {
2016-09-01 15:08:15 -07:00
intel_dp_dump_link_status ( link_status ) ;
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm ,
" Clock recovery check failed, cannot "
" continue channel equalization \n " ) ;
2016-09-01 15:08:15 -07:00
break ;
2015-10-23 13:01:48 +03:00
}
if ( drm_dp_channel_eq_ok ( link_status ,
intel_dp - > lane_count ) ) {
2018-01-17 21:21:48 +02:00
channel_eq = true ;
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm , " Channel EQ done. DP Training "
" successful \n " ) ;
2015-10-23 13:01:48 +03:00
break ;
}
/* Update training set as requested by target */
2020-03-16 16:07:55 +05:30
intel_dp_get_adjust_train ( intel_dp , link_status ) ;
2015-10-23 13:01:48 +03:00
if ( ! intel_dp_update_link_train ( intel_dp ) ) {
2020-02-06 11:00:03 +03:00
drm_err ( & i915 - > drm ,
" failed to update link training \n " ) ;
2015-10-23 13:01:48 +03:00
break ;
}
2016-09-01 15:08:15 -07:00
}
/* Try 5 times, else fail and try at lower BW */
if ( tries = = 5 ) {
intel_dp_dump_link_status ( link_status ) ;
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & i915 - > drm ,
" Channel equalization failed 5 times \n " ) ;
2015-10-23 13:01:48 +03:00
}
intel_dp_set_idle_link_train ( intel_dp ) ;
2018-01-17 21:21:48 +02:00
return channel_eq ;
2016-09-01 15:08:15 -07:00
2015-10-23 13:01:48 +03:00
}
void intel_dp_stop_link_train ( struct intel_dp * intel_dp )
{
2018-01-17 21:21:49 +02:00
intel_dp - > link_trained = true ;
2015-10-23 13:01:48 +03:00
intel_dp_set_link_train ( intel_dp ,
DP_TRAINING_PATTERN_DISABLE ) ;
}
void
intel_dp_start_link_train ( struct intel_dp * intel_dp )
{
2017-04-06 16:44:19 +03:00
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 ;
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" [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 ) ;
2017-04-06 16:44:19 +03:00
return ;
failure_handling :
2020-02-06 11:00:03 +03:00
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" [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 ) ;
2020-07-15 10:56:37 -07:00
if ( intel_dp - > hobl_active ) {
drm_dbg_kms ( & dp_to_i915 ( intel_dp ) - > drm ,
" Link Training failed with HOBL active, not enabling it from now on " ) ;
intel_dp - > hobl_failed = true ;
} else if ( intel_dp_get_link_train_fallback_values ( intel_dp ,
intel_dp - > link_rate ,
intel_dp - > lane_count ) ) {
return ;
}
/* Schedule a Hotplug Uevent to userspace to start modeset */
schedule_work ( & intel_connector - > modeset_retry_work ) ;
2015-10-23 13:01:48 +03:00
}