2014-04-10 12:51:11 +04:00
/*
* Copyright ( c ) 2006 - 2008 Intel Corporation
* Copyright ( c ) 2007 Dave Airlie < airlied @ linux . ie >
*
* DRM core CRTC related functions
*
* Permission to use , copy , modify , distribute , and sell this software and its
* documentation for any purpose is hereby granted without fee , provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation , and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific ,
* written prior permission . The copyright holders make no representations
* about the suitability of this software for any purpose . It is provided " as
* is " without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE ,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS , IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL , INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE ,
* DATA OR PROFITS , WHETHER IN AN ACTION OF CONTRACT , NEGLIGENCE OR OTHER
* TORTIOUS ACTION , ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE .
*
* Authors :
* Keith Packard
* Eric Anholt < eric @ anholt . net >
* Dave Airlie < airlied @ linux . ie >
* Jesse Barnes < jesse . barnes @ intel . com >
*/
# include <linux/export.h>
# include <linux/moduleparam.h>
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_fourcc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_fb_helper.h>
# include <drm/drm_edid.h>
/**
* DOC : output probing helper overview
*
* This library provides some helper code for output probing . It provides an
2017-01-25 09:26:43 +03:00
* implementation of the core & drm_connector_funcs . fill_modes interface with
2017-04-04 12:53:04 +03:00
* drm_helper_probe_single_connector_modes ( ) .
2014-04-10 12:51:11 +04:00
*
* It also provides support for polling connectors with a work item and for
* generic hotplug interrupt handling where the driver doesn ' t or cannot keep
* track of a per - connector hpd interrupt .
*
* This helper library can be used independently of the modeset helper library .
* Drivers can also overwrite different parts e . g . use their own hotplug
* handling code to avoid probing unrelated outputs .
2015-12-04 11:45:44 +03:00
*
* The probe helpers share the function table structures with other display
2016-12-29 23:48:26 +03:00
* helper libraries . See & struct drm_connector_helper_funcs for the details .
2014-04-10 12:51:11 +04:00
*/
static bool drm_kms_helper_poll = true ;
module_param_named ( poll , drm_kms_helper_poll , bool , 0600 ) ;
2014-12-17 14:56:22 +03:00
static enum drm_mode_status
drm_mode_validate_flag ( const struct drm_display_mode * mode ,
int flags )
2014-04-10 12:51:11 +04:00
{
2014-12-17 14:56:22 +03:00
if ( ( mode - > flags & DRM_MODE_FLAG_INTERLACE ) & &
! ( flags & DRM_MODE_FLAG_INTERLACE ) )
return MODE_NO_INTERLACE ;
2014-04-10 12:51:11 +04:00
2014-12-17 14:56:22 +03:00
if ( ( mode - > flags & DRM_MODE_FLAG_DBLSCAN ) & &
! ( flags & DRM_MODE_FLAG_DBLSCAN ) )
return MODE_NO_DBLESCAN ;
2014-04-10 12:51:11 +04:00
2014-12-17 14:56:22 +03:00
if ( ( mode - > flags & DRM_MODE_FLAG_3D_MASK ) & &
! ( flags & DRM_MODE_FLAG_3D_MASK ) )
return MODE_NO_STEREO ;
2014-04-10 12:51:11 +04:00
2014-12-17 14:56:22 +03:00
return MODE_OK ;
2014-04-10 12:51:11 +04:00
}
2014-08-06 12:08:32 +04:00
static int drm_helper_probe_add_cmdline_mode ( struct drm_connector * connector )
{
2016-06-01 12:50:51 +03:00
struct drm_cmdline_mode * cmdline_mode ;
2014-08-06 12:08:32 +04:00
struct drm_display_mode * mode ;
2016-06-01 12:50:51 +03:00
cmdline_mode = & connector - > cmdline_mode ;
if ( ! cmdline_mode - > specified )
2014-08-06 12:08:32 +04:00
return 0 ;
2016-06-01 12:50:51 +03:00
/* Only add a GTF mode if we find no matching probed modes */
list_for_each_entry ( mode , & connector - > probed_modes , head ) {
if ( mode - > hdisplay ! = cmdline_mode - > xres | |
mode - > vdisplay ! = cmdline_mode - > yres )
continue ;
if ( cmdline_mode - > refresh_specified ) {
/* The probed mode's vrefresh is set until later */
if ( drm_mode_vrefresh ( mode ) ! = cmdline_mode - > refresh )
continue ;
}
return 0 ;
}
2014-08-06 12:08:32 +04:00
mode = drm_mode_create_from_cmdline_mode ( connector - > dev ,
2016-06-01 12:50:51 +03:00
cmdline_mode ) ;
2014-08-06 12:08:32 +04:00
if ( mode = = NULL )
return 0 ;
drm_mode_probed_add ( connector , mode ) ;
return 1 ;
}
2015-07-10 00:44:26 +03:00
# define DRM_OUTPUT_POLL_PERIOD (10*HZ)
2015-09-23 17:13:00 +03:00
/**
2017-01-27 05:04:08 +03:00
* drm_kms_helper_poll_enable - re - enable output polling .
2015-09-23 17:13:00 +03:00
* @ dev : drm_device
*
2017-01-27 05:04:08 +03:00
* This function re - enables the output polling work , after it has been
* temporarily disabled using drm_kms_helper_poll_disable ( ) , for example over
* suspend / resume .
2015-09-23 17:13:00 +03:00
*
2017-01-27 05:04:08 +03:00
* Drivers can call this helper from their device resume implementation . It is
* an error to call this when the output polling support has not yet been set
* up .
*
* Note that calls to enable and disable polling must be strictly ordered , which
* is automatically the case when they ' re only call from suspend / resume
* callbacks .
2015-09-23 17:13:00 +03:00
*/
2017-01-27 05:04:08 +03:00
void drm_kms_helper_poll_enable ( struct drm_device * dev )
2015-07-10 00:44:26 +03:00
{
bool poll = false ;
struct drm_connector * connector ;
2016-12-15 18:58:43 +03:00
struct drm_connector_list_iter conn_iter ;
2016-08-31 14:09:05 +03:00
unsigned long delay = DRM_OUTPUT_POLL_PERIOD ;
2015-07-10 00:44:26 +03:00
if ( ! dev - > mode_config . poll_enabled | | ! drm_kms_helper_poll )
return ;
2017-02-28 17:46:43 +03:00
drm_connector_list_iter_begin ( dev , & conn_iter ) ;
2016-12-15 18:58:43 +03:00
drm_for_each_connector_iter ( connector , & conn_iter ) {
2015-07-10 00:44:26 +03:00
if ( connector - > polled & ( DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT ) )
poll = true ;
}
2017-02-28 17:46:43 +03:00
drm_connector_list_iter_end ( & conn_iter ) ;
2015-07-10 00:44:26 +03:00
2016-08-31 14:09:05 +03:00
if ( dev - > mode_config . delayed_event ) {
2017-01-09 17:31:58 +03:00
/*
* FIXME :
*
* Use short ( 1 s ) delay to handle the initial delayed event .
* This delay should not be needed , but Optimus / nouveau will
* fail in a mysterious way if the delayed event is handled as
* soon as possible like it is done in
* drm_helper_probe_single_connector_modes ( ) in case the poll
* was enabled before .
*/
2016-08-31 14:09:05 +03:00
poll = true ;
2017-01-09 17:31:58 +03:00
delay = HZ ;
2016-08-31 14:09:05 +03:00
}
2015-07-10 00:44:26 +03:00
if ( poll )
2016-08-31 14:09:05 +03:00
schedule_delayed_work ( & dev - > mode_config . output_poll_work , delay ) ;
2015-07-10 00:44:26 +03:00
}
2017-01-27 05:04:08 +03:00
EXPORT_SYMBOL ( drm_kms_helper_poll_enable ) ;
2015-09-23 17:13:00 +03:00
2016-11-29 23:56:30 +03:00
static enum drm_connector_status
2017-04-06 21:55:20 +03:00
drm_helper_probe_detect_ctx ( struct drm_connector * connector , bool force )
2016-11-29 23:56:30 +03:00
{
2017-04-06 21:55:20 +03:00
const struct drm_connector_helper_funcs * funcs = connector - > helper_private ;
struct drm_modeset_acquire_ctx ctx ;
int ret ;
drm_modeset_acquire_init ( & ctx , 0 ) ;
retry :
ret = drm_modeset_lock ( & connector - > dev - > mode_config . connection_mutex , & ctx ) ;
if ( ! ret ) {
if ( funcs - > detect_ctx )
ret = funcs - > detect_ctx ( connector , & ctx , force ) ;
else if ( connector - > funcs - > detect )
ret = connector - > funcs - > detect ( connector , force ) ;
else
ret = connector_status_connected ;
}
if ( ret = = - EDEADLK ) {
drm_modeset_backoff ( & ctx ) ;
goto retry ;
}
if ( WARN_ON ( ret < 0 ) )
ret = connector_status_unknown ;
drm_modeset_drop_locks ( & ctx ) ;
drm_modeset_acquire_fini ( & ctx ) ;
return ret ;
}
/**
* drm_helper_probe_detect - probe connector status
* @ connector : connector to probe
* @ ctx : acquire_ctx , or NULL to let this function handle locking .
* @ force : Whether destructive probe operations should be performed .
*
* This function calls the detect callbacks of the connector .
* This function returns & drm_connector_status , or
* if @ ctx is set , it might also return - EDEADLK .
*/
int
drm_helper_probe_detect ( struct drm_connector * connector ,
struct drm_modeset_acquire_ctx * ctx ,
bool force )
{
const struct drm_connector_helper_funcs * funcs = connector - > helper_private ;
struct drm_device * dev = connector - > dev ;
int ret ;
if ( ! ctx )
return drm_helper_probe_detect_ctx ( connector , force ) ;
ret = drm_modeset_lock ( & dev - > mode_config . connection_mutex , ctx ) ;
if ( ret )
return ret ;
if ( funcs - > detect_ctx )
return funcs - > detect_ctx ( connector , ctx , force ) ;
else if ( connector - > funcs - > detect )
return connector - > funcs - > detect ( connector , force ) ;
else
return connector_status_connected ;
2016-11-29 23:56:30 +03:00
}
2017-04-06 21:55:20 +03:00
EXPORT_SYMBOL ( drm_helper_probe_detect ) ;
2016-11-29 23:56:30 +03:00
2015-12-04 00:14:14 +03:00
/**
* drm_helper_probe_single_connector_modes - get complete set of display modes
* @ connector : connector to probe
* @ maxX : max width for modes
* @ maxY : max height for modes
*
* Based on the helper callbacks implemented by @ connector in struct
* & drm_connector_helper_funcs try to detect all valid modes . Modes will first
* be added to the connector ' s probed_modes list , then culled ( based on validity
* and the @ maxX , @ maxY parameters ) and put into the normal modes list .
*
2017-01-25 09:26:43 +03:00
* Intended to be used as a generic implementation of the
* & drm_connector_funcs . fill_modes ( ) vfunc for drivers that use the CRTC helpers
* for output mode filtering and detection .
2015-12-04 00:14:14 +03:00
*
2015-12-11 00:13:57 +03:00
* The basic procedure is as follows
*
* 1. All modes currently on the connector ' s modes list are marked as stale
*
* 2. New modes are added to the connector ' s probed_modes list with
* drm_mode_probed_add ( ) . New modes start their life with status as OK .
* Modes are added from a single source using the following priority order .
*
* - debugfs ' override_edid ' ( used for testing only )
* - firmware EDID ( drm_load_edid_firmware ( ) )
2017-01-25 09:26:43 +03:00
* - & drm_connector_helper_funcs . get_modes vfunc
2015-12-11 00:13:57 +03:00
* - if the connector status is connector_status_connected , standard
* VESA DMT modes up to 1024 x768 are automatically added
* ( drm_add_modes_noedid ( ) )
*
* Finally modes specified via the kernel command line ( video = . . . ) are
* added in addition to what the earlier probes produced
* ( drm_helper_probe_add_cmdline_mode ( ) ) . These modes are generated
* using the VESA GTF / CVT formulas .
*
* 3. Modes are moved from the probed_modes list to the modes list . Potential
* duplicates are merged together ( see drm_mode_connector_list_update ( ) ) .
* After this step the probed_modes list will be empty again .
*
* 4. Any non - stale mode on the modes list then undergoes validation
*
* - drm_mode_validate_basic ( ) performs basic sanity checks
* - drm_mode_validate_size ( ) filters out modes larger than @ maxX and @ maxY
* ( if specified )
2017-02-28 01:29:12 +03:00
* - drm_mode_validate_flag ( ) checks the modes against basic connector
* capabilities ( interlace_allowed , doublescan_allowed , stereo_allowed )
2017-01-25 09:26:43 +03:00
* - the optional & drm_connector_helper_funcs . mode_valid helper can perform
* driver and / or hardware specific checks
2015-12-11 00:13:57 +03:00
*
* 5. Any mode whose status is not OK is pruned from the connector ' s modes list ,
* accompanied by a debug message indicating the reason for the mode ' s
* rejection ( see drm_mode_prune_invalid ( ) ) .
2015-12-04 00:14:14 +03:00
*
* Returns :
* The number of modes found on @ connector .
*/
int drm_helper_probe_single_connector_modes ( struct drm_connector * connector ,
uint32_t maxX , uint32_t maxY )
2014-04-10 12:51:11 +04:00
{
struct drm_device * dev = connector - > dev ;
struct drm_display_mode * mode ;
2015-03-11 12:51:06 +03:00
const struct drm_connector_helper_funcs * connector_funcs =
2014-04-10 12:51:11 +04:00
connector - > helper_private ;
2017-04-06 21:55:20 +03:00
int count = 0 , ret ;
2014-04-10 12:51:11 +04:00
int mode_flags = 0 ;
bool verbose_prune = true ;
2015-01-21 10:45:21 +03:00
enum drm_connector_status old_status ;
2017-04-06 21:55:20 +03:00
struct drm_modeset_acquire_ctx ctx ;
2014-04-10 12:51:11 +04:00
WARN_ON ( ! mutex_is_locked ( & dev - > mode_config . mutex ) ) ;
2017-04-06 21:55:20 +03:00
drm_modeset_acquire_init ( & ctx , 0 ) ;
2014-04-10 12:51:11 +04:00
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] \n " , connector - > base . id ,
2014-06-03 15:56:20 +04:00
connector - > name ) ;
2017-04-06 21:55:20 +03:00
retry :
ret = drm_modeset_lock ( & dev - > mode_config . connection_mutex , & ctx ) ;
if ( ret = = - EDEADLK ) {
drm_modeset_backoff ( & ctx ) ;
goto retry ;
} else
WARN_ON ( ret < 0 ) ;
2015-12-10 23:39:08 +03:00
/* set all old modes to the stale state */
2014-04-10 12:51:11 +04:00
list_for_each_entry ( mode , & connector - > modes , head )
2015-12-10 23:39:08 +03:00
mode - > status = MODE_STALE ;
2014-04-10 12:51:11 +04:00
2015-11-19 19:46:50 +03:00
old_status = connector - > status ;
2014-04-10 12:51:11 +04:00
if ( connector - > force ) {
2014-11-04 04:51:45 +03:00
if ( connector - > force = = DRM_FORCE_ON | |
connector - > force = = DRM_FORCE_ON_DIGITAL )
2014-04-10 12:51:11 +04:00
connector - > status = connector_status_connected ;
else
connector - > status = connector_status_disconnected ;
if ( connector - > funcs - > force )
connector - > funcs - > force ( connector ) ;
} else {
2017-04-06 21:55:20 +03:00
ret = drm_helper_probe_detect ( connector , & ctx , true ) ;
if ( ret = = - EDEADLK ) {
drm_modeset_backoff ( & ctx ) ;
goto retry ;
} else if ( WARN ( ret < 0 , " Invalid return value %i for connector detection \n " , ret ) )
ret = connector_status_unknown ;
connector - > status = ret ;
2015-11-19 19:46:50 +03:00
}
/*
* Normally either the driver ' s hpd code or the poll loop should
* pick up any changes and fire the hotplug event . But if
* userspace sneaks in a probe , we might miss a change . Hence
* check here , and if anything changed start the hotplug code .
*/
if ( old_status ! = connector - > status ) {
2015-12-03 15:00:03 +03:00
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] status updated from %s to %s \n " ,
2015-11-19 19:46:50 +03:00
connector - > base . id ,
connector - > name ,
2015-12-03 15:00:03 +03:00
drm_get_connector_status_name ( old_status ) ,
drm_get_connector_status_name ( connector - > status ) ) ;
2015-01-21 10:45:21 +03:00
/*
2015-11-19 19:46:50 +03:00
* The hotplug event code might call into the fb
* helpers , and so expects that we do not hold any
* locks . Fire up the poll struct instead , it will
* disable itself again .
2015-01-21 10:45:21 +03:00
*/
2015-11-19 19:46:50 +03:00
dev - > mode_config . delayed_event = true ;
if ( dev - > mode_config . poll_enabled )
schedule_delayed_work ( & dev - > mode_config . output_poll_work ,
0 ) ;
2014-04-10 12:51:11 +04:00
}
/* Re-enable polling in case the global poll config changed. */
if ( drm_kms_helper_poll ! = dev - > mode_config . poll_running )
2017-01-27 05:04:08 +03:00
drm_kms_helper_poll_enable ( dev ) ;
2014-04-10 12:51:11 +04:00
dev - > mode_config . poll_running = drm_kms_helper_poll ;
if ( connector - > status = = connector_status_disconnected ) {
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] disconnected \n " ,
2014-06-03 15:56:20 +04:00
connector - > base . id , connector - > name ) ;
2014-04-10 12:51:11 +04:00
drm_mode_connector_update_edid_property ( connector , NULL ) ;
verbose_prune = false ;
goto prune ;
}
2015-12-11 00:13:56 +03:00
if ( connector - > override_edid ) {
struct edid * edid = ( struct edid * ) connector - > edid_blob_ptr - > data ;
count = drm_add_edid_modes ( connector , edid ) ;
drm_edid_to_eld ( connector , edid ) ;
} else {
2017-02-17 18:20:51 +03:00
struct edid * edid = drm_load_edid_firmware ( connector ) ;
if ( ! IS_ERR_OR_NULL ( edid ) ) {
drm_mode_connector_update_edid_property ( connector , edid ) ;
count = drm_add_edid_modes ( connector , edid ) ;
drm_edid_to_eld ( connector , edid ) ;
kfree ( edid ) ;
}
2015-12-11 00:13:56 +03:00
if ( count = = 0 )
2014-06-18 20:52:33 +04:00
count = ( * connector_funcs - > get_modes ) ( connector ) ;
}
2014-04-10 12:51:11 +04:00
if ( count = = 0 & & connector - > status = = connector_status_connected )
count = drm_add_modes_noedid ( connector , 1024 , 768 ) ;
2014-08-06 12:08:32 +04:00
count + = drm_helper_probe_add_cmdline_mode ( connector ) ;
2014-04-10 12:51:11 +04:00
if ( count = = 0 )
goto prune ;
2015-12-04 00:14:14 +03:00
drm_mode_connector_list_update ( connector ) ;
2014-04-10 12:51:11 +04:00
if ( connector - > interlace_allowed )
mode_flags | = DRM_MODE_FLAG_INTERLACE ;
if ( connector - > doublescan_allowed )
mode_flags | = DRM_MODE_FLAG_DBLSCAN ;
if ( connector - > stereo_allowed )
mode_flags | = DRM_MODE_FLAG_3D_MASK ;
list_for_each_entry ( mode , & connector - > modes , head ) {
2015-12-04 00:14:09 +03:00
if ( mode - > status = = MODE_OK )
mode - > status = drm_mode_validate_basic ( mode ) ;
2014-12-17 14:56:23 +03:00
if ( mode - > status = = MODE_OK )
mode - > status = drm_mode_validate_size ( mode , maxX , maxY ) ;
2014-12-17 14:56:22 +03:00
if ( mode - > status = = MODE_OK )
mode - > status = drm_mode_validate_flag ( mode , mode_flags ) ;
2014-04-02 14:29:46 +04:00
if ( mode - > status = = MODE_OK & & connector_funcs - > mode_valid )
2014-04-10 12:51:11 +04:00
mode - > status = connector_funcs - > mode_valid ( connector ,
mode ) ;
}
prune :
drm_mode_prune_invalid ( dev , & connector - > modes , verbose_prune ) ;
2017-04-06 21:55:20 +03:00
drm_modeset_drop_locks ( & ctx ) ;
drm_modeset_acquire_fini ( & ctx ) ;
2014-04-10 12:51:11 +04:00
if ( list_empty ( & connector - > modes ) )
return 0 ;
list_for_each_entry ( mode , & connector - > modes , head )
mode - > vrefresh = drm_mode_vrefresh ( mode ) ;
drm_mode_sort ( & connector - > modes ) ;
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] probed modes : \n " , connector - > base . id ,
2014-06-03 15:56:20 +04:00
connector - > name ) ;
2014-04-10 12:51:11 +04:00
list_for_each_entry ( mode , & connector - > modes , head ) {
drm_mode_set_crtcinfo ( mode , CRTC_INTERLACE_HALVE_V ) ;
drm_mode_debug_printmodeline ( mode ) ;
}
return count ;
}
EXPORT_SYMBOL ( drm_helper_probe_single_connector_modes ) ;
/**
* drm_kms_helper_hotplug_event - fire off KMS hotplug events
* @ dev : drm_device whose connector state changed
*
* This function fires off the uevent for userspace and also calls the
* output_poll_changed function , which is most commonly used to inform the fbdev
* emulation code and allow it to update the fbcon output configuration .
*
* Drivers should call this from their hotplug handling code when a change is
* detected . Note that this function does not do any output detection of its
* own , like drm_helper_hpd_irq_event ( ) does - this is assumed to be done by the
* driver already .
*
* This function must be called from process context with no mode
* setting locks held .
*/
void drm_kms_helper_hotplug_event ( struct drm_device * dev )
{
/* send a uevent + call fbdev */
drm_sysfs_hotplug_event ( dev ) ;
if ( dev - > mode_config . funcs - > output_poll_changed )
dev - > mode_config . funcs - > output_poll_changed ( dev ) ;
}
EXPORT_SYMBOL ( drm_kms_helper_hotplug_event ) ;
static void output_poll_execute ( struct work_struct * work )
{
struct delayed_work * delayed_work = to_delayed_work ( work ) ;
struct drm_device * dev = container_of ( delayed_work , struct drm_device , mode_config . output_poll_work ) ;
struct drm_connector * connector ;
2016-12-15 18:58:43 +03:00
struct drm_connector_list_iter conn_iter ;
2014-04-10 12:51:11 +04:00
enum drm_connector_status old_status ;
2015-01-21 10:45:21 +03:00
bool repoll = false , changed ;
/* Pick up any changes detected by the probe functions. */
changed = dev - > mode_config . delayed_event ;
dev - > mode_config . delayed_event = false ;
2014-04-10 12:51:11 +04:00
if ( ! drm_kms_helper_poll )
2015-01-21 10:45:21 +03:00
goto out ;
2014-04-10 12:51:11 +04:00
2016-12-06 14:37:15 +03:00
if ( ! mutex_trylock ( & dev - > mode_config . mutex ) ) {
repoll = true ;
goto out ;
}
2017-02-28 17:46:43 +03:00
drm_connector_list_iter_begin ( dev , & conn_iter ) ;
2016-12-15 18:58:43 +03:00
drm_for_each_connector_iter ( connector , & conn_iter ) {
2014-04-10 12:51:11 +04:00
/* Ignore forced connectors. */
if ( connector - > force )
continue ;
/* Ignore HPD capable connectors and connectors where we don't
* want any hotplug detection at all for polling . */
if ( ! connector - > polled | | connector - > polled = = DRM_CONNECTOR_POLL_HPD )
continue ;
old_status = connector - > status ;
/* if we are connected and don't want to poll for disconnect
skip it */
if ( old_status = = connector_status_connected & &
! ( connector - > polled & DRM_CONNECTOR_POLL_DISCONNECT ) )
continue ;
2015-04-16 15:16:29 +03:00
repoll = true ;
2017-04-06 21:55:20 +03:00
connector - > status = drm_helper_probe_detect ( connector , NULL , false ) ;
2014-04-10 12:51:11 +04:00
if ( old_status ! = connector - > status ) {
const char * old , * new ;
2015-01-21 10:45:22 +03:00
/*
* The poll work sets force = false when calling detect so
* that drivers can avoid to do disruptive tests ( e . g .
* when load detect cycles could cause flickering on
* other , running displays ) . This bears the risk that we
* flip - flop between unknown here in the poll work and
* the real state when userspace forces a full detect
* call after receiving a hotplug event due to this
* change .
*
* Hence clamp an unknown detect status to the old
* value .
*/
if ( connector - > status = = connector_status_unknown ) {
connector - > status = old_status ;
continue ;
}
2014-04-10 12:51:11 +04:00
old = drm_get_connector_status_name ( old_status ) ;
new = drm_get_connector_status_name ( connector - > status ) ;
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] "
" status updated from %s to %s \n " ,
connector - > base . id ,
2014-06-03 15:56:20 +04:00
connector - > name ,
2014-04-10 12:51:11 +04:00
old , new ) ;
changed = true ;
}
}
2017-02-28 17:46:43 +03:00
drm_connector_list_iter_end ( & conn_iter ) ;
2014-04-10 12:51:11 +04:00
mutex_unlock ( & dev - > mode_config . mutex ) ;
2015-01-21 10:45:21 +03:00
out :
2014-04-10 12:51:11 +04:00
if ( changed )
drm_kms_helper_hotplug_event ( dev ) ;
if ( repoll )
schedule_delayed_work ( delayed_work , DRM_OUTPUT_POLL_PERIOD ) ;
}
/**
* drm_kms_helper_poll_disable - disable output polling
* @ dev : drm_device
*
* This function disables the output polling work .
*
* Drivers can call this helper from their device suspend implementation . It is
2017-01-27 05:04:08 +03:00
* not an error to call this even when output polling isn ' t enabled or already
* disabled . Polling is re - enabled by calling drm_kms_helper_poll_enable ( ) .
*
* Note that calls to enable and disable polling must be strictly ordered , which
* is automatically the case when they ' re only call from suspend / resume
* callbacks .
2014-04-10 12:51:11 +04:00
*/
void drm_kms_helper_poll_disable ( struct drm_device * dev )
{
if ( ! dev - > mode_config . poll_enabled )
return ;
cancel_delayed_work_sync ( & dev - > mode_config . output_poll_work ) ;
}
EXPORT_SYMBOL ( drm_kms_helper_poll_disable ) ;
/**
* drm_kms_helper_poll_init - initialize and enable output polling
* @ dev : drm_device
*
* This function intializes and then also enables output polling support for
* @ dev . Drivers which do not have reliable hotplug support in hardware can use
* this helper infrastructure to regularly poll such connectors for changes in
* their connection state .
*
* Drivers can control which connectors are polled by setting the
* DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags . On
* connectors where probing live outputs can result in visual distortion drivers
* should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this .
* Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are
* completely ignored by the polling logic .
*
* Note that a connector can be both polled and probed from the hotplug handler ,
* in case the hotplug interrupt is known to be unreliable .
*/
void drm_kms_helper_poll_init ( struct drm_device * dev )
{
INIT_DELAYED_WORK ( & dev - > mode_config . output_poll_work , output_poll_execute ) ;
dev - > mode_config . poll_enabled = true ;
drm_kms_helper_poll_enable ( dev ) ;
}
EXPORT_SYMBOL ( drm_kms_helper_poll_init ) ;
/**
* drm_kms_helper_poll_fini - disable output polling and clean it up
* @ dev : drm_device
*/
void drm_kms_helper_poll_fini ( struct drm_device * dev )
{
drm_kms_helper_poll_disable ( dev ) ;
}
EXPORT_SYMBOL ( drm_kms_helper_poll_fini ) ;
/**
* drm_helper_hpd_irq_event - hotplug processing
* @ dev : drm_device
*
* Drivers can use this helper function to run a detect cycle on all connectors
* which have the DRM_CONNECTOR_POLL_HPD flag set in their & polled member . All
* other connectors are ignored , which is useful to avoid reprobing fixed
* panels .
*
* This helper function is useful for drivers which can ' t or don ' t track hotplug
* interrupts for each connector .
*
* Drivers which support hotplug interrupts for each connector individually and
* which have a more fine - grained detect logic should bypass this code and
* directly call drm_kms_helper_hotplug_event ( ) in case the connector state
* changed .
*
* This function must be called from process context with no mode
* setting locks held .
*
* Note that a connector can be both polled and probed from the hotplug handler ,
* in case the hotplug interrupt is known to be unreliable .
*/
bool drm_helper_hpd_irq_event ( struct drm_device * dev )
{
struct drm_connector * connector ;
2016-12-15 18:58:43 +03:00
struct drm_connector_list_iter conn_iter ;
2014-04-10 12:51:11 +04:00
enum drm_connector_status old_status ;
bool changed = false ;
if ( ! dev - > mode_config . poll_enabled )
return false ;
mutex_lock ( & dev - > mode_config . mutex ) ;
2017-02-28 17:46:43 +03:00
drm_connector_list_iter_begin ( dev , & conn_iter ) ;
2016-12-15 18:58:43 +03:00
drm_for_each_connector_iter ( connector , & conn_iter ) {
2014-04-10 12:51:11 +04:00
/* Only handle HPD capable connectors. */
if ( ! ( connector - > polled & DRM_CONNECTOR_POLL_HPD ) )
continue ;
old_status = connector - > status ;
2017-04-06 21:55:20 +03:00
connector - > status = drm_helper_probe_detect ( connector , NULL , false ) ;
2014-04-10 12:51:11 +04:00
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] status updated from %s to %s \n " ,
connector - > base . id ,
2014-06-03 15:56:20 +04:00
connector - > name ,
2014-04-10 12:51:11 +04:00
drm_get_connector_status_name ( old_status ) ,
drm_get_connector_status_name ( connector - > status ) ) ;
if ( old_status ! = connector - > status )
changed = true ;
}
2017-02-28 17:46:43 +03:00
drm_connector_list_iter_end ( & conn_iter ) ;
2014-04-10 12:51:11 +04:00
mutex_unlock ( & dev - > mode_config . mutex ) ;
if ( changed )
drm_kms_helper_hotplug_event ( dev ) ;
return changed ;
}
EXPORT_SYMBOL ( drm_helper_hpd_irq_event ) ;