2014-05-02 14:02:48 +10:00
/*
* Copyright © 2008 Intel Corporation
* 2014 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 ( 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 .
*
*/
# include <drm/drmP.h>
# include "i915_drv.h"
# include "intel_drv.h"
2015-01-22 16:50:32 -08:00
# include <drm/drm_atomic_helper.h>
2014-05-02 14:02:48 +10:00
# include <drm/drm_crtc_helper.h>
# include <drm/drm_edid.h>
static bool intel_dp_mst_compute_config ( struct intel_encoder * encoder ,
2016-08-09 17:04:05 +02:00
struct intel_crtc_state * pipe_config ,
struct drm_connector_state * conn_state )
2014-05-02 14:02:48 +10:00
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
struct intel_digital_port * intel_dig_port = intel_mst - > primary ;
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
2016-11-28 20:07:07 +08:00
struct intel_connector * connector =
to_intel_connector ( conn_state - > connector ) ;
2017-04-28 16:14:20 -07:00
struct drm_atomic_state * state = pipe_config - > base . state ;
2016-08-09 17:04:06 +02:00
int bpp ;
2015-07-06 15:10:06 +03:00
int lane_count , slots ;
2015-09-08 13:40:49 +03:00
const struct drm_display_mode * adjusted_mode = & pipe_config - > base . adjusted_mode ;
2014-05-02 14:02:48 +10:00
int mst_pbn ;
2017-05-18 14:10:25 +03:00
bool reduce_m_n = drm_dp_has_quirk ( & intel_dp - > desc ,
DP_DPCD_QUIRK_LIMITED_M_N ) ;
2014-05-02 14:02:48 +10:00
pipe_config - > has_pch_encoder = false ;
bpp = 24 ;
2017-01-24 08:21:49 -08:00
if ( intel_dp - > compliance . test_data . bpc ) {
bpp = intel_dp - > compliance . test_data . bpc * 3 ;
DRM_DEBUG_KMS ( " Setting pipe bpp to %d \n " ,
bpp ) ;
}
2014-05-02 14:02:48 +10:00
/*
* for MST we always configure max link bw - the spec doesn ' t
* seem to suggest we should do otherwise .
*/
2017-04-06 16:44:14 +03:00
lane_count = intel_dp_max_lane_count ( intel_dp ) ;
2015-03-12 17:10:36 +02:00
2015-07-06 16:39:15 +03:00
pipe_config - > lane_count = lane_count ;
2014-05-02 14:02:48 +10:00
2017-01-24 08:21:49 -08:00
pipe_config - > pipe_bpp = bpp ;
2014-05-02 14:02:48 +10:00
2017-04-28 16:14:20 -07:00
pipe_config - > port_clock = intel_dp_max_link_rate ( intel_dp ) ;
2015-03-20 16:18:13 +02:00
2016-11-28 20:07:07 +08:00
if ( drm_dp_mst_port_has_audio ( & intel_dp - > mst_mgr , connector - > port ) )
pipe_config - > has_audio = true ;
2014-05-02 14:02:48 +10:00
2017-04-28 16:14:20 -07:00
mst_pbn = drm_dp_calc_pbn_mode ( adjusted_mode - > crtc_clock , bpp ) ;
2014-05-02 14:02:48 +10:00
pipe_config - > pbn = mst_pbn ;
2017-04-28 16:14:20 -07:00
slots = drm_dp_atomic_find_vcpi_slots ( state , & intel_dp - > mst_mgr ,
connector - > port , mst_pbn ) ;
if ( slots < 0 ) {
DRM_DEBUG_KMS ( " failed finding vcpi slots:%d \n " , slots ) ;
return false ;
}
2014-05-02 14:02:48 +10:00
intel_link_compute_m_n ( bpp , lane_count ,
adjusted_mode - > crtc_clock ,
pipe_config - > port_clock ,
2017-05-18 14:10:25 +03:00
& pipe_config - > dp_m_n ,
reduce_m_n ) ;
2014-05-02 14:02:48 +10:00
pipe_config - > dp_m_n . tu = slots ;
2015-08-31 11:23:28 +03:00
2014-05-02 14:02:48 +10:00
return true ;
2017-04-28 16:14:20 -07:00
}
2014-05-02 14:02:48 +10:00
2017-04-28 16:14:20 -07:00
static int intel_dp_mst_atomic_check ( struct drm_connector * connector ,
struct drm_connector_state * new_conn_state )
{
struct drm_atomic_state * state = new_conn_state - > state ;
struct drm_connector_state * old_conn_state ;
struct drm_crtc * old_crtc ;
struct drm_crtc_state * crtc_state ;
int slots , ret = 0 ;
old_conn_state = drm_atomic_get_old_connector_state ( state , connector ) ;
old_crtc = old_conn_state - > crtc ;
if ( ! old_crtc )
return ret ;
crtc_state = drm_atomic_get_new_crtc_state ( state , old_crtc ) ;
slots = to_intel_crtc_state ( crtc_state ) - > dp_m_n . tu ;
if ( drm_atomic_crtc_needs_modeset ( crtc_state ) & & slots > 0 ) {
struct drm_dp_mst_topology_mgr * mgr ;
struct drm_encoder * old_encoder ;
old_encoder = old_conn_state - > best_encoder ;
mgr = & enc_to_mst ( old_encoder ) - > primary - > dp . mst_mgr ;
ret = drm_dp_atomic_release_vcpi_slots ( state , mgr , slots ) ;
if ( ret )
DRM_DEBUG_KMS ( " failed releasing %d vcpi slots:%d \n " , slots , ret ) ;
else
to_intel_crtc_state ( crtc_state ) - > dp_m_n . tu = 0 ;
}
return ret ;
2014-05-02 14:02:48 +10:00
}
2016-08-09 17:04:04 +02:00
static void intel_mst_disable_dp ( struct intel_encoder * encoder ,
struct intel_crtc_state * old_crtc_state ,
struct drm_connector_state * old_conn_state )
2014-05-02 14:02:48 +10:00
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
struct intel_digital_port * intel_dig_port = intel_mst - > primary ;
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
2016-08-09 17:04:12 +02:00
struct intel_connector * connector =
to_intel_connector ( old_conn_state - > connector ) ;
2014-05-02 14:02:48 +10:00
int ret ;
2016-08-05 19:05:42 +03:00
DRM_DEBUG_KMS ( " %d \n " , intel_dp - > active_mst_links ) ;
2014-05-02 14:02:48 +10:00
2016-08-09 17:04:12 +02:00
drm_dp_mst_reset_vcpi_slots ( & intel_dp - > mst_mgr , connector - > port ) ;
2014-05-02 14:02:48 +10:00
ret = drm_dp_update_payload_part1 ( & intel_dp - > mst_mgr ) ;
if ( ret ) {
DRM_ERROR ( " failed to update payload %d \n " , ret ) ;
}
2016-12-15 15:29:43 +01:00
if ( old_crtc_state - > has_audio )
2016-11-28 20:07:07 +08:00
intel_audio_codec_disable ( encoder ) ;
2014-05-02 14:02:48 +10:00
}
2016-08-09 17:04:04 +02:00
static void intel_mst_post_disable_dp ( struct intel_encoder * encoder ,
struct intel_crtc_state * old_crtc_state ,
struct drm_connector_state * old_conn_state )
2014-05-02 14:02:48 +10:00
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
struct intel_digital_port * intel_dig_port = intel_mst - > primary ;
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
2016-08-09 17:04:12 +02:00
struct intel_connector * connector =
to_intel_connector ( old_conn_state - > connector ) ;
2014-05-02 14:02:48 +10:00
2016-08-05 19:05:42 +03:00
DRM_DEBUG_KMS ( " %d \n " , intel_dp - > active_mst_links ) ;
2014-05-02 14:02:48 +10:00
/* this can fail */
drm_dp_check_act_status ( & intel_dp - > mst_mgr ) ;
/* and this can also fail */
drm_dp_update_payload_part2 ( & intel_dp - > mst_mgr ) ;
2016-08-09 17:04:12 +02:00
drm_dp_mst_deallocate_vcpi ( & intel_dp - > mst_mgr , connector - > port ) ;
2014-05-02 14:02:48 +10:00
2016-08-05 19:05:42 +03:00
intel_dp - > active_mst_links - - ;
2016-03-09 11:14:38 +10:00
intel_mst - > connector = NULL ;
2016-08-05 19:05:42 +03:00
if ( intel_dp - > active_mst_links = = 0 ) {
2016-08-09 17:04:04 +02:00
intel_dig_port - > base . post_disable ( & intel_dig_port - > base ,
NULL , NULL ) ;
2014-05-02 14:02:48 +10:00
intel_dp_sink_dpms ( intel_dp , DRM_MODE_DPMS_OFF ) ;
}
}
2016-08-09 17:04:04 +02:00
static void intel_mst_pre_enable_dp ( struct intel_encoder * encoder ,
struct intel_crtc_state * pipe_config ,
struct drm_connector_state * conn_state )
2014-05-02 14:02:48 +10:00
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
struct intel_digital_port * intel_dig_port = intel_mst - > primary ;
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
2016-08-09 17:04:12 +02:00
struct drm_i915_private * dev_priv = to_i915 ( encoder - > base . dev ) ;
2014-05-02 14:02:48 +10:00
enum port port = intel_dig_port - > port ;
2016-08-09 17:04:12 +02:00
struct intel_connector * connector =
to_intel_connector ( conn_state - > connector ) ;
2014-05-02 14:02:48 +10:00
int ret ;
uint32_t temp ;
2015-08-27 13:13:31 +02:00
/* MST encoders are bound to a crtc, not to a connector,
* force the mapping here for get_hw_state .
*/
2016-08-09 17:04:12 +02:00
connector - > encoder = encoder ;
intel_mst - > connector = connector ;
2015-08-27 13:13:31 +02:00
2016-08-05 19:05:42 +03:00
DRM_DEBUG_KMS ( " %d \n " , intel_dp - > active_mst_links ) ;
2016-03-09 11:14:38 +10:00
2017-03-02 14:58:57 +02:00
if ( intel_dp - > active_mst_links = = 0 )
intel_dig_port - > base . pre_enable ( & intel_dig_port - > base ,
pipe_config , NULL ) ;
2014-05-02 14:02:48 +10:00
ret = drm_dp_mst_allocate_vcpi ( & intel_dp - > mst_mgr ,
2016-08-09 17:04:12 +02:00
connector - > port ,
2017-03-16 00:10:26 -07:00
pipe_config - > pbn ,
pipe_config - > dp_m_n . tu ) ;
2014-05-02 14:02:48 +10:00
if ( ret = = false ) {
DRM_ERROR ( " failed to allocate vcpi \n " ) ;
return ;
}
2016-08-05 19:05:42 +03:00
intel_dp - > active_mst_links + + ;
2014-05-02 14:02:48 +10:00
temp = I915_READ ( DP_TP_STATUS ( port ) ) ;
I915_WRITE ( DP_TP_STATUS ( port ) , temp ) ;
ret = drm_dp_update_payload_part1 ( & intel_dp - > mst_mgr ) ;
}
2016-08-09 17:04:04 +02:00
static void intel_mst_enable_dp ( struct intel_encoder * encoder ,
struct intel_crtc_state * pipe_config ,
struct drm_connector_state * conn_state )
2014-05-02 14:02:48 +10:00
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
struct intel_digital_port * intel_dig_port = intel_mst - > primary ;
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
2016-08-09 17:04:12 +02:00
struct drm_i915_private * dev_priv = to_i915 ( encoder - > base . dev ) ;
2014-05-02 14:02:48 +10:00
enum port port = intel_dig_port - > port ;
int ret ;
2016-08-05 19:05:42 +03:00
DRM_DEBUG_KMS ( " %d \n " , intel_dp - > active_mst_links ) ;
2014-05-02 14:02:48 +10:00
2016-06-30 15:33:11 +01:00
if ( intel_wait_for_register ( dev_priv ,
DP_TP_STATUS ( port ) ,
DP_TP_STATUS_ACT_SENT ,
DP_TP_STATUS_ACT_SENT ,
1 ) )
2014-05-02 14:02:48 +10:00
DRM_ERROR ( " Timed out waiting for ACT sent \n " ) ;
ret = drm_dp_check_act_status ( & intel_dp - > mst_mgr ) ;
ret = drm_dp_update_payload_part2 ( & intel_dp - > mst_mgr ) ;
2016-12-15 15:29:43 +01:00
if ( pipe_config - > has_audio )
2016-11-28 20:07:07 +08:00
intel_audio_codec_enable ( encoder , pipe_config , conn_state ) ;
2014-05-02 14:02:48 +10:00
}
static bool intel_dp_mst_enc_get_hw_state ( struct intel_encoder * encoder ,
enum pipe * pipe )
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
* pipe = intel_mst - > pipe ;
2016-03-09 11:14:38 +10:00
if ( intel_mst - > connector )
2014-05-02 14:02:48 +10:00
return true ;
return false ;
}
static void intel_dp_mst_enc_get_config ( struct intel_encoder * encoder ,
2015-01-15 14:55:21 +02:00
struct intel_crtc_state * pipe_config )
2014-05-02 14:02:48 +10:00
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( & encoder - > base ) ;
struct intel_digital_port * intel_dig_port = intel_mst - > primary ;
2016-08-09 17:04:12 +02:00
struct intel_crtc * crtc = to_intel_crtc ( pipe_config - > base . crtc ) ;
struct drm_i915_private * dev_priv = to_i915 ( encoder - > base . dev ) ;
2015-01-30 12:17:23 +02:00
enum transcoder cpu_transcoder = pipe_config - > cpu_transcoder ;
2014-05-02 14:02:48 +10:00
u32 temp , flags = 0 ;
2016-11-28 20:07:07 +08:00
pipe_config - > has_audio =
intel_ddi_is_audio_enabled ( dev_priv , crtc ) ;
2014-05-02 14:02:48 +10:00
temp = I915_READ ( TRANS_DDI_FUNC_CTL ( cpu_transcoder ) ) ;
if ( temp & TRANS_DDI_PHSYNC )
flags | = DRM_MODE_FLAG_PHSYNC ;
else
flags | = DRM_MODE_FLAG_NHSYNC ;
if ( temp & TRANS_DDI_PVSYNC )
flags | = DRM_MODE_FLAG_PVSYNC ;
else
flags | = DRM_MODE_FLAG_NVSYNC ;
switch ( temp & TRANS_DDI_BPC_MASK ) {
case TRANS_DDI_BPC_6 :
pipe_config - > pipe_bpp = 18 ;
break ;
case TRANS_DDI_BPC_8 :
pipe_config - > pipe_bpp = 24 ;
break ;
case TRANS_DDI_BPC_10 :
pipe_config - > pipe_bpp = 30 ;
break ;
case TRANS_DDI_BPC_12 :
pipe_config - > pipe_bpp = 36 ;
break ;
default :
break ;
}
2015-01-15 14:55:22 +02:00
pipe_config - > base . adjusted_mode . flags | = flags ;
2015-07-06 16:39:15 +03:00
pipe_config - > lane_count =
( ( temp & DDI_PORT_WIDTH_MASK ) > > DDI_PORT_WIDTH_SHIFT ) + 1 ;
2014-05-02 14:02:48 +10:00
intel_dp_get_m_n ( crtc , pipe_config ) ;
intel_ddi_clock_get ( & intel_dig_port - > base , pipe_config ) ;
}
static int intel_dp_mst_get_ddc_modes ( struct drm_connector * connector )
{
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
struct intel_dp * intel_dp = intel_connector - > mst_port ;
struct edid * edid ;
int ret ;
2016-03-09 11:14:38 +10:00
if ( ! intel_dp ) {
return intel_connector_update_modes ( connector , NULL ) ;
}
2014-05-02 14:02:48 +10:00
2016-03-09 11:14:38 +10:00
edid = drm_dp_mst_get_edid ( connector , & intel_dp - > mst_mgr , intel_connector - > port ) ;
2014-05-02 14:02:48 +10:00
ret = intel_connector_update_modes ( connector , edid ) ;
kfree ( edid ) ;
return ret ;
}
static enum drm_connector_status
2014-10-22 11:06:44 +02:00
intel_dp_mst_detect ( struct drm_connector * connector , bool force )
2014-05-02 14:02:48 +10:00
{
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
struct intel_dp * intel_dp = intel_connector - > mst_port ;
2016-03-09 11:14:38 +10:00
if ( ! intel_dp )
return connector_status_disconnected ;
2014-10-20 16:28:02 +10:00
return drm_dp_mst_detect_port ( connector , & intel_dp - > mst_mgr , intel_connector - > port ) ;
2014-05-02 14:02:48 +10:00
}
static void
intel_dp_mst_connector_destroy ( struct drm_connector * connector )
{
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
if ( ! IS_ERR_OR_NULL ( intel_connector - > edid ) )
kfree ( intel_connector - > edid ) ;
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
}
static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
. detect = intel_dp_mst_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
2016-06-24 14:00:15 +01:00
. late_register = intel_connector_register ,
2016-06-17 11:40:33 +01:00
. early_unregister = intel_connector_unregister ,
2014-05-02 14:02:48 +10:00
. destroy = intel_dp_mst_connector_destroy ,
2015-01-22 16:50:32 -08:00
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
2015-03-20 16:18:06 +02:00
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
2014-05-02 14:02:48 +10:00
} ;
static int intel_dp_mst_get_modes ( struct drm_connector * connector )
{
return intel_dp_mst_get_ddc_modes ( connector ) ;
}
static enum drm_mode_status
intel_dp_mst_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
2016-11-15 12:59:06 -08:00
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
struct intel_dp * intel_dp = intel_connector - > mst_port ;
2016-02-02 15:16:40 +02:00
int max_dotclk = to_i915 ( connector - > dev ) - > max_dotclk_freq ;
2016-11-15 12:59:06 -08:00
int bpp = 24 ; /* MST uses fixed bpp */
int max_rate , mode_rate , max_lanes , max_link_clock ;
max_link_clock = intel_dp_max_link_rate ( intel_dp ) ;
2017-04-06 16:44:14 +03:00
max_lanes = intel_dp_max_lane_count ( intel_dp ) ;
2016-11-15 12:59:06 -08:00
max_rate = intel_dp_max_data_rate ( max_link_clock , max_lanes ) ;
mode_rate = intel_dp_link_required ( mode - > clock , bpp ) ;
2016-02-02 15:16:40 +02:00
2014-05-02 14:02:48 +10:00
/* TODO - validate mode against available PBN for link */
if ( mode - > clock < 10000 )
return MODE_CLOCK_LOW ;
if ( mode - > flags & DRM_MODE_FLAG_DBLCLK )
return MODE_H_ILLEGAL ;
2016-11-15 12:59:06 -08:00
if ( mode_rate > max_rate | | mode - > clock > max_dotclk )
2016-02-02 15:16:40 +02:00
return MODE_CLOCK_HIGH ;
2014-05-02 14:02:48 +10:00
return MODE_OK ;
}
2015-08-03 17:24:09 +02:00
static struct drm_encoder * intel_mst_atomic_best_encoder ( struct drm_connector * connector ,
struct drm_connector_state * state )
{
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
struct intel_dp * intel_dp = intel_connector - > mst_port ;
struct intel_crtc * crtc = to_intel_crtc ( state - > crtc ) ;
2016-03-09 11:14:38 +10:00
if ( ! intel_dp )
return NULL ;
2015-08-03 17:24:09 +02:00
return & intel_dp - > mst_encoders [ crtc - > pipe ] - > base . base ;
}
2014-05-02 14:02:48 +10:00
static struct drm_encoder * intel_mst_best_encoder ( struct drm_connector * connector )
{
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
struct intel_dp * intel_dp = intel_connector - > mst_port ;
2016-03-09 11:14:38 +10:00
if ( ! intel_dp )
return NULL ;
2014-05-02 14:02:48 +10:00
return & intel_dp - > mst_encoders [ 0 ] - > base . base ;
}
static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
. get_modes = intel_dp_mst_get_modes ,
. mode_valid = intel_dp_mst_mode_valid ,
2015-08-03 17:24:09 +02:00
. atomic_best_encoder = intel_mst_atomic_best_encoder ,
2014-05-02 14:02:48 +10:00
. best_encoder = intel_mst_best_encoder ,
2017-04-28 16:14:20 -07:00
. atomic_check = intel_dp_mst_atomic_check ,
2014-05-02 14:02:48 +10:00
} ;
static void intel_dp_mst_encoder_destroy ( struct drm_encoder * encoder )
{
struct intel_dp_mst_encoder * intel_mst = enc_to_mst ( encoder ) ;
drm_encoder_cleanup ( encoder ) ;
kfree ( intel_mst ) ;
}
static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
. destroy = intel_dp_mst_encoder_destroy ,
} ;
static bool intel_dp_mst_get_hw_state ( struct intel_connector * connector )
{
2015-08-27 13:13:31 +02:00
if ( connector - > encoder & & connector - > base . state - > crtc ) {
2014-05-02 14:02:48 +10:00
enum pipe pipe ;
if ( ! connector - > encoder - > get_hw_state ( connector - > encoder , & pipe ) )
return false ;
return true ;
}
return false ;
}
2014-05-13 11:38:36 +02:00
static struct drm_connector * intel_dp_add_mst_connector ( struct drm_dp_mst_topology_mgr * mgr , struct drm_dp_mst_port * port , const char * pathprop )
2014-05-02 14:02:48 +10:00
{
struct intel_dp * intel_dp = container_of ( mgr , struct intel_dp , mst_mgr ) ;
struct intel_digital_port * intel_dig_port = dp_to_dig_port ( intel_dp ) ;
struct drm_device * dev = intel_dig_port - > base . base . dev ;
struct intel_connector * intel_connector ;
struct drm_connector * connector ;
int i ;
2015-04-10 10:59:10 +03:00
intel_connector = intel_connector_alloc ( ) ;
2014-05-02 14:02:48 +10:00
if ( ! intel_connector )
return NULL ;
connector = & intel_connector - > base ;
drm_connector_init ( dev , connector , & intel_dp_mst_connector_funcs , DRM_MODE_CONNECTOR_DisplayPort ) ;
drm_connector_helper_add ( connector , & intel_dp_mst_connector_helper_funcs ) ;
intel_connector - > get_hw_state = intel_dp_mst_get_hw_state ;
intel_connector - > mst_port = intel_dp ;
intel_connector - > port = port ;
for ( i = PIPE_A ; i < = PIPE_C ; i + + ) {
drm_mode_connector_attach_encoder ( & intel_connector - > base ,
& intel_dp - > mst_encoders [ i ] - > base . base ) ;
}
drm_object_attach_property ( & connector - > base , dev - > mode_config . path_property , 0 ) ;
2014-10-20 16:30:50 +10:00
drm_object_attach_property ( & connector - > base , dev - > mode_config . tile_property , 0 ) ;
2014-05-02 14:02:48 +10:00
drm_mode_connector_set_path_property ( connector , pathprop ) ;
2015-09-16 17:55:23 +10:00
return connector ;
}
static void intel_dp_register_mst_connector ( struct drm_connector * connector )
{
2017-07-04 17:18:22 +02:00
struct drm_i915_private * dev_priv = to_i915 ( connector - > dev ) ;
2016-06-24 14:00:14 +01:00
2017-07-04 17:18:22 +02:00
if ( dev_priv - > fbdev )
drm_fb_helper_add_one_connector ( & dev_priv - > fbdev - > helper ,
connector ) ;
2016-06-24 14:00:14 +01:00
2017-07-04 17:18:22 +02:00
drm_connector_register ( connector ) ;
2014-05-02 14:02:48 +10:00
}
static void intel_dp_destroy_mst_connector ( struct drm_dp_mst_topology_mgr * mgr ,
struct drm_connector * connector )
{
struct intel_connector * intel_connector = to_intel_connector ( connector ) ;
2017-07-04 17:18:22 +02:00
struct drm_i915_private * dev_priv = to_i915 ( connector - > dev ) ;
2015-08-06 13:47:36 +02:00
2016-06-17 11:40:33 +01:00
drm_connector_unregister ( connector ) ;
drm/i915: Fix race condition in intel_dp_destroy_mst_connector()
After unplugging a DP MST display from the system, we have to go through
and destroy all of the DRM connectors associated with it since none of
them are valid anymore. Unfortunately, intel_dp_destroy_mst_connector()
doesn't do a good enough job of ensuring that throughout the destruction
process that no modesettings can be done with the connectors. As it is
right now, intel_dp_destroy_mst_connector() works like this:
* Take all modeset locks
* Clear the configuration of the crtc on the connector, if there is one
* Drop all modeset locks, this is required because of circular
dependency issues that arise with trying to remove the connector from
sysfs with modeset locks held
* Unregister the connector
* Take all modeset locks, again
* Do the rest of the required cleaning for destroying the connector
* Finally drop all modeset locks for good
This only works sometimes. During the destruction process, it's very
possible that a userspace application will attempt to do a modesetting
using the connector. When we drop the modeset locks, an ioctl handler
such as drm_mode_setcrtc has the oppurtunity to take all of the modeset
locks from us. When this happens, one thing leads to another and
eventually we end up committing a mode with the non-existent connector:
[drm:intel_dp_link_training_clock_recovery [i915]] *ERROR* failed to enable link training
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x7cf0001f
[drm:intel_dp_start_link_train [i915]] *ERROR* failed to start channel equalization
[drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x7cf0001f
[drm:intel_mst_pre_enable_dp [i915]] *ERROR* failed to allocate vcpi
And in some cases, such as with the T460s using an MST dock, this
results in breaking modesetting and/or panicking the system.
To work around this, we now unregister the connector at the very
beginning of intel_dp_destroy_mst_connector(), grab all the modesetting
locks, and then hold them until we finish the rest of the function.
CC: stable@vger.kernel.org
Signed-off-by: Lyude <cpaul@redhat.com>
Signed-off-by: Rob Clark <rclark@redhat.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1458155884-13877-1-git-send-email-cpaul@redhat.com
2016-03-16 15:18:04 -04:00
2017-07-04 17:18:22 +02:00
if ( dev_priv - > fbdev )
drm_fb_helper_remove_one_connector ( & dev_priv - > fbdev - > helper ,
connector ) ;
2017-07-04 17:18:21 +02:00
/* prevent race with the check in ->detect */
drm_modeset_lock ( & connector - > dev - > mode_config . connection_mutex , NULL ) ;
2016-03-09 11:14:38 +10:00
intel_connector - > mst_port = NULL ;
2017-07-04 17:18:21 +02:00
drm_modeset_unlock ( & connector - > dev - > mode_config . connection_mutex ) ;
2014-05-02 14:02:48 +10:00
2017-07-04 17:18:22 +02:00
drm_connector_unreference ( connector ) ;
2014-05-02 14:02:48 +10:00
DRM_DEBUG_KMS ( " \n " ) ;
}
static void intel_dp_mst_hotplug ( struct drm_dp_mst_topology_mgr * mgr )
{
struct intel_dp * intel_dp = container_of ( mgr , struct intel_dp , mst_mgr ) ;
struct intel_digital_port * intel_dig_port = dp_to_dig_port ( intel_dp ) ;
struct drm_device * dev = intel_dig_port - > base . base . dev ;
drm_kms_helper_hotplug_event ( dev ) ;
}
2015-12-30 22:20:30 +01:00
static const struct drm_dp_mst_topology_cbs mst_cbs = {
2014-05-02 14:02:48 +10:00
. add_connector = intel_dp_add_mst_connector ,
2015-09-16 17:55:23 +10:00
. register_connector = intel_dp_register_mst_connector ,
2014-05-02 14:02:48 +10:00
. destroy_connector = intel_dp_destroy_mst_connector ,
. hotplug = intel_dp_mst_hotplug ,
} ;
static struct intel_dp_mst_encoder *
intel_dp_create_fake_mst_encoder ( struct intel_digital_port * intel_dig_port , enum pipe pipe )
{
struct intel_dp_mst_encoder * intel_mst ;
struct intel_encoder * intel_encoder ;
struct drm_device * dev = intel_dig_port - > base . base . dev ;
intel_mst = kzalloc ( sizeof ( * intel_mst ) , GFP_KERNEL ) ;
if ( ! intel_mst )
return NULL ;
intel_mst - > pipe = pipe ;
intel_encoder = & intel_mst - > base ;
intel_mst - > primary = intel_dig_port ;
drm_encoder_init ( dev , & intel_encoder - > base , & intel_dp_mst_enc_funcs ,
2016-05-27 20:59:24 +03:00
DRM_MODE_ENCODER_DPMST , " DP-MST %c " , pipe_name ( pipe ) ) ;
2014-05-02 14:02:48 +10:00
intel_encoder - > type = INTEL_OUTPUT_DP_MST ;
2017-02-22 08:34:27 +02:00
intel_encoder - > power_domain = intel_dig_port - > base . power_domain ;
2016-09-19 18:24:38 -07:00
intel_encoder - > port = intel_dig_port - > port ;
2014-05-02 14:02:48 +10:00
intel_encoder - > crtc_mask = 0x7 ;
intel_encoder - > cloneable = 0 ;
intel_encoder - > compute_config = intel_dp_mst_compute_config ;
intel_encoder - > disable = intel_mst_disable_dp ;
intel_encoder - > post_disable = intel_mst_post_disable_dp ;
intel_encoder - > pre_enable = intel_mst_pre_enable_dp ;
intel_encoder - > enable = intel_mst_enable_dp ;
intel_encoder - > get_hw_state = intel_dp_mst_enc_get_hw_state ;
intel_encoder - > get_config = intel_dp_mst_enc_get_config ;
return intel_mst ;
}
static bool
intel_dp_create_fake_mst_encoders ( struct intel_digital_port * intel_dig_port )
{
int i ;
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
for ( i = PIPE_A ; i < = PIPE_C ; i + + )
intel_dp - > mst_encoders [ i ] = intel_dp_create_fake_mst_encoder ( intel_dig_port , i ) ;
return true ;
}
int
intel_dp_mst_encoder_init ( struct intel_digital_port * intel_dig_port , int conn_base_id )
{
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
struct drm_device * dev = intel_dig_port - > base . base . dev ;
int ret ;
intel_dp - > can_mst = true ;
intel_dp - > mst_mgr . cbs = & mst_cbs ;
/* create encoders */
intel_dp_create_fake_mst_encoders ( intel_dig_port ) ;
2017-01-24 15:49:29 -08:00
ret = drm_dp_mst_topology_mgr_init ( & intel_dp - > mst_mgr , dev ,
& intel_dp - > aux , 16 , 3 , conn_base_id ) ;
2014-05-02 14:02:48 +10:00
if ( ret ) {
intel_dp - > can_mst = false ;
return ret ;
}
return 0 ;
}
void
intel_dp_mst_encoder_cleanup ( struct intel_digital_port * intel_dig_port )
{
struct intel_dp * intel_dp = & intel_dig_port - > dp ;
if ( ! intel_dp - > can_mst )
return ;
drm_dp_mst_topology_mgr_destroy ( & intel_dp - > mst_mgr ) ;
/* encoders will get killed by normal cleanup */
}