2017-09-12 15:58:20 -04:00
/*
* Copyright 2015 Advanced Micro Devices , 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 : AMD
*
*/
# include "dm_services_types.h"
# include "dc.h"
# include "vid.h"
# include "amdgpu.h"
# include "atom.h"
# include "amdgpu_dm.h"
# include "amdgpu_dm_types.h"
# include "amd_shared.h"
# include "amdgpu_dm_irq.h"
# include "dm_helpers.h"
# include "ivsrcid/ivsrcid_vislands30.h"
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/version.h>
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_dp_mst_helper.h>
# include "modules/inc/mod_freesync.h"
/*
* dm_vblank_get_counter
*
* @ brief
* Get counter for number of vertical blanks
*
* @ param
* struct amdgpu_device * adev - [ in ] desired amdgpu device
* int disp_idx - [ in ] which CRTC to get the counter from
*
* @ return
* Counter for vertical blanks
*/
static u32 dm_vblank_get_counter ( struct amdgpu_device * adev , int crtc )
{
if ( crtc > = adev - > mode_info . num_crtc )
return 0 ;
else {
struct amdgpu_crtc * acrtc = adev - > mode_info . crtcs [ crtc ] ;
2016-12-29 15:27:12 -05:00
if ( NULL = = acrtc - > stream ) {
DRM_ERROR ( " dc_stream is NULL for crtc '%d'! \n " , crtc ) ;
2017-09-12 15:58:20 -04:00
return 0 ;
}
2016-12-29 15:27:12 -05:00
return dc_stream_get_vblank_counter ( acrtc - > stream ) ;
2017-09-12 15:58:20 -04:00
}
}
static int dm_crtc_get_scanoutpos ( struct amdgpu_device * adev , int crtc ,
u32 * vbl , u32 * position )
{
if ( ( crtc < 0 ) | | ( crtc > = adev - > mode_info . num_crtc ) )
return - EINVAL ;
else {
struct amdgpu_crtc * acrtc = adev - > mode_info . crtcs [ crtc ] ;
2016-12-29 15:27:12 -05:00
if ( NULL = = acrtc - > stream ) {
DRM_ERROR ( " dc_stream is NULL for crtc '%d'! \n " , crtc ) ;
2017-09-12 15:58:20 -04:00
return 0 ;
}
2016-12-29 15:27:12 -05:00
return dc_stream_get_scanoutpos ( acrtc - > stream , vbl , position ) ;
2017-09-12 15:58:20 -04:00
}
return 0 ;
}
static bool dm_is_idle ( void * handle )
{
/* XXX todo */
return true ;
}
static int dm_wait_for_idle ( void * handle )
{
/* XXX todo */
return 0 ;
}
static bool dm_check_soft_reset ( void * handle )
{
return false ;
}
static int dm_soft_reset ( void * handle )
{
/* XXX todo */
return 0 ;
}
static struct amdgpu_crtc * get_crtc_by_otg_inst (
struct amdgpu_device * adev ,
int otg_inst )
{
struct drm_device * dev = adev - > ddev ;
struct drm_crtc * crtc ;
struct amdgpu_crtc * amdgpu_crtc ;
/*
* following if is check inherited from both functions where this one is
* used now . Need to be checked why it could happen .
*/
if ( otg_inst = = - 1 ) {
WARN_ON ( 1 ) ;
return adev - > mode_info . crtcs [ 0 ] ;
}
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
if ( amdgpu_crtc - > otg_inst = = otg_inst )
return amdgpu_crtc ;
}
return NULL ;
}
static void dm_pflip_high_irq ( void * interrupt_params )
{
struct amdgpu_flip_work * works ;
struct amdgpu_crtc * amdgpu_crtc ;
struct common_irq_params * irq_params = interrupt_params ;
struct amdgpu_device * adev = irq_params - > adev ;
unsigned long flags ;
amdgpu_crtc = get_crtc_by_otg_inst ( adev , irq_params - > irq_src - IRQ_TYPE_PFLIP ) ;
/* IRQ could occur when in initial stage */
/*TODO work and BO cleanup */
if ( amdgpu_crtc = = NULL ) {
DRM_DEBUG_DRIVER ( " CRTC is null, returning. \n " ) ;
return ;
}
spin_lock_irqsave ( & adev - > ddev - > event_lock , flags ) ;
works = amdgpu_crtc - > pflip_works ;
if ( amdgpu_crtc - > pflip_status ! = AMDGPU_FLIP_SUBMITTED ) {
DRM_DEBUG_DRIVER ( " amdgpu_crtc->pflip_status = %d !=AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p] \n " ,
amdgpu_crtc - > pflip_status ,
AMDGPU_FLIP_SUBMITTED ,
amdgpu_crtc - > crtc_id ,
amdgpu_crtc ) ;
spin_unlock_irqrestore ( & adev - > ddev - > event_lock , flags ) ;
return ;
}
/* page flip completed. clean up */
amdgpu_crtc - > pflip_status = AMDGPU_FLIP_NONE ;
amdgpu_crtc - > pflip_works = NULL ;
/* wakeup usersapce */
if ( works - > event )
drm_crtc_send_vblank_event ( & amdgpu_crtc - > base ,
works - > event ) ;
spin_unlock_irqrestore ( & adev - > ddev - > event_lock , flags ) ;
DRM_DEBUG_DRIVER ( " %s - crtc :%d[%p], pflip_stat:AMDGPU_FLIP_NONE, work: %p, \n " ,
__func__ , amdgpu_crtc - > crtc_id , amdgpu_crtc , works ) ;
drm_crtc_vblank_put ( & amdgpu_crtc - > base ) ;
schedule_work ( & works - > unpin_work ) ;
}
static void dm_crtc_high_irq ( void * interrupt_params )
{
struct common_irq_params * irq_params = interrupt_params ;
struct amdgpu_device * adev = irq_params - > adev ;
uint8_t crtc_index = 0 ;
struct amdgpu_crtc * acrtc ;
2017-04-19 13:29:11 -04:00
acrtc = get_crtc_by_otg_inst ( adev , irq_params - > irq_src - IRQ_TYPE_VBLANK ) ;
2017-09-12 15:58:20 -04:00
if ( acrtc )
crtc_index = acrtc - > crtc_id ;
drm_handle_vblank ( adev - > ddev , crtc_index ) ;
}
static int dm_set_clockgating_state ( void * handle ,
enum amd_clockgating_state state )
{
return 0 ;
}
static int dm_set_powergating_state ( void * handle ,
enum amd_powergating_state state )
{
return 0 ;
}
/* Prototypes of private functions */
static int dm_early_init ( void * handle ) ;
static void hotplug_notify_work_func ( struct work_struct * work )
{
struct amdgpu_display_manager * dm = container_of ( work , struct amdgpu_display_manager , mst_hotplug_work ) ;
struct drm_device * dev = dm - > ddev ;
drm_kms_helper_hotplug_event ( dev ) ;
}
/* Init display KMS
*
* Returns 0 on success
*/
int amdgpu_dm_init ( struct amdgpu_device * adev )
{
struct dc_init_data init_data ;
adev - > dm . ddev = adev - > ddev ;
adev - > dm . adev = adev ;
DRM_INFO ( " DAL is enabled \n " ) ;
/* Zero all the fields */
memset ( & init_data , 0 , sizeof ( init_data ) ) ;
/* initialize DAL's lock (for SYNC context use) */
spin_lock_init ( & adev - > dm . dal_lock ) ;
/* initialize DAL's mutex */
mutex_init ( & adev - > dm . dal_mutex ) ;
if ( amdgpu_dm_irq_init ( adev ) ) {
DRM_ERROR ( " amdgpu: failed to initialize DM IRQ support. \n " ) ;
goto error ;
}
init_data . asic_id . chip_family = adev - > family ;
init_data . asic_id . pci_revision_id = adev - > rev_id ;
init_data . asic_id . hw_internal_rev = adev - > external_rev_id ;
init_data . asic_id . vram_width = adev - > mc . vram_width ;
/* TODO: initialize init_data.asic_id.vram_type here!!!! */
init_data . asic_id . atombios_base_address =
adev - > mode_info . atom_context - > bios ;
init_data . driver = adev ;
adev - > dm . cgs_device = amdgpu_cgs_create_device ( adev ) ;
if ( ! adev - > dm . cgs_device ) {
DRM_ERROR ( " amdgpu: failed to create cgs device. \n " ) ;
goto error ;
}
init_data . cgs_device = adev - > dm . cgs_device ;
adev - > dm . dal = NULL ;
init_data . dce_environment = DCE_ENV_PRODUCTION_DRV ;
/* Display Core create. */
adev - > dm . dc = dc_create ( & init_data ) ;
if ( ! adev - > dm . dc )
DRM_INFO ( " Display Core failed to initialize! \n " ) ;
INIT_WORK ( & adev - > dm . mst_hotplug_work , hotplug_notify_work_func ) ;
adev - > dm . freesync_module = mod_freesync_create ( adev - > dm . dc ) ;
if ( ! adev - > dm . freesync_module ) {
DRM_ERROR (
" amdgpu: failed to initialize freesync_module. \n " ) ;
} else
DRM_INFO ( " amdgpu: freesync_module init done %p. \n " ,
adev - > dm . freesync_module ) ;
if ( amdgpu_dm_initialize_drm_device ( adev ) ) {
DRM_ERROR (
" amdgpu: failed to initialize sw for display support. \n " ) ;
goto error ;
}
/* Update the actual used number of crtc */
adev - > mode_info . num_crtc = adev - > dm . display_indexes_num ;
/* TODO: Add_display_info? */
/* TODO use dynamic cursor width */
adev - > ddev - > mode_config . cursor_width = 128 ;
adev - > ddev - > mode_config . cursor_height = 128 ;
if ( drm_vblank_init ( adev - > ddev , adev - > dm . display_indexes_num ) ) {
DRM_ERROR (
" amdgpu: failed to initialize sw for display support. \n " ) ;
goto error ;
}
DRM_INFO ( " KMS initialized. \n " ) ;
return 0 ;
error :
amdgpu_dm_fini ( adev ) ;
return - 1 ;
}
void amdgpu_dm_fini ( struct amdgpu_device * adev )
{
amdgpu_dm_destroy_drm_device ( & adev - > dm ) ;
/*
* TODO : pageflip , vlank interrupt
*
* amdgpu_dm_irq_fini ( adev ) ;
*/
if ( adev - > dm . cgs_device ) {
amdgpu_cgs_destroy_device ( adev - > dm . cgs_device ) ;
adev - > dm . cgs_device = NULL ;
}
if ( adev - > dm . freesync_module ) {
mod_freesync_destroy ( adev - > dm . freesync_module ) ;
adev - > dm . freesync_module = NULL ;
}
/* DC Destroy TODO: Replace destroy DAL */
{
dc_destroy ( & adev - > dm . dc ) ;
}
return ;
}
/* moved from amdgpu_dm_kms.c */
void amdgpu_dm_destroy ( )
{
}
static int dm_sw_init ( void * handle )
{
return 0 ;
}
static int dm_sw_fini ( void * handle )
{
return 0 ;
}
2016-12-29 13:21:31 -05:00
static int detect_mst_link_for_all_connectors ( struct drm_device * dev )
2017-09-12 15:58:20 -04:00
{
struct amdgpu_connector * aconnector ;
struct drm_connector * connector ;
2016-12-29 13:21:31 -05:00
int ret = 0 ;
2017-09-12 15:58:20 -04:00
drm_modeset_lock ( & dev - > mode_config . connection_mutex , NULL ) ;
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
aconnector = to_amdgpu_connector ( connector ) ;
2016-12-29 13:21:31 -05:00
if ( aconnector - > dc_link - > type = = dc_connection_mst_branch ) {
DRM_INFO ( " DM_MST: starting TM on aconnector: %p [id: %d] \n " ,
aconnector , aconnector - > base . base . id ) ;
ret = drm_dp_mst_topology_mgr_set_mst ( & aconnector - > mst_mgr , true ) ;
if ( ret < 0 ) {
DRM_ERROR ( " DM_MST: Failed to start MST \n " ) ;
( ( struct dc_link * ) aconnector - > dc_link ) - > type = dc_connection_single ;
return ret ;
2017-09-12 15:58:20 -04:00
}
2016-12-29 13:21:31 -05:00
}
2017-09-12 15:58:20 -04:00
}
drm_modeset_unlock ( & dev - > mode_config . connection_mutex ) ;
2016-12-29 13:21:31 -05:00
return ret ;
}
static int dm_late_init ( void * handle )
{
struct drm_device * dev = ( ( struct amdgpu_device * ) handle ) - > ddev ;
int r = detect_mst_link_for_all_connectors ( dev ) ;
return r ;
2017-09-12 15:58:20 -04:00
}
static void s3_handle_mst ( struct drm_device * dev , bool suspend )
{
struct amdgpu_connector * aconnector ;
struct drm_connector * connector ;
drm_modeset_lock ( & dev - > mode_config . connection_mutex , NULL ) ;
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
aconnector = to_amdgpu_connector ( connector ) ;
if ( aconnector - > dc_link - > type = = dc_connection_mst_branch & &
! aconnector - > mst_port ) {
if ( suspend )
drm_dp_mst_topology_mgr_suspend ( & aconnector - > mst_mgr ) ;
else
drm_dp_mst_topology_mgr_resume ( & aconnector - > mst_mgr ) ;
}
}
drm_modeset_unlock ( & dev - > mode_config . connection_mutex ) ;
}
static int dm_hw_init ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
/* Create DAL display manager */
amdgpu_dm_init ( adev ) ;
amdgpu_dm_hpd_init ( adev ) ;
return 0 ;
}
static int dm_hw_fini ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
amdgpu_dm_hpd_fini ( adev ) ;
amdgpu_dm_irq_fini ( adev ) ;
return 0 ;
}
static int dm_suspend ( void * handle )
{
struct amdgpu_device * adev = handle ;
struct amdgpu_display_manager * dm = & adev - > dm ;
int ret = 0 ;
struct drm_crtc * crtc ;
s3_handle_mst ( adev - > ddev , true ) ;
/* flash all pending vblank events and turn interrupt off
* before disabling CRTCs . They will be enabled back in
* dm_display_resume
*/
drm_modeset_lock_all ( adev - > ddev ) ;
list_for_each_entry ( crtc , & adev - > ddev - > mode_config . crtc_list , head ) {
struct amdgpu_crtc * acrtc = to_amdgpu_crtc ( crtc ) ;
2016-12-29 15:27:12 -05:00
if ( acrtc - > stream )
2017-09-12 15:58:20 -04:00
drm_crtc_vblank_off ( crtc ) ;
}
drm_modeset_unlock_all ( adev - > ddev ) ;
amdgpu_dm_irq_suspend ( adev ) ;
dc_set_power_state (
dm - > dc ,
DC_ACPI_CM_POWER_STATE_D3 ,
DC_VIDEO_POWER_SUSPEND ) ;
return ret ;
}
struct amdgpu_connector * amdgpu_dm_find_first_crct_matching_connector (
struct drm_atomic_state * state ,
struct drm_crtc * crtc ,
bool from_state_var )
{
uint32_t i ;
struct drm_connector_state * conn_state ;
struct drm_connector * connector ;
struct drm_crtc * crtc_from_state ;
for_each_connector_in_state (
state ,
connector ,
conn_state ,
i ) {
crtc_from_state =
from_state_var ?
conn_state - > crtc :
connector - > state - > crtc ;
if ( crtc_from_state = = crtc )
return to_amdgpu_connector ( connector ) ;
}
return NULL ;
}
static int dm_display_resume ( struct drm_device * ddev )
{
int ret = 0 ;
struct drm_connector * connector ;
struct drm_atomic_state * state = drm_atomic_state_alloc ( ddev ) ;
struct drm_plane * plane ;
struct drm_crtc * crtc ;
struct amdgpu_connector * aconnector ;
struct drm_connector_state * conn_state ;
if ( ! state )
return ENOMEM ;
state - > acquire_ctx = ddev - > mode_config . acquire_ctx ;
/* Construct an atomic state to restore previous display setting */
/*
* Attach connectors to drm_atomic_state
* Should be done in the first place in order to make connectors
* available in state during crtc state processing . It is used for
* making decision if crtc should be disabled in case sink got
* disconnected .
*
* Connectors state crtc with NULL dc_sink should be cleared , because it
* will fail validation during commit
*/
list_for_each_entry ( connector , & ddev - > mode_config . connector_list , head ) {
aconnector = to_amdgpu_connector ( connector ) ;
conn_state = drm_atomic_get_connector_state ( state , connector ) ;
ret = PTR_ERR_OR_ZERO ( conn_state ) ;
if ( ret )
goto err ;
}
/* Attach crtcs to drm_atomic_state*/
list_for_each_entry ( crtc , & ddev - > mode_config . crtc_list , head ) {
struct drm_crtc_state * crtc_state =
drm_atomic_get_crtc_state ( state , crtc ) ;
ret = PTR_ERR_OR_ZERO ( crtc_state ) ;
if ( ret )
goto err ;
/* force a restore */
crtc_state - > mode_changed = true ;
}
/* Attach planes to drm_atomic_state */
list_for_each_entry ( plane , & ddev - > mode_config . plane_list , head ) {
struct drm_crtc * crtc ;
struct drm_gem_object * obj ;
struct drm_framebuffer * fb ;
struct amdgpu_framebuffer * afb ;
struct amdgpu_bo * rbo ;
int r ;
struct drm_plane_state * plane_state = drm_atomic_get_plane_state ( state , plane ) ;
ret = PTR_ERR_OR_ZERO ( plane_state ) ;
if ( ret )
goto err ;
crtc = plane_state - > crtc ;
fb = plane_state - > fb ;
if ( ! crtc | | ! crtc - > state | | ! crtc - > state - > active )
continue ;
if ( ! fb ) {
DRM_DEBUG_KMS ( " No FB bound \n " ) ;
return 0 ;
}
/*
* Pin back the front buffers , cursor buffer was already pinned
* back in amdgpu_resume_kms
*/
afb = to_amdgpu_framebuffer ( fb ) ;
obj = afb - > obj ;
rbo = gem_to_amdgpu_bo ( obj ) ;
r = amdgpu_bo_reserve ( rbo , false ) ;
if ( unlikely ( r ! = 0 ) )
return r ;
r = amdgpu_bo_pin ( rbo , AMDGPU_GEM_DOMAIN_VRAM , NULL ) ;
amdgpu_bo_unreserve ( rbo ) ;
if ( unlikely ( r ! = 0 ) ) {
DRM_ERROR ( " Failed to pin framebuffer \n " ) ;
return r ;
}
}
/* Call commit internally with the state we just constructed */
ret = drm_atomic_commit ( state ) ;
if ( ! ret )
return 0 ;
err :
DRM_ERROR ( " Restoring old state failed with %i \n " , ret ) ;
drm_atomic_state_put ( state ) ;
return ret ;
}
static int dm_resume ( void * handle )
{
struct amdgpu_device * adev = handle ;
struct amdgpu_display_manager * dm = & adev - > dm ;
/* power on hardware */
dc_set_power_state (
dm - > dc ,
DC_ACPI_CM_POWER_STATE_D0 ,
DC_VIDEO_POWER_ON ) ;
return 0 ;
}
int amdgpu_dm_display_resume ( struct amdgpu_device * adev )
{
struct drm_device * ddev = adev - > ddev ;
struct amdgpu_display_manager * dm = & adev - > dm ;
struct amdgpu_connector * aconnector ;
struct drm_connector * connector ;
int ret = 0 ;
struct drm_crtc * crtc ;
/* program HPD filter */
dc_resume ( dm - > dc ) ;
/* On resume we need to rewrite the MSTM control bits to enamble MST*/
s3_handle_mst ( ddev , false ) ;
/*
* early enable HPD Rx IRQ , should be done before set mode as short
* pulse interrupts are used for MST
*/
amdgpu_dm_irq_resume_early ( adev ) ;
drm_modeset_lock_all ( ddev ) ;
list_for_each_entry ( crtc , & ddev - > mode_config . crtc_list , head ) {
struct amdgpu_crtc * acrtc = to_amdgpu_crtc ( crtc ) ;
2016-12-29 15:27:12 -05:00
if ( acrtc - > stream )
2017-09-12 15:58:20 -04:00
drm_crtc_vblank_on ( crtc ) ;
}
drm_modeset_unlock_all ( ddev ) ;
/* Do detection*/
list_for_each_entry ( connector ,
& ddev - > mode_config . connector_list , head ) {
aconnector = to_amdgpu_connector ( connector ) ;
/*
* this is the case when traversing through already created
* MST connectors , should be skipped
*/
if ( aconnector - > mst_port )
continue ;
dc_link_detect ( aconnector - > dc_link , false ) ;
aconnector - > dc_sink = NULL ;
amdgpu_dm_update_connector_after_detect ( aconnector ) ;
}
drm_modeset_lock_all ( ddev ) ;
ret = dm_display_resume ( ddev ) ;
drm_modeset_unlock_all ( ddev ) ;
amdgpu_dm_irq_resume ( adev ) ;
return ret ;
}
static const struct amd_ip_funcs amdgpu_dm_funcs = {
. name = " dm " ,
. early_init = dm_early_init ,
2016-12-29 13:21:31 -05:00
. late_init = dm_late_init ,
2017-09-12 15:58:20 -04:00
. sw_init = dm_sw_init ,
. sw_fini = dm_sw_fini ,
. hw_init = dm_hw_init ,
. hw_fini = dm_hw_fini ,
. suspend = dm_suspend ,
. resume = dm_resume ,
. is_idle = dm_is_idle ,
. wait_for_idle = dm_wait_for_idle ,
. check_soft_reset = dm_check_soft_reset ,
. soft_reset = dm_soft_reset ,
. set_clockgating_state = dm_set_clockgating_state ,
. set_powergating_state = dm_set_powergating_state ,
} ;
const struct amdgpu_ip_block_version dm_ip_block =
{
. type = AMD_IP_BLOCK_TYPE_DCE ,
. major = 1 ,
. minor = 0 ,
. rev = 0 ,
. funcs = & amdgpu_dm_funcs ,
} ;
/* TODO: it is temporary non-const, should fixed later */
static struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
. atomic_check = amdgpu_dm_atomic_check ,
. atomic_commit = amdgpu_dm_atomic_commit
} ;
void amdgpu_dm_update_connector_after_detect (
struct amdgpu_connector * aconnector )
{
struct drm_connector * connector = & aconnector - > base ;
struct drm_device * dev = connector - > dev ;
const struct dc_sink * sink ;
/* MST handled by drm_mst framework */
if ( aconnector - > mst_mgr . mst_state = = true )
return ;
sink = aconnector - > dc_link - > local_sink ;
/* Edid mgmt connector gets first update only in mode_valid hook and then
* the connector sink is set to either fake or physical sink depends on link status .
* don ' t do it here if u are during boot
*/
if ( aconnector - > base . force ! = DRM_FORCE_UNSPECIFIED
& & aconnector - > dc_em_sink ) {
2016-12-29 15:27:12 -05:00
/* For S3 resume with headless use eml_sink to fake stream
2017-09-12 15:58:20 -04:00
* because on resume connecotr - > sink is set ti NULL
*/
mutex_lock ( & dev - > mode_config . mutex ) ;
if ( sink ) {
2016-12-07 14:10:05 -05:00
if ( aconnector - > dc_sink ) {
2017-09-12 15:58:20 -04:00
amdgpu_dm_remove_sink_from_freesync_module (
connector ) ;
2016-12-07 14:10:05 -05:00
/* retain and release bellow are used for
* bump up refcount for sink because the link don ' t point
* to it anymore after disconnect so on next crtc to connector
* reshuffle by UMD we will get into unwanted dc_sink release
*/
if ( aconnector - > dc_sink ! = aconnector - > dc_em_sink )
dc_sink_release ( aconnector - > dc_sink ) ;
}
2017-09-12 15:58:20 -04:00
aconnector - > dc_sink = sink ;
amdgpu_dm_add_sink_to_freesync_module (
connector , aconnector - > edid ) ;
} else {
amdgpu_dm_remove_sink_from_freesync_module ( connector ) ;
if ( ! aconnector - > dc_sink )
aconnector - > dc_sink = aconnector - > dc_em_sink ;
2016-12-07 14:10:05 -05:00
else if ( aconnector - > dc_sink ! = aconnector - > dc_em_sink )
dc_sink_retain ( aconnector - > dc_sink ) ;
2017-09-12 15:58:20 -04:00
}
mutex_unlock ( & dev - > mode_config . mutex ) ;
return ;
}
/*
* TODO : temporary guard to look for proper fix
* if this sink is MST sink , we should not do anything
*/
if ( sink & & sink - > sink_signal = = SIGNAL_TYPE_DISPLAY_PORT_MST )
return ;
if ( aconnector - > dc_sink = = sink ) {
/* We got a DP short pulse (Link Loss, DP CTS, etc...).
* Do nothing ! ! */
DRM_INFO ( " DCHPD: connector_id=%d: dc_sink didn't change. \n " ,
aconnector - > connector_id ) ;
return ;
}
DRM_INFO ( " DCHPD: connector_id=%d: Old sink=%p New sink=%p \n " ,
aconnector - > connector_id , aconnector - > dc_sink , sink ) ;
mutex_lock ( & dev - > mode_config . mutex ) ;
/* 1. Update status of the drm connector
* 2. Send an event and let userspace tell us what to do */
if ( sink ) {
/* TODO: check if we still need the S3 mode update workaround.
* If yes , put it here . */
if ( aconnector - > dc_sink )
amdgpu_dm_remove_sink_from_freesync_module (
connector ) ;
aconnector - > dc_sink = sink ;
if ( sink - > dc_edid . length = = 0 )
aconnector - > edid = NULL ;
else {
aconnector - > edid =
( struct edid * ) sink - > dc_edid . raw_edid ;
drm_mode_connector_update_edid_property ( connector ,
aconnector - > edid ) ;
}
amdgpu_dm_add_sink_to_freesync_module ( connector , aconnector - > edid ) ;
} else {
amdgpu_dm_remove_sink_from_freesync_module ( connector ) ;
drm_mode_connector_update_edid_property ( connector , NULL ) ;
aconnector - > num_modes = 0 ;
aconnector - > dc_sink = NULL ;
}
mutex_unlock ( & dev - > mode_config . mutex ) ;
}
static void handle_hpd_irq ( void * param )
{
struct amdgpu_connector * aconnector = ( struct amdgpu_connector * ) param ;
struct drm_connector * connector = & aconnector - > base ;
struct drm_device * dev = connector - > dev ;
/* In case of failure or MST no need to update connector status or notify the OS
* since ( for MST case ) MST does this in it ' s own context .
*/
mutex_lock ( & aconnector - > hpd_lock ) ;
if ( dc_link_detect ( aconnector - > dc_link , false ) ) {
amdgpu_dm_update_connector_after_detect ( aconnector ) ;
drm_modeset_lock_all ( dev ) ;
dm_restore_drm_connector_state ( dev , connector ) ;
drm_modeset_unlock_all ( dev ) ;
if ( aconnector - > base . force = = DRM_FORCE_UNSPECIFIED )
drm_kms_helper_hotplug_event ( dev ) ;
}
mutex_unlock ( & aconnector - > hpd_lock ) ;
}
static void dm_handle_hpd_rx_irq ( struct amdgpu_connector * aconnector )
{
uint8_t esi [ DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI ] = { 0 } ;
uint8_t dret ;
bool new_irq_handled = false ;
int dpcd_addr ;
int dpcd_bytes_to_read ;
const int max_process_count = 30 ;
int process_count = 0 ;
const struct dc_link_status * link_status = dc_link_get_status ( aconnector - > dc_link ) ;
if ( link_status - > dpcd_caps - > dpcd_rev . raw < 0x12 ) {
dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT ;
/* DPCD 0x200 - 0x201 for downstream IRQ */
dpcd_addr = DP_SINK_COUNT ;
} else {
dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI ;
/* DPCD 0x2002 - 0x2005 for downstream IRQ */
dpcd_addr = DP_SINK_COUNT_ESI ;
}
dret = drm_dp_dpcd_read (
& aconnector - > dm_dp_aux . aux ,
dpcd_addr ,
esi ,
dpcd_bytes_to_read ) ;
while ( dret = = dpcd_bytes_to_read & &
process_count < max_process_count ) {
uint8_t retry ;
dret = 0 ;
process_count + + ;
DRM_DEBUG_KMS ( " ESI %02x %02x %02x \n " , esi [ 0 ] , esi [ 1 ] , esi [ 2 ] ) ;
/* handle HPD short pulse irq */
if ( aconnector - > mst_mgr . mst_state )
drm_dp_mst_hpd_irq (
& aconnector - > mst_mgr ,
esi ,
& new_irq_handled ) ;
if ( new_irq_handled ) {
/* ACK at DPCD to notify down stream */
const int ack_dpcd_bytes_to_write =
dpcd_bytes_to_read - 1 ;
for ( retry = 0 ; retry < 3 ; retry + + ) {
uint8_t wret ;
wret = drm_dp_dpcd_write (
& aconnector - > dm_dp_aux . aux ,
dpcd_addr + 1 ,
& esi [ 1 ] ,
ack_dpcd_bytes_to_write ) ;
if ( wret = = ack_dpcd_bytes_to_write )
break ;
}
/* check if there is new irq to be handle */
dret = drm_dp_dpcd_read (
& aconnector - > dm_dp_aux . aux ,
dpcd_addr ,
esi ,
dpcd_bytes_to_read ) ;
new_irq_handled = false ;
} else
break ;
}
if ( process_count = = max_process_count )
DRM_DEBUG_KMS ( " Loop exceeded max iterations \n " ) ;
}
static void handle_hpd_rx_irq ( void * param )
{
struct amdgpu_connector * aconnector = ( struct amdgpu_connector * ) param ;
struct drm_connector * connector = & aconnector - > base ;
struct drm_device * dev = connector - > dev ;
const struct dc_link * dc_link = aconnector - > dc_link ;
bool is_mst_root_connector = aconnector - > mst_mgr . mst_state ;
/* TODO:Temporary add mutex to protect hpd interrupt not have a gpio
* conflict , after implement i2c helper , this mutex should be
* retired .
*/
if ( aconnector - > dc_link - > type ! = dc_connection_mst_branch )
mutex_lock ( & aconnector - > hpd_lock ) ;
if ( dc_link_handle_hpd_rx_irq ( aconnector - > dc_link ) & &
! is_mst_root_connector ) {
/* Downstream Port status changed. */
if ( dc_link_detect ( aconnector - > dc_link , false ) ) {
amdgpu_dm_update_connector_after_detect ( aconnector ) ;
drm_modeset_lock_all ( dev ) ;
dm_restore_drm_connector_state ( dev , connector ) ;
drm_modeset_unlock_all ( dev ) ;
drm_kms_helper_hotplug_event ( dev ) ;
}
}
if ( ( dc_link - > cur_link_settings . lane_count ! = LANE_COUNT_UNKNOWN ) | |
( dc_link - > type = = dc_connection_mst_branch ) )
dm_handle_hpd_rx_irq ( aconnector ) ;
if ( aconnector - > dc_link - > type ! = dc_connection_mst_branch )
mutex_unlock ( & aconnector - > hpd_lock ) ;
}
static void register_hpd_handlers ( struct amdgpu_device * adev )
{
struct drm_device * dev = adev - > ddev ;
struct drm_connector * connector ;
struct amdgpu_connector * aconnector ;
const struct dc_link * dc_link ;
struct dc_interrupt_params int_params = { 0 } ;
int_params . requested_polarity = INTERRUPT_POLARITY_DEFAULT ;
int_params . current_polarity = INTERRUPT_POLARITY_DEFAULT ;
list_for_each_entry ( connector ,
& dev - > mode_config . connector_list , head ) {
aconnector = to_amdgpu_connector ( connector ) ;
dc_link = aconnector - > dc_link ;
if ( DC_IRQ_SOURCE_INVALID ! = dc_link - > irq_source_hpd ) {
int_params . int_context = INTERRUPT_LOW_IRQ_CONTEXT ;
int_params . irq_source = dc_link - > irq_source_hpd ;
amdgpu_dm_irq_register_interrupt ( adev , & int_params ,
handle_hpd_irq ,
( void * ) aconnector ) ;
}
if ( DC_IRQ_SOURCE_INVALID ! = dc_link - > irq_source_hpd_rx ) {
/* Also register for DP short pulse (hpd_rx). */
int_params . int_context = INTERRUPT_LOW_IRQ_CONTEXT ;
int_params . irq_source = dc_link - > irq_source_hpd_rx ;
amdgpu_dm_irq_register_interrupt ( adev , & int_params ,
handle_hpd_rx_irq ,
( void * ) aconnector ) ;
}
}
}
/* Register IRQ sources and initialize IRQ callbacks */
static int dce110_register_irq_handlers ( struct amdgpu_device * adev )
{
struct dc * dc = adev - > dm . dc ;
struct common_irq_params * c_irq_params ;
struct dc_interrupt_params int_params = { 0 } ;
int r ;
int i ;
int_params . requested_polarity = INTERRUPT_POLARITY_DEFAULT ;
int_params . current_polarity = INTERRUPT_POLARITY_DEFAULT ;
/* Actions of amdgpu_irq_add_id():
* 1. Register a set ( ) function with base driver .
* Base driver will call set ( ) function to enable / disable an
* interrupt in DC hardware .
* 2. Register amdgpu_dm_irq_handler ( ) .
* Base driver will call amdgpu_dm_irq_handler ( ) for ALL interrupts
* coming from DC hardware .
* amdgpu_dm_irq_handler ( ) will re - direct the interrupt to DC
* for acknowledging and handling . */
2017-04-19 13:29:11 -04:00
/* Use VBLANK interrupt */
2017-04-19 13:55:35 -04:00
for ( i = 1 ; i < = adev - > mode_info . num_crtc ; i + + ) {
r = amdgpu_irq_add_id ( adev , AMDGPU_IH_CLIENTID_LEGACY , i , & adev - > crtc_irq ) ;
2017-04-19 13:29:11 -04:00
2017-09-12 15:58:20 -04:00
if ( r ) {
DRM_ERROR ( " Failed to add crtc irq id! \n " ) ;
return r ;
}
int_params . int_context = INTERRUPT_HIGH_IRQ_CONTEXT ;
int_params . irq_source =
2017-04-19 13:55:35 -04:00
dc_interrupt_to_irq_source ( dc , i , 0 ) ;
2017-09-12 15:58:20 -04:00
2017-04-19 13:29:11 -04:00
c_irq_params = & adev - > dm . vblank_params [ int_params . irq_source - DC_IRQ_SOURCE_VBLANK1 ] ;
2017-09-12 15:58:20 -04:00
c_irq_params - > adev = adev ;
c_irq_params - > irq_src = int_params . irq_source ;
amdgpu_dm_irq_register_interrupt ( adev , & int_params ,
dm_crtc_high_irq , c_irq_params ) ;
}
2017-04-19 13:55:35 -04:00
/* Use GRPH_PFLIP interrupt */
2017-09-12 15:58:20 -04:00
for ( i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP ;
i < = VISLANDS30_IV_SRCID_D6_GRPH_PFLIP ; i + = 2 ) {
r = amdgpu_irq_add_id ( adev , AMDGPU_IH_CLIENTID_LEGACY , i , & adev - > pageflip_irq ) ;
if ( r ) {
DRM_ERROR ( " Failed to add page flip irq id! \n " ) ;
return r ;
}
int_params . int_context = INTERRUPT_HIGH_IRQ_CONTEXT ;
int_params . irq_source =
dc_interrupt_to_irq_source ( dc , i , 0 ) ;
c_irq_params = & adev - > dm . pflip_params [ int_params . irq_source - DC_IRQ_SOURCE_PFLIP_FIRST ] ;
c_irq_params - > adev = adev ;
c_irq_params - > irq_src = int_params . irq_source ;
amdgpu_dm_irq_register_interrupt ( adev , & int_params ,
dm_pflip_high_irq , c_irq_params ) ;
}
/* HPD */
r = amdgpu_irq_add_id ( adev , AMDGPU_IH_CLIENTID_LEGACY , VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A ,
& adev - > hpd_irq ) ;
if ( r ) {
DRM_ERROR ( " Failed to add hpd irq id! \n " ) ;
return r ;
}
register_hpd_handlers ( adev ) ;
return 0 ;
}
static int amdgpu_dm_mode_config_init ( struct amdgpu_device * adev )
{
int r ;
adev - > mode_info . mode_config_initialized = true ;
amdgpu_dm_mode_funcs . fb_create =
amdgpu_mode_funcs . fb_create ;
amdgpu_dm_mode_funcs . output_poll_changed =
amdgpu_mode_funcs . output_poll_changed ;
adev - > ddev - > mode_config . funcs = ( void * ) & amdgpu_dm_mode_funcs ;
adev - > ddev - > mode_config . max_width = 16384 ;
adev - > ddev - > mode_config . max_height = 16384 ;
adev - > ddev - > mode_config . preferred_depth = 24 ;
adev - > ddev - > mode_config . prefer_shadow = 1 ;
/* indicate support of immediate flip */
adev - > ddev - > mode_config . async_page_flip = true ;
adev - > ddev - > mode_config . fb_base = adev - > mc . aper_base ;
r = amdgpu_modeset_create_props ( adev ) ;
if ( r )
return r ;
return 0 ;
}
# if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
defined ( CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE )
static int amdgpu_dm_backlight_update_status ( struct backlight_device * bd )
{
struct amdgpu_display_manager * dm = bl_get_data ( bd ) ;
if ( dc_link_set_backlight_level ( dm - > backlight_link ,
bd - > props . brightness , 0 , 0 ) )
return 0 ;
else
return 1 ;
}
static int amdgpu_dm_backlight_get_brightness ( struct backlight_device * bd )
{
return bd - > props . brightness ;
}
static const struct backlight_ops amdgpu_dm_backlight_ops = {
. get_brightness = amdgpu_dm_backlight_get_brightness ,
. update_status = amdgpu_dm_backlight_update_status ,
} ;
void amdgpu_dm_register_backlight_device ( struct amdgpu_display_manager * dm )
{
char bl_name [ 16 ] ;
struct backlight_properties props = { 0 } ;
props . max_brightness = AMDGPU_MAX_BL_LEVEL ;
props . type = BACKLIGHT_RAW ;
snprintf ( bl_name , sizeof ( bl_name ) , " amdgpu_bl%d " ,
dm - > adev - > ddev - > primary - > index ) ;
dm - > backlight_dev = backlight_device_register ( bl_name ,
dm - > adev - > ddev - > dev ,
dm ,
& amdgpu_dm_backlight_ops ,
& props ) ;
if ( NULL = = dm - > backlight_dev )
DRM_ERROR ( " DM: Backlight registration failed! \n " ) ;
else
DRM_INFO ( " DM: Registered Backlight device: %s \n " , bl_name ) ;
}
# endif
/* In this architecture, the association
* connector - > encoder - > crtc
* id not really requried . The crtc and connector will hold the
* display_index as an abstraction to use with DAL component
*
* Returns 0 on success
*/
int amdgpu_dm_initialize_drm_device ( struct amdgpu_device * adev )
{
struct amdgpu_display_manager * dm = & adev - > dm ;
uint32_t i ;
struct amdgpu_connector * aconnector ;
struct amdgpu_encoder * aencoder ;
struct amdgpu_crtc * acrtc ;
uint32_t link_cnt ;
link_cnt = dm - > dc - > caps . max_links ;
if ( amdgpu_dm_mode_config_init ( dm - > adev ) ) {
DRM_ERROR ( " DM: Failed to initialize mode config \n " ) ;
return - 1 ;
}
2016-12-29 15:27:12 -05:00
for ( i = 0 ; i < dm - > dc - > caps . max_streams ; i + + ) {
2017-09-12 15:58:20 -04:00
acrtc = kzalloc ( sizeof ( struct amdgpu_crtc ) , GFP_KERNEL ) ;
if ( ! acrtc )
goto fail ;
if ( amdgpu_dm_crtc_init (
dm ,
acrtc ,
i ) ) {
DRM_ERROR ( " KMS: Failed to initialize crtc \n " ) ;
kfree ( acrtc ) ;
goto fail ;
}
}
2016-12-29 15:27:12 -05:00
dm - > display_indexes_num = dm - > dc - > caps . max_streams ;
2017-09-12 15:58:20 -04:00
/* loops over all connectors on the board */
for ( i = 0 ; i < link_cnt ; i + + ) {
if ( i > AMDGPU_DM_MAX_DISPLAY_INDEX ) {
DRM_ERROR (
" KMS: Cannot support more than %d display indexes \n " ,
AMDGPU_DM_MAX_DISPLAY_INDEX ) ;
continue ;
}
aconnector = kzalloc ( sizeof ( * aconnector ) , GFP_KERNEL ) ;
if ( ! aconnector )
goto fail ;
aencoder = kzalloc ( sizeof ( * aencoder ) , GFP_KERNEL ) ;
if ( ! aencoder ) {
goto fail_free_connector ;
}
if ( amdgpu_dm_encoder_init ( dm - > ddev , aencoder , i ) ) {
DRM_ERROR ( " KMS: Failed to initialize encoder \n " ) ;
goto fail_free_encoder ;
}
if ( amdgpu_dm_connector_init ( dm , aconnector , i , aencoder ) ) {
DRM_ERROR ( " KMS: Failed to initialize connector \n " ) ;
goto fail_free_connector ;
}
if ( dc_link_detect ( dc_get_link_at_index ( dm - > dc , i ) , true ) )
amdgpu_dm_update_connector_after_detect ( aconnector ) ;
}
/* Software is initialized. Now we can register interrupt handlers. */
switch ( adev - > asic_type ) {
case CHIP_BONAIRE :
case CHIP_HAWAII :
case CHIP_TONGA :
case CHIP_FIJI :
case CHIP_CARRIZO :
case CHIP_STONEY :
case CHIP_POLARIS11 :
case CHIP_POLARIS10 :
2016-12-14 15:35:13 -05:00
case CHIP_POLARIS12 :
2017-09-12 15:58:20 -04:00
if ( dce110_register_irq_handlers ( dm - > adev ) ) {
DRM_ERROR ( " DM: Failed to initialize IRQ \n " ) ;
return - 1 ;
}
break ;
default :
DRM_ERROR ( " Usupported ASIC type: 0x%X \n " , adev - > asic_type ) ;
return - 1 ;
}
drm_mode_config_reset ( dm - > ddev ) ;
return 0 ;
fail_free_encoder :
kfree ( aencoder ) ;
fail_free_connector :
kfree ( aconnector ) ;
fail :
return - 1 ;
}
void amdgpu_dm_destroy_drm_device ( struct amdgpu_display_manager * dm )
{
drm_mode_config_cleanup ( dm - > ddev ) ;
return ;
}
/******************************************************************************
* amdgpu_display_funcs functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* dm_bandwidth_update - program display watermarks
*
* @ adev : amdgpu_device pointer
*
* Calculate and program the display watermarks and line buffer allocation .
*/
static void dm_bandwidth_update ( struct amdgpu_device * adev )
{
2016-12-05 19:21:26 +05:30
/* TODO: implement later */
2017-09-12 15:58:20 -04:00
}
static void dm_set_backlight_level ( struct amdgpu_encoder * amdgpu_encoder ,
u8 level )
{
/* TODO: translate amdgpu_encoder to display_index and call DAL */
}
static u8 dm_get_backlight_level ( struct amdgpu_encoder * amdgpu_encoder )
{
/* TODO: translate amdgpu_encoder to display_index and call DAL */
return 0 ;
}
/******************************************************************************
* Page Flip functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* dm_page_flip - called by amdgpu_flip_work_func ( ) , which is triggered
* via DRM IOCTL , by user mode .
*
* @ adev : amdgpu_device pointer
* @ crtc_id : crtc to cleanup pageflip on
* @ crtc_base : new address of the crtc ( GPU MC address )
*
* Does the actual pageflip ( surface address update ) .
*/
static void dm_page_flip ( struct amdgpu_device * adev ,
int crtc_id , u64 crtc_base , bool async )
{
struct amdgpu_crtc * acrtc ;
2016-12-29 15:27:12 -05:00
const struct dc_stream * stream ;
2017-09-12 15:58:20 -04:00
struct dc_flip_addrs addr = { { 0 } } ;
/*
* TODO risk of concurrency issues
*
* This should guarded by the dal_mutex but we can ' t do this since the
* caller uses a spin_lock on event_lock .
*
* If we wait on the dal_mutex a second page flip interrupt might come ,
* spin on the event_lock , disabling interrupts while it does so . At
* this point the core can no longer be pre - empted and return to the
* thread that waited on the dal_mutex and we ' re deadlocked .
*
* With multiple cores the same essentially happens but might just take
* a little longer to lock up all cores .
*
* The reason we should lock on dal_mutex is so that we can be sure
2016-12-29 15:27:12 -05:00
* nobody messes with acrtc - > stream after we read and check its value .
2017-09-12 15:58:20 -04:00
*
* We might be able to fix our concurrency issues with a work queue
* where we schedule all work items ( mode_set , page_flip , etc . ) and
* execute them one by one . Care needs to be taken to still deal with
* any potential concurrency issues arising from interrupt calls .
*/
acrtc = adev - > mode_info . crtcs [ crtc_id ] ;
2016-12-29 15:27:12 -05:00
stream = acrtc - > stream ;
2017-09-12 15:58:20 -04:00
/*
* Received a page flip call after the display has been reset .
* Just return in this case . Everything should be clean - up on reset .
*/
2016-12-29 15:27:12 -05:00
if ( ! stream ) {
2017-09-12 15:58:20 -04:00
WARN_ON ( 1 ) ;
return ;
}
addr . address . grph . addr . low_part = lower_32_bits ( crtc_base ) ;
addr . address . grph . addr . high_part = upper_32_bits ( crtc_base ) ;
addr . flip_immediate = async ;
DRM_DEBUG_DRIVER ( " %s Flipping to hi: 0x%x, low: 0x%x \n " ,
__func__ ,
addr . address . grph . addr . high_part ,
addr . address . grph . addr . low_part ) ;
dc_flip_surface_addrs (
adev - > dm . dc ,
2016-12-29 15:27:12 -05:00
dc_stream_get_status ( stream ) - > surfaces ,
2017-09-12 15:58:20 -04:00
& addr , 1 ) ;
}
static int amdgpu_notify_freesync ( struct drm_device * dev , void * data ,
struct drm_file * filp )
{
struct mod_freesync_params freesync_params ;
2016-12-29 15:27:12 -05:00
uint8_t num_streams ;
2017-09-12 15:58:20 -04:00
uint8_t i ;
struct amdgpu_device * adev = dev - > dev_private ;
int r = 0 ;
/* Get freesync enable flag from DRM */
2016-12-29 15:27:12 -05:00
num_streams = dc_get_current_stream_count ( adev - > dm . dc ) ;
2017-09-12 15:58:20 -04:00
2016-12-29 15:27:12 -05:00
for ( i = 0 ; i < num_streams ; i + + ) {
const struct dc_stream * stream ;
stream = dc_get_stream_at_index ( adev - > dm . dc , i ) ;
2017-09-12 15:58:20 -04:00
mod_freesync_update_state ( adev - > dm . freesync_module ,
2016-12-29 15:27:12 -05:00
& stream , 1 , & freesync_params ) ;
2017-09-12 15:58:20 -04:00
}
return r ;
}
2016-12-13 15:42:48 -05:00
static const struct amdgpu_display_funcs dm_display_funcs = {
2017-09-12 15:58:20 -04:00
. bandwidth_update = dm_bandwidth_update , /* called unconditionally */
. vblank_get_counter = dm_vblank_get_counter , /* called unconditionally */
. vblank_wait = NULL ,
. backlight_set_level =
dm_set_backlight_level , /* called unconditionally */
. backlight_get_level =
dm_get_backlight_level , /* called unconditionally */
. hpd_sense = NULL , /* called unconditionally */
. hpd_set_polarity = NULL , /* called unconditionally */
. hpd_get_gpio_reg = NULL , /* VBIOS parsing. DAL does it. */
. page_flip = dm_page_flip , /* called unconditionally */
. page_flip_get_scanoutpos =
dm_crtc_get_scanoutpos , /* called unconditionally */
. add_encoder = NULL , /* VBIOS parsing. DAL does it. */
. add_connector = NULL , /* VBIOS parsing. DAL does it. */
. notify_freesync = amdgpu_notify_freesync ,
} ;
# if defined(CONFIG_DEBUG_KERNEL_DC)
static ssize_t s3_debug_store (
struct device * device ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
int ret ;
int s3_state ;
struct pci_dev * pdev = to_pci_dev ( device ) ;
struct drm_device * drm_dev = pci_get_drvdata ( pdev ) ;
struct amdgpu_device * adev = drm_dev - > dev_private ;
ret = kstrtoint ( buf , 0 , & s3_state ) ;
if ( ret = = 0 ) {
if ( s3_state ) {
dm_resume ( adev ) ;
amdgpu_dm_display_resume ( adev ) ;
drm_kms_helper_hotplug_event ( adev - > ddev ) ;
} else
dm_suspend ( adev ) ;
}
return ret = = 0 ? count : 0 ;
}
DEVICE_ATTR_WO ( s3_debug ) ;
# endif
static int dm_early_init ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
amdgpu_dm_set_irq_funcs ( adev ) ;
switch ( adev - > asic_type ) {
case CHIP_BONAIRE :
case CHIP_HAWAII :
adev - > mode_info . num_crtc = 6 ;
adev - > mode_info . num_hpd = 6 ;
adev - > mode_info . num_dig = 6 ;
break ;
case CHIP_FIJI :
case CHIP_TONGA :
adev - > mode_info . num_crtc = 6 ;
adev - > mode_info . num_hpd = 6 ;
adev - > mode_info . num_dig = 7 ;
break ;
case CHIP_CARRIZO :
adev - > mode_info . num_crtc = 3 ;
adev - > mode_info . num_hpd = 6 ;
adev - > mode_info . num_dig = 9 ;
break ;
case CHIP_STONEY :
adev - > mode_info . num_crtc = 2 ;
adev - > mode_info . num_hpd = 6 ;
adev - > mode_info . num_dig = 9 ;
break ;
case CHIP_POLARIS11 :
2016-12-14 15:35:13 -05:00
case CHIP_POLARIS12 :
2017-09-12 15:58:20 -04:00
adev - > mode_info . num_crtc = 5 ;
adev - > mode_info . num_hpd = 5 ;
adev - > mode_info . num_dig = 5 ;
break ;
case CHIP_POLARIS10 :
adev - > mode_info . num_crtc = 6 ;
adev - > mode_info . num_hpd = 6 ;
adev - > mode_info . num_dig = 6 ;
break ;
default :
DRM_ERROR ( " Usupported ASIC type: 0x%X \n " , adev - > asic_type ) ;
return - EINVAL ;
}
2016-12-13 15:42:48 -05:00
if ( adev - > mode_info . funcs = = NULL )
adev - > mode_info . funcs = & dm_display_funcs ;
2017-09-12 15:58:20 -04:00
/* Note: Do NOT change adev->audio_endpt_rreg and
* adev - > audio_endpt_wreg because they are initialised in
* amdgpu_device_init ( ) */
# if defined(CONFIG_DEBUG_KERNEL_DC)
device_create_file (
adev - > ddev - > dev ,
& dev_attr_s3_debug ) ;
# endif
return 0 ;
}
bool amdgpu_dm_acquire_dal_lock ( struct amdgpu_display_manager * dm )
{
/* TODO */
return true ;
}
bool amdgpu_dm_release_dal_lock ( struct amdgpu_display_manager * dm )
{
/* TODO */
return true ;
}