2014-07-25 23:30:38 +04:00
/*
* Copyright ( C ) 2014 Red Hat
* Copyright ( C ) 2014 Intel Corp .
*
* 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 :
* Rob Clark < robdclark @ gmail . com >
* Daniel Vetter < daniel . vetter @ ffwll . ch >
*/
# include <drm/drmP.h>
# include <drm/drm_atomic.h>
2016-02-26 20:05:00 +03:00
# include <drm/drm_mode.h>
2014-07-25 23:30:38 +04:00
# include <drm/drm_plane_helper.h>
2016-11-05 18:08:09 +03:00
# include <drm/drm_print.h>
2014-07-25 23:30:38 +04:00
2016-04-28 16:19:56 +03:00
# include "drm_crtc_internal.h"
2016-06-08 15:19:00 +03:00
static void crtc_commit_free ( struct kref * kref )
{
struct drm_crtc_commit * commit =
container_of ( kref , struct drm_crtc_commit , ref ) ;
kfree ( commit ) ;
}
void drm_crtc_commit_put ( struct drm_crtc_commit * commit )
{
kref_put ( & commit - > ref , crtc_commit_free ) ;
}
EXPORT_SYMBOL ( drm_crtc_commit_put ) ;
2015-05-18 11:06:40 +03:00
/**
* drm_atomic_state_default_release -
* release memory initialized by drm_atomic_state_init
* @ state : atomic state
*
* Free all the memory allocated by drm_atomic_state_init .
* This is useful for drivers that subclass the atomic state .
*/
void drm_atomic_state_default_release ( struct drm_atomic_state * state )
2014-07-25 23:30:38 +04:00
{
kfree ( state - > connectors ) ;
kfree ( state - > crtcs ) ;
kfree ( state - > planes ) ;
}
2015-05-18 11:06:40 +03:00
EXPORT_SYMBOL ( drm_atomic_state_default_release ) ;
2014-07-25 23:30:38 +04:00
/**
2015-05-18 11:06:40 +03:00
* drm_atomic_state_init - init new atomic state
2014-07-25 23:30:38 +04:00
* @ dev : DRM device
2015-05-18 11:06:40 +03:00
* @ state : atomic state
2014-07-25 23:30:38 +04:00
*
2015-05-18 11:06:40 +03:00
* Default implementation for filling in a new atomic state .
* This is useful for drivers that subclass the atomic state .
2014-07-25 23:30:38 +04:00
*/
2015-05-18 11:06:40 +03:00
int
drm_atomic_state_init ( struct drm_device * dev , struct drm_atomic_state * state )
2014-07-25 23:30:38 +04:00
{
2016-10-14 15:18:18 +03:00
kref_init ( & state - > ref ) ;
2014-12-19 00:01:56 +03:00
/* TODO legacy paths should maybe do a better job about
* setting this appropriately ?
*/
state - > allow_modeset = true ;
2014-07-25 23:30:38 +04:00
state - > crtcs = kcalloc ( dev - > mode_config . num_crtc ,
sizeof ( * state - > crtcs ) , GFP_KERNEL ) ;
if ( ! state - > crtcs )
goto fail ;
state - > planes = kcalloc ( dev - > mode_config . num_total_plane ,
sizeof ( * state - > planes ) , GFP_KERNEL ) ;
if ( ! state - > planes )
goto fail ;
state - > dev = dev ;
2015-05-18 11:06:40 +03:00
DRM_DEBUG_ATOMIC ( " Allocated atomic state %p \n " , state ) ;
2014-07-25 23:30:38 +04:00
2015-05-18 11:06:40 +03:00
return 0 ;
2014-07-25 23:30:38 +04:00
fail :
2015-05-18 11:06:40 +03:00
drm_atomic_state_default_release ( state ) ;
return - ENOMEM ;
}
EXPORT_SYMBOL ( drm_atomic_state_init ) ;
/**
* drm_atomic_state_alloc - allocate atomic state
* @ dev : DRM device
*
* This allocates an empty atomic state to track updates .
*/
struct drm_atomic_state *
drm_atomic_state_alloc ( struct drm_device * dev )
{
struct drm_mode_config * config = & dev - > mode_config ;
struct drm_atomic_state * state ;
if ( ! config - > funcs - > atomic_state_alloc ) {
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
return NULL ;
if ( drm_atomic_state_init ( dev , state ) < 0 ) {
kfree ( state ) ;
return NULL ;
}
return state ;
}
2014-07-25 23:30:38 +04:00
2015-05-18 11:06:40 +03:00
return config - > funcs - > atomic_state_alloc ( dev ) ;
2014-07-25 23:30:38 +04:00
}
EXPORT_SYMBOL ( drm_atomic_state_alloc ) ;
/**
2015-05-18 11:06:40 +03:00
* drm_atomic_state_default_clear - clear base atomic state
2014-07-25 23:30:38 +04:00
* @ state : atomic state
*
2015-05-18 11:06:40 +03:00
* Default implementation for clearing atomic state .
* This is useful for drivers that subclass the atomic state .
2014-07-25 23:30:38 +04:00
*/
2015-05-18 11:06:40 +03:00
void drm_atomic_state_default_clear ( struct drm_atomic_state * state )
2014-07-25 23:30:38 +04:00
{
struct drm_device * dev = state - > dev ;
2014-11-19 20:38:07 +03:00
struct drm_mode_config * config = & dev - > mode_config ;
2014-07-25 23:30:38 +04:00
int i ;
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Clearing atomic state %p \n " , state ) ;
2014-07-25 23:30:38 +04:00
2014-11-19 20:38:08 +03:00
for ( i = 0 ; i < state - > num_connector ; i + + ) {
2016-06-02 01:06:32 +03:00
struct drm_connector * connector = state - > connectors [ i ] . ptr ;
2014-07-25 23:30:38 +04:00
if ( ! connector )
continue ;
2016-04-27 04:27:39 +03:00
connector - > funcs - > atomic_destroy_state ( connector ,
2016-06-02 01:06:32 +03:00
state - > connectors [ i ] . state ) ;
state - > connectors [ i ] . ptr = NULL ;
state - > connectors [ i ] . state = NULL ;
2016-04-27 04:10:09 +03:00
drm_connector_unreference ( connector ) ;
2014-07-25 23:30:38 +04:00
}
2014-11-19 20:38:07 +03:00
for ( i = 0 ; i < config - > num_crtc ; i + + ) {
2016-06-02 01:06:34 +03:00
struct drm_crtc * crtc = state - > crtcs [ i ] . ptr ;
2014-07-25 23:30:38 +04:00
if ( ! crtc )
continue ;
crtc - > funcs - > atomic_destroy_state ( crtc ,
2016-06-02 01:06:34 +03:00
state - > crtcs [ i ] . state ) ;
2016-06-08 15:19:00 +03:00
if ( state - > crtcs [ i ] . commit ) {
kfree ( state - > crtcs [ i ] . commit - > event ) ;
state - > crtcs [ i ] . commit - > event = NULL ;
drm_crtc_commit_put ( state - > crtcs [ i ] . commit ) ;
}
state - > crtcs [ i ] . commit = NULL ;
2016-06-02 01:06:34 +03:00
state - > crtcs [ i ] . ptr = NULL ;
state - > crtcs [ i ] . state = NULL ;
2014-07-25 23:30:38 +04:00
}
2014-11-19 20:38:07 +03:00
for ( i = 0 ; i < config - > num_total_plane ; i + + ) {
2016-06-02 01:06:33 +03:00
struct drm_plane * plane = state - > planes [ i ] . ptr ;
2014-07-25 23:30:38 +04:00
if ( ! plane )
continue ;
plane - > funcs - > atomic_destroy_state ( plane ,
2016-06-02 01:06:33 +03:00
state - > planes [ i ] . state ) ;
state - > planes [ i ] . ptr = NULL ;
state - > planes [ i ] . state = NULL ;
2014-07-25 23:30:38 +04:00
}
}
2015-05-18 11:06:40 +03:00
EXPORT_SYMBOL ( drm_atomic_state_default_clear ) ;
/**
* drm_atomic_state_clear - clear state object
* @ state : atomic state
*
* When the w / w mutex algorithm detects a deadlock we need to back off and drop
* all locks . So someone else could sneak in and change the current modeset
* configuration . Which means that all the state assembled in @ state is no
* longer an atomic update to the current state , but to some arbitrary earlier
* state . Which could break assumptions the driver ' s - > atomic_check likely
* relies on .
*
* Hence we must clear all cached state and completely start over , using this
* function .
*/
void drm_atomic_state_clear ( struct drm_atomic_state * state )
{
struct drm_device * dev = state - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
if ( config - > funcs - > atomic_state_clear )
config - > funcs - > atomic_state_clear ( state ) ;
else
drm_atomic_state_default_clear ( state ) ;
}
2014-07-25 23:30:38 +04:00
EXPORT_SYMBOL ( drm_atomic_state_clear ) ;
/**
2016-10-14 15:18:18 +03:00
* __drm_atomic_state_free - free all memory for an atomic state
* @ ref : This atomic state to deallocate
2014-07-25 23:30:38 +04:00
*
* This frees all memory associated with an atomic state , including all the
* per - object state for planes , crtcs and connectors .
*/
2016-10-14 15:18:18 +03:00
void __drm_atomic_state_free ( struct kref * ref )
2014-07-25 23:30:38 +04:00
{
2016-10-14 15:18:18 +03:00
struct drm_atomic_state * state = container_of ( ref , typeof ( * state ) , ref ) ;
struct drm_mode_config * config = & state - > dev - > mode_config ;
2015-05-18 11:06:40 +03:00
2014-07-25 23:30:38 +04:00
drm_atomic_state_clear ( state ) ;
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Freeing atomic state %p \n " , state ) ;
2014-07-25 23:30:38 +04:00
2015-05-18 11:06:40 +03:00
if ( config - > funcs - > atomic_state_free ) {
config - > funcs - > atomic_state_free ( state ) ;
} else {
drm_atomic_state_default_release ( state ) ;
kfree ( state ) ;
}
2014-07-25 23:30:38 +04:00
}
2016-10-14 15:18:18 +03:00
EXPORT_SYMBOL ( __drm_atomic_state_free ) ;
2014-07-25 23:30:38 +04:00
/**
* drm_atomic_get_crtc_state - get crtc state
* @ state : global atomic state object
* @ crtc : crtc to get state object for
*
* This function returns the crtc state for the given crtc , allocating it if
* needed . It will also grab the relevant crtc lock to make sure that the state
* is consistent .
*
* Returns :
*
* Either the allocated state or the error code encoded into the pointer . When
* the error is EDEADLK then the w / w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted . All other errors are fatal .
*/
struct drm_crtc_state *
drm_atomic_get_crtc_state ( struct drm_atomic_state * state ,
struct drm_crtc * crtc )
{
2015-05-13 11:37:25 +03:00
int ret , index = drm_crtc_index ( crtc ) ;
2014-07-25 23:30:38 +04:00
struct drm_crtc_state * crtc_state ;
2016-05-03 12:12:31 +03:00
WARN_ON ( ! state - > acquire_ctx ) ;
2015-05-13 11:37:25 +03:00
crtc_state = drm_atomic_get_existing_crtc_state ( state , crtc ) ;
if ( crtc_state )
return crtc_state ;
2014-07-25 23:30:38 +04:00
ret = drm_modeset_lock ( & crtc - > mutex , state - > acquire_ctx ) ;
if ( ret )
return ERR_PTR ( ret ) ;
crtc_state = crtc - > funcs - > atomic_duplicate_state ( crtc ) ;
if ( ! crtc_state )
return ERR_PTR ( - ENOMEM ) ;
2016-06-02 01:06:34 +03:00
state - > crtcs [ index ] . state = crtc_state ;
state - > crtcs [ index ] . ptr = crtc ;
2014-07-25 23:30:38 +04:00
crtc_state - > state = state ;
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " Added [CRTC:%d:%s] %p state to %p \n " ,
crtc - > base . id , crtc - > name , crtc_state , state ) ;
2014-07-25 23:30:38 +04:00
return crtc_state ;
}
EXPORT_SYMBOL ( drm_atomic_get_crtc_state ) ;
2015-05-26 16:36:48 +03:00
/**
* drm_atomic_set_mode_for_crtc - set mode for CRTC
* @ state : the CRTC whose incoming state to update
* @ mode : kernel - internal mode to use for the CRTC , or NULL to disable
*
* Set a mode ( originating from the kernel ) on the desired CRTC state . Does
* not change any other state properties , including enable , active , or
* mode_changed .
*
* RETURNS :
* Zero on success , error code on failure . Cannot return - EDEADLK .
*/
int drm_atomic_set_mode_for_crtc ( struct drm_crtc_state * state ,
struct drm_display_mode * mode )
{
2015-05-25 21:11:51 +03:00
struct drm_mode_modeinfo umode ;
2015-05-26 16:36:48 +03:00
/* Early return for no change. */
if ( mode & & memcmp ( & state - > mode , mode , sizeof ( * mode ) ) = = 0 )
return 0 ;
2015-11-06 14:03:46 +03:00
drm_property_unreference_blob ( state - > mode_blob ) ;
2015-05-25 21:11:51 +03:00
state - > mode_blob = NULL ;
2015-05-26 16:36:48 +03:00
if ( mode ) {
2015-05-25 21:11:51 +03:00
drm_mode_convert_to_umode ( & umode , mode ) ;
state - > mode_blob =
drm_property_create_blob ( state - > crtc - > dev ,
sizeof ( umode ) ,
& umode ) ;
if ( IS_ERR ( state - > mode_blob ) )
return PTR_ERR ( state - > mode_blob ) ;
2015-05-26 16:36:48 +03:00
drm_mode_copy ( & state - > mode , mode ) ;
state - > enable = true ;
DRM_DEBUG_ATOMIC ( " Set [MODE:%s] for CRTC state %p \n " ,
mode - > name , state ) ;
} else {
memset ( & state - > mode , 0 , sizeof ( state - > mode ) ) ;
state - > enable = false ;
DRM_DEBUG_ATOMIC ( " Set [NOMODE] for CRTC state %p \n " ,
state ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( drm_atomic_set_mode_for_crtc ) ;
2015-05-25 21:11:52 +03:00
/**
* drm_atomic_set_mode_prop_for_crtc - set mode for CRTC
* @ state : the CRTC whose incoming state to update
* @ blob : pointer to blob property to use for mode
*
* Set a mode ( originating from a blob property ) on the desired CRTC state .
* This function will take a reference on the blob property for the CRTC state ,
* and release the reference held on the state ' s existing mode property , if any
* was set .
*
* RETURNS :
* Zero on success , error code on failure . Cannot return - EDEADLK .
*/
int drm_atomic_set_mode_prop_for_crtc ( struct drm_crtc_state * state ,
struct drm_property_blob * blob )
{
if ( blob = = state - > mode_blob )
return 0 ;
2015-11-06 14:03:46 +03:00
drm_property_unreference_blob ( state - > mode_blob ) ;
2015-05-25 21:11:52 +03:00
state - > mode_blob = NULL ;
2016-05-31 15:03:17 +03:00
memset ( & state - > mode , 0 , sizeof ( state - > mode ) ) ;
2015-05-25 21:11:52 +03:00
if ( blob ) {
if ( blob - > length ! = sizeof ( struct drm_mode_modeinfo ) | |
drm_mode_convert_umode ( & state - > mode ,
( const struct drm_mode_modeinfo * )
blob - > data ) )
return - EINVAL ;
state - > mode_blob = drm_property_reference_blob ( blob ) ;
state - > enable = true ;
DRM_DEBUG_ATOMIC ( " Set [MODE:%s] for CRTC state %p \n " ,
state - > mode . name , state ) ;
} else {
state - > enable = false ;
DRM_DEBUG_ATOMIC ( " Set [NOMODE] for CRTC state %p \n " ,
state ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( drm_atomic_set_mode_prop_for_crtc ) ;
2016-02-26 20:05:00 +03:00
/**
* drm_atomic_replace_property_blob - replace a blob property
* @ blob : a pointer to the member blob to be replaced
* @ new_blob : the new blob to replace with
* @ replaced : whether the blob has been replaced
*
* RETURNS :
* Zero on success , error code on failure
*/
static void
drm_atomic_replace_property_blob ( struct drm_property_blob * * blob ,
struct drm_property_blob * new_blob ,
bool * replaced )
{
struct drm_property_blob * old_blob = * blob ;
if ( old_blob = = new_blob )
return ;
2016-07-20 18:54:32 +03:00
drm_property_unreference_blob ( old_blob ) ;
2016-02-26 20:05:00 +03:00
if ( new_blob )
drm_property_reference_blob ( new_blob ) ;
* blob = new_blob ;
* replaced = true ;
return ;
}
static int
drm_atomic_replace_property_blob_from_id ( struct drm_crtc * crtc ,
struct drm_property_blob * * blob ,
uint64_t blob_id ,
ssize_t expected_size ,
bool * replaced )
{
struct drm_device * dev = crtc - > dev ;
struct drm_property_blob * new_blob = NULL ;
if ( blob_id ! = 0 ) {
new_blob = drm_property_lookup_blob ( dev , blob_id ) ;
if ( new_blob = = NULL )
return - EINVAL ;
if ( expected_size > 0 & & expected_size ! = new_blob - > length )
return - EINVAL ;
}
drm_atomic_replace_property_blob ( blob , new_blob , replaced ) ;
return 0 ;
}
2014-12-19 00:01:46 +03:00
/**
* drm_atomic_crtc_set_property - set property on CRTC
* @ crtc : the drm CRTC to set a property on
* @ state : the state object to update with the new property value
* @ property : the property to set
* @ val : the new property value
*
* Use this instead of calling crtc - > atomic_set_property directly .
* This function handles generic / core properties and calls out to
* driver ' s - > atomic_set_property ( ) for driver properties . To ensure
* consistent behavior you must call this function rather than the
* driver hook directly .
*
* RETURNS :
* Zero on success , error code on failure
*/
int drm_atomic_crtc_set_property ( struct drm_crtc * crtc ,
struct drm_crtc_state * state , struct drm_property * property ,
uint64_t val )
{
2015-01-22 18:36:21 +03:00
struct drm_device * dev = crtc - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
2016-02-26 20:05:00 +03:00
bool replaced = false ;
2015-05-25 21:11:52 +03:00
int ret ;
2015-01-22 18:36:21 +03:00
2015-03-19 07:33:26 +03:00
if ( property = = config - > prop_active )
2015-01-22 18:36:21 +03:00
state - > active = val ;
2015-05-25 21:11:52 +03:00
else if ( property = = config - > prop_mode_id ) {
struct drm_property_blob * mode =
drm_property_lookup_blob ( dev , val ) ;
ret = drm_atomic_set_mode_prop_for_crtc ( state , mode ) ;
2015-11-06 14:03:46 +03:00
drm_property_unreference_blob ( mode ) ;
2015-05-25 21:11:52 +03:00
return ret ;
2016-02-26 20:05:00 +03:00
} else if ( property = = config - > degamma_lut_property ) {
ret = drm_atomic_replace_property_blob_from_id ( crtc ,
& state - > degamma_lut ,
val ,
- 1 ,
& replaced ) ;
2016-08-27 02:02:28 +03:00
state - > color_mgmt_changed | = replaced ;
2016-02-26 20:05:00 +03:00
return ret ;
} else if ( property = = config - > ctm_property ) {
ret = drm_atomic_replace_property_blob_from_id ( crtc ,
& state - > ctm ,
val ,
sizeof ( struct drm_color_ctm ) ,
& replaced ) ;
2016-08-27 02:02:28 +03:00
state - > color_mgmt_changed | = replaced ;
2016-02-26 20:05:00 +03:00
return ret ;
} else if ( property = = config - > gamma_lut_property ) {
ret = drm_atomic_replace_property_blob_from_id ( crtc ,
& state - > gamma_lut ,
val ,
- 1 ,
& replaced ) ;
2016-08-27 02:02:28 +03:00
state - > color_mgmt_changed | = replaced ;
2016-02-26 20:05:00 +03:00
return ret ;
} else if ( crtc - > funcs - > atomic_set_property )
2014-12-19 00:01:46 +03:00
return crtc - > funcs - > atomic_set_property ( crtc , state , property , val ) ;
2015-03-19 07:33:26 +03:00
else
return - EINVAL ;
return 0 ;
2014-12-19 00:01:46 +03:00
}
EXPORT_SYMBOL ( drm_atomic_crtc_set_property ) ;
2015-12-04 11:45:57 +03:00
/**
* drm_atomic_crtc_get_property - get property value from CRTC state
* @ crtc : the drm CRTC to set a property on
* @ state : the state object to get the property value from
* @ property : the property to set
* @ val : return location for the property value
*
2014-12-19 00:01:47 +03:00
* This function handles generic / core properties and calls out to
* driver ' s - > atomic_get_property ( ) for driver properties . To ensure
* consistent behavior you must call this function rather than the
* driver hook directly .
2015-12-04 11:45:57 +03:00
*
* RETURNS :
* Zero on success , error code on failure
2014-12-19 00:01:47 +03:00
*/
2015-09-24 13:01:03 +03:00
static int
drm_atomic_crtc_get_property ( struct drm_crtc * crtc ,
2014-12-19 00:01:47 +03:00
const struct drm_crtc_state * state ,
struct drm_property * property , uint64_t * val )
{
2015-03-19 07:33:25 +03:00
struct drm_device * dev = crtc - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
if ( property = = config - > prop_active )
* val = state - > active ;
2015-05-25 21:11:52 +03:00
else if ( property = = config - > prop_mode_id )
* val = ( state - > mode_blob ) ? state - > mode_blob - > base . id : 0 ;
2016-02-26 20:05:00 +03:00
else if ( property = = config - > degamma_lut_property )
* val = ( state - > degamma_lut ) ? state - > degamma_lut - > base . id : 0 ;
else if ( property = = config - > ctm_property )
* val = ( state - > ctm ) ? state - > ctm - > base . id : 0 ;
else if ( property = = config - > gamma_lut_property )
* val = ( state - > gamma_lut ) ? state - > gamma_lut - > base . id : 0 ;
2015-03-19 07:33:25 +03:00
else if ( crtc - > funcs - > atomic_get_property )
2014-12-19 00:01:47 +03:00
return crtc - > funcs - > atomic_get_property ( crtc , state , property , val ) ;
2015-03-19 07:33:25 +03:00
else
return - EINVAL ;
return 0 ;
2014-12-19 00:01:47 +03:00
}
2014-12-19 00:01:51 +03:00
/**
* drm_atomic_crtc_check - check crtc state
* @ crtc : crtc to check
* @ state : crtc state to check
*
* Provides core sanity checks for crtc state .
*
* RETURNS :
* Zero on success , error code on failure
*/
static int drm_atomic_crtc_check ( struct drm_crtc * crtc ,
struct drm_crtc_state * state )
{
/* NOTE: we explicitly don't enforce constraints such as primary
* layer covering entire screen , since that is something we want
* to allow ( on hw that supports it ) . For hw that does not , it
* should be checked in driver ' s crtc - > atomic_check ( ) vfunc .
*
* TODO : Add generic modeset state checks once we support those .
*/
2015-01-22 18:36:21 +03:00
if ( state - > active & & ! state - > enable ) {
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " [CRTC:%d:%s] active without enabled \n " ,
crtc - > base . id , crtc - > name ) ;
2015-01-22 18:36:21 +03:00
return - EINVAL ;
}
2015-05-25 21:11:51 +03:00
/* The state->enable vs. state->mode_blob checks can be WARN_ON,
* as this is a kernel - internal detail that userspace should never
* be able to trigger . */
if ( drm_core_check_feature ( crtc - > dev , DRIVER_ATOMIC ) & &
WARN_ON ( state - > enable & & ! state - > mode_blob ) ) {
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " [CRTC:%d:%s] enabled without mode blob \n " ,
crtc - > base . id , crtc - > name ) ;
2015-05-25 21:11:51 +03:00
return - EINVAL ;
}
if ( drm_core_check_feature ( crtc - > dev , DRIVER_ATOMIC ) & &
WARN_ON ( ! state - > enable & & state - > mode_blob ) ) {
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " [CRTC:%d:%s] disabled with mode blob \n " ,
crtc - > base . id , crtc - > name ) ;
2015-05-25 21:11:51 +03:00
return - EINVAL ;
}
2015-12-08 11:49:20 +03:00
/*
* Reject event generation for when a CRTC is off and stays off .
* It wouldn ' t be hard to implement this , but userspace has a track
* record of happily burning through 100 % cpu ( or worse , crash ) when the
* display pipe is suspended . To avoid all that fun just reject updates
* that ask for events since likely that indicates a bug in the
* compositor ' s drawing loop . This is consistent with the vblank IOCTL
* and legacy page_flip IOCTL which also reject service on a disabled
* pipe .
*/
if ( state - > event & & ! state - > active & & ! crtc - > state - > active ) {
DRM_DEBUG_ATOMIC ( " [CRTC:%d] requesting event but off \n " ,
crtc - > base . id ) ;
return - EINVAL ;
}
2014-12-19 00:01:51 +03:00
return 0 ;
}
2016-11-05 18:08:09 +03:00
static void drm_atomic_crtc_print_state ( struct drm_printer * p ,
const struct drm_crtc_state * state )
{
struct drm_crtc * crtc = state - > crtc ;
drm_printf ( p , " crtc[%u]: %s \n " , crtc - > base . id , crtc - > name ) ;
drm_printf ( p , " \t enable=%d \n " , state - > enable ) ;
drm_printf ( p , " \t active=%d \n " , state - > active ) ;
drm_printf ( p , " \t planes_changed=%d \n " , state - > planes_changed ) ;
drm_printf ( p , " \t mode_changed=%d \n " , state - > mode_changed ) ;
drm_printf ( p , " \t active_changed=%d \n " , state - > active_changed ) ;
drm_printf ( p , " \t connectors_changed=%d \n " , state - > connectors_changed ) ;
drm_printf ( p , " \t color_mgmt_changed=%d \n " , state - > color_mgmt_changed ) ;
drm_printf ( p , " \t plane_mask=%x \n " , state - > plane_mask ) ;
drm_printf ( p , " \t connector_mask=%x \n " , state - > connector_mask ) ;
drm_printf ( p , " \t encoder_mask=%x \n " , state - > encoder_mask ) ;
drm_printf ( p , " \t mode: " DRM_MODE_FMT " \n " , DRM_MODE_ARG ( & state - > mode ) ) ;
if ( crtc - > funcs - > atomic_print_state )
crtc - > funcs - > atomic_print_state ( p , state ) ;
}
2014-07-25 23:30:38 +04:00
/**
* drm_atomic_get_plane_state - get plane state
* @ state : global atomic state object
* @ plane : plane to get state object for
*
* This function returns the plane state for the given plane , allocating it if
* needed . It will also grab the relevant plane lock to make sure that the state
* is consistent .
*
* Returns :
*
* Either the allocated state or the error code encoded into the pointer . When
* the error is EDEADLK then the w / w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted . All other errors are fatal .
*/
struct drm_plane_state *
drm_atomic_get_plane_state ( struct drm_atomic_state * state ,
struct drm_plane * plane )
{
2015-05-13 11:37:25 +03:00
int ret , index = drm_plane_index ( plane ) ;
2014-07-25 23:30:38 +04:00
struct drm_plane_state * plane_state ;
2016-05-03 12:12:31 +03:00
WARN_ON ( ! state - > acquire_ctx ) ;
2015-05-13 11:37:25 +03:00
plane_state = drm_atomic_get_existing_plane_state ( state , plane ) ;
if ( plane_state )
return plane_state ;
2014-07-25 23:30:38 +04:00
2014-11-11 12:12:00 +03:00
ret = drm_modeset_lock ( & plane - > mutex , state - > acquire_ctx ) ;
2014-07-25 23:30:38 +04:00
if ( ret )
return ERR_PTR ( ret ) ;
plane_state = plane - > funcs - > atomic_duplicate_state ( plane ) ;
if ( ! plane_state )
return ERR_PTR ( - ENOMEM ) ;
2016-06-02 01:06:33 +03:00
state - > planes [ index ] . state = plane_state ;
state - > planes [ index ] . ptr = plane ;
2014-07-25 23:30:38 +04:00
plane_state - > state = state ;
2015-12-08 19:41:54 +03:00
DRM_DEBUG_ATOMIC ( " Added [PLANE:%d:%s] %p state to %p \n " ,
plane - > base . id , plane - > name , plane_state , state ) ;
2014-07-25 23:30:38 +04:00
if ( plane_state - > crtc ) {
struct drm_crtc_state * crtc_state ;
crtc_state = drm_atomic_get_crtc_state ( state ,
plane_state - > crtc ) ;
if ( IS_ERR ( crtc_state ) )
return ERR_CAST ( crtc_state ) ;
}
return plane_state ;
}
EXPORT_SYMBOL ( drm_atomic_get_plane_state ) ;
2014-12-19 00:01:46 +03:00
/**
* drm_atomic_plane_set_property - set property on plane
* @ plane : the drm plane to set a property on
* @ state : the state object to update with the new property value
* @ property : the property to set
* @ val : the new property value
*
* Use this instead of calling plane - > atomic_set_property directly .
* This function handles generic / core properties and calls out to
* driver ' s - > atomic_set_property ( ) for driver properties . To ensure
* consistent behavior you must call this function rather than the
* driver hook directly .
*
* RETURNS :
* Zero on success , error code on failure
*/
int drm_atomic_plane_set_property ( struct drm_plane * plane ,
struct drm_plane_state * state , struct drm_property * property ,
uint64_t val )
{
2014-12-19 00:01:53 +03:00
struct drm_device * dev = plane - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
if ( property = = config - > prop_fb_id ) {
struct drm_framebuffer * fb = drm_framebuffer_lookup ( dev , val ) ;
drm_atomic_set_fb_for_plane ( state , fb ) ;
if ( fb )
drm_framebuffer_unreference ( fb ) ;
} else if ( property = = config - > prop_crtc_id ) {
struct drm_crtc * crtc = drm_crtc_find ( dev , val ) ;
return drm_atomic_set_crtc_for_plane ( state , crtc ) ;
} else if ( property = = config - > prop_crtc_x ) {
state - > crtc_x = U642I64 ( val ) ;
} else if ( property = = config - > prop_crtc_y ) {
state - > crtc_y = U642I64 ( val ) ;
} else if ( property = = config - > prop_crtc_w ) {
state - > crtc_w = val ;
} else if ( property = = config - > prop_crtc_h ) {
state - > crtc_h = val ;
} else if ( property = = config - > prop_src_x ) {
state - > src_x = val ;
} else if ( property = = config - > prop_src_y ) {
state - > src_y = val ;
} else if ( property = = config - > prop_src_w ) {
state - > src_w = val ;
} else if ( property = = config - > prop_src_h ) {
state - > src_h = val ;
2016-10-21 22:22:45 +03:00
} else if ( property = = plane - > rotation_property ) {
2016-09-26 19:30:47 +03:00
if ( ! is_power_of_2 ( val & DRM_ROTATE_MASK ) )
return - EINVAL ;
2015-01-22 03:35:40 +03:00
state - > rotation = val ;
2016-06-13 12:11:26 +03:00
} else if ( property = = plane - > zpos_property ) {
state - > zpos = val ;
2014-12-19 00:01:53 +03:00
} else if ( plane - > funcs - > atomic_set_property ) {
return plane - > funcs - > atomic_set_property ( plane , state ,
property , val ) ;
} else {
return - EINVAL ;
}
return 0 ;
2014-12-19 00:01:46 +03:00
}
EXPORT_SYMBOL ( drm_atomic_plane_set_property ) ;
2015-12-04 11:45:57 +03:00
/**
* drm_atomic_plane_get_property - get property value from plane state
* @ plane : the drm plane to set a property on
* @ state : the state object to get the property value from
* @ property : the property to set
* @ val : return location for the property value
*
2014-12-19 00:01:47 +03:00
* This function handles generic / core properties and calls out to
* driver ' s - > atomic_get_property ( ) for driver properties . To ensure
* consistent behavior you must call this function rather than the
* driver hook directly .
2015-12-04 11:45:57 +03:00
*
* RETURNS :
* Zero on success , error code on failure
2014-12-19 00:01:47 +03:00
*/
2014-12-19 00:49:02 +03:00
static int
drm_atomic_plane_get_property ( struct drm_plane * plane ,
2014-12-19 00:01:47 +03:00
const struct drm_plane_state * state ,
struct drm_property * property , uint64_t * val )
{
2014-12-19 00:01:53 +03:00
struct drm_device * dev = plane - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
if ( property = = config - > prop_fb_id ) {
* val = ( state - > fb ) ? state - > fb - > base . id : 0 ;
} else if ( property = = config - > prop_crtc_id ) {
* val = ( state - > crtc ) ? state - > crtc - > base . id : 0 ;
} else if ( property = = config - > prop_crtc_x ) {
* val = I642U64 ( state - > crtc_x ) ;
} else if ( property = = config - > prop_crtc_y ) {
* val = I642U64 ( state - > crtc_y ) ;
} else if ( property = = config - > prop_crtc_w ) {
* val = state - > crtc_w ;
} else if ( property = = config - > prop_crtc_h ) {
* val = state - > crtc_h ;
} else if ( property = = config - > prop_src_x ) {
* val = state - > src_x ;
} else if ( property = = config - > prop_src_y ) {
* val = state - > src_y ;
} else if ( property = = config - > prop_src_w ) {
* val = state - > src_w ;
} else if ( property = = config - > prop_src_h ) {
* val = state - > src_h ;
2016-10-21 22:22:45 +03:00
} else if ( property = = plane - > rotation_property ) {
2015-02-26 16:49:17 +03:00
* val = state - > rotation ;
2016-06-13 12:11:26 +03:00
} else if ( property = = plane - > zpos_property ) {
* val = state - > zpos ;
2014-12-19 00:01:53 +03:00
} else if ( plane - > funcs - > atomic_get_property ) {
2014-12-19 00:01:47 +03:00
return plane - > funcs - > atomic_get_property ( plane , state , property , val ) ;
2014-12-19 00:01:53 +03:00
} else {
return - EINVAL ;
}
return 0 ;
2014-12-19 00:01:47 +03:00
}
2015-08-26 22:49:42 +03:00
static bool
plane_switching_crtc ( struct drm_atomic_state * state ,
struct drm_plane * plane ,
struct drm_plane_state * plane_state )
{
if ( ! plane - > state - > crtc | | ! plane_state - > crtc )
return false ;
if ( plane - > state - > crtc = = plane_state - > crtc )
return false ;
/* This could be refined, but currently there's no helper or driver code
* to implement direct switching of active planes nor userspace to take
* advantage of more direct plane switching without the intermediate
* full OFF state .
*/
return true ;
}
2014-12-19 00:01:51 +03:00
/**
* drm_atomic_plane_check - check plane state
* @ plane : plane to check
* @ state : plane state to check
*
* Provides core sanity checks for plane state .
*
* RETURNS :
* Zero on success , error code on failure
*/
static int drm_atomic_plane_check ( struct drm_plane * plane ,
struct drm_plane_state * state )
{
unsigned int fb_width , fb_height ;
2015-03-05 03:25:43 +03:00
int ret ;
2014-12-19 00:01:51 +03:00
/* either *both* CRTC and FB must be set, or neither */
if ( WARN_ON ( state - > crtc & & ! state - > fb ) ) {
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " CRTC set but no FB \n " ) ;
2014-12-19 00:01:51 +03:00
return - EINVAL ;
} else if ( WARN_ON ( state - > fb & & ! state - > crtc ) ) {
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " FB set but no CRTC \n " ) ;
2014-12-19 00:01:51 +03:00
return - EINVAL ;
}
/* if disabled, we don't care about the rest of the state: */
if ( ! state - > crtc )
return 0 ;
/* Check whether this plane is usable on this CRTC */
if ( ! ( plane - > possible_crtcs & drm_crtc_mask ( state - > crtc ) ) ) {
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Invalid crtc for plane \n " ) ;
2014-12-19 00:01:51 +03:00
return - EINVAL ;
}
/* Check whether this plane supports the fb pixel format. */
2015-03-05 03:25:43 +03:00
ret = drm_plane_check_pixel_format ( plane , state - > fb - > pixel_format ) ;
if ( ret ) {
2016-08-15 18:29:55 +03:00
char * format_name = drm_get_format_name ( state - > fb - > pixel_format ) ;
2016-08-15 03:02:38 +03:00
DRM_DEBUG_ATOMIC ( " Invalid pixel format %s \n " , format_name ) ;
kfree ( format_name ) ;
2015-03-05 03:25:43 +03:00
return ret ;
2014-12-19 00:01:51 +03:00
}
/* Give drivers some help against integer overflows */
if ( state - > crtc_w > INT_MAX | |
state - > crtc_x > INT_MAX - ( int32_t ) state - > crtc_w | |
state - > crtc_h > INT_MAX | |
state - > crtc_y > INT_MAX - ( int32_t ) state - > crtc_h ) {
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Invalid CRTC coordinates %ux%u+%d+%d \n " ,
state - > crtc_w , state - > crtc_h ,
state - > crtc_x , state - > crtc_y ) ;
2014-12-19 00:01:51 +03:00
return - ERANGE ;
}
fb_width = state - > fb - > width < < 16 ;
fb_height = state - > fb - > height < < 16 ;
/* Make sure source coordinates are inside the fb. */
if ( state - > src_w > fb_width | |
state - > src_x > fb_width - state - > src_w | |
state - > src_h > fb_height | |
state - > src_y > fb_height - state - > src_h ) {
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Invalid source coordinates "
" %u.%06ux%u.%06u+%u.%06u+%u.%06u \n " ,
state - > src_w > > 16 , ( ( state - > src_w & 0xffff ) * 15625 ) > > 10 ,
state - > src_h > > 16 , ( ( state - > src_h & 0xffff ) * 15625 ) > > 10 ,
state - > src_x > > 16 , ( ( state - > src_x & 0xffff ) * 15625 ) > > 10 ,
state - > src_y > > 16 , ( ( state - > src_y & 0xffff ) * 15625 ) > > 10 ) ;
2014-12-19 00:01:51 +03:00
return - ENOSPC ;
}
2015-08-26 22:49:42 +03:00
if ( plane_switching_crtc ( state - > state , plane , state ) ) {
2015-12-08 19:41:54 +03:00
DRM_DEBUG_ATOMIC ( " [PLANE:%d:%s] switching CRTC directly \n " ,
plane - > base . id , plane - > name ) ;
2015-08-26 22:49:42 +03:00
return - EINVAL ;
}
2014-12-19 00:01:51 +03:00
return 0 ;
}
2016-11-05 18:08:09 +03:00
static void drm_atomic_plane_print_state ( struct drm_printer * p ,
const struct drm_plane_state * state )
{
struct drm_plane * plane = state - > plane ;
struct drm_rect src = drm_plane_state_src ( state ) ;
struct drm_rect dest = drm_plane_state_dest ( state ) ;
drm_printf ( p , " plane[%u]: %s \n " , plane - > base . id , plane - > name ) ;
drm_printf ( p , " \t crtc=%s \n " , state - > crtc ? state - > crtc - > name : " (null) " ) ;
drm_printf ( p , " \t fb=%u \n " , state - > fb ? state - > fb - > base . id : 0 ) ;
if ( state - > fb ) {
struct drm_framebuffer * fb = state - > fb ;
int i , n = drm_format_num_planes ( fb - > pixel_format ) ;
drm_printf ( p , " \t \t format=%s \n " ,
drm_get_format_name ( fb - > pixel_format ) ) ;
drm_printf ( p , " \t \t size=%dx%d \n " , fb - > width , fb - > height ) ;
drm_printf ( p , " \t \t layers: \n " ) ;
for ( i = 0 ; i < n ; i + + ) {
drm_printf ( p , " \t \t \t pitch[%d]=%u \n " , i , fb - > pitches [ i ] ) ;
drm_printf ( p , " \t \t \t offset[%d]=%u \n " , i , fb - > offsets [ i ] ) ;
drm_printf ( p , " \t \t \t modifier[%d]=0x%llx \n " , i , fb - > modifier [ i ] ) ;
}
}
drm_printf ( p , " \t crtc-pos= " DRM_RECT_FMT " \n " , DRM_RECT_ARG ( & dest ) ) ;
drm_printf ( p , " \t src-pos= " DRM_RECT_FP_FMT " \n " , DRM_RECT_FP_ARG ( & src ) ) ;
drm_printf ( p , " \t rotation=%x \n " , state - > rotation ) ;
if ( plane - > funcs - > atomic_print_state )
plane - > funcs - > atomic_print_state ( p , state ) ;
}
2014-07-25 23:30:38 +04:00
/**
* drm_atomic_get_connector_state - get connector state
* @ state : global atomic state object
* @ connector : connector to get state object for
*
* This function returns the connector state for the given connector ,
* allocating it if needed . It will also grab the relevant connector lock to
* make sure that the state is consistent .
*
* Returns :
*
* Either the allocated state or the error code encoded into the pointer . When
* the error is EDEADLK then the w / w mutex code has detected a deadlock and the
* entire atomic sequence must be restarted . All other errors are fatal .
*/
struct drm_connector_state *
drm_atomic_get_connector_state ( struct drm_atomic_state * state ,
struct drm_connector * connector )
{
int ret , index ;
struct drm_mode_config * config = & connector - > dev - > mode_config ;
struct drm_connector_state * connector_state ;
2016-05-03 12:12:31 +03:00
WARN_ON ( ! state - > acquire_ctx ) ;
2014-11-19 20:38:06 +03:00
ret = drm_modeset_lock ( & config - > connection_mutex , state - > acquire_ctx ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2014-07-25 23:30:38 +04:00
index = drm_connector_index ( connector ) ;
2014-11-19 20:38:08 +03:00
if ( index > = state - > num_connector ) {
2016-06-02 01:06:32 +03:00
struct __drm_connnectors_state * c ;
2016-02-17 10:32:05 +03:00
int alloc = max ( index + 1 , config - > num_connector ) ;
c = krealloc ( state - > connectors , alloc * sizeof ( * state - > connectors ) , GFP_KERNEL ) ;
if ( ! c )
return ERR_PTR ( - ENOMEM ) ;
state - > connectors = c ;
memset ( & state - > connectors [ state - > num_connector ] , 0 ,
sizeof ( * state - > connectors ) * ( alloc - state - > num_connector ) ) ;
state - > num_connector = alloc ;
2014-11-19 20:38:08 +03:00
}
2016-06-02 01:06:32 +03:00
if ( state - > connectors [ index ] . state )
return state - > connectors [ index ] . state ;
2014-07-25 23:30:38 +04:00
connector_state = connector - > funcs - > atomic_duplicate_state ( connector ) ;
if ( ! connector_state )
return ERR_PTR ( - ENOMEM ) ;
2016-04-27 04:10:09 +03:00
drm_connector_reference ( connector ) ;
2016-06-02 01:06:32 +03:00
state - > connectors [ index ] . state = connector_state ;
state - > connectors [ index ] . ptr = connector ;
2014-07-25 23:30:38 +04:00
connector_state - > state = state ;
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Added [CONNECTOR:%d] %p state to %p \n " ,
connector - > base . id , connector_state , state ) ;
2014-07-25 23:30:38 +04:00
if ( connector_state - > crtc ) {
struct drm_crtc_state * crtc_state ;
crtc_state = drm_atomic_get_crtc_state ( state ,
connector_state - > crtc ) ;
if ( IS_ERR ( crtc_state ) )
return ERR_CAST ( crtc_state ) ;
}
return connector_state ;
}
EXPORT_SYMBOL ( drm_atomic_get_connector_state ) ;
2014-12-19 00:01:46 +03:00
/**
* drm_atomic_connector_set_property - set property on connector .
* @ connector : the drm connector to set a property on
* @ state : the state object to update with the new property value
* @ property : the property to set
* @ val : the new property value
*
* Use this instead of calling connector - > atomic_set_property directly .
* This function handles generic / core properties and calls out to
* driver ' s - > atomic_set_property ( ) for driver properties . To ensure
* consistent behavior you must call this function rather than the
* driver hook directly .
*
* RETURNS :
* Zero on success , error code on failure
*/
int drm_atomic_connector_set_property ( struct drm_connector * connector ,
struct drm_connector_state * state , struct drm_property * property ,
uint64_t val )
{
struct drm_device * dev = connector - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
2014-12-19 00:01:54 +03:00
if ( property = = config - > prop_crtc_id ) {
struct drm_crtc * crtc = drm_crtc_find ( dev , val ) ;
return drm_atomic_set_crtc_for_connector ( state , crtc ) ;
} else if ( property = = config - > dpms_property ) {
2014-12-19 00:01:46 +03:00
/* setting DPMS property requires special handling, which
* is done in legacy setprop path for us . Disallow ( for
* now ? ) atomic writes to DPMS property :
*/
return - EINVAL ;
} else if ( connector - > funcs - > atomic_set_property ) {
return connector - > funcs - > atomic_set_property ( connector ,
state , property , val ) ;
} else {
return - EINVAL ;
}
}
EXPORT_SYMBOL ( drm_atomic_connector_set_property ) ;
2016-11-05 18:08:09 +03:00
static void drm_atomic_connector_print_state ( struct drm_printer * p ,
const struct drm_connector_state * state )
{
struct drm_connector * connector = state - > connector ;
drm_printf ( p , " connector[%u]: %s \n " , connector - > base . id , connector - > name ) ;
drm_printf ( p , " \t crtc=%s \n " , state - > crtc ? state - > crtc - > name : " (null) " ) ;
if ( connector - > funcs - > atomic_print_state )
connector - > funcs - > atomic_print_state ( p , state ) ;
}
2015-12-04 11:45:57 +03:00
/**
* drm_atomic_connector_get_property - get property value from connector state
* @ connector : the drm connector to set a property on
* @ state : the state object to get the property value from
* @ property : the property to set
* @ val : return location for the property value
*
2014-12-19 00:01:47 +03:00
* This function handles generic / core properties and calls out to
* driver ' s - > atomic_get_property ( ) for driver properties . To ensure
* consistent behavior you must call this function rather than the
* driver hook directly .
2015-12-04 11:45:57 +03:00
*
* RETURNS :
* Zero on success , error code on failure
2014-12-19 00:01:47 +03:00
*/
2014-12-19 00:49:02 +03:00
static int
drm_atomic_connector_get_property ( struct drm_connector * connector ,
2014-12-19 00:01:47 +03:00
const struct drm_connector_state * state ,
struct drm_property * property , uint64_t * val )
{
struct drm_device * dev = connector - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
2014-12-19 00:01:54 +03:00
if ( property = = config - > prop_crtc_id ) {
* val = ( state - > crtc ) ? state - > crtc - > base . id : 0 ;
} else if ( property = = config - > dpms_property ) {
2014-12-19 00:01:47 +03:00
* val = connector - > dpms ;
} else if ( connector - > funcs - > atomic_get_property ) {
return connector - > funcs - > atomic_get_property ( connector ,
state , property , val ) ;
} else {
return - EINVAL ;
}
return 0 ;
}
2014-12-19 00:01:50 +03:00
int drm_atomic_get_property ( struct drm_mode_object * obj ,
struct drm_property * property , uint64_t * val )
{
struct drm_device * dev = property - > dev ;
int ret ;
switch ( obj - > type ) {
case DRM_MODE_OBJECT_CONNECTOR : {
struct drm_connector * connector = obj_to_connector ( obj ) ;
WARN_ON ( ! drm_modeset_is_locked ( & dev - > mode_config . connection_mutex ) ) ;
ret = drm_atomic_connector_get_property ( connector ,
connector - > state , property , val ) ;
break ;
}
case DRM_MODE_OBJECT_CRTC : {
struct drm_crtc * crtc = obj_to_crtc ( obj ) ;
WARN_ON ( ! drm_modeset_is_locked ( & crtc - > mutex ) ) ;
ret = drm_atomic_crtc_get_property ( crtc ,
crtc - > state , property , val ) ;
break ;
}
case DRM_MODE_OBJECT_PLANE : {
struct drm_plane * plane = obj_to_plane ( obj ) ;
WARN_ON ( ! drm_modeset_is_locked ( & plane - > mutex ) ) ;
ret = drm_atomic_plane_get_property ( plane ,
plane - > state , property , val ) ;
break ;
}
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
2014-07-25 23:30:38 +04:00
/**
* drm_atomic_set_crtc_for_plane - set crtc for plane
2014-11-27 17:49:39 +03:00
* @ plane_state : the plane whose incoming state to update
2014-07-25 23:30:38 +04:00
* @ crtc : crtc to use for the plane
*
* Changing the assigned crtc for a plane requires us to grab the lock and state
* for the new crtc , as needed . This function takes care of all these details
* besides updating the pointer in the state object itself .
*
* Returns :
* 0 on success or can fail with - EDEADLK or - ENOMEM . When the error is EDEADLK
* then the w / w mutex code has detected a deadlock and the entire atomic
* sequence must be restarted . All other errors are fatal .
*/
int
2014-11-27 17:49:39 +03:00
drm_atomic_set_crtc_for_plane ( struct drm_plane_state * plane_state ,
struct drm_crtc * crtc )
2014-07-25 23:30:38 +04:00
{
2014-11-27 17:49:39 +03:00
struct drm_plane * plane = plane_state - > plane ;
2014-07-25 23:30:38 +04:00
struct drm_crtc_state * crtc_state ;
2014-11-21 23:28:31 +03:00
if ( plane_state - > crtc ) {
crtc_state = drm_atomic_get_crtc_state ( plane_state - > state ,
plane_state - > crtc ) ;
if ( WARN_ON ( IS_ERR ( crtc_state ) ) )
return PTR_ERR ( crtc_state ) ;
crtc_state - > plane_mask & = ~ ( 1 < < drm_plane_index ( plane ) ) ;
}
plane_state - > crtc = crtc ;
2014-07-25 23:30:38 +04:00
if ( crtc ) {
crtc_state = drm_atomic_get_crtc_state ( plane_state - > state ,
crtc ) ;
if ( IS_ERR ( crtc_state ) )
return PTR_ERR ( crtc_state ) ;
2014-11-21 23:28:31 +03:00
crtc_state - > plane_mask | = ( 1 < < drm_plane_index ( plane ) ) ;
2014-07-25 23:30:38 +04:00
}
if ( crtc )
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " Link plane state %p to [CRTC:%d:%s] \n " ,
plane_state , crtc - > base . id , crtc - > name ) ;
2014-07-25 23:30:38 +04:00
else
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Link plane state %p to [NOCRTC] \n " ,
plane_state ) ;
2014-07-25 23:30:38 +04:00
return 0 ;
}
EXPORT_SYMBOL ( drm_atomic_set_crtc_for_plane ) ;
2014-11-05 00:57:27 +03:00
/**
2015-04-07 14:38:50 +03:00
* drm_atomic_set_fb_for_plane - set framebuffer for plane
2014-11-05 00:57:27 +03:00
* @ plane_state : atomic state object for the plane
* @ fb : fb to use for the plane
*
* Changing the assigned framebuffer for a plane requires us to grab a reference
* to the new fb and drop the reference to the old fb , if there is one . This
* function takes care of all these details besides updating the pointer in the
* state object itself .
*/
void
drm_atomic_set_fb_for_plane ( struct drm_plane_state * plane_state ,
struct drm_framebuffer * fb )
{
if ( plane_state - > fb )
drm_framebuffer_unreference ( plane_state - > fb ) ;
if ( fb )
drm_framebuffer_reference ( fb ) ;
plane_state - > fb = fb ;
if ( fb )
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Set [FB:%d] for plane state %p \n " ,
fb - > base . id , plane_state ) ;
2014-11-05 00:57:27 +03:00
else
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Set [NOFB] for plane state %p \n " ,
plane_state ) ;
2014-11-05 00:57:27 +03:00
}
EXPORT_SYMBOL ( drm_atomic_set_fb_for_plane ) ;
2016-11-07 13:03:30 +03:00
/**
* drm_atomic_set_fence_for_plane - set fence for plane
* @ plane_state : atomic state object for the plane
* @ fence : dma_fence to use for the plane
*
* Helper to setup the plane_state fence in case it is not set yet .
* By using this drivers doesn ' t need to worry if the user choose
* implicit or explicit fencing .
*
* This function will not set the fence to the state if it was set
* via explicit fencing interfaces on the atomic ioctl . It will
* all drope the reference to the fence as we not storing it
* anywhere .
*
* Otherwise , if plane_state - > fence is not set this function we
* just set it with the received implict fence .
*/
void
drm_atomic_set_fence_for_plane ( struct drm_plane_state * plane_state ,
struct dma_fence * fence )
{
if ( plane_state - > fence ) {
dma_fence_put ( fence ) ;
return ;
}
plane_state - > fence = fence ;
}
EXPORT_SYMBOL ( drm_atomic_set_fence_for_plane ) ;
2014-07-25 23:30:38 +04:00
/**
* drm_atomic_set_crtc_for_connector - set crtc for connector
* @ conn_state : atomic state object for the connector
* @ crtc : crtc to use for the connector
*
* Changing the assigned crtc for a connector requires us to grab the lock and
* state for the new crtc , as needed . This function takes care of all these
* details besides updating the pointer in the state object itself .
*
* Returns :
* 0 on success or can fail with - EDEADLK or - ENOMEM . When the error is EDEADLK
* then the w / w mutex code has detected a deadlock and the entire atomic
* sequence must be restarted . All other errors are fatal .
*/
int
drm_atomic_set_crtc_for_connector ( struct drm_connector_state * conn_state ,
struct drm_crtc * crtc )
{
struct drm_crtc_state * crtc_state ;
2016-05-06 14:47:45 +03:00
if ( conn_state - > crtc = = crtc )
return 0 ;
if ( conn_state - > crtc ) {
2016-01-04 14:53:18 +03:00
crtc_state = drm_atomic_get_existing_crtc_state ( conn_state - > state ,
conn_state - > crtc ) ;
crtc_state - > connector_mask & =
~ ( 1 < < drm_connector_index ( conn_state - > connector ) ) ;
2016-05-06 14:47:45 +03:00
drm_connector_unreference ( conn_state - > connector ) ;
conn_state - > crtc = NULL ;
2016-01-04 14:53:18 +03:00
}
2014-07-25 23:30:38 +04:00
if ( crtc ) {
crtc_state = drm_atomic_get_crtc_state ( conn_state - > state , crtc ) ;
if ( IS_ERR ( crtc_state ) )
return PTR_ERR ( crtc_state ) ;
2016-01-04 14:53:18 +03:00
crtc_state - > connector_mask | =
1 < < drm_connector_index ( conn_state - > connector ) ;
2014-07-25 23:30:38 +04:00
2016-05-06 14:47:45 +03:00
drm_connector_reference ( conn_state - > connector ) ;
conn_state - > crtc = crtc ;
2014-07-25 23:30:38 +04:00
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " Link connector state %p to [CRTC:%d:%s] \n " ,
conn_state , crtc - > base . id , crtc - > name ) ;
2016-05-06 14:47:45 +03:00
} else {
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " Link connector state %p to [NOCRTC] \n " ,
conn_state ) ;
2016-05-06 14:47:45 +03:00
}
2014-07-25 23:30:38 +04:00
return 0 ;
}
EXPORT_SYMBOL ( drm_atomic_set_crtc_for_connector ) ;
/**
* drm_atomic_add_affected_connectors - add connectors for crtc
* @ state : atomic state
* @ crtc : DRM crtc
*
* This function walks the current configuration and adds all connectors
* currently using @ crtc to the atomic configuration @ state . Note that this
* function must acquire the connection mutex . This can potentially cause
* unneeded seralization if the update is just for the planes on one crtc . Hence
* drivers and helpers should only call this when really needed ( e . g . when a
* full modeset needs to happen due to some change ) .
*
* Returns :
* 0 on success or can fail with - EDEADLK or - ENOMEM . When the error is EDEADLK
* then the w / w mutex code has detected a deadlock and the entire atomic
* sequence must be restarted . All other errors are fatal .
*/
int
drm_atomic_add_affected_connectors ( struct drm_atomic_state * state ,
struct drm_crtc * crtc )
{
struct drm_mode_config * config = & state - > dev - > mode_config ;
struct drm_connector * connector ;
struct drm_connector_state * conn_state ;
int ret ;
ret = drm_modeset_lock ( & config - > connection_mutex , state - > acquire_ctx ) ;
if ( ret )
return ret ;
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " Adding all current connectors for [CRTC:%d:%s] to %p \n " ,
crtc - > base . id , crtc - > name , state ) ;
2014-07-25 23:30:38 +04:00
/*
* Changed connectors are already in @ state , so only need to look at the
* current configuration .
*/
2015-07-10 00:44:34 +03:00
drm_for_each_connector ( connector , state - > dev ) {
2014-07-25 23:30:38 +04:00
if ( connector - > state - > crtc ! = crtc )
continue ;
conn_state = drm_atomic_get_connector_state ( state , connector ) ;
if ( IS_ERR ( conn_state ) )
return PTR_ERR ( conn_state ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( drm_atomic_add_affected_connectors ) ;
2015-05-19 17:41:02 +03:00
/**
* drm_atomic_add_affected_planes - add planes for crtc
* @ state : atomic state
* @ crtc : DRM crtc
*
* This function walks the current configuration and adds all planes
* currently used by @ crtc to the atomic configuration @ state . This is useful
* when an atomic commit also needs to check all currently enabled plane on
* @ crtc , e . g . when changing the mode . It ' s also useful when re - enabling a CRTC
* to avoid special code to force - enable all planes .
*
* Since acquiring a plane state will always also acquire the w / w mutex of the
* current CRTC for that plane ( if there is any ) adding all the plane states for
* a CRTC will not reduce parallism of atomic updates .
*
* Returns :
* 0 on success or can fail with - EDEADLK or - ENOMEM . When the error is EDEADLK
* then the w / w mutex code has detected a deadlock and the entire atomic
* sequence must be restarted . All other errors are fatal .
*/
int
drm_atomic_add_affected_planes ( struct drm_atomic_state * state ,
struct drm_crtc * crtc )
{
struct drm_plane * plane ;
WARN_ON ( ! drm_atomic_get_existing_crtc_state ( state , crtc ) ) ;
drm_for_each_plane_mask ( plane , state - > dev , crtc - > state - > plane_mask ) {
struct drm_plane_state * plane_state =
drm_atomic_get_plane_state ( state , plane ) ;
if ( IS_ERR ( plane_state ) )
return PTR_ERR ( plane_state ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( drm_atomic_add_affected_planes ) ;
2014-07-25 23:30:38 +04:00
/**
* drm_atomic_legacy_backoff - locking backoff for legacy ioctls
* @ state : atomic state
*
* This function should be used by legacy entry points which don ' t understand
* - EDEADLK semantics . For simplicity this one will grab all modeset locks after
2015-04-07 14:38:50 +03:00
* the slowpath completed .
2014-07-25 23:30:38 +04:00
*/
void drm_atomic_legacy_backoff ( struct drm_atomic_state * state )
{
2016-06-23 14:45:06 +03:00
struct drm_device * dev = state - > dev ;
unsigned crtc_mask = 0 ;
struct drm_crtc * crtc ;
2014-07-25 23:30:38 +04:00
int ret ;
2016-06-23 14:45:06 +03:00
bool global = false ;
drm_for_each_crtc ( crtc , dev ) {
if ( crtc - > acquire_ctx ! = state - > acquire_ctx )
continue ;
crtc_mask | = drm_crtc_mask ( crtc ) ;
crtc - > acquire_ctx = NULL ;
}
if ( WARN_ON ( dev - > mode_config . acquire_ctx = = state - > acquire_ctx ) ) {
global = true ;
dev - > mode_config . acquire_ctx = NULL ;
}
2014-07-25 23:30:38 +04:00
retry :
drm_modeset_backoff ( state - > acquire_ctx ) ;
2016-06-23 14:45:06 +03:00
ret = drm_modeset_lock_all_ctx ( dev , state - > acquire_ctx ) ;
2014-07-25 23:30:38 +04:00
if ( ret )
goto retry ;
2016-06-23 14:45:06 +03:00
drm_for_each_crtc ( crtc , dev )
if ( drm_crtc_mask ( crtc ) & crtc_mask )
crtc - > acquire_ctx = state - > acquire_ctx ;
if ( global )
dev - > mode_config . acquire_ctx = state - > acquire_ctx ;
2014-07-25 23:30:38 +04:00
}
EXPORT_SYMBOL ( drm_atomic_legacy_backoff ) ;
/**
* drm_atomic_check_only - check whether a given config would work
* @ state : atomic configuration to check
*
* Note that this function can return - EDEADLK if the driver needed to acquire
* more locks but encountered a deadlock . The caller must then do the usual w / w
* backoff dance and restart . All other errors are fatal .
*
* Returns :
* 0 on success , negative error code on failure .
*/
int drm_atomic_check_only ( struct drm_atomic_state * state )
{
2014-12-19 00:01:51 +03:00
struct drm_device * dev = state - > dev ;
struct drm_mode_config * config = & dev - > mode_config ;
2015-04-10 14:58:39 +03:00
struct drm_plane * plane ;
struct drm_plane_state * plane_state ;
struct drm_crtc * crtc ;
struct drm_crtc_state * crtc_state ;
2014-12-19 00:01:51 +03:00
int i , ret = 0 ;
2014-07-25 23:30:38 +04:00
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " checking %p \n " , state ) ;
2014-07-25 23:30:38 +04:00
2015-04-10 14:58:39 +03:00
for_each_plane_in_state ( state , plane , plane_state , i ) {
ret = drm_atomic_plane_check ( plane , plane_state ) ;
2014-12-19 00:01:51 +03:00
if ( ret ) {
2015-12-08 19:41:54 +03:00
DRM_DEBUG_ATOMIC ( " [PLANE:%d:%s] atomic core check failed \n " ,
plane - > base . id , plane - > name ) ;
2014-12-19 00:01:51 +03:00
return ret ;
}
}
2015-04-10 14:58:39 +03:00
for_each_crtc_in_state ( state , crtc , crtc_state , i ) {
ret = drm_atomic_crtc_check ( crtc , crtc_state ) ;
2014-12-19 00:01:51 +03:00
if ( ret ) {
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " [CRTC:%d:%s] atomic core check failed \n " ,
crtc - > base . id , crtc - > name ) ;
2014-12-19 00:01:51 +03:00
return ret ;
}
}
2014-07-25 23:30:38 +04:00
if ( config - > funcs - > atomic_check )
2014-12-19 00:01:51 +03:00
ret = config - > funcs - > atomic_check ( state - > dev , state ) ;
2014-12-19 00:01:56 +03:00
if ( ! state - > allow_modeset ) {
2015-04-10 14:58:39 +03:00
for_each_crtc_in_state ( state , crtc , crtc_state , i ) {
2015-06-18 10:58:55 +03:00
if ( drm_atomic_crtc_needs_modeset ( crtc_state ) ) {
2015-12-08 19:41:53 +03:00
DRM_DEBUG_ATOMIC ( " [CRTC:%d:%s] requires full modeset \n " ,
crtc - > base . id , crtc - > name ) ;
2014-12-19 00:01:56 +03:00
return - EINVAL ;
}
}
}
2014-12-19 00:01:51 +03:00
return ret ;
2014-07-25 23:30:38 +04:00
}
EXPORT_SYMBOL ( drm_atomic_check_only ) ;
/**
* drm_atomic_commit - commit configuration atomically
* @ state : atomic configuration to check
*
* Note that this function can return - EDEADLK if the driver needed to acquire
* more locks but encountered a deadlock . The caller must then do the usual w / w
* backoff dance and restart . All other errors are fatal .
*
* Also note that on successful execution ownership of @ state is transferred
* from the caller of this function to the function itself . The caller must not
* free or in any other way access @ state . If the function fails then the caller
* must clean up @ state itself .
*
* Returns :
* 0 on success , negative error code on failure .
*/
int drm_atomic_commit ( struct drm_atomic_state * state )
{
struct drm_mode_config * config = & state - > dev - > mode_config ;
int ret ;
ret = drm_atomic_check_only ( state ) ;
if ( ret )
return ret ;
2015-02-22 14:24:16 +03:00
DRM_DEBUG_ATOMIC ( " commiting %p \n " , state ) ;
2014-07-25 23:30:38 +04:00
return config - > funcs - > atomic_commit ( state - > dev , state , false ) ;
}
EXPORT_SYMBOL ( drm_atomic_commit ) ;
/**
2016-04-26 17:11:35 +03:00
* drm_atomic_nonblocking_commit - atomic & nonblocking configuration commit
2014-07-25 23:30:38 +04:00
* @ state : atomic configuration to check
*
* Note that this function can return - EDEADLK if the driver needed to acquire
* more locks but encountered a deadlock . The caller must then do the usual w / w
* backoff dance and restart . All other errors are fatal .
*
* Also note that on successful execution ownership of @ state is transferred
* from the caller of this function to the function itself . The caller must not
* free or in any other way access @ state . If the function fails then the caller
* must clean up @ state itself .
*
* Returns :
* 0 on success , negative error code on failure .
*/
2016-04-26 17:11:35 +03:00
int drm_atomic_nonblocking_commit ( struct drm_atomic_state * state )
2014-07-25 23:30:38 +04:00
{
struct drm_mode_config * config = & state - > dev - > mode_config ;
int ret ;
ret = drm_atomic_check_only ( state ) ;
if ( ret )
return ret ;
2016-04-26 17:11:35 +03:00
DRM_DEBUG_ATOMIC ( " commiting %p nonblocking \n " , state ) ;
2014-07-25 23:30:38 +04:00
return config - > funcs - > atomic_commit ( state - > dev , state , true ) ;
}
2016-04-26 17:11:35 +03:00
EXPORT_SYMBOL ( drm_atomic_nonblocking_commit ) ;
2014-12-19 00:01:56 +03:00
2016-11-05 18:08:09 +03:00
static void drm_atomic_print_state ( const struct drm_atomic_state * state )
{
struct drm_printer p = drm_info_printer ( state - > dev - > dev ) ;
struct drm_plane * plane ;
struct drm_plane_state * plane_state ;
struct drm_crtc * crtc ;
struct drm_crtc_state * crtc_state ;
struct drm_connector * connector ;
struct drm_connector_state * connector_state ;
int i ;
DRM_DEBUG_ATOMIC ( " checking %p \n " , state ) ;
for_each_plane_in_state ( state , plane , plane_state , i )
drm_atomic_plane_print_state ( & p , plane_state ) ;
for_each_crtc_in_state ( state , crtc , crtc_state , i )
drm_atomic_crtc_print_state ( & p , crtc_state ) ;
for_each_connector_in_state ( state , connector , connector_state , i )
drm_atomic_connector_print_state ( & p , connector_state ) ;
}
2014-12-19 00:01:56 +03:00
/*
* The big monstor ioctl
*/
static struct drm_pending_vblank_event * create_vblank_event (
2016-06-02 01:06:35 +03:00
struct drm_device * dev , struct drm_file * file_priv ,
2016-10-25 15:00:45 +03:00
struct dma_fence * fence , uint64_t user_data )
2014-12-19 00:01:56 +03:00
{
struct drm_pending_vblank_event * e = NULL ;
2016-01-12 00:40:56 +03:00
int ret ;
2014-12-19 00:01:56 +03:00
e = kzalloc ( sizeof * e , GFP_KERNEL ) ;
2016-01-12 00:40:56 +03:00
if ( ! e )
return NULL ;
2014-12-19 00:01:56 +03:00
e - > event . base . type = DRM_EVENT_FLIP_COMPLETE ;
2016-01-12 00:40:56 +03:00
e - > event . base . length = sizeof ( e - > event ) ;
2014-12-19 00:01:56 +03:00
e - > event . user_data = user_data ;
2016-06-02 01:06:35 +03:00
if ( file_priv ) {
ret = drm_event_reserve_init ( dev , file_priv , & e - > base ,
& e - > event . base ) ;
if ( ret ) {
kfree ( e ) ;
return NULL ;
}
2016-01-12 00:40:56 +03:00
}
2014-12-19 00:01:56 +03:00
2016-06-02 01:06:35 +03:00
e - > base . fence = fence ;
2016-01-12 00:40:56 +03:00
return e ;
2014-12-19 00:01:56 +03:00
}
static int atomic_set_prop ( struct drm_atomic_state * state ,
struct drm_mode_object * obj , struct drm_property * prop ,
uint64_t prop_value )
{
struct drm_mode_object * ref ;
int ret ;
if ( ! drm_property_change_valid_get ( prop , prop_value , & ref ) )
return - EINVAL ;
switch ( obj - > type ) {
case DRM_MODE_OBJECT_CONNECTOR : {
struct drm_connector * connector = obj_to_connector ( obj ) ;
struct drm_connector_state * connector_state ;
connector_state = drm_atomic_get_connector_state ( state , connector ) ;
if ( IS_ERR ( connector_state ) ) {
ret = PTR_ERR ( connector_state ) ;
break ;
}
ret = drm_atomic_connector_set_property ( connector ,
connector_state , prop , prop_value ) ;
break ;
}
case DRM_MODE_OBJECT_CRTC : {
struct drm_crtc * crtc = obj_to_crtc ( obj ) ;
struct drm_crtc_state * crtc_state ;
crtc_state = drm_atomic_get_crtc_state ( state , crtc ) ;
if ( IS_ERR ( crtc_state ) ) {
ret = PTR_ERR ( crtc_state ) ;
break ;
}
ret = drm_atomic_crtc_set_property ( crtc ,
crtc_state , prop , prop_value ) ;
break ;
}
case DRM_MODE_OBJECT_PLANE : {
struct drm_plane * plane = obj_to_plane ( obj ) ;
struct drm_plane_state * plane_state ;
plane_state = drm_atomic_get_plane_state ( state , plane ) ;
if ( IS_ERR ( plane_state ) ) {
ret = PTR_ERR ( plane_state ) ;
break ;
}
ret = drm_atomic_plane_set_property ( plane ,
plane_state , prop , prop_value ) ;
break ;
}
default :
ret = - EINVAL ;
break ;
}
drm_property_change_valid_put ( prop , ref ) ;
return ret ;
}
2015-11-11 13:29:09 +03:00
/**
2015-11-24 12:34:34 +03:00
* drm_atomic_clean_old_fb - - Unset old_fb pointers and set plane - > fb pointers .
2015-11-11 13:29:09 +03:00
*
* @ dev : drm device to check .
* @ plane_mask : plane mask for planes that were updated .
* @ ret : return value , can be - EDEADLK for a retry .
*
* Before doing an update plane - > old_fb is set to plane - > fb ,
* but before dropping the locks old_fb needs to be set to NULL
* and plane - > fb updated . This is a common operation for each
* atomic update , so this call is split off as a helper .
*/
void drm_atomic_clean_old_fb ( struct drm_device * dev ,
unsigned plane_mask ,
int ret )
{
struct drm_plane * plane ;
/* if succeeded, fixup legacy plane crtc/fb ptrs before dropping
* locks ( ie . while it is still safe to deref plane - > state ) . We
* need to do this here because the driver entry points cannot
* distinguish between legacy and atomic ioctls .
*/
drm_for_each_plane_mask ( plane , dev , plane_mask ) {
if ( ret = = 0 ) {
struct drm_framebuffer * new_fb = plane - > state - > fb ;
if ( new_fb )
drm_framebuffer_reference ( new_fb ) ;
plane - > fb = new_fb ;
plane - > crtc = plane - > state - > crtc ;
if ( plane - > old_fb )
drm_framebuffer_unreference ( plane - > old_fb ) ;
}
plane - > old_fb = NULL ;
}
}
EXPORT_SYMBOL ( drm_atomic_clean_old_fb ) ;
2014-12-19 00:01:56 +03:00
int drm_mode_atomic_ioctl ( struct drm_device * dev ,
void * data , struct drm_file * file_priv )
{
struct drm_mode_atomic * arg = data ;
uint32_t __user * objs_ptr = ( uint32_t __user * ) ( unsigned long ) ( arg - > objs_ptr ) ;
uint32_t __user * count_props_ptr = ( uint32_t __user * ) ( unsigned long ) ( arg - > count_props_ptr ) ;
uint32_t __user * props_ptr = ( uint32_t __user * ) ( unsigned long ) ( arg - > props_ptr ) ;
uint64_t __user * prop_values_ptr = ( uint64_t __user * ) ( unsigned long ) ( arg - > prop_values_ptr ) ;
unsigned int copied_objs , copied_props ;
struct drm_atomic_state * state ;
struct drm_modeset_acquire_ctx ctx ;
struct drm_plane * plane ;
2015-04-10 14:58:39 +03:00
struct drm_crtc * crtc ;
struct drm_crtc_state * crtc_state ;
2015-11-11 13:29:08 +03:00
unsigned plane_mask ;
2014-12-19 00:01:56 +03:00
int ret = 0 ;
2016-09-08 13:30:01 +03:00
unsigned int i , j ;
2014-12-19 00:01:56 +03:00
/* disallow for drivers not supporting atomic: */
if ( ! drm_core_check_feature ( dev , DRIVER_ATOMIC ) )
return - EINVAL ;
/* disallow for userspace that has not enabled atomic cap (even
* though this may be a bit overkill , since legacy userspace
* wouldn ' t know how to call this ioctl )
*/
if ( ! file_priv - > atomic )
return - EINVAL ;
if ( arg - > flags & ~ DRM_MODE_ATOMIC_FLAGS )
return - EINVAL ;
if ( arg - > reserved )
return - EINVAL ;
if ( ( arg - > flags & DRM_MODE_PAGE_FLIP_ASYNC ) & &
! dev - > mode_config . async_page_flip )
return - EINVAL ;
/* can't test and expect an event at the same time. */
if ( ( arg - > flags & DRM_MODE_ATOMIC_TEST_ONLY ) & &
( arg - > flags & DRM_MODE_PAGE_FLIP_EVENT ) )
return - EINVAL ;
drm_modeset_acquire_init ( & ctx , 0 ) ;
state = drm_atomic_state_alloc ( dev ) ;
if ( ! state )
return - ENOMEM ;
state - > acquire_ctx = & ctx ;
state - > allow_modeset = ! ! ( arg - > flags & DRM_MODE_ATOMIC_ALLOW_MODESET ) ;
retry :
2015-11-11 13:29:08 +03:00
plane_mask = 0 ;
2014-12-19 00:01:56 +03:00
copied_objs = 0 ;
copied_props = 0 ;
for ( i = 0 ; i < arg - > count_objs ; i + + ) {
uint32_t obj_id , count_props ;
struct drm_mode_object * obj ;
if ( get_user ( obj_id , objs_ptr + copied_objs ) ) {
ret = - EFAULT ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
obj = drm_mode_object_find ( dev , obj_id , DRM_MODE_OBJECT_ANY ) ;
2016-04-27 04:10:09 +03:00
if ( ! obj ) {
ret = - ENOENT ;
goto out ;
}
if ( ! obj - > properties ) {
drm_mode_object_unreference ( obj ) ;
2014-12-19 00:01:56 +03:00
ret = - ENOENT ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
if ( get_user ( count_props , count_props_ptr + copied_objs ) ) {
2016-04-27 04:10:09 +03:00
drm_mode_object_unreference ( obj ) ;
2014-12-19 00:01:56 +03:00
ret = - EFAULT ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
copied_objs + + ;
for ( j = 0 ; j < count_props ; j + + ) {
uint32_t prop_id ;
uint64_t prop_value ;
struct drm_property * prop ;
if ( get_user ( prop_id , props_ptr + copied_props ) ) {
2016-04-27 04:10:09 +03:00
drm_mode_object_unreference ( obj ) ;
2014-12-19 00:01:56 +03:00
ret = - EFAULT ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
2016-09-08 13:30:01 +03:00
prop = drm_mode_obj_find_prop_id ( obj , prop_id ) ;
2014-12-19 00:01:56 +03:00
if ( ! prop ) {
2016-04-27 04:10:09 +03:00
drm_mode_object_unreference ( obj ) ;
2014-12-19 00:01:56 +03:00
ret = - ENOENT ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
2015-01-13 08:12:17 +03:00
if ( copy_from_user ( & prop_value ,
prop_values_ptr + copied_props ,
sizeof ( prop_value ) ) ) {
2016-04-27 04:10:09 +03:00
drm_mode_object_unreference ( obj ) ;
2014-12-19 00:01:56 +03:00
ret = - EFAULT ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
ret = atomic_set_prop ( state , obj , prop , prop_value ) ;
2016-04-27 04:10:09 +03:00
if ( ret ) {
drm_mode_object_unreference ( obj ) ;
2015-06-24 09:59:25 +03:00
goto out ;
2016-04-27 04:10:09 +03:00
}
2014-12-19 00:01:56 +03:00
copied_props + + ;
}
2015-06-24 09:59:24 +03:00
2015-08-31 13:25:04 +03:00
if ( obj - > type = = DRM_MODE_OBJECT_PLANE & & count_props & &
! ( arg - > flags & DRM_MODE_ATOMIC_TEST_ONLY ) ) {
2015-06-24 09:59:24 +03:00
plane = obj_to_plane ( obj ) ;
plane_mask | = ( 1 < < drm_plane_index ( plane ) ) ;
plane - > old_fb = plane - > fb ;
}
2016-04-27 04:10:09 +03:00
drm_mode_object_unreference ( obj ) ;
2014-12-19 00:01:56 +03:00
}
if ( arg - > flags & DRM_MODE_PAGE_FLIP_EVENT ) {
2015-04-10 14:58:39 +03:00
for_each_crtc_in_state ( state , crtc , crtc_state , i ) {
2014-12-19 00:01:56 +03:00
struct drm_pending_vblank_event * e ;
2016-06-02 01:06:35 +03:00
e = create_vblank_event ( dev , file_priv , NULL ,
arg - > user_data ) ;
2014-12-19 00:01:56 +03:00
if ( ! e ) {
ret = - ENOMEM ;
2015-06-24 09:59:25 +03:00
goto out ;
2014-12-19 00:01:56 +03:00
}
crtc_state - > event = e ;
}
}
if ( arg - > flags & DRM_MODE_ATOMIC_TEST_ONLY ) {
2015-08-31 13:25:04 +03:00
/*
* Unlike commit , check_only does not clean up state .
2016-10-14 15:18:18 +03:00
* Below we call drm_atomic_state_put for it .
2015-08-31 13:25:04 +03:00
*/
2014-12-19 00:01:56 +03:00
ret = drm_atomic_check_only ( state ) ;
} else if ( arg - > flags & DRM_MODE_ATOMIC_NONBLOCK ) {
2016-04-26 17:11:35 +03:00
ret = drm_atomic_nonblocking_commit ( state ) ;
2014-12-19 00:01:56 +03:00
} else {
2016-11-05 18:08:09 +03:00
if ( unlikely ( drm_debug & DRM_UT_STATE ) )
drm_atomic_print_state ( state ) ;
2014-12-19 00:01:56 +03:00
ret = drm_atomic_commit ( state ) ;
}
2015-06-24 09:59:25 +03:00
out :
2015-11-11 13:29:09 +03:00
drm_atomic_clean_old_fb ( dev , plane_mask , ret ) ;
2014-12-19 00:01:56 +03:00
2015-08-31 13:25:04 +03:00
if ( ret & & arg - > flags & DRM_MODE_PAGE_FLIP_EVENT ) {
/*
* TEST_ONLY and PAGE_FLIP_EVENT are mutually exclusive ,
* if they weren ' t , this code should be called on success
* for TEST_ONLY too .
*/
for_each_crtc_in_state ( state , crtc , crtc_state , i ) {
if ( ! crtc_state - > event )
continue ;
2016-01-12 00:40:56 +03:00
drm_event_cancel_free ( dev , & crtc_state - > event - > base ) ;
2015-08-31 13:25:04 +03:00
}
}
2015-06-24 09:59:25 +03:00
if ( ret = = - EDEADLK ) {
drm_atomic_state_clear ( state ) ;
drm_modeset_backoff ( & ctx ) ;
goto retry ;
}
2014-12-19 00:01:56 +03:00
2016-10-14 15:18:18 +03:00
drm_atomic_state_put ( state ) ;
2014-12-19 00:01:56 +03:00
drm_modeset_drop_locks ( & ctx ) ;
drm_modeset_acquire_fini ( & ctx ) ;
return ret ;
}