2016-08-12 22:48:50 +02:00
/*
* Copyright ( c ) 2016 Intel Corporation
*
* 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 .
*/
# include <drm/drmP.h>
# include <drm/drm_connector.h>
# include <drm/drm_edid.h>
# include "drm_crtc_internal.h"
# include "drm_internal.h"
2016-08-12 22:48:53 +02:00
/**
* DOC : overview
*
* In DRM connectors are the general abstraction for display sinks , and include
* als fixed panels or anything else that can display pixels in some form . As
* opposed to all other KMS objects representing hardware ( like CRTC , encoder or
* plane abstractions ) connectors can be hotplugged and unplugged at runtime .
* Hence they are reference - counted using drm_connector_reference ( ) and
* drm_connector_unreference ( ) .
*
* KMS driver must create , initialize , register and attach at a struct
* & drm_connector for each such sink . The instance is created as other KMS
* objects and initialized by setting the following fields .
*
* The connector is then registered with a call to drm_connector_init ( ) with a
* pointer to the connector functions and a connector type , and exposed through
* sysfs with a call to drm_connector_register ( ) .
*
* Connectors must be attached to an encoder to be used . For devices that map
* connectors to encoders 1 : 1 , the connector should be attached at
* initialization time with a call to drm_mode_connector_attach_encoder ( ) . The
* driver must also set the struct & drm_connector encoder field to point to the
* attached encoder .
*
* For connectors which are not fixed ( like built - in panels ) the driver needs to
* support hotplug notifications . The simplest way to do that is by using the
* probe helpers , see drm_kms_helper_poll_init ( ) for connectors which don ' t have
* hardware support for hotplug interrupts . Connectors with hardware hotplug
* support can instead use e . g . drm_helper_hpd_irq_event ( ) .
*/
2016-08-12 22:48:50 +02:00
struct drm_conn_prop_enum_list {
int type ;
const char * name ;
struct ida ida ;
} ;
/*
* Connector and encoder types .
*/
static struct drm_conn_prop_enum_list drm_connector_enum_list [ ] = {
{ DRM_MODE_CONNECTOR_Unknown , " Unknown " } ,
{ DRM_MODE_CONNECTOR_VGA , " VGA " } ,
{ DRM_MODE_CONNECTOR_DVII , " DVI-I " } ,
{ DRM_MODE_CONNECTOR_DVID , " DVI-D " } ,
{ DRM_MODE_CONNECTOR_DVIA , " DVI-A " } ,
{ DRM_MODE_CONNECTOR_Composite , " Composite " } ,
{ DRM_MODE_CONNECTOR_SVIDEO , " SVIDEO " } ,
{ DRM_MODE_CONNECTOR_LVDS , " LVDS " } ,
{ DRM_MODE_CONNECTOR_Component , " Component " } ,
{ DRM_MODE_CONNECTOR_9PinDIN , " DIN " } ,
{ DRM_MODE_CONNECTOR_DisplayPort , " DP " } ,
{ DRM_MODE_CONNECTOR_HDMIA , " HDMI-A " } ,
{ DRM_MODE_CONNECTOR_HDMIB , " HDMI-B " } ,
{ DRM_MODE_CONNECTOR_TV , " TV " } ,
{ DRM_MODE_CONNECTOR_eDP , " eDP " } ,
{ DRM_MODE_CONNECTOR_VIRTUAL , " Virtual " } ,
{ DRM_MODE_CONNECTOR_DSI , " DSI " } ,
{ DRM_MODE_CONNECTOR_DPI , " DPI " } ,
} ;
void drm_connector_ida_init ( void )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( drm_connector_enum_list ) ; i + + )
ida_init ( & drm_connector_enum_list [ i ] . ida ) ;
}
void drm_connector_ida_destroy ( void )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( drm_connector_enum_list ) ; i + + )
ida_destroy ( & drm_connector_enum_list [ i ] . ida ) ;
}
/**
* drm_connector_get_cmdline_mode - reads the user ' s cmdline mode
* @ connector : connector to quwery
*
2016-08-12 22:48:53 +02:00
* The kernel supports per - connector configuration of its consoles through
2016-08-12 22:48:50 +02:00
* use of the video = parameter . This function parses that option and
* extracts the user ' s specified mode ( or enable / disable status ) for a
* particular connector . This is typically only used during the early fbdev
* setup .
*/
static void drm_connector_get_cmdline_mode ( struct drm_connector * connector )
{
struct drm_cmdline_mode * mode = & connector - > cmdline_mode ;
char * option = NULL ;
if ( fb_get_options ( connector - > name , & option ) )
return ;
if ( ! drm_mode_parse_command_line_for_connector ( option ,
connector ,
mode ) )
return ;
if ( mode - > force ) {
const char * s ;
switch ( mode - > force ) {
case DRM_FORCE_OFF :
s = " OFF " ;
break ;
case DRM_FORCE_ON_DIGITAL :
s = " ON - dig " ;
break ;
default :
case DRM_FORCE_ON :
s = " ON " ;
break ;
}
DRM_INFO ( " forcing %s connector %s \n " , connector - > name , s ) ;
connector - > force = mode - > force ;
}
DRM_DEBUG_KMS ( " cmdline mode for connector %s %dx%d@%dHz%s%s%s \n " ,
connector - > name ,
mode - > xres , mode - > yres ,
mode - > refresh_specified ? mode - > refresh : 60 ,
mode - > rb ? " reduced blanking " : " " ,
mode - > margins ? " with margins " : " " ,
mode - > interlace ? " interlaced " : " " ) ;
}
static void drm_connector_free ( struct kref * kref )
{
struct drm_connector * connector =
container_of ( kref , struct drm_connector , base . refcount ) ;
struct drm_device * dev = connector - > dev ;
drm_mode_object_unregister ( dev , & connector - > base ) ;
connector - > funcs - > destroy ( connector ) ;
}
/**
* drm_connector_init - Init a preallocated connector
* @ dev : DRM device
* @ connector : the connector to init
* @ funcs : callbacks for this connector
* @ connector_type : user visible type of the connector
*
* Initialises a preallocated connector . Connectors should be
* subclassed as part of driver connector objects .
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_connector_init ( struct drm_device * dev ,
struct drm_connector * connector ,
const struct drm_connector_funcs * funcs ,
int connector_type )
{
struct drm_mode_config * config = & dev - > mode_config ;
int ret ;
struct ida * connector_ida =
& drm_connector_enum_list [ connector_type ] . ida ;
drm_modeset_lock_all ( dev ) ;
ret = drm_mode_object_get_reg ( dev , & connector - > base ,
DRM_MODE_OBJECT_CONNECTOR ,
false , drm_connector_free ) ;
if ( ret )
goto out_unlock ;
connector - > base . properties = & connector - > properties ;
connector - > dev = dev ;
connector - > funcs = funcs ;
ret = ida_simple_get ( & config - > connector_ida , 0 , 0 , GFP_KERNEL ) ;
if ( ret < 0 )
goto out_put ;
connector - > index = ret ;
ret = 0 ;
connector - > connector_type = connector_type ;
connector - > connector_type_id =
ida_simple_get ( connector_ida , 1 , 0 , GFP_KERNEL ) ;
if ( connector - > connector_type_id < 0 ) {
ret = connector - > connector_type_id ;
goto out_put_id ;
}
connector - > name =
kasprintf ( GFP_KERNEL , " %s-%d " ,
drm_connector_enum_list [ connector_type ] . name ,
connector - > connector_type_id ) ;
if ( ! connector - > name ) {
ret = - ENOMEM ;
goto out_put_type_id ;
}
INIT_LIST_HEAD ( & connector - > probed_modes ) ;
INIT_LIST_HEAD ( & connector - > modes ) ;
connector - > edid_blob_ptr = NULL ;
connector - > status = connector_status_unknown ;
drm_connector_get_cmdline_mode ( connector ) ;
/* We should add connectors at the end to avoid upsetting the connector
* index too much . */
list_add_tail ( & connector - > head , & config - > connector_list ) ;
config - > num_connector + + ;
if ( connector_type ! = DRM_MODE_CONNECTOR_VIRTUAL )
drm_object_attach_property ( & connector - > base ,
config - > edid_property ,
0 ) ;
drm_object_attach_property ( & connector - > base ,
config - > dpms_property , 0 ) ;
if ( drm_core_check_feature ( dev , DRIVER_ATOMIC ) ) {
drm_object_attach_property ( & connector - > base , config - > prop_crtc_id , 0 ) ;
}
connector - > debugfs_entry = NULL ;
out_put_type_id :
if ( ret )
2016-10-02 08:01:22 +02:00
ida_simple_remove ( connector_ida , connector - > connector_type_id ) ;
2016-08-12 22:48:50 +02:00
out_put_id :
if ( ret )
2016-10-02 08:01:22 +02:00
ida_simple_remove ( & config - > connector_ida , connector - > index ) ;
2016-08-12 22:48:50 +02:00
out_put :
if ( ret )
drm_mode_object_unregister ( dev , & connector - > base ) ;
out_unlock :
drm_modeset_unlock_all ( dev ) ;
return ret ;
}
EXPORT_SYMBOL ( drm_connector_init ) ;
/**
* drm_mode_connector_attach_encoder - attach a connector to an encoder
* @ connector : connector to attach
* @ encoder : encoder to attach @ connector to
*
* This function links up a connector to an encoder . Note that the routing
* restrictions between encoders and crtcs are exposed to userspace through the
* possible_clones and possible_crtcs bitmasks .
*
* Returns :
* Zero on success , negative errno on failure .
*/
int drm_mode_connector_attach_encoder ( struct drm_connector * connector ,
struct drm_encoder * encoder )
{
int i ;
/*
* In the past , drivers have attempted to model the static association
* of connector to encoder in simple connector / encoder devices using a
* direct assignment of connector - > encoder = encoder . This connection
* is a logical one and the responsibility of the core , so drivers are
* expected not to mess with this .
*
* Note that the error return should ' ve been enough here , but a large
* majority of drivers ignores the return value , so add in a big WARN
* to get people ' s attention .
*/
if ( WARN_ON ( connector - > encoder ) )
return - EINVAL ;
for ( i = 0 ; i < DRM_CONNECTOR_MAX_ENCODER ; i + + ) {
if ( connector - > encoder_ids [ i ] = = 0 ) {
connector - > encoder_ids [ i ] = encoder - > base . id ;
return 0 ;
}
}
return - ENOMEM ;
}
EXPORT_SYMBOL ( drm_mode_connector_attach_encoder ) ;
static void drm_mode_remove ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
list_del ( & mode - > head ) ;
drm_mode_destroy ( connector - > dev , mode ) ;
}
/**
* drm_connector_cleanup - cleans up an initialised connector
* @ connector : connector to cleanup
*
* Cleans up the connector but doesn ' t free the object .
*/
void drm_connector_cleanup ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
struct drm_display_mode * mode , * t ;
/* The connector should have been removed from userspace long before
* it is finally destroyed .
*/
if ( WARN_ON ( connector - > registered ) )
drm_connector_unregister ( connector ) ;
if ( connector - > tile_group ) {
drm_mode_put_tile_group ( dev , connector - > tile_group ) ;
connector - > tile_group = NULL ;
}
list_for_each_entry_safe ( mode , t , & connector - > probed_modes , head )
drm_mode_remove ( connector , mode ) ;
list_for_each_entry_safe ( mode , t , & connector - > modes , head )
drm_mode_remove ( connector , mode ) ;
2016-10-07 09:27:41 +02:00
ida_simple_remove ( & drm_connector_enum_list [ connector - > connector_type ] . ida ,
connector - > connector_type_id ) ;
2016-08-12 22:48:50 +02:00
2016-10-07 09:27:41 +02:00
ida_simple_remove ( & dev - > mode_config . connector_ida ,
connector - > index ) ;
2016-08-12 22:48:50 +02:00
kfree ( connector - > display_info . bus_formats ) ;
drm_mode_object_unregister ( dev , & connector - > base ) ;
kfree ( connector - > name ) ;
connector - > name = NULL ;
list_del ( & connector - > head ) ;
dev - > mode_config . num_connector - - ;
WARN_ON ( connector - > state & & ! connector - > funcs - > atomic_destroy_state ) ;
if ( connector - > state & & connector - > funcs - > atomic_destroy_state )
connector - > funcs - > atomic_destroy_state ( connector ,
connector - > state ) ;
memset ( connector , 0 , sizeof ( * connector ) ) ;
}
EXPORT_SYMBOL ( drm_connector_cleanup ) ;
/**
* drm_connector_register - register a connector
* @ connector : the connector to register
*
* Register userspace interfaces for a connector
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_connector_register ( struct drm_connector * connector )
{
int ret ;
if ( connector - > registered )
return 0 ;
ret = drm_sysfs_connector_add ( connector ) ;
if ( ret )
return ret ;
ret = drm_debugfs_connector_add ( connector ) ;
if ( ret ) {
goto err_sysfs ;
}
if ( connector - > funcs - > late_register ) {
ret = connector - > funcs - > late_register ( connector ) ;
if ( ret )
goto err_debugfs ;
}
drm_mode_object_register ( connector - > dev , & connector - > base ) ;
connector - > registered = true ;
return 0 ;
err_debugfs :
drm_debugfs_connector_remove ( connector ) ;
err_sysfs :
drm_sysfs_connector_remove ( connector ) ;
return ret ;
}
EXPORT_SYMBOL ( drm_connector_register ) ;
/**
* drm_connector_unregister - unregister a connector
* @ connector : the connector to unregister
*
* Unregister userspace interfaces for a connector
*/
void drm_connector_unregister ( struct drm_connector * connector )
{
if ( ! connector - > registered )
return ;
if ( connector - > funcs - > early_unregister )
connector - > funcs - > early_unregister ( connector ) ;
drm_sysfs_connector_remove ( connector ) ;
drm_debugfs_connector_remove ( connector ) ;
connector - > registered = false ;
}
EXPORT_SYMBOL ( drm_connector_unregister ) ;
void drm_connector_unregister_all ( struct drm_device * dev )
{
struct drm_connector * connector ;
/* FIXME: taking the mode config mutex ends up in a clash with sysfs */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head )
drm_connector_unregister ( connector ) ;
}
int drm_connector_register_all ( struct drm_device * dev )
{
struct drm_connector * connector ;
int ret ;
/* FIXME: taking the mode config mutex ends up in a clash with
* fbcon / backlight registration */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
ret = drm_connector_register ( connector ) ;
if ( ret )
goto err ;
}
return 0 ;
err :
mutex_unlock ( & dev - > mode_config . mutex ) ;
drm_connector_unregister_all ( dev ) ;
return ret ;
}
/**
* drm_get_connector_status_name - return a string for connector status
* @ status : connector status to compute name of
*
* In contrast to the other drm_get_ * _name functions this one here returns a
* const pointer and hence is threadsafe .
*/
const char * drm_get_connector_status_name ( enum drm_connector_status status )
{
if ( status = = connector_status_connected )
return " connected " ;
else if ( status = = connector_status_disconnected )
return " disconnected " ;
else
return " unknown " ;
}
EXPORT_SYMBOL ( drm_get_connector_status_name ) ;
static const struct drm_prop_enum_list drm_subpixel_enum_list [ ] = {
{ SubPixelUnknown , " Unknown " } ,
{ SubPixelHorizontalRGB , " Horizontal RGB " } ,
{ SubPixelHorizontalBGR , " Horizontal BGR " } ,
{ SubPixelVerticalRGB , " Vertical RGB " } ,
{ SubPixelVerticalBGR , " Vertical BGR " } ,
{ SubPixelNone , " None " } ,
} ;
/**
* drm_get_subpixel_order_name - return a string for a given subpixel enum
* @ order : enum of subpixel_order
*
* Note you could abuse this and return something out of bounds , but that
* would be a caller error . No unscrubbed user data should make it here .
*/
const char * drm_get_subpixel_order_name ( enum subpixel_order order )
{
return drm_subpixel_enum_list [ order ] . name ;
}
EXPORT_SYMBOL ( drm_get_subpixel_order_name ) ;
static const struct drm_prop_enum_list drm_dpms_enum_list [ ] = {
{ DRM_MODE_DPMS_ON , " On " } ,
{ DRM_MODE_DPMS_STANDBY , " Standby " } ,
{ DRM_MODE_DPMS_SUSPEND , " Suspend " } ,
{ DRM_MODE_DPMS_OFF , " Off " }
} ;
DRM_ENUM_NAME_FN ( drm_get_dpms_name , drm_dpms_enum_list )
2016-08-12 22:48:55 +02:00
/**
* drm_display_info_set_bus_formats - set the supported bus formats
* @ info : display info to store bus formats in
* @ formats : array containing the supported bus formats
* @ num_formats : the number of entries in the fmts array
*
* Store the supported bus formats in display info structure .
* See MEDIA_BUS_FMT_ * definitions in include / uapi / linux / media - bus - format . h for
* a full list of available formats .
*/
int drm_display_info_set_bus_formats ( struct drm_display_info * info ,
const u32 * formats ,
unsigned int num_formats )
{
u32 * fmts = NULL ;
if ( ! formats & & num_formats )
return - EINVAL ;
if ( formats & & num_formats ) {
fmts = kmemdup ( formats , sizeof ( * formats ) * num_formats ,
GFP_KERNEL ) ;
if ( ! fmts )
return - ENOMEM ;
}
kfree ( info - > bus_formats ) ;
info - > bus_formats = fmts ;
info - > num_bus_formats = num_formats ;
return 0 ;
}
EXPORT_SYMBOL ( drm_display_info_set_bus_formats ) ;
2016-08-12 22:48:50 +02:00
/* Optional connector properties. */
static const struct drm_prop_enum_list drm_scaling_mode_enum_list [ ] = {
{ DRM_MODE_SCALE_NONE , " None " } ,
{ DRM_MODE_SCALE_FULLSCREEN , " Full " } ,
{ DRM_MODE_SCALE_CENTER , " Center " } ,
{ DRM_MODE_SCALE_ASPECT , " Full aspect " } ,
} ;
static const struct drm_prop_enum_list drm_aspect_ratio_enum_list [ ] = {
{ DRM_MODE_PICTURE_ASPECT_NONE , " Automatic " } ,
{ DRM_MODE_PICTURE_ASPECT_4_3 , " 4:3 " } ,
{ DRM_MODE_PICTURE_ASPECT_16_9 , " 16:9 " } ,
} ;
static const struct drm_prop_enum_list drm_dvi_i_select_enum_list [ ] = {
{ DRM_MODE_SUBCONNECTOR_Automatic , " Automatic " } , /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_DVID , " DVI-D " } , /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_DVIA , " DVI-A " } , /* DVI-I */
} ;
DRM_ENUM_NAME_FN ( drm_get_dvi_i_select_name , drm_dvi_i_select_enum_list )
static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list [ ] = {
{ DRM_MODE_SUBCONNECTOR_Unknown , " Unknown " } , /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_DVID , " DVI-D " } , /* DVI-I */
{ DRM_MODE_SUBCONNECTOR_DVIA , " DVI-A " } , /* DVI-I */
} ;
DRM_ENUM_NAME_FN ( drm_get_dvi_i_subconnector_name ,
drm_dvi_i_subconnector_enum_list )
static const struct drm_prop_enum_list drm_tv_select_enum_list [ ] = {
{ DRM_MODE_SUBCONNECTOR_Automatic , " Automatic " } , /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Composite , " Composite " } , /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SVIDEO , " SVIDEO " } , /* TV-out */
{ DRM_MODE_SUBCONNECTOR_Component , " Component " } , /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SCART , " SCART " } , /* TV-out */
} ;
DRM_ENUM_NAME_FN ( drm_get_tv_select_name , drm_tv_select_enum_list )
static const struct drm_prop_enum_list drm_tv_subconnector_enum_list [ ] = {
{ DRM_MODE_SUBCONNECTOR_Unknown , " Unknown " } , /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Composite , " Composite " } , /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SVIDEO , " SVIDEO " } , /* TV-out */
{ DRM_MODE_SUBCONNECTOR_Component , " Component " } , /* TV-out */
{ DRM_MODE_SUBCONNECTOR_SCART , " SCART " } , /* TV-out */
} ;
DRM_ENUM_NAME_FN ( drm_get_tv_subconnector_name ,
drm_tv_subconnector_enum_list )
int drm_connector_create_standard_properties ( struct drm_device * dev )
{
struct drm_property * prop ;
prop = drm_property_create ( dev , DRM_MODE_PROP_BLOB |
DRM_MODE_PROP_IMMUTABLE ,
" EDID " , 0 ) ;
if ( ! prop )
return - ENOMEM ;
dev - > mode_config . edid_property = prop ;
prop = drm_property_create_enum ( dev , 0 ,
" DPMS " , drm_dpms_enum_list ,
ARRAY_SIZE ( drm_dpms_enum_list ) ) ;
if ( ! prop )
return - ENOMEM ;
dev - > mode_config . dpms_property = prop ;
prop = drm_property_create ( dev ,
DRM_MODE_PROP_BLOB |
DRM_MODE_PROP_IMMUTABLE ,
" PATH " , 0 ) ;
if ( ! prop )
return - ENOMEM ;
dev - > mode_config . path_property = prop ;
prop = drm_property_create ( dev ,
DRM_MODE_PROP_BLOB |
DRM_MODE_PROP_IMMUTABLE ,
" TILE " , 0 ) ;
if ( ! prop )
return - ENOMEM ;
dev - > mode_config . tile_property = prop ;
return 0 ;
}
/**
* drm_mode_create_dvi_i_properties - create DVI - I specific connector properties
* @ dev : DRM device
*
* Called by a driver the first time a DVI - I connector is made .
*/
int drm_mode_create_dvi_i_properties ( struct drm_device * dev )
{
struct drm_property * dvi_i_selector ;
struct drm_property * dvi_i_subconnector ;
if ( dev - > mode_config . dvi_i_select_subconnector_property )
return 0 ;
dvi_i_selector =
drm_property_create_enum ( dev , 0 ,
" select subconnector " ,
drm_dvi_i_select_enum_list ,
ARRAY_SIZE ( drm_dvi_i_select_enum_list ) ) ;
dev - > mode_config . dvi_i_select_subconnector_property = dvi_i_selector ;
dvi_i_subconnector = drm_property_create_enum ( dev , DRM_MODE_PROP_IMMUTABLE ,
" subconnector " ,
drm_dvi_i_subconnector_enum_list ,
ARRAY_SIZE ( drm_dvi_i_subconnector_enum_list ) ) ;
dev - > mode_config . dvi_i_subconnector_property = dvi_i_subconnector ;
return 0 ;
}
EXPORT_SYMBOL ( drm_mode_create_dvi_i_properties ) ;
/**
* drm_create_tv_properties - create TV specific connector properties
* @ dev : DRM device
* @ num_modes : number of different TV formats ( modes ) supported
* @ modes : array of pointers to strings containing name of each format
*
* Called by a driver ' s TV initialization routine , this function creates
* the TV specific connector properties for a given device . Caller is
* responsible for allocating a list of format names and passing them to
* this routine .
*/
int drm_mode_create_tv_properties ( struct drm_device * dev ,
unsigned int num_modes ,
const char * const modes [ ] )
{
struct drm_property * tv_selector ;
struct drm_property * tv_subconnector ;
unsigned int i ;
if ( dev - > mode_config . tv_select_subconnector_property )
return 0 ;
/*
* Basic connector properties
*/
tv_selector = drm_property_create_enum ( dev , 0 ,
" select subconnector " ,
drm_tv_select_enum_list ,
ARRAY_SIZE ( drm_tv_select_enum_list ) ) ;
if ( ! tv_selector )
goto nomem ;
dev - > mode_config . tv_select_subconnector_property = tv_selector ;
tv_subconnector =
drm_property_create_enum ( dev , DRM_MODE_PROP_IMMUTABLE ,
" subconnector " ,
drm_tv_subconnector_enum_list ,
ARRAY_SIZE ( drm_tv_subconnector_enum_list ) ) ;
if ( ! tv_subconnector )
goto nomem ;
dev - > mode_config . tv_subconnector_property = tv_subconnector ;
/*
* Other , TV specific properties : margins & TV modes .
*/
dev - > mode_config . tv_left_margin_property =
drm_property_create_range ( dev , 0 , " left margin " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_left_margin_property )
goto nomem ;
dev - > mode_config . tv_right_margin_property =
drm_property_create_range ( dev , 0 , " right margin " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_right_margin_property )
goto nomem ;
dev - > mode_config . tv_top_margin_property =
drm_property_create_range ( dev , 0 , " top margin " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_top_margin_property )
goto nomem ;
dev - > mode_config . tv_bottom_margin_property =
drm_property_create_range ( dev , 0 , " bottom margin " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_bottom_margin_property )
goto nomem ;
dev - > mode_config . tv_mode_property =
drm_property_create ( dev , DRM_MODE_PROP_ENUM ,
" mode " , num_modes ) ;
if ( ! dev - > mode_config . tv_mode_property )
goto nomem ;
for ( i = 0 ; i < num_modes ; i + + )
drm_property_add_enum ( dev - > mode_config . tv_mode_property , i ,
i , modes [ i ] ) ;
dev - > mode_config . tv_brightness_property =
drm_property_create_range ( dev , 0 , " brightness " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_brightness_property )
goto nomem ;
dev - > mode_config . tv_contrast_property =
drm_property_create_range ( dev , 0 , " contrast " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_contrast_property )
goto nomem ;
dev - > mode_config . tv_flicker_reduction_property =
drm_property_create_range ( dev , 0 , " flicker reduction " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_flicker_reduction_property )
goto nomem ;
dev - > mode_config . tv_overscan_property =
drm_property_create_range ( dev , 0 , " overscan " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_overscan_property )
goto nomem ;
dev - > mode_config . tv_saturation_property =
drm_property_create_range ( dev , 0 , " saturation " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_saturation_property )
goto nomem ;
dev - > mode_config . tv_hue_property =
drm_property_create_range ( dev , 0 , " hue " , 0 , 100 ) ;
if ( ! dev - > mode_config . tv_hue_property )
goto nomem ;
return 0 ;
nomem :
return - ENOMEM ;
}
EXPORT_SYMBOL ( drm_mode_create_tv_properties ) ;
/**
* drm_mode_create_scaling_mode_property - create scaling mode property
* @ dev : DRM device
*
* Called by a driver the first time it ' s needed , must be attached to desired
* connectors .
*/
int drm_mode_create_scaling_mode_property ( struct drm_device * dev )
{
struct drm_property * scaling_mode ;
if ( dev - > mode_config . scaling_mode_property )
return 0 ;
scaling_mode =
drm_property_create_enum ( dev , 0 , " scaling mode " ,
drm_scaling_mode_enum_list ,
ARRAY_SIZE ( drm_scaling_mode_enum_list ) ) ;
dev - > mode_config . scaling_mode_property = scaling_mode ;
return 0 ;
}
EXPORT_SYMBOL ( drm_mode_create_scaling_mode_property ) ;
/**
* drm_mode_create_aspect_ratio_property - create aspect ratio property
* @ dev : DRM device
*
* Called by a driver the first time it ' s needed , must be attached to desired
* connectors .
*
* Returns :
* Zero on success , negative errno on failure .
*/
int drm_mode_create_aspect_ratio_property ( struct drm_device * dev )
{
if ( dev - > mode_config . aspect_ratio_property )
return 0 ;
dev - > mode_config . aspect_ratio_property =
drm_property_create_enum ( dev , 0 , " aspect ratio " ,
drm_aspect_ratio_enum_list ,
ARRAY_SIZE ( drm_aspect_ratio_enum_list ) ) ;
if ( dev - > mode_config . aspect_ratio_property = = NULL )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL ( drm_mode_create_aspect_ratio_property ) ;
/**
* drm_mode_create_suggested_offset_properties - create suggests offset properties
* @ dev : DRM device
*
* Create the the suggested x / y offset property for connectors .
*/
int drm_mode_create_suggested_offset_properties ( struct drm_device * dev )
{
if ( dev - > mode_config . suggested_x_property & & dev - > mode_config . suggested_y_property )
return 0 ;
dev - > mode_config . suggested_x_property =
drm_property_create_range ( dev , DRM_MODE_PROP_IMMUTABLE , " suggested X " , 0 , 0xffffffff ) ;
dev - > mode_config . suggested_y_property =
drm_property_create_range ( dev , DRM_MODE_PROP_IMMUTABLE , " suggested Y " , 0 , 0xffffffff ) ;
if ( dev - > mode_config . suggested_x_property = = NULL | |
dev - > mode_config . suggested_y_property = = NULL )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL ( drm_mode_create_suggested_offset_properties ) ;
/**
* drm_mode_connector_set_path_property - set tile property on connector
* @ connector : connector to set property on .
* @ path : path to use for property ; must not be NULL .
*
* This creates a property to expose to userspace to specify a
* connector path . This is mainly used for DisplayPort MST where
* connectors have a topology and we want to allow userspace to give
* them more meaningful names .
*
* Returns :
* Zero on success , negative errno on failure .
*/
int drm_mode_connector_set_path_property ( struct drm_connector * connector ,
const char * path )
{
struct drm_device * dev = connector - > dev ;
int ret ;
ret = drm_property_replace_global_blob ( dev ,
& connector - > path_blob_ptr ,
strlen ( path ) + 1 ,
path ,
& connector - > base ,
dev - > mode_config . path_property ) ;
return ret ;
}
EXPORT_SYMBOL ( drm_mode_connector_set_path_property ) ;
/**
* drm_mode_connector_set_tile_property - set tile property on connector
* @ connector : connector to set property on .
*
* This looks up the tile information for a connector , and creates a
* property for userspace to parse if it exists . The property is of
* the form of 8 integers using ' : ' as a separator .
*
* Returns :
* Zero on success , errno on failure .
*/
int drm_mode_connector_set_tile_property ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
char tile [ 256 ] ;
int ret ;
if ( ! connector - > has_tile ) {
ret = drm_property_replace_global_blob ( dev ,
& connector - > tile_blob_ptr ,
0 ,
NULL ,
& connector - > base ,
dev - > mode_config . tile_property ) ;
return ret ;
}
snprintf ( tile , 256 , " %d:%d:%d:%d:%d:%d:%d:%d " ,
connector - > tile_group - > id , connector - > tile_is_single_monitor ,
connector - > num_h_tile , connector - > num_v_tile ,
connector - > tile_h_loc , connector - > tile_v_loc ,
connector - > tile_h_size , connector - > tile_v_size ) ;
ret = drm_property_replace_global_blob ( dev ,
& connector - > tile_blob_ptr ,
strlen ( tile ) + 1 ,
tile ,
& connector - > base ,
dev - > mode_config . tile_property ) ;
return ret ;
}
EXPORT_SYMBOL ( drm_mode_connector_set_tile_property ) ;
/**
* drm_mode_connector_update_edid_property - update the edid property of a connector
* @ connector : drm connector
* @ edid : new value of the edid property
*
* This function creates a new blob modeset object and assigns its id to the
* connector ' s edid property .
*
* Returns :
* Zero on success , negative errno on failure .
*/
int drm_mode_connector_update_edid_property ( struct drm_connector * connector ,
const struct edid * edid )
{
struct drm_device * dev = connector - > dev ;
size_t size = 0 ;
int ret ;
/* ignore requests to set edid when overridden */
if ( connector - > override_edid )
return 0 ;
if ( edid )
size = EDID_LENGTH * ( 1 + edid - > extensions ) ;
ret = drm_property_replace_global_blob ( dev ,
& connector - > edid_blob_ptr ,
size ,
edid ,
& connector - > base ,
dev - > mode_config . edid_property ) ;
return ret ;
}
EXPORT_SYMBOL ( drm_mode_connector_update_edid_property ) ;
int drm_mode_connector_set_obj_prop ( struct drm_mode_object * obj ,
struct drm_property * property ,
uint64_t value )
{
int ret = - EINVAL ;
struct drm_connector * connector = obj_to_connector ( obj ) ;
/* Do DPMS ourselves */
if ( property = = connector - > dev - > mode_config . dpms_property ) {
ret = ( * connector - > funcs - > dpms ) ( connector , ( int ) value ) ;
} else if ( connector - > funcs - > set_property )
ret = connector - > funcs - > set_property ( connector , property , value ) ;
/* store the property value if successful */
if ( ! ret )
drm_object_property_set_value ( & connector - > base , property , value ) ;
return ret ;
}
int drm_mode_connector_property_set_ioctl ( struct drm_device * dev ,
void * data , struct drm_file * file_priv )
{
struct drm_mode_connector_set_property * conn_set_prop = data ;
struct drm_mode_obj_set_property obj_set_prop = {
. value = conn_set_prop - > value ,
. prop_id = conn_set_prop - > prop_id ,
. obj_id = conn_set_prop - > connector_id ,
. obj_type = DRM_MODE_OBJECT_CONNECTOR
} ;
/* It does all the locking and checking we need */
return drm_mode_obj_set_property_ioctl ( dev , & obj_set_prop , file_priv ) ;
}
static struct drm_encoder * drm_connector_get_encoder ( struct drm_connector * connector )
{
/* For atomic drivers only state objects are synchronously updated and
* protected by modeset locks , so check those first . */
if ( connector - > state )
return connector - > state - > best_encoder ;
return connector - > encoder ;
}
static bool drm_mode_expose_to_userspace ( const struct drm_display_mode * mode ,
const struct drm_file * file_priv )
{
/*
* If user - space hasn ' t configured the driver to expose the stereo 3 D
* modes , don ' t expose them .
*/
if ( ! file_priv - > stereo_allowed & & drm_mode_is_stereo ( mode ) )
return false ;
return true ;
}
int drm_mode_getconnector ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_mode_get_connector * out_resp = data ;
struct drm_connector * connector ;
struct drm_encoder * encoder ;
struct drm_display_mode * mode ;
int mode_count = 0 ;
int encoders_count = 0 ;
int ret = 0 ;
int copied = 0 ;
int i ;
struct drm_mode_modeinfo u_mode ;
struct drm_mode_modeinfo __user * mode_ptr ;
uint32_t __user * encoder_ptr ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
memset ( & u_mode , 0 , sizeof ( struct drm_mode_modeinfo ) ) ;
mutex_lock ( & dev - > mode_config . mutex ) ;
connector = drm_connector_lookup ( dev , out_resp - > connector_id ) ;
if ( ! connector ) {
ret = - ENOENT ;
goto out_unlock ;
}
for ( i = 0 ; i < DRM_CONNECTOR_MAX_ENCODER ; i + + )
if ( connector - > encoder_ids [ i ] ! = 0 )
encoders_count + + ;
if ( out_resp - > count_modes = = 0 ) {
connector - > funcs - > fill_modes ( connector ,
dev - > mode_config . max_width ,
dev - > mode_config . max_height ) ;
}
/* delayed so we get modes regardless of pre-fill_modes state */
list_for_each_entry ( mode , & connector - > modes , head )
if ( drm_mode_expose_to_userspace ( mode , file_priv ) )
mode_count + + ;
out_resp - > connector_id = connector - > base . id ;
out_resp - > connector_type = connector - > connector_type ;
out_resp - > connector_type_id = connector - > connector_type_id ;
out_resp - > mm_width = connector - > display_info . width_mm ;
out_resp - > mm_height = connector - > display_info . height_mm ;
out_resp - > subpixel = connector - > display_info . subpixel_order ;
out_resp - > connection = connector - > status ;
drm_modeset_lock ( & dev - > mode_config . connection_mutex , NULL ) ;
encoder = drm_connector_get_encoder ( connector ) ;
if ( encoder )
out_resp - > encoder_id = encoder - > base . id ;
else
out_resp - > encoder_id = 0 ;
/*
* This ioctl is called twice , once to determine how much space is
* needed , and the 2 nd time to fill it .
*/
if ( ( out_resp - > count_modes > = mode_count ) & & mode_count ) {
copied = 0 ;
mode_ptr = ( struct drm_mode_modeinfo __user * ) ( unsigned long ) out_resp - > modes_ptr ;
list_for_each_entry ( mode , & connector - > modes , head ) {
if ( ! drm_mode_expose_to_userspace ( mode , file_priv ) )
continue ;
drm_mode_convert_to_umode ( & u_mode , mode ) ;
if ( copy_to_user ( mode_ptr + copied ,
& u_mode , sizeof ( u_mode ) ) ) {
ret = - EFAULT ;
goto out ;
}
copied + + ;
}
}
out_resp - > count_modes = mode_count ;
ret = drm_mode_object_get_properties ( & connector - > base , file_priv - > atomic ,
( uint32_t __user * ) ( unsigned long ) ( out_resp - > props_ptr ) ,
( uint64_t __user * ) ( unsigned long ) ( out_resp - > prop_values_ptr ) ,
& out_resp - > count_props ) ;
if ( ret )
goto out ;
if ( ( out_resp - > count_encoders > = encoders_count ) & & encoders_count ) {
copied = 0 ;
encoder_ptr = ( uint32_t __user * ) ( unsigned long ) ( out_resp - > encoders_ptr ) ;
for ( i = 0 ; i < DRM_CONNECTOR_MAX_ENCODER ; i + + ) {
if ( connector - > encoder_ids [ i ] ! = 0 ) {
if ( put_user ( connector - > encoder_ids [ i ] ,
encoder_ptr + copied ) ) {
ret = - EFAULT ;
goto out ;
}
copied + + ;
}
}
}
out_resp - > count_encoders = encoders_count ;
out :
drm_modeset_unlock ( & dev - > mode_config . connection_mutex ) ;
drm_connector_unreference ( connector ) ;
out_unlock :
mutex_unlock ( & dev - > mode_config . mutex ) ;
return ret ;
}