2016-08-29 10:27:51 +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 <linux/export.h>
# include <drm/drmP.h>
# include <drm/drm_mode_object.h>
2016-12-22 00:50:43 -08:00
# include <drm/drm_atomic.h>
2016-08-29 10:27:51 +02:00
# include "drm_crtc_internal.h"
/*
* Internal function to assign a slot in the object idr and optionally
* register the object into the idr .
*/
2017-02-28 15:46:37 +01:00
int __drm_mode_object_add ( struct drm_device * dev , struct drm_mode_object * obj ,
uint32_t obj_type , bool register_obj ,
void ( * obj_free_cb ) ( struct kref * kref ) )
2016-08-29 10:27:51 +02:00
{
int ret ;
mutex_lock ( & dev - > mode_config . idr_mutex ) ;
ret = idr_alloc ( & dev - > mode_config . crtc_idr , register_obj ? obj : NULL , 1 , 0 , GFP_KERNEL ) ;
if ( ret > = 0 ) {
/*
* Set up the object linking under the protection of the idr
* lock so that other users can ' t see inconsistent state .
*/
obj - > id = ret ;
obj - > type = obj_type ;
if ( obj_free_cb ) {
obj - > free_cb = obj_free_cb ;
kref_init ( & obj - > refcount ) ;
}
}
mutex_unlock ( & dev - > mode_config . idr_mutex ) ;
return ret < 0 ? ret : 0 ;
}
/**
2017-02-28 15:46:37 +01:00
* drm_mode_object_add - allocate a new modeset identifier
2016-08-29 10:27:51 +02:00
* @ dev : DRM device
* @ obj : object pointer , used to generate unique ID
* @ obj_type : object type
*
* Create a unique identifier based on @ ptr in @ dev ' s identifier space . Used
2017-02-28 15:46:37 +01:00
* for tracking modes , CRTCs and connectors .
2016-08-29 10:27:51 +02:00
*
* Returns :
* Zero on success , error code on failure .
*/
2017-02-28 15:46:37 +01:00
int drm_mode_object_add ( struct drm_device * dev ,
2016-08-29 10:27:51 +02:00
struct drm_mode_object * obj , uint32_t obj_type )
{
2017-02-28 15:46:37 +01:00
return __drm_mode_object_add ( dev , obj , obj_type , true , NULL ) ;
2016-08-29 10:27:51 +02:00
}
void drm_mode_object_register ( struct drm_device * dev ,
struct drm_mode_object * obj )
{
mutex_lock ( & dev - > mode_config . idr_mutex ) ;
idr_replace ( & dev - > mode_config . crtc_idr , obj , obj - > id ) ;
mutex_unlock ( & dev - > mode_config . idr_mutex ) ;
}
/**
* drm_mode_object_unregister - free a modeset identifer
* @ dev : DRM device
* @ object : object to free
*
* Free @ id from @ dev ' s unique identifier pool .
* This function can be called multiple times , and guards against
* multiple removals .
* These modeset identifiers are _not_ reference counted . Hence don ' t use this
* for reference counted modeset objects like framebuffers .
*/
void drm_mode_object_unregister ( struct drm_device * dev ,
2016-08-29 10:27:53 +02:00
struct drm_mode_object * object )
2016-08-29 10:27:51 +02:00
{
mutex_lock ( & dev - > mode_config . idr_mutex ) ;
if ( object - > id ) {
idr_remove ( & dev - > mode_config . crtc_idr , object - > id ) ;
object - > id = 0 ;
}
mutex_unlock ( & dev - > mode_config . idr_mutex ) ;
}
2017-04-09 22:35:34 -06:00
/**
* drm_lease_required - check types which must be leased to be used
* @ type : type of object
*
* Returns whether the provided type of drm_mode_object must
* be owned or leased to be used by a process .
*/
2017-03-16 17:56:28 -07:00
bool drm_mode_object_lease_required ( uint32_t type )
2017-04-09 22:35:34 -06:00
{
switch ( type ) {
case DRM_MODE_OBJECT_CRTC :
case DRM_MODE_OBJECT_CONNECTOR :
case DRM_MODE_OBJECT_PLANE :
return true ;
default :
return false ;
}
}
2016-08-29 10:27:51 +02:00
struct drm_mode_object * __drm_mode_object_find ( struct drm_device * dev ,
2017-03-14 23:25:07 -07:00
struct drm_file * file_priv ,
2016-08-29 10:27:51 +02:00
uint32_t id , uint32_t type )
{
struct drm_mode_object * obj = NULL ;
mutex_lock ( & dev - > mode_config . idr_mutex ) ;
obj = idr_find ( & dev - > mode_config . crtc_idr , id ) ;
if ( obj & & type ! = DRM_MODE_OBJECT_ANY & & obj - > type ! = type )
obj = NULL ;
if ( obj & & obj - > id ! = id )
obj = NULL ;
2017-03-16 17:56:28 -07:00
if ( obj & & drm_mode_object_lease_required ( obj - > type ) & &
! _drm_lease_held ( file_priv , obj - > id ) )
2017-04-09 22:35:34 -06:00
obj = NULL ;
2016-08-29 10:27:51 +02:00
if ( obj & & obj - > free_cb ) {
if ( ! kref_get_unless_zero ( & obj - > refcount ) )
obj = NULL ;
}
mutex_unlock ( & dev - > mode_config . idr_mutex ) ;
return obj ;
}
/**
* drm_mode_object_find - look up a drm object with static lifetime
2017-03-14 23:25:07 -07:00
* @ file_priv : drm file
2016-08-29 10:27:51 +02:00
* @ id : id of the mode object
* @ type : type of the mode object
*
* This function is used to look up a modeset object . It will acquire a
* reference for reference counted objects . This reference must be dropped again
2017-02-28 15:46:38 +01:00
* by callind drm_mode_object_put ( ) .
2016-08-29 10:27:51 +02:00
*/
struct drm_mode_object * drm_mode_object_find ( struct drm_device * dev ,
2017-03-14 23:25:07 -07:00
struct drm_file * file_priv ,
2016-08-29 10:27:51 +02:00
uint32_t id , uint32_t type )
{
struct drm_mode_object * obj = NULL ;
2017-03-14 23:25:07 -07:00
obj = __drm_mode_object_find ( dev , file_priv , id , type ) ;
2016-08-29 10:27:51 +02:00
return obj ;
}
EXPORT_SYMBOL ( drm_mode_object_find ) ;
/**
2017-02-28 15:46:38 +01:00
* drm_mode_object_put - release a mode object reference
* @ obj : DRM mode object
2016-08-29 10:27:51 +02:00
*
2016-08-29 10:27:53 +02:00
* This function decrements the object ' s refcount if it is a refcounted modeset
2016-08-29 10:27:51 +02:00
* object . It is a no - op on any other object . This is used to drop references
2017-02-28 15:46:38 +01:00
* acquired with drm_mode_object_get ( ) .
2016-08-29 10:27:51 +02:00
*/
2017-02-28 15:46:38 +01:00
void drm_mode_object_put ( struct drm_mode_object * obj )
2016-08-29 10:27:51 +02:00
{
if ( obj - > free_cb ) {
2016-11-14 17:29:48 +01:00
DRM_DEBUG ( " OBJ ID: %d (%d) \n " , obj - > id , kref_read ( & obj - > refcount ) ) ;
2016-08-29 10:27:51 +02:00
kref_put ( & obj - > refcount , obj - > free_cb ) ;
}
}
2017-02-28 15:46:38 +01:00
EXPORT_SYMBOL ( drm_mode_object_put ) ;
2016-08-29 10:27:51 +02:00
/**
2017-02-28 15:46:38 +01:00
* drm_mode_object_get - acquire a mode object reference
* @ obj : DRM mode object
2016-08-29 10:27:51 +02:00
*
2016-08-29 10:27:53 +02:00
* This function increments the object ' s refcount if it is a refcounted modeset
2016-08-29 10:27:51 +02:00
* object . It is a no - op on any other object . References should be dropped again
2017-02-28 15:46:38 +01:00
* by calling drm_mode_object_put ( ) .
2016-08-29 10:27:51 +02:00
*/
2017-02-28 15:46:38 +01:00
void drm_mode_object_get ( struct drm_mode_object * obj )
2016-08-29 10:27:51 +02:00
{
if ( obj - > free_cb ) {
2016-11-14 17:29:48 +01:00
DRM_DEBUG ( " OBJ ID: %d (%d) \n " , obj - > id , kref_read ( & obj - > refcount ) ) ;
2016-08-29 10:27:51 +02:00
kref_get ( & obj - > refcount ) ;
}
}
2017-02-28 15:46:38 +01:00
EXPORT_SYMBOL ( drm_mode_object_get ) ;
2016-08-29 10:27:51 +02:00
/**
* drm_object_attach_property - attach a property to a modeset object
* @ obj : drm modeset object
* @ property : property to attach
* @ init_val : initial value of the property
*
* This attaches the given property to the modeset object with the given initial
* value . Currently this function cannot fail since the properties are stored in
* a statically sized array .
*/
void drm_object_attach_property ( struct drm_mode_object * obj ,
struct drm_property * property ,
uint64_t init_val )
{
int count = obj - > properties - > count ;
if ( count = = DRM_OBJECT_MAX_PROPERTY ) {
WARN ( 1 , " Failed to attach object property (type: 0x%x). Please "
" increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
" you see this message on the same object type. \n " ,
obj - > type ) ;
return ;
}
obj - > properties - > properties [ count ] = property ;
obj - > properties - > values [ count ] = init_val ;
obj - > properties - > count + + ;
}
EXPORT_SYMBOL ( drm_object_attach_property ) ;
/**
* drm_object_property_set_value - set the value of a property
* @ obj : drm mode object to set property value for
* @ property : property to set
* @ val : value the property should be set to
*
2016-08-29 10:27:53 +02:00
* This function sets a given property on a given object . This function only
2016-08-29 10:27:51 +02:00
* changes the software state of the property , it does not call into the
* driver ' s - > set_property callback .
*
2016-08-29 10:27:53 +02:00
* Note that atomic drivers should not have any need to call this , the core will
* ensure consistency of values reported back to userspace through the
* appropriate - > atomic_get_property callback . Only legacy drivers should call
* this function to update the tracked value ( after clamping and other
* restrictions have been applied ) .
*
2016-08-29 10:27:51 +02:00
* Returns :
* Zero on success , error code on failure .
*/
int drm_object_property_set_value ( struct drm_mode_object * obj ,
struct drm_property * property , uint64_t val )
{
int i ;
2017-07-25 14:01:37 +02:00
WARN_ON ( drm_drv_uses_atomic_modeset ( property - > dev ) & &
! ( property - > flags & DRM_MODE_PROP_IMMUTABLE ) ) ;
2016-08-29 10:27:51 +02:00
for ( i = 0 ; i < obj - > properties - > count ; i + + ) {
if ( obj - > properties - > properties [ i ] = = property ) {
obj - > properties - > values [ i ] = val ;
return 0 ;
}
}
return - EINVAL ;
}
EXPORT_SYMBOL ( drm_object_property_set_value ) ;
2017-09-01 19:53:28 +03:00
static int __drm_object_property_get_value ( struct drm_mode_object * obj ,
struct drm_property * property ,
uint64_t * val )
2016-08-29 10:27:51 +02:00
{
int i ;
/* read-only properties bypass atomic mechanism and still store
* their value in obj - > properties - > values [ ] . . mostly to avoid
* having to deal w / EDID and similar props in atomic paths :
*/
2016-12-22 00:50:43 -08:00
if ( drm_drv_uses_atomic_modeset ( property - > dev ) & &
2016-08-29 10:27:51 +02:00
! ( property - > flags & DRM_MODE_PROP_IMMUTABLE ) )
return drm_atomic_get_property ( obj , property , val ) ;
for ( i = 0 ; i < obj - > properties - > count ; i + + ) {
if ( obj - > properties - > properties [ i ] = = property ) {
* val = obj - > properties - > values [ i ] ;
return 0 ;
}
}
return - EINVAL ;
}
2017-07-25 14:01:37 +02:00
/**
* drm_object_property_get_value - retrieve the value of a property
* @ obj : drm mode object to get property value from
* @ property : property to retrieve
* @ val : storage for the property value
*
* This function retrieves the softare state of the given property for the given
* property . Since there is no driver callback to retrieve the current property
* value this might be out of sync with the hardware , depending upon the driver
* and property .
*
* Atomic drivers should never call this function directly , the core will read
* out property values through the various - > atomic_get_property callbacks .
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_object_property_get_value ( struct drm_mode_object * obj ,
struct drm_property * property , uint64_t * val )
{
WARN_ON ( drm_drv_uses_atomic_modeset ( property - > dev ) ) ;
return __drm_object_property_get_value ( obj , property , val ) ;
}
2016-08-29 10:27:51 +02:00
EXPORT_SYMBOL ( drm_object_property_get_value ) ;
/* helper for getconnector and getproperties ioctls */
int drm_mode_object_get_properties ( struct drm_mode_object * obj , bool atomic ,
uint32_t __user * prop_ptr ,
uint64_t __user * prop_values ,
uint32_t * arg_count_props )
{
2016-08-29 10:27:52 +02:00
int i , ret , count ;
2016-08-29 10:27:51 +02:00
2016-08-29 10:27:52 +02:00
for ( i = 0 , count = 0 ; i < obj - > properties - > count ; i + + ) {
struct drm_property * prop = obj - > properties - > properties [ i ] ;
uint64_t val ;
2016-08-29 10:27:51 +02:00
2016-08-29 10:27:52 +02:00
if ( ( prop - > flags & DRM_MODE_PROP_ATOMIC ) & & ! atomic )
continue ;
2016-08-29 10:27:51 +02:00
2016-08-29 10:27:52 +02:00
if ( * arg_count_props > count ) {
2017-07-25 14:01:37 +02:00
ret = __drm_object_property_get_value ( obj , prop , & val ) ;
2016-08-29 10:27:51 +02:00
if ( ret )
return ret ;
2016-08-29 10:27:52 +02:00
if ( put_user ( prop - > base . id , prop_ptr + count ) )
2016-08-29 10:27:51 +02:00
return - EFAULT ;
2016-08-29 10:27:52 +02:00
if ( put_user ( val , prop_values + count ) )
2016-08-29 10:27:51 +02:00
return - EFAULT ;
}
2016-08-29 10:27:52 +02:00
count + + ;
2016-08-29 10:27:51 +02:00
}
2016-08-29 10:27:52 +02:00
* arg_count_props = count ;
2016-08-29 10:27:51 +02:00
return 0 ;
}
/**
* drm_mode_obj_get_properties_ioctl - get the current value of a object ' s property
* @ dev : DRM device
* @ data : ioctl data
* @ file_priv : DRM file info
*
* This function retrieves the current value for an object ' s property . Compared
* to the connector specific ioctl this one is extended to also work on crtc and
* plane objects .
*
* Called by the user via ioctl .
*
* Returns :
* Zero on success , negative errno on failure .
*/
int drm_mode_obj_get_properties_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_mode_obj_get_properties * arg = data ;
struct drm_mode_object * obj ;
int ret = 0 ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
drm_modeset_lock_all ( dev ) ;
2017-03-14 23:25:07 -07:00
obj = drm_mode_object_find ( dev , file_priv , arg - > obj_id , arg - > obj_type ) ;
2016-08-29 10:27:51 +02:00
if ( ! obj ) {
ret = - ENOENT ;
goto out ;
}
if ( ! obj - > properties ) {
ret = - EINVAL ;
goto out_unref ;
}
ret = drm_mode_object_get_properties ( obj , file_priv - > atomic ,
( uint32_t __user * ) ( unsigned long ) ( arg - > props_ptr ) ,
( uint64_t __user * ) ( unsigned long ) ( arg - > prop_values_ptr ) ,
& arg - > count_props ) ;
out_unref :
2017-02-28 15:46:38 +01:00
drm_mode_object_put ( obj ) ;
2016-08-29 10:27:51 +02:00
out :
drm_modeset_unlock_all ( dev ) ;
return ret ;
}
2016-09-08 12:30:01 +02:00
struct drm_property * drm_mode_obj_find_prop_id ( struct drm_mode_object * obj ,
uint32_t prop_id )
{
int i ;
for ( i = 0 ; i < obj - > properties - > count ; i + + )
if ( obj - > properties - > properties [ i ] - > base . id = = prop_id )
return obj - > properties - > properties [ i ] ;
return NULL ;
}
2017-07-25 14:02:04 +02:00
static int set_property_legacy ( struct drm_mode_object * obj ,
struct drm_property * prop ,
uint64_t prop_value )
{
struct drm_device * dev = prop - > dev ;
struct drm_mode_object * ref ;
int ret = - EINVAL ;
if ( ! drm_property_change_valid_get ( prop , prop_value , & ref ) )
return - EINVAL ;
drm_modeset_lock_all ( dev ) ;
switch ( obj - > type ) {
case DRM_MODE_OBJECT_CONNECTOR :
ret = drm_mode_connector_set_obj_prop ( obj , prop ,
prop_value ) ;
break ;
case DRM_MODE_OBJECT_CRTC :
ret = drm_mode_crtc_set_obj_prop ( obj , prop , prop_value ) ;
break ;
case DRM_MODE_OBJECT_PLANE :
ret = drm_mode_plane_set_obj_prop ( obj_to_plane ( obj ) ,
prop , prop_value ) ;
break ;
}
drm_property_change_valid_put ( prop , ref ) ;
drm_modeset_unlock_all ( dev ) ;
return ret ;
}
static int set_property_atomic ( struct drm_mode_object * obj ,
struct drm_property * prop ,
uint64_t prop_value )
{
struct drm_device * dev = prop - > dev ;
struct drm_atomic_state * state ;
struct drm_modeset_acquire_ctx ctx ;
int ret ;
drm_modeset_acquire_init ( & ctx , 0 ) ;
state = drm_atomic_state_alloc ( dev ) ;
if ( ! state )
return - ENOMEM ;
state - > acquire_ctx = & ctx ;
retry :
if ( prop = = state - > dev - > mode_config . dpms_property ) {
if ( obj - > type ! = DRM_MODE_OBJECT_CONNECTOR ) {
ret = - EINVAL ;
goto out ;
}
ret = drm_atomic_connector_commit_dpms ( state ,
obj_to_connector ( obj ) ,
prop_value ) ;
} else {
ret = drm_atomic_set_property ( state , obj , prop , prop_value ) ;
if ( ret )
goto out ;
ret = drm_atomic_commit ( state ) ;
}
out :
if ( ret = = - EDEADLK ) {
drm_atomic_state_clear ( state ) ;
drm_modeset_backoff ( & ctx ) ;
goto retry ;
}
drm_atomic_state_put ( state ) ;
drm_modeset_drop_locks ( & ctx ) ;
drm_modeset_acquire_fini ( & ctx ) ;
return ret ;
}
2016-08-29 10:27:51 +02:00
int drm_mode_obj_set_property_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_mode_obj_set_property * arg = data ;
struct drm_mode_object * arg_obj ;
struct drm_property * property ;
2016-09-08 12:30:01 +02:00
int ret = - EINVAL ;
2016-08-29 10:27:51 +02:00
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
2017-03-14 23:25:07 -07:00
arg_obj = drm_mode_object_find ( dev , file_priv , arg - > obj_id , arg - > obj_type ) ;
2017-07-25 14:02:04 +02:00
if ( ! arg_obj )
return - ENOENT ;
2016-08-29 10:27:51 +02:00
2016-09-08 12:30:01 +02:00
if ( ! arg_obj - > properties )
2016-08-29 10:27:51 +02:00
goto out_unref ;
2016-09-08 12:30:01 +02:00
property = drm_mode_obj_find_prop_id ( arg_obj , arg - > prop_id ) ;
if ( ! property )
2016-08-29 10:27:51 +02:00
goto out_unref ;
2017-07-25 14:02:04 +02:00
if ( drm_drv_uses_atomic_modeset ( property - > dev ) )
ret = set_property_atomic ( arg_obj , property , arg - > value ) ;
else
ret = set_property_legacy ( arg_obj , property , arg - > value ) ;
2016-08-29 10:27:51 +02:00
out_unref :
2017-02-28 15:46:38 +01:00
drm_mode_object_put ( arg_obj ) ;
2016-08-29 10:27:51 +02:00
return ret ;
}