2009-12-08 00:07:28 +03:00
/*
* Copyright 2007 - 8 Advanced Micro Devices , Inc .
* Copyright 2008 Red Hat Inc .
*
* 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 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 COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) 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 .
*
* Authors : Dave Airlie
* Alex Deucher
2012-07-18 01:17:16 +04:00
* Jerome Glisse
2009-12-08 00:07:28 +03:00
*/
2019-06-08 11:02:41 +03:00
2012-10-02 21:01:07 +04:00
# include <drm/radeon_drm.h>
2009-12-08 00:07:28 +03:00
# include "radeon.h"
# include "atom.h"
# include "atom-bits.h"
2012-10-02 21:01:07 +04:00
# include <drm/drm_dp_helper.h>
2009-12-08 00:07:28 +03:00
2009-11-24 02:40:40 +03:00
/* move these to drm_dp_helper.c/h */
2009-11-24 21:32:59 +03:00
# define DP_LINK_CONFIGURATION_SIZE 9
2012-10-18 17:32:40 +04:00
# define DP_DPCD_SIZE DP_RECEIVER_CAP_SIZE
2009-11-24 21:32:59 +03:00
static char * voltage_names [ ] = {
2016-03-16 14:56:45 +03:00
" 0.4V " , " 0.6V " , " 0.8V " , " 1.2V "
2009-11-24 21:32:59 +03:00
} ;
static char * pre_emph_names [ ] = {
2016-03-16 14:56:45 +03:00
" 0dB " , " 3.5dB " , " 6dB " , " 9.5dB "
2009-11-24 21:32:59 +03:00
} ;
2009-11-24 02:40:40 +03:00
2011-05-20 12:34:28 +04:00
/***** radeon AUX functions *****/
2013-07-18 19:13:53 +04:00
2017-10-30 13:56:13 +03:00
/* Atom needs data in little endian format so swap as appropriate when copying
* data to or from atom . Note that atom operates on dw units .
*
* Use to_le = true when sending data to atom and provide at least
* ALIGN ( num_bytes , 4 ) bytes in the dst buffer .
*
* Use to_le = false when receiving data from atom and provide ALIGN ( num_bytes , 4 )
* byes in the src buffer .
2013-07-18 19:13:53 +04:00
*/
2013-08-08 03:34:53 +04:00
void radeon_atom_copy_swap ( u8 * dst , u8 * src , u8 num_bytes , bool to_le )
2013-07-18 19:13:53 +04:00
{
# ifdef __BIG_ENDIAN
2017-10-30 13:56:13 +03:00
u32 src_tmp [ 5 ] , dst_tmp [ 5 ] ;
2013-07-18 19:13:53 +04:00
int i ;
2017-10-30 13:56:13 +03:00
u8 align_num_bytes = ALIGN ( num_bytes , 4 ) ;
2013-07-18 19:13:53 +04:00
if ( to_le ) {
2017-10-30 13:56:13 +03:00
memcpy ( src_tmp , src , num_bytes ) ;
for ( i = 0 ; i < align_num_bytes / 4 ; i + + )
dst_tmp [ i ] = cpu_to_le32 ( src_tmp [ i ] ) ;
memcpy ( dst , dst_tmp , align_num_bytes ) ;
2013-07-18 19:13:53 +04:00
} else {
2017-10-30 13:56:13 +03:00
memcpy ( src_tmp , src , align_num_bytes ) ;
for ( i = 0 ; i < align_num_bytes / 4 ; i + + )
dst_tmp [ i ] = le32_to_cpu ( src_tmp [ i ] ) ;
memcpy ( dst , dst_tmp , num_bytes ) ;
2013-07-18 19:13:53 +04:00
}
# else
memcpy ( dst , src , num_bytes ) ;
# endif
}
2011-05-20 12:34:28 +04:00
union aux_channel_transaction {
PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1 ;
PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2 ;
2009-11-24 02:40:40 +03:00
} ;
2011-05-20 12:34:28 +04:00
static int radeon_process_aux_ch ( struct radeon_i2c_chan * chan ,
u8 * send , int send_bytes ,
u8 * recv , int recv_size ,
u8 delay , u8 * ack )
{
struct drm_device * dev = chan - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
union aux_channel_transaction args ;
int index = GetIndexIntoMasterTable ( COMMAND , ProcessAuxChannelTransaction ) ;
unsigned char * base ;
int recv_bytes ;
2014-05-08 18:58:04 +04:00
int r = 0 ;
2011-05-20 12:34:28 +04:00
memset ( & args , 0 , sizeof ( args ) ) ;
2009-11-24 02:40:40 +03:00
2014-05-08 18:58:04 +04:00
mutex_lock ( & chan - > mutex ) ;
2014-11-11 02:16:15 +03:00
mutex_lock ( & rdev - > mode_info . atom_context - > scratch_mutex ) ;
2014-05-08 18:58:04 +04:00
2012-03-21 01:18:06 +04:00
base = ( unsigned char * ) ( rdev - > mode_info . atom_context - > scratch + 1 ) ;
2011-05-20 12:34:28 +04:00
2013-08-08 03:34:53 +04:00
radeon_atom_copy_swap ( base , send , send_bytes , true ) ;
2011-05-20 12:34:28 +04:00
2013-07-18 19:13:53 +04:00
args . v1 . lpAuxRequest = cpu_to_le16 ( ( u16 ) ( 0 + 4 ) ) ;
args . v1 . lpDataOut = cpu_to_le16 ( ( u16 ) ( 16 + 4 ) ) ;
2011-05-20 12:34:28 +04:00
args . v1 . ucDataOutLen = 0 ;
args . v1 . ucChannelID = chan - > rec . i2c_id ;
args . v1 . ucDelay = delay / 10 ;
if ( ASIC_IS_DCE4 ( rdev ) )
args . v2 . ucHPD_ID = chan - > rec . hpd ;
2014-11-11 02:16:15 +03:00
atom_execute_table_scratch_unlocked ( rdev - > mode_info . atom_context , index , ( uint32_t * ) & args ) ;
2011-05-20 12:34:28 +04:00
* ack = args . v1 . ucReplyStatus ;
/* timeout */
if ( args . v1 . ucReplyStatus = = 1 ) {
DRM_DEBUG_KMS ( " dp_aux_ch timeout \n " ) ;
2014-05-08 18:58:04 +04:00
r = - ETIMEDOUT ;
goto done ;
2011-05-20 12:34:28 +04:00
}
/* flags not zero */
if ( args . v1 . ucReplyStatus = = 2 ) {
DRM_DEBUG_KMS ( " dp_aux_ch flags not zero \n " ) ;
2014-07-03 19:17:55 +04:00
r = - EIO ;
2014-05-08 18:58:04 +04:00
goto done ;
2011-05-20 12:34:28 +04:00
}
/* error */
if ( args . v1 . ucReplyStatus = = 3 ) {
DRM_DEBUG_KMS ( " dp_aux_ch error \n " ) ;
2014-05-08 18:58:04 +04:00
r = - EIO ;
goto done ;
2011-05-20 12:34:28 +04:00
}
recv_bytes = args . v1 . ucDataOutLen ;
if ( recv_bytes > recv_size )
recv_bytes = recv_size ;
if ( recv & & recv_size )
2013-08-08 03:34:53 +04:00
radeon_atom_copy_swap ( recv , base + 16 , recv_bytes , false ) ;
2011-05-20 12:34:28 +04:00
2014-05-08 18:58:04 +04:00
r = recv_bytes ;
done :
2014-11-11 02:16:15 +03:00
mutex_unlock ( & rdev - > mode_info . atom_context - > scratch_mutex ) ;
2014-05-08 18:58:04 +04:00
mutex_unlock ( & chan - > mutex ) ;
return r ;
2011-05-20 12:34:28 +04:00
}
2014-04-07 18:33:43 +04:00
# define BARE_ADDRESS_SIZE 3
# define HEADER_SIZE (BARE_ADDRESS_SIZE + 1)
2009-11-24 21:32:59 +03:00
2014-03-21 18:34:07 +04:00
static ssize_t
2015-02-20 02:21:36 +03:00
radeon_dp_aux_transfer_atom ( struct drm_dp_aux * aux , struct drm_dp_aux_msg * msg )
2009-11-24 02:40:40 +03:00
{
2014-03-21 18:34:07 +04:00
struct radeon_i2c_chan * chan =
container_of ( aux , struct radeon_i2c_chan , aux ) ;
2011-05-20 12:34:28 +04:00
int ret ;
2014-03-21 18:34:07 +04:00
u8 tx_buf [ 20 ] ;
size_t tx_size ;
u8 ack , delay = 0 ;
if ( WARN_ON ( msg - > size > 16 ) )
return - E2BIG ;
tx_buf [ 0 ] = msg - > address & 0xff ;
2015-08-27 17:23:31 +03:00
tx_buf [ 1 ] = ( msg - > address > > 8 ) & 0xff ;
tx_buf [ 2 ] = ( msg - > request < < 4 ) |
( ( msg - > address > > 16 ) & 0xf ) ;
2014-04-07 18:33:43 +04:00
tx_buf [ 3 ] = msg - > size ? ( msg - > size - 1 ) : 0 ;
2014-03-21 18:34:07 +04:00
switch ( msg - > request & ~ DP_AUX_I2C_MOT ) {
case DP_AUX_NATIVE_WRITE :
case DP_AUX_I2C_WRITE :
2015-08-27 17:23:28 +03:00
case DP_AUX_I2C_WRITE_STATUS_UPDATE :
2015-02-20 19:51:38 +03:00
/* The atom implementation only supports writes with a max payload of
* 12 bytes since it uses 4 bits for the total count ( header + payload )
* in the parameter space . The atom interface supports 16 byte
* payloads for reads . The hw itself supports up to 16 bytes of payload .
*/
if ( WARN_ON_ONCE ( msg - > size > 12 ) )
return - E2BIG ;
2014-04-07 18:33:43 +04:00
/* tx_size needs to be 4 even for bare address packets since the atom
* table needs the info in tx_buf [ 3 ] .
*/
2014-03-21 18:34:07 +04:00
tx_size = HEADER_SIZE + msg - > size ;
2014-04-07 18:33:43 +04:00
if ( msg - > size = = 0 )
tx_buf [ 3 ] | = BARE_ADDRESS_SIZE < < 4 ;
else
tx_buf [ 3 ] | = tx_size < < 4 ;
2014-03-21 18:34:07 +04:00
memcpy ( tx_buf + HEADER_SIZE , msg - > buffer , msg - > size ) ;
ret = radeon_process_aux_ch ( chan ,
tx_buf , tx_size , NULL , 0 , delay , & ack ) ;
if ( ret > = 0 )
/* Return payload size. */
ret = msg - > size ;
break ;
case DP_AUX_NATIVE_READ :
case DP_AUX_I2C_READ :
2014-04-07 18:33:43 +04:00
/* tx_size needs to be 4 even for bare address packets since the atom
* table needs the info in tx_buf [ 3 ] .
*/
2014-03-21 18:34:07 +04:00
tx_size = HEADER_SIZE ;
2014-04-07 18:33:43 +04:00
if ( msg - > size = = 0 )
tx_buf [ 3 ] | = BARE_ADDRESS_SIZE < < 4 ;
else
tx_buf [ 3 ] | = tx_size < < 4 ;
2014-03-21 18:34:07 +04:00
ret = radeon_process_aux_ch ( chan ,
tx_buf , tx_size , msg - > buffer , msg - > size , delay , & ack ) ;
break ;
default :
ret = - EINVAL ;
break ;
2011-05-20 12:34:28 +04:00
}
2011-10-03 17:13:46 +04:00
2014-04-07 18:33:43 +04:00
if ( ret > = 0 )
2014-03-21 18:34:07 +04:00
msg - > reply = ack > > 4 ;
2009-11-24 02:40:40 +03:00
2014-03-21 18:34:07 +04:00
return ret ;
2011-05-20 12:34:28 +04:00
}
2014-03-21 18:34:07 +04:00
void radeon_dp_aux_init ( struct radeon_connector * radeon_connector )
2011-05-20 12:34:28 +04:00
{
2015-02-20 02:21:36 +03:00
struct drm_device * dev = radeon_connector - > base . dev ;
struct radeon_device * rdev = dev - > dev_private ;
2011-05-20 12:34:28 +04:00
int ret ;
2009-11-24 02:40:40 +03:00
2014-04-22 10:02:06 +04:00
radeon_connector - > ddc_bus - > rec . hpd = radeon_connector - > hpd . hpd ;
2014-04-07 18:33:46 +04:00
radeon_connector - > ddc_bus - > aux . dev = radeon_connector - > base . kdev ;
2015-02-20 02:21:36 +03:00
if ( ASIC_IS_DCE5 ( rdev ) ) {
if ( radeon_auxch )
radeon_connector - > ddc_bus - > aux . transfer = radeon_dp_aux_transfer_native ;
else
radeon_connector - > ddc_bus - > aux . transfer = radeon_dp_aux_transfer_atom ;
} else {
radeon_connector - > ddc_bus - > aux . transfer = radeon_dp_aux_transfer_atom ;
}
2014-06-04 10:02:28 +04:00
ret = drm_dp_aux_register ( & radeon_connector - > ddc_bus - > aux ) ;
2014-04-07 18:33:46 +04:00
if ( ! ret )
radeon_connector - > ddc_bus - > has_aux = true ;
2009-11-24 02:40:40 +03:00
2014-06-04 10:02:28 +04:00
WARN ( ret , " drm_dp_aux_register() failed with error %d \n " , ret ) ;
2009-11-24 21:32:59 +03:00
}
2011-05-20 12:34:28 +04:00
/***** general DP utility functions *****/
2014-08-08 14:53:44 +04:00
# define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3
# define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPH_LEVEL_3
2009-11-24 21:32:59 +03:00
2015-05-14 19:47:45 +03:00
static void dp_get_adjust_train ( const u8 link_status [ DP_LINK_STATUS_SIZE ] ,
2009-11-24 21:32:59 +03:00
int lane_count ,
u8 train_set [ 4 ] )
{
u8 v = 0 ;
u8 p = 0 ;
int lane ;
for ( lane = 0 ; lane < lane_count ; lane + + ) {
2012-10-18 12:15:27 +04:00
u8 this_v = drm_dp_get_adjust_request_voltage ( link_status , lane ) ;
u8 this_p = drm_dp_get_adjust_request_pre_emphasis ( link_status , lane ) ;
2009-11-24 21:32:59 +03:00
2010-08-02 04:42:55 +04:00
DRM_DEBUG_KMS ( " requested signal parameters: lane %d voltage %s pre_emph %s \n " ,
2009-11-27 21:14:37 +03:00
lane ,
voltage_names [ this_v > > DP_TRAIN_VOLTAGE_SWING_SHIFT ] ,
pre_emph_names [ this_p > > DP_TRAIN_PRE_EMPHASIS_SHIFT ] ) ;
2009-11-24 21:32:59 +03:00
if ( this_v > v )
v = this_v ;
if ( this_p > p )
p = this_p ;
}
if ( v > = DP_VOLTAGE_MAX )
2011-05-20 12:34:28 +04:00
v | = DP_TRAIN_MAX_SWING_REACHED ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
if ( p > = DP_PRE_EMPHASIS_MAX )
p | = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED ;
2009-11-24 21:32:59 +03:00
2010-08-02 04:42:55 +04:00
DRM_DEBUG_KMS ( " using signal parameters: voltage %s pre_emph %s \n " ,
2009-11-27 21:14:37 +03:00
voltage_names [ ( v & DP_TRAIN_VOLTAGE_SWING_MASK ) > > DP_TRAIN_VOLTAGE_SWING_SHIFT ] ,
pre_emph_names [ ( p & DP_TRAIN_PRE_EMPHASIS_MASK ) > > DP_TRAIN_PRE_EMPHASIS_SHIFT ] ) ;
2009-11-24 21:32:59 +03:00
for ( lane = 0 ; lane < 4 ; lane + + )
train_set [ lane ] = v | p ;
}
2011-05-20 12:34:28 +04:00
/* convert bits per color to bits per pixel */
/* get bpc from the EDID */
static int convert_bpc_to_bpp ( int bpc )
2009-12-08 00:07:28 +03:00
{
2011-05-20 12:34:28 +04:00
if ( bpc = = 0 )
return 24 ;
else
return bpc * 3 ;
}
2009-12-08 00:07:28 +03:00
2011-05-20 12:34:28 +04:00
/***** radeon specific DP functions *****/
2009-12-08 00:07:28 +03:00
2017-09-29 18:35:52 +03:00
static int radeon_dp_get_dp_link_config ( struct drm_connector * connector ,
const u8 dpcd [ DP_DPCD_SIZE ] ,
unsigned pix_clock ,
unsigned * dp_lanes , unsigned * dp_rate )
2011-05-20 12:34:28 +04:00
{
2012-03-26 23:12:54 +04:00
int bpp = convert_bpc_to_bpp ( radeon_get_monitor_bpc ( connector ) ) ;
2015-12-17 18:23:34 +03:00
static const unsigned link_rates [ 3 ] = { 162000 , 270000 , 540000 } ;
unsigned max_link_rate = drm_dp_max_link_rate ( dpcd ) ;
unsigned max_lane_num = drm_dp_max_lane_count ( dpcd ) ;
unsigned lane_num , i , max_pix_clock ;
2016-03-04 03:26:24 +03:00
if ( radeon_connector_encoder_get_dp_bridge_encoder_id ( connector ) = =
ENCODER_OBJECT_ID_NUTMEG ) {
for ( lane_num = 1 ; lane_num < = max_lane_num ; lane_num < < = 1 ) {
max_pix_clock = ( lane_num * 270000 * 8 ) / bpp ;
2015-12-17 18:23:34 +03:00
if ( max_pix_clock > = pix_clock ) {
* dp_lanes = lane_num ;
2016-03-04 03:26:24 +03:00
* dp_rate = 270000 ;
2015-12-17 18:23:34 +03:00
return 0 ;
}
}
2016-03-04 03:26:24 +03:00
} else {
2016-05-11 23:16:53 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( link_rates ) & & link_rates [ i ] < = max_link_rate ; i + + ) {
for ( lane_num = 1 ; lane_num < = max_lane_num ; lane_num < < = 1 ) {
2016-03-04 03:26:24 +03:00
max_pix_clock = ( lane_num * link_rates [ i ] * 8 ) / bpp ;
if ( max_pix_clock > = pix_clock ) {
* dp_lanes = lane_num ;
* dp_rate = link_rates [ i ] ;
return 0 ;
}
}
}
2011-05-20 12:34:24 +04:00
}
2011-05-20 12:34:28 +04:00
2015-12-17 18:23:34 +03:00
return - EINVAL ;
2009-12-08 00:07:28 +03:00
}
2011-05-20 12:34:24 +04:00
static u8 radeon_dp_encoder_service ( struct radeon_device * rdev ,
int action , int dp_clock ,
2011-05-20 12:34:28 +04:00
u8 ucconfig , u8 lane_num )
2009-11-24 21:32:59 +03:00
{
DP_ENCODER_SERVICE_PARAMETERS args ;
int index = GetIndexIntoMasterTable ( COMMAND , DPEncoderService ) ;
memset ( & args , 0 , sizeof ( args ) ) ;
args . ucLinkClock = dp_clock / 10 ;
args . ucConfig = ucconfig ;
args . ucAction = action ;
args . ucLaneNum = lane_num ;
args . ucStatus = 0 ;
atom_execute_table ( rdev - > mode_info . atom_context , index , ( uint32_t * ) & args ) ;
return args . ucStatus ;
}
u8 radeon_dp_getsinktype ( struct radeon_connector * radeon_connector )
{
struct drm_device * dev = radeon_connector - > base . dev ;
struct radeon_device * rdev = dev - > dev_private ;
return radeon_dp_encoder_service ( rdev , ATOM_DP_ACTION_GET_SINK_TYPE , 0 ,
2014-04-07 18:33:46 +04:00
radeon_connector - > ddc_bus - > rec . i2c_id , 0 ) ;
2009-11-24 21:32:59 +03:00
}
2012-05-15 00:05:48 +04:00
static void radeon_dp_probe_oui ( struct radeon_connector * radeon_connector )
{
struct radeon_connector_atom_dig * dig_connector = radeon_connector - > con_priv ;
u8 buf [ 3 ] ;
if ( ! ( dig_connector - > dpcd [ DP_DOWN_STREAM_PORT_COUNT ] & DP_OUI_SUPPORT ) )
return ;
2014-04-30 17:27:15 +04:00
if ( drm_dp_dpcd_read ( & radeon_connector - > ddc_bus - > aux , DP_SINK_OUI , buf , 3 ) = = 3 )
2012-05-15 00:05:48 +04:00
DRM_DEBUG_KMS ( " Sink OUI: %02hx%02hx%02hx \n " ,
buf [ 0 ] , buf [ 1 ] , buf [ 2 ] ) ;
2014-04-30 17:27:15 +04:00
if ( drm_dp_dpcd_read ( & radeon_connector - > ddc_bus - > aux , DP_BRANCH_OUI , buf , 3 ) = = 3 )
2012-05-15 00:05:48 +04:00
DRM_DEBUG_KMS ( " Branch OUI: %02hx%02hx%02hx \n " ,
buf [ 0 ] , buf [ 1 ] , buf [ 2 ] ) ;
}
2009-11-27 21:01:46 +03:00
bool radeon_dp_getdpcd ( struct radeon_connector * radeon_connector )
2009-12-08 00:07:28 +03:00
{
2009-11-24 21:32:59 +03:00
struct radeon_connector_atom_dig * dig_connector = radeon_connector - > con_priv ;
2012-10-18 17:32:40 +04:00
u8 msg [ DP_DPCD_SIZE ] ;
2016-08-06 03:30:35 +03:00
int ret ;
2014-06-29 23:03:53 +04:00
2016-08-06 03:30:35 +03:00
ret = drm_dp_dpcd_read ( & radeon_connector - > ddc_bus - > aux , DP_DPCD_REV , msg ,
DP_DPCD_SIZE ) ;
if ( ret = = DP_DPCD_SIZE ) {
memcpy ( dig_connector - > dpcd , msg , DP_DPCD_SIZE ) ;
2014-06-29 23:03:53 +04:00
2016-08-06 03:30:35 +03:00
DRM_DEBUG_KMS ( " DPCD: %*ph \n " , ( int ) sizeof ( dig_connector - > dpcd ) ,
dig_connector - > dpcd ) ;
2012-05-15 00:05:48 +04:00
2016-08-06 03:30:35 +03:00
radeon_dp_probe_oui ( radeon_connector ) ;
2012-05-15 00:05:48 +04:00
2016-08-06 03:30:35 +03:00
return true ;
2009-12-08 00:07:28 +03:00
}
2016-08-06 03:30:35 +03:00
2009-11-24 21:32:59 +03:00
dig_connector - > dpcd [ 0 ] = 0 ;
2009-11-27 21:01:46 +03:00
return false ;
2009-12-08 00:07:28 +03:00
}
2012-01-21 00:01:29 +04:00
int radeon_dp_get_panel_mode ( struct drm_encoder * encoder ,
struct drm_connector * connector )
2011-05-20 12:34:28 +04:00
{
struct drm_device * dev = encoder - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
2011-10-31 16:54:41 +04:00
struct radeon_connector * radeon_connector = to_radeon_connector ( connector ) ;
2014-03-21 18:34:07 +04:00
struct radeon_connector_atom_dig * dig_connector ;
2011-05-20 12:34:28 +04:00
int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE ;
2012-08-28 01:48:18 +04:00
u16 dp_bridge = radeon_connector_encoder_get_dp_bridge_encoder_id ( connector ) ;
u8 tmp ;
2011-05-20 12:34:28 +04:00
if ( ! ASIC_IS_DCE4 ( rdev ) )
2012-01-21 00:01:29 +04:00
return panel_mode ;
2011-05-20 12:34:28 +04:00
2014-03-21 18:34:07 +04:00
if ( ! radeon_connector - > con_priv )
return panel_mode ;
dig_connector = radeon_connector - > con_priv ;
2012-08-28 01:48:18 +04:00
if ( dp_bridge ! = ENCODER_OBJECT_ID_NONE ) {
/* DP bridge chips */
2014-04-30 17:27:15 +04:00
if ( drm_dp_dpcd_readb ( & radeon_connector - > ddc_bus - > aux ,
DP_EDP_CONFIGURATION_CAP , & tmp ) = = 1 ) {
if ( tmp & 1 )
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE ;
else if ( ( dp_bridge = = ENCODER_OBJECT_ID_NUTMEG ) | |
( dp_bridge = = ENCODER_OBJECT_ID_TRAVIS ) )
panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE ;
else
panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE ;
}
2012-02-02 19:18:00 +04:00
} else if ( connector - > connector_type = = DRM_MODE_CONNECTOR_eDP ) {
2012-08-28 01:48:18 +04:00
/* eDP */
2014-04-30 17:27:15 +04:00
if ( drm_dp_dpcd_readb ( & radeon_connector - > ddc_bus - > aux ,
DP_EDP_CONFIGURATION_CAP , & tmp ) = = 1 ) {
if ( tmp & 1 )
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE ;
}
2011-10-31 16:54:41 +04:00
}
2011-05-20 12:34:28 +04:00
2012-01-21 00:01:29 +04:00
return panel_mode ;
2011-05-20 12:34:28 +04:00
}
2009-11-24 21:32:59 +03:00
void radeon_dp_set_link_config ( struct drm_connector * connector ,
2012-07-17 19:56:50 +04:00
const struct drm_display_mode * mode )
2009-11-24 21:32:59 +03:00
{
2011-05-20 12:34:28 +04:00
struct radeon_connector * radeon_connector = to_radeon_connector ( connector ) ;
2009-11-24 21:32:59 +03:00
struct radeon_connector_atom_dig * dig_connector ;
2015-12-17 18:23:34 +03:00
int ret ;
2009-11-24 21:32:59 +03:00
if ( ! radeon_connector - > con_priv )
return ;
dig_connector = radeon_connector - > con_priv ;
2011-05-20 12:34:28 +04:00
if ( ( dig_connector - > dp_sink_type = = CONNECTOR_OBJECT_ID_DISPLAYPORT ) | |
( dig_connector - > dp_sink_type = = CONNECTOR_OBJECT_ID_eDP ) ) {
2015-12-17 18:23:34 +03:00
ret = radeon_dp_get_dp_link_config ( connector , dig_connector - > dpcd ,
mode - > clock ,
& dig_connector - > dp_lane_count ,
& dig_connector - > dp_clock ) ;
if ( ret ) {
dig_connector - > dp_clock = 0 ;
dig_connector - > dp_lane_count = 0 ;
}
2011-05-20 12:34:28 +04:00
}
2009-11-24 21:32:59 +03:00
}
2011-05-20 12:34:28 +04:00
int radeon_dp_mode_valid_helper ( struct drm_connector * connector ,
2009-11-24 21:32:59 +03:00
struct drm_display_mode * mode )
{
2011-05-20 12:34:28 +04:00
struct radeon_connector * radeon_connector = to_radeon_connector ( connector ) ;
struct radeon_connector_atom_dig * dig_connector ;
2015-12-17 18:23:34 +03:00
unsigned dp_clock , dp_lanes ;
int ret ;
2009-11-24 21:32:59 +03:00
2014-12-10 17:42:10 +03:00
if ( ( mode - > clock > 340000 ) & &
( ! radeon_connector_is_dp12_capable ( connector ) ) )
return MODE_CLOCK_HIGH ;
2011-05-20 12:34:28 +04:00
if ( ! radeon_connector - > con_priv )
return MODE_CLOCK_HIGH ;
dig_connector = radeon_connector - > con_priv ;
2015-12-17 18:23:34 +03:00
ret = radeon_dp_get_dp_link_config ( connector , dig_connector - > dpcd ,
mode - > clock ,
& dp_lanes ,
& dp_clock ) ;
if ( ret )
return MODE_CLOCK_HIGH ;
2011-05-20 12:34:28 +04:00
if ( ( dp_clock = = 540000 ) & &
( ! radeon_connector_is_dp12_capable ( connector ) ) )
return MODE_CLOCK_HIGH ;
return MODE_OK ;
2009-11-24 21:32:59 +03:00
}
2011-08-13 21:36:13 +04:00
bool radeon_dp_needs_link_train ( struct radeon_connector * radeon_connector )
{
u8 link_status [ DP_LINK_STATUS_SIZE ] ;
struct radeon_connector_atom_dig * dig = radeon_connector - > con_priv ;
2014-04-07 18:33:46 +04:00
if ( drm_dp_dpcd_read_link_status ( & radeon_connector - > ddc_bus - > aux , link_status )
< = 0 )
2011-08-13 21:36:13 +04:00
return false ;
2012-10-18 12:15:24 +04:00
if ( drm_dp_channel_eq_ok ( link_status , dig - > dp_lane_count ) )
2011-08-13 21:36:13 +04:00
return false ;
return true ;
}
2014-03-18 07:48:15 +04:00
void radeon_dp_set_rx_power_state ( struct drm_connector * connector ,
u8 power_state )
{
struct radeon_connector * radeon_connector = to_radeon_connector ( connector ) ;
struct radeon_connector_atom_dig * dig_connector ;
if ( ! radeon_connector - > con_priv )
return ;
dig_connector = radeon_connector - > con_priv ;
/* power up/down the sink */
if ( dig_connector - > dpcd [ 0 ] > = 0x11 ) {
2014-04-07 18:33:46 +04:00
drm_dp_dpcd_writeb ( & radeon_connector - > ddc_bus - > aux ,
2014-03-18 07:48:15 +04:00
DP_SET_POWER , power_state ) ;
usleep_range ( 1000 , 2000 ) ;
}
}
2011-05-20 12:34:28 +04:00
struct radeon_dp_link_train_info {
struct radeon_device * rdev ;
struct drm_encoder * encoder ;
struct drm_connector * connector ;
int enc_id ;
int dp_clock ;
int dp_lane_count ;
bool tp3_supported ;
2012-10-18 17:32:40 +04:00
u8 dpcd [ DP_RECEIVER_CAP_SIZE ] ;
2011-05-20 12:34:28 +04:00
u8 train_set [ 4 ] ;
u8 link_status [ DP_LINK_STATUS_SIZE ] ;
u8 tries ;
2011-07-25 19:57:43 +04:00
bool use_dpencoder ;
2014-03-21 18:34:07 +04:00
struct drm_dp_aux * aux ;
2011-05-20 12:34:28 +04:00
} ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
static void radeon_dp_update_vs_emph ( struct radeon_dp_link_train_info * dp_info )
2009-11-24 21:32:59 +03:00
{
2011-05-20 12:34:28 +04:00
/* set the initial vs/emph on the source */
atombios_dig_transmitter_setup ( dp_info - > encoder ,
ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH ,
0 , dp_info - > train_set [ 0 ] ) ; /* sets all lanes at once */
/* set the vs/emph on the sink */
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_write ( dp_info - > aux , DP_TRAINING_LANE0_SET ,
dp_info - > train_set , dp_info - > dp_lane_count ) ;
2009-11-24 21:32:59 +03:00
}
2011-05-20 12:34:28 +04:00
static void radeon_dp_set_tp ( struct radeon_dp_link_train_info * dp_info , int tp )
2009-12-08 00:07:28 +03:00
{
2011-05-20 12:34:28 +04:00
int rtp = 0 ;
2009-12-08 00:07:28 +03:00
2011-05-20 12:34:28 +04:00
/* set training pattern on the source */
2011-07-25 19:57:43 +04:00
if ( ASIC_IS_DCE4 ( dp_info - > rdev ) | | ! dp_info - > use_dpencoder ) {
2011-05-20 12:34:28 +04:00
switch ( tp ) {
case DP_TRAINING_PATTERN_1 :
rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1 ;
break ;
case DP_TRAINING_PATTERN_2 :
rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2 ;
break ;
case DP_TRAINING_PATTERN_3 :
rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3 ;
break ;
}
atombios_dig_encoder_setup ( dp_info - > encoder , rtp , 0 ) ;
} else {
switch ( tp ) {
case DP_TRAINING_PATTERN_1 :
rtp = 0 ;
break ;
case DP_TRAINING_PATTERN_2 :
rtp = 1 ;
break ;
}
radeon_dp_encoder_service ( dp_info - > rdev , ATOM_DP_ACTION_TRAINING_PATTERN_SEL ,
dp_info - > dp_clock , dp_info - > enc_id , rtp ) ;
}
2009-12-08 00:07:28 +03:00
2011-05-20 12:34:28 +04:00
/* enable training pattern on the sink */
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux , DP_TRAINING_PATTERN_SET , tp ) ;
2009-12-08 00:07:28 +03:00
}
2011-05-20 12:34:28 +04:00
static int radeon_dp_link_train_init ( struct radeon_dp_link_train_info * dp_info )
2009-11-24 21:32:59 +03:00
{
2012-01-21 00:01:29 +04:00
struct radeon_encoder * radeon_encoder = to_radeon_encoder ( dp_info - > encoder ) ;
struct radeon_encoder_atom_dig * dig = radeon_encoder - > enc_priv ;
2011-05-20 12:34:28 +04:00
u8 tmp ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
/* power up the sink */
2014-03-18 07:48:15 +04:00
radeon_dp_set_rx_power_state ( dp_info - > connector , DP_SET_POWER_D0 ) ;
2011-05-20 12:34:28 +04:00
/* possibly enable downspread on the sink */
if ( dp_info - > dpcd [ 3 ] & 0x1 )
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux ,
DP_DOWNSPREAD_CTRL , DP_SPREAD_AMP_0_5 ) ;
2011-05-20 12:34:28 +04:00
else
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux ,
DP_DOWNSPREAD_CTRL , 0 ) ;
2009-11-24 21:32:59 +03:00
2015-02-12 02:34:36 +03:00
if ( dig - > panel_mode = = DP_PANEL_MODE_INTERNAL_DP2_MODE )
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux , DP_EDP_CONFIGURATION_SET , 1 ) ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
/* set the lane count on the sink */
tmp = dp_info - > dp_lane_count ;
2013-10-04 16:08:09 +04:00
if ( drm_dp_enhanced_frame_cap ( dp_info - > dpcd ) )
2011-05-20 12:34:28 +04:00
tmp | = DP_LANE_COUNT_ENHANCED_FRAME_EN ;
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux , DP_LANE_COUNT_SET , tmp ) ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
/* set the link rate on the sink */
2012-10-18 12:15:31 +04:00
tmp = drm_dp_link_rate_to_bw_code ( dp_info - > dp_clock ) ;
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux , DP_LINK_BW_SET , tmp ) ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
/* start training on the source */
2011-07-25 19:57:43 +04:00
if ( ASIC_IS_DCE4 ( dp_info - > rdev ) | | ! dp_info - > use_dpencoder )
2011-05-20 12:34:28 +04:00
atombios_dig_encoder_setup ( dp_info - > encoder ,
ATOM_ENCODER_CMD_DP_LINK_TRAINING_START , 0 ) ;
2009-11-24 21:32:59 +03:00
else
2011-05-20 12:34:28 +04:00
radeon_dp_encoder_service ( dp_info - > rdev , ATOM_DP_ACTION_TRAINING_START ,
dp_info - > dp_clock , dp_info - > enc_id , 0 ) ;
2009-11-24 21:32:59 +03:00
/* disable the training pattern on the sink */
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux ,
DP_TRAINING_PATTERN_SET ,
DP_TRAINING_PATTERN_DISABLE ) ;
2011-05-20 12:34:28 +04:00
return 0 ;
}
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
static int radeon_dp_link_train_finish ( struct radeon_dp_link_train_info * dp_info )
{
2009-11-24 21:32:59 +03:00
udelay ( 400 ) ;
2011-05-20 12:34:28 +04:00
/* disable the training pattern on the sink */
2014-03-21 18:34:07 +04:00
drm_dp_dpcd_writeb ( dp_info - > aux ,
DP_TRAINING_PATTERN_SET ,
DP_TRAINING_PATTERN_DISABLE ) ;
2011-05-20 12:34:28 +04:00
/* disable the training pattern on the source */
2011-07-25 19:57:43 +04:00
if ( ASIC_IS_DCE4 ( dp_info - > rdev ) | | ! dp_info - > use_dpencoder )
2011-05-20 12:34:28 +04:00
atombios_dig_encoder_setup ( dp_info - > encoder ,
ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE , 0 ) ;
else
radeon_dp_encoder_service ( dp_info - > rdev , ATOM_DP_ACTION_TRAINING_COMPLETE ,
dp_info - > dp_clock , dp_info - > enc_id , 0 ) ;
return 0 ;
}
static int radeon_dp_link_train_cr ( struct radeon_dp_link_train_info * dp_info )
{
bool clock_recovery ;
u8 voltage ;
int i ;
radeon_dp_set_tp ( dp_info , DP_TRAINING_PATTERN_1 ) ;
memset ( dp_info - > train_set , 0 , 4 ) ;
radeon_dp_update_vs_emph ( dp_info ) ;
udelay ( 400 ) ;
2009-11-26 01:55:18 +03:00
2009-11-24 21:32:59 +03:00
/* clock recovery loop */
clock_recovery = false ;
2011-05-20 12:34:28 +04:00
dp_info - > tries = 0 ;
2009-11-24 21:32:59 +03:00
voltage = 0xff ;
2011-05-20 12:34:28 +04:00
while ( 1 ) {
2012-10-18 17:32:40 +04:00
drm_dp_link_train_clock_recovery_delay ( dp_info - > dpcd ) ;
2011-05-20 12:34:28 +04:00
2014-03-21 18:34:08 +04:00
if ( drm_dp_dpcd_read_link_status ( dp_info - > aux ,
dp_info - > link_status ) < = 0 ) {
2012-07-18 01:17:16 +04:00
DRM_ERROR ( " displayport link status failed \n " ) ;
2009-11-24 21:32:59 +03:00
break ;
2012-07-18 01:17:16 +04:00
}
2009-11-24 21:32:59 +03:00
2012-10-18 12:15:25 +04:00
if ( drm_dp_clock_recovery_ok ( dp_info - > link_status , dp_info - > dp_lane_count ) ) {
2009-11-24 21:32:59 +03:00
clock_recovery = true ;
break ;
}
2011-05-20 12:34:28 +04:00
for ( i = 0 ; i < dp_info - > dp_lane_count ; i + + ) {
if ( ( dp_info - > train_set [ i ] & DP_TRAIN_MAX_SWING_REACHED ) = = 0 )
2009-11-24 21:32:59 +03:00
break ;
}
2011-05-20 12:34:28 +04:00
if ( i = = dp_info - > dp_lane_count ) {
2009-11-24 21:32:59 +03:00
DRM_ERROR ( " clock recovery reached max voltage \n " ) ;
break ;
}
2011-05-20 12:34:28 +04:00
if ( ( dp_info - > train_set [ 0 ] & DP_TRAIN_VOLTAGE_SWING_MASK ) = = voltage ) {
+ + dp_info - > tries ;
if ( dp_info - > tries = = 5 ) {
2009-11-24 21:32:59 +03:00
DRM_ERROR ( " clock recovery tried 5 times \n " ) ;
break ;
}
} else
2011-05-20 12:34:28 +04:00
dp_info - > tries = 0 ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
voltage = dp_info - > train_set [ 0 ] & DP_TRAIN_VOLTAGE_SWING_MASK ;
2009-11-24 21:32:59 +03:00
/* Compute new train_set as requested by sink */
2011-05-20 12:34:28 +04:00
dp_get_adjust_train ( dp_info - > link_status , dp_info - > dp_lane_count , dp_info - > train_set ) ;
radeon_dp_update_vs_emph ( dp_info ) ;
2009-11-24 21:32:59 +03:00
}
2011-05-20 12:34:28 +04:00
if ( ! clock_recovery ) {
2009-11-24 21:32:59 +03:00
DRM_ERROR ( " clock recovery failed \n " ) ;
2011-05-20 12:34:28 +04:00
return - 1 ;
} else {
2010-08-02 04:42:55 +04:00
DRM_DEBUG_KMS ( " clock recovery at voltage %d pre-emphasis %d \n " ,
2011-05-20 12:34:28 +04:00
dp_info - > train_set [ 0 ] & DP_TRAIN_VOLTAGE_SWING_MASK ,
( dp_info - > train_set [ 0 ] & DP_TRAIN_PRE_EMPHASIS_MASK ) > >
2009-11-27 21:14:37 +03:00
DP_TRAIN_PRE_EMPHASIS_SHIFT ) ;
2011-05-20 12:34:28 +04:00
return 0 ;
}
}
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
static int radeon_dp_link_train_ce ( struct radeon_dp_link_train_info * dp_info )
{
bool channel_eq ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
if ( dp_info - > tp3_supported )
radeon_dp_set_tp ( dp_info , DP_TRAINING_PATTERN_3 ) ;
2010-01-13 01:54:34 +03:00
else
2011-05-20 12:34:28 +04:00
radeon_dp_set_tp ( dp_info , DP_TRAINING_PATTERN_2 ) ;
2009-11-24 21:32:59 +03:00
/* channel equalization loop */
2011-05-20 12:34:28 +04:00
dp_info - > tries = 0 ;
2009-11-24 21:32:59 +03:00
channel_eq = false ;
2011-05-20 12:34:28 +04:00
while ( 1 ) {
2012-10-18 17:32:40 +04:00
drm_dp_link_train_channel_eq_delay ( dp_info - > dpcd ) ;
2011-05-20 12:34:28 +04:00
2014-03-21 18:34:08 +04:00
if ( drm_dp_dpcd_read_link_status ( dp_info - > aux ,
dp_info - > link_status ) < = 0 ) {
2012-07-18 01:17:16 +04:00
DRM_ERROR ( " displayport link status failed \n " ) ;
2009-11-24 21:32:59 +03:00
break ;
2012-07-18 01:17:16 +04:00
}
2009-11-24 21:32:59 +03:00
2012-10-18 12:15:24 +04:00
if ( drm_dp_channel_eq_ok ( dp_info - > link_status , dp_info - > dp_lane_count ) ) {
2009-11-24 21:32:59 +03:00
channel_eq = true ;
break ;
}
/* Try 5 times */
2011-05-20 12:34:28 +04:00
if ( dp_info - > tries > 5 ) {
2009-11-24 21:32:59 +03:00
DRM_ERROR ( " channel eq failed: 5 tries \n " ) ;
break ;
}
/* Compute new train_set as requested by sink */
2011-05-20 12:34:28 +04:00
dp_get_adjust_train ( dp_info - > link_status , dp_info - > dp_lane_count , dp_info - > train_set ) ;
2009-11-24 21:32:59 +03:00
2011-05-20 12:34:28 +04:00
radeon_dp_update_vs_emph ( dp_info ) ;
dp_info - > tries + + ;
2009-11-24 21:32:59 +03:00
}
2011-05-20 12:34:28 +04:00
if ( ! channel_eq ) {
2009-11-24 21:32:59 +03:00
DRM_ERROR ( " channel eq failed \n " ) ;
2011-05-20 12:34:28 +04:00
return - 1 ;
} else {
2010-08-02 04:42:55 +04:00
DRM_DEBUG_KMS ( " channel eq at voltage %d pre-emphasis %d \n " ,
2011-05-20 12:34:28 +04:00
dp_info - > train_set [ 0 ] & DP_TRAIN_VOLTAGE_SWING_MASK ,
( dp_info - > train_set [ 0 ] & DP_TRAIN_PRE_EMPHASIS_MASK )
2009-11-27 21:14:37 +03:00
> > DP_TRAIN_PRE_EMPHASIS_SHIFT ) ;
2011-05-20 12:34:28 +04:00
return 0 ;
}
2009-11-24 21:32:59 +03:00
}
2011-05-20 12:34:28 +04:00
void radeon_dp_link_train ( struct drm_encoder * encoder ,
struct drm_connector * connector )
2009-12-08 00:07:28 +03:00
{
2011-05-20 12:34:28 +04:00
struct drm_device * dev = encoder - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
struct radeon_encoder * radeon_encoder = to_radeon_encoder ( encoder ) ;
struct radeon_encoder_atom_dig * dig ;
struct radeon_connector * radeon_connector ;
struct radeon_connector_atom_dig * dig_connector ;
struct radeon_dp_link_train_info dp_info ;
2011-07-25 19:57:43 +04:00
int index ;
u8 tmp , frev , crev ;
2009-12-08 00:07:28 +03:00
2011-05-20 12:34:28 +04:00
if ( ! radeon_encoder - > enc_priv )
return ;
dig = radeon_encoder - > enc_priv ;
2009-12-08 00:07:28 +03:00
2011-05-20 12:34:28 +04:00
radeon_connector = to_radeon_connector ( connector ) ;
if ( ! radeon_connector - > con_priv )
return ;
dig_connector = radeon_connector - > con_priv ;
2011-05-20 12:34:24 +04:00
2011-05-20 12:34:28 +04:00
if ( ( dig_connector - > dp_sink_type ! = CONNECTOR_OBJECT_ID_DISPLAYPORT ) & &
( dig_connector - > dp_sink_type ! = CONNECTOR_OBJECT_ID_eDP ) )
return ;
2009-12-08 00:07:28 +03:00
2011-07-25 19:57:43 +04:00
/* DPEncoderService newer than 1.1 can't program properly the
* training pattern . When facing such version use the
* DIGXEncoderControl ( X = = 1 | 2 )
*/
dp_info . use_dpencoder = true ;
index = GetIndexIntoMasterTable ( COMMAND , DPEncoderService ) ;
if ( atom_parse_cmd_header ( rdev - > mode_info . atom_context , index , & frev , & crev ) ) {
if ( crev > 1 ) {
dp_info . use_dpencoder = false ;
}
}
2011-05-20 12:34:28 +04:00
dp_info . enc_id = 0 ;
if ( dig - > dig_encoder )
dp_info . enc_id | = ATOM_DP_CONFIG_DIG2_ENCODER ;
else
dp_info . enc_id | = ATOM_DP_CONFIG_DIG1_ENCODER ;
if ( dig - > linkb )
dp_info . enc_id | = ATOM_DP_CONFIG_LINK_B ;
else
dp_info . enc_id | = ATOM_DP_CONFIG_LINK_A ;
2011-05-20 12:34:24 +04:00
2014-04-30 17:27:15 +04:00
if ( drm_dp_dpcd_readb ( & radeon_connector - > ddc_bus - > aux , DP_MAX_LANE_COUNT , & tmp )
= = 1 ) {
if ( ASIC_IS_DCE5 ( rdev ) & & ( tmp & DP_TPS3_SUPPORTED ) )
dp_info . tp3_supported = true ;
else
dp_info . tp3_supported = false ;
} else {
2011-05-20 12:34:28 +04:00
dp_info . tp3_supported = false ;
2014-04-30 17:27:15 +04:00
}
2011-05-20 12:34:28 +04:00
2012-10-18 17:32:40 +04:00
memcpy ( dp_info . dpcd , dig_connector - > dpcd , DP_RECEIVER_CAP_SIZE ) ;
2011-05-20 12:34:28 +04:00
dp_info . rdev = rdev ;
dp_info . encoder = encoder ;
dp_info . connector = connector ;
dp_info . dp_lane_count = dig_connector - > dp_lane_count ;
dp_info . dp_clock = dig_connector - > dp_clock ;
2014-04-07 18:33:46 +04:00
dp_info . aux = & radeon_connector - > ddc_bus - > aux ;
2011-05-20 12:34:28 +04:00
if ( radeon_dp_link_train_init ( & dp_info ) )
goto done ;
if ( radeon_dp_link_train_cr ( & dp_info ) )
goto done ;
if ( radeon_dp_link_train_ce ( & dp_info ) )
goto done ;
done :
if ( radeon_dp_link_train_finish ( & dp_info ) )
return ;
2009-12-08 00:07:28 +03:00
}