2016-09-21 11:59:24 +03: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_plane.h>
# include "drm_crtc_internal.h"
2016-09-21 11:59:25 +03:00
/**
* DOC : overview
*
* A plane represents an image source that can be blended with or overlayed on
* top of a CRTC during the scanout process . Planes take their input data from a
* & drm_framebuffer object . The plane itself specifies the cropping and scaling
* of that image , and where it is placed on the visible are of a display
* pipeline , represented by & drm_crtc . A plane can also have additional
* properties that specify how the pixels are positioned and blended , like
* rotation or Z - position . All these properties are stored in & drm_plane_state .
*
* To create a plane , a KMS drivers allocates and zeroes an instances of
* struct & drm_plane ( possibly as part of a larger structure ) and registers it
* with a call to drm_universal_plane_init ( ) .
*
* Cursor and overlay planes are optional . All drivers should provide one
* primary plane per CRTC to avoid surprising userspace too much . See enum
* & drm_plane_type for a more in - depth discussion of these special uapi - relevant
* plane types . Special planes are associated with their CRTC by calling
* drm_crtc_init_with_planes ( ) .
2016-09-21 11:59:27 +03:00
*
* The type of a plane is exposed in the immutable " type " enumeration property ,
* which has one of the following values : " Overlay " , " Primary " , " Cursor " .
2016-09-21 11:59:25 +03:00
*/
2016-09-21 11:59:24 +03:00
static unsigned int drm_num_planes ( struct drm_device * dev )
{
unsigned int num = 0 ;
struct drm_plane * tmp ;
drm_for_each_plane ( tmp , dev ) {
num + + ;
}
return num ;
}
/**
* drm_universal_plane_init - Initialize a new universal plane object
* @ dev : DRM device
* @ plane : plane object to init
* @ possible_crtcs : bitmask of possible CRTCs
* @ funcs : callbacks for the new plane
* @ formats : array of supported formats ( DRM_FORMAT \ _ \ * )
* @ format_count : number of elements in @ formats
* @ type : type of plane ( overlay , primary , cursor )
* @ name : printf style format string for the plane name , or NULL for default name
*
* Initializes a plane object of type @ type .
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_universal_plane_init ( struct drm_device * dev , struct drm_plane * plane ,
2016-12-02 16:45:35 +03:00
uint32_t possible_crtcs ,
2016-09-21 11:59:24 +03:00
const struct drm_plane_funcs * funcs ,
const uint32_t * formats , unsigned int format_count ,
enum drm_plane_type type ,
const char * name , . . . )
{
struct drm_mode_config * config = & dev - > mode_config ;
int ret ;
ret = drm_mode_object_get ( dev , & plane - > base , DRM_MODE_OBJECT_PLANE ) ;
if ( ret )
return ret ;
drm_modeset_lock_init ( & plane - > mutex ) ;
plane - > base . properties = & plane - > properties ;
plane - > dev = dev ;
plane - > funcs = funcs ;
plane - > format_types = kmalloc_array ( format_count , sizeof ( uint32_t ) ,
GFP_KERNEL ) ;
if ( ! plane - > format_types ) {
DRM_DEBUG_KMS ( " out of memory when allocating plane \n " ) ;
drm_mode_object_unregister ( dev , & plane - > base ) ;
return - ENOMEM ;
}
if ( name ) {
va_list ap ;
va_start ( ap , name ) ;
plane - > name = kvasprintf ( GFP_KERNEL , name , ap ) ;
va_end ( ap ) ;
} else {
plane - > name = kasprintf ( GFP_KERNEL , " plane-%d " ,
drm_num_planes ( dev ) ) ;
}
if ( ! plane - > name ) {
kfree ( plane - > format_types ) ;
drm_mode_object_unregister ( dev , & plane - > base ) ;
return - ENOMEM ;
}
memcpy ( plane - > format_types , formats , format_count * sizeof ( uint32_t ) ) ;
plane - > format_count = format_count ;
plane - > possible_crtcs = possible_crtcs ;
plane - > type = type ;
list_add_tail ( & plane - > head , & config - > plane_list ) ;
plane - > index = config - > num_total_plane + + ;
if ( plane - > type = = DRM_PLANE_TYPE_OVERLAY )
config - > num_overlay_plane + + ;
drm_object_attach_property ( & plane - > base ,
config - > plane_type_property ,
plane - > type ) ;
if ( drm_core_check_feature ( dev , DRIVER_ATOMIC ) ) {
drm_object_attach_property ( & plane - > base , config - > prop_fb_id , 0 ) ;
2016-11-15 16:06:39 +03:00
drm_object_attach_property ( & plane - > base , config - > prop_in_fence_fd , - 1 ) ;
2016-09-21 11:59:24 +03:00
drm_object_attach_property ( & plane - > base , config - > prop_crtc_id , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_crtc_x , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_crtc_y , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_crtc_w , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_crtc_h , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_src_x , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_src_y , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_src_w , 0 ) ;
drm_object_attach_property ( & plane - > base , config - > prop_src_h , 0 ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( drm_universal_plane_init ) ;
int drm_plane_register_all ( struct drm_device * dev )
{
struct drm_plane * plane ;
int ret = 0 ;
drm_for_each_plane ( plane , dev ) {
if ( plane - > funcs - > late_register )
ret = plane - > funcs - > late_register ( plane ) ;
if ( ret )
return ret ;
}
return 0 ;
}
void drm_plane_unregister_all ( struct drm_device * dev )
{
struct drm_plane * plane ;
drm_for_each_plane ( plane , dev ) {
if ( plane - > funcs - > early_unregister )
plane - > funcs - > early_unregister ( plane ) ;
}
}
/**
* drm_plane_init - Initialize a legacy plane
* @ dev : DRM device
* @ plane : plane object to init
* @ possible_crtcs : bitmask of possible CRTCs
* @ funcs : callbacks for the new plane
* @ formats : array of supported formats ( DRM_FORMAT \ _ \ * )
* @ format_count : number of elements in @ formats
* @ is_primary : plane type ( primary vs overlay )
*
* Legacy API to initialize a DRM plane .
*
* New drivers should call drm_universal_plane_init ( ) instead .
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_plane_init ( struct drm_device * dev , struct drm_plane * plane ,
2016-12-02 16:45:35 +03:00
uint32_t possible_crtcs ,
2016-09-21 11:59:24 +03:00
const struct drm_plane_funcs * funcs ,
const uint32_t * formats , unsigned int format_count ,
bool is_primary )
{
enum drm_plane_type type ;
type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY ;
return drm_universal_plane_init ( dev , plane , possible_crtcs , funcs ,
formats , format_count , type , NULL ) ;
}
EXPORT_SYMBOL ( drm_plane_init ) ;
/**
* drm_plane_cleanup - Clean up the core plane usage
* @ plane : plane to cleanup
*
* This function cleans up @ plane and removes it from the DRM mode setting
* core . Note that the function does * not * free the plane structure itself ,
* this is the responsibility of the caller .
*/
void drm_plane_cleanup ( struct drm_plane * plane )
{
struct drm_device * dev = plane - > dev ;
2016-11-29 12:45:38 +03:00
drm_modeset_lock_fini ( & plane - > mutex ) ;
2016-09-21 11:59:24 +03:00
kfree ( plane - > format_types ) ;
drm_mode_object_unregister ( dev , & plane - > base ) ;
BUG_ON ( list_empty ( & plane - > head ) ) ;
/* Note that the plane_list is considered to be static; should we
* remove the drm_plane at runtime we would have to decrement all
* the indices on the drm_plane after us in the plane_list .
*/
list_del ( & plane - > head ) ;
dev - > mode_config . num_total_plane - - ;
if ( plane - > type = = DRM_PLANE_TYPE_OVERLAY )
dev - > mode_config . num_overlay_plane - - ;
WARN_ON ( plane - > state & & ! plane - > funcs - > atomic_destroy_state ) ;
if ( plane - > state & & plane - > funcs - > atomic_destroy_state )
plane - > funcs - > atomic_destroy_state ( plane , plane - > state ) ;
kfree ( plane - > name ) ;
memset ( plane , 0 , sizeof ( * plane ) ) ;
}
EXPORT_SYMBOL ( drm_plane_cleanup ) ;
/**
* drm_plane_from_index - find the registered plane at an index
* @ dev : DRM device
* @ idx : index of registered plane to find for
*
* Given a plane index , return the registered plane from DRM device ' s
* list of planes with matching index .
*/
struct drm_plane *
drm_plane_from_index ( struct drm_device * dev , int idx )
{
struct drm_plane * plane ;
drm_for_each_plane ( plane , dev )
if ( idx = = plane - > index )
return plane ;
return NULL ;
}
EXPORT_SYMBOL ( drm_plane_from_index ) ;
/**
* drm_plane_force_disable - Forcibly disable a plane
* @ plane : plane to disable
*
* Forces the plane to be disabled .
*
* Used when the plane ' s current framebuffer is destroyed ,
* and when restoring fbdev mode .
*/
void drm_plane_force_disable ( struct drm_plane * plane )
{
int ret ;
if ( ! plane - > fb )
return ;
plane - > old_fb = plane - > fb ;
ret = plane - > funcs - > disable_plane ( plane ) ;
if ( ret ) {
DRM_ERROR ( " failed to disable plane with busy fb \n " ) ;
plane - > old_fb = NULL ;
return ;
}
/* disconnect the plane from the fb and crtc: */
drm_framebuffer_unreference ( plane - > old_fb ) ;
plane - > old_fb = NULL ;
plane - > fb = NULL ;
plane - > crtc = NULL ;
}
EXPORT_SYMBOL ( drm_plane_force_disable ) ;
/**
* drm_mode_plane_set_obj_prop - set the value of a property
* @ plane : drm plane object to set property value for
* @ property : property to set
* @ value : value the property should be set to
*
* This functions sets a given property on a given plane object . This function
* calls the driver ' s - > set_property callback and changes the software state of
* the property if the callback succeeds .
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_mode_plane_set_obj_prop ( struct drm_plane * plane ,
struct drm_property * property ,
uint64_t value )
{
int ret = - EINVAL ;
struct drm_mode_object * obj = & plane - > base ;
if ( plane - > funcs - > set_property )
ret = plane - > funcs - > set_property ( plane , property , value ) ;
if ( ! ret )
drm_object_property_set_value ( obj , property , value ) ;
return ret ;
}
EXPORT_SYMBOL ( drm_mode_plane_set_obj_prop ) ;
int drm_mode_getplane_res ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_mode_get_plane_res * plane_resp = data ;
struct drm_mode_config * config ;
struct drm_plane * plane ;
uint32_t __user * plane_ptr ;
int copied = 0 ;
unsigned num_planes ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
config = & dev - > mode_config ;
if ( file_priv - > universal_planes )
num_planes = config - > num_total_plane ;
else
num_planes = config - > num_overlay_plane ;
/*
* This ioctl is called twice , once to determine how much space is
* needed , and the 2 nd time to fill it .
*/
if ( num_planes & &
( plane_resp - > count_planes > = num_planes ) ) {
plane_ptr = ( uint32_t __user * ) ( unsigned long ) plane_resp - > plane_id_ptr ;
/* Plane lists are invariant, no locking needed. */
drm_for_each_plane ( plane , dev ) {
/*
* Unless userspace set the ' universal planes '
* capability bit , only advertise overlays .
*/
if ( plane - > type ! = DRM_PLANE_TYPE_OVERLAY & &
! file_priv - > universal_planes )
continue ;
if ( put_user ( plane - > base . id , plane_ptr + copied ) )
return - EFAULT ;
copied + + ;
}
}
plane_resp - > count_planes = num_planes ;
return 0 ;
}
int drm_mode_getplane ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_mode_get_plane * plane_resp = data ;
struct drm_plane * plane ;
uint32_t __user * format_ptr ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
plane = drm_plane_find ( dev , plane_resp - > plane_id ) ;
if ( ! plane )
return - ENOENT ;
drm_modeset_lock ( & plane - > mutex , NULL ) ;
2016-12-13 21:19:12 +03:00
if ( plane - > state & & plane - > state - > crtc )
plane_resp - > crtc_id = plane - > state - > crtc - > base . id ;
else if ( ! plane - > state & & plane - > crtc )
2016-09-21 11:59:24 +03:00
plane_resp - > crtc_id = plane - > crtc - > base . id ;
else
plane_resp - > crtc_id = 0 ;
2016-12-13 21:19:12 +03:00
if ( plane - > state & & plane - > state - > fb )
plane_resp - > fb_id = plane - > state - > fb - > base . id ;
else if ( ! plane - > state & & plane - > fb )
2016-09-21 11:59:24 +03:00
plane_resp - > fb_id = plane - > fb - > base . id ;
else
plane_resp - > fb_id = 0 ;
drm_modeset_unlock ( & plane - > mutex ) ;
plane_resp - > plane_id = plane - > base . id ;
plane_resp - > possible_crtcs = plane - > possible_crtcs ;
plane_resp - > gamma_size = 0 ;
/*
* This ioctl is called twice , once to determine how much space is
* needed , and the 2 nd time to fill it .
*/
if ( plane - > format_count & &
( plane_resp - > count_format_types > = plane - > format_count ) ) {
format_ptr = ( uint32_t __user * ) ( unsigned long ) plane_resp - > format_type_ptr ;
if ( copy_to_user ( format_ptr ,
plane - > format_types ,
sizeof ( uint32_t ) * plane - > format_count ) ) {
return - EFAULT ;
}
}
plane_resp - > count_format_types = plane - > format_count ;
return 0 ;
}
int drm_plane_check_pixel_format ( const struct drm_plane * plane , u32 format )
{
unsigned int i ;
for ( i = 0 ; i < plane - > format_count ; i + + ) {
if ( format = = plane - > format_types [ i ] )
return 0 ;
}
return - EINVAL ;
}
/*
* setplane_internal - setplane handler for internal callers
*
* Note that we assume an extra reference has already been taken on fb . If the
* update fails , this reference will be dropped before return ; if it succeeds ,
* the previous framebuffer ( if any ) will be unreferenced instead .
*
* src_ { x , y , w , h } are provided in 16.16 fixed point format
*/
static int __setplane_internal ( struct drm_plane * plane ,
struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
int32_t crtc_x , int32_t crtc_y ,
uint32_t crtc_w , uint32_t crtc_h ,
/* src_{x,y,w,h} values are 16.16 fixed point */
uint32_t src_x , uint32_t src_y ,
uint32_t src_w , uint32_t src_h )
{
int ret = 0 ;
/* No fb means shut it down */
if ( ! fb ) {
plane - > old_fb = plane - > fb ;
ret = plane - > funcs - > disable_plane ( plane ) ;
if ( ! ret ) {
plane - > crtc = NULL ;
plane - > fb = NULL ;
} else {
plane - > old_fb = NULL ;
}
goto out ;
}
/* Check whether this plane is usable on this CRTC */
if ( ! ( plane - > possible_crtcs & drm_crtc_mask ( crtc ) ) ) {
DRM_DEBUG_KMS ( " Invalid crtc for plane \n " ) ;
ret = - EINVAL ;
goto out ;
}
/* Check whether this plane supports the fb pixel format. */
2016-12-15 00:32:55 +03:00
ret = drm_plane_check_pixel_format ( plane , fb - > format - > format ) ;
2016-09-21 11:59:24 +03:00
if ( ret ) {
2016-11-12 04:12:56 +03:00
struct drm_format_name_buf format_name ;
DRM_DEBUG_KMS ( " Invalid pixel format %s \n " ,
2016-12-15 00:32:55 +03:00
drm_get_format_name ( fb - > format - > format ,
2016-11-12 04:12:56 +03:00
& format_name ) ) ;
2016-09-21 11:59:24 +03:00
goto out ;
}
/* Give drivers some help against integer overflows */
if ( crtc_w > INT_MAX | |
crtc_x > INT_MAX - ( int32_t ) crtc_w | |
crtc_h > INT_MAX | |
crtc_y > INT_MAX - ( int32_t ) crtc_h ) {
DRM_DEBUG_KMS ( " Invalid CRTC coordinates %ux%u+%d+%d \n " ,
crtc_w , crtc_h , crtc_x , crtc_y ) ;
ret = - ERANGE ;
goto out ;
}
ret = drm_framebuffer_check_src_coords ( src_x , src_y , src_w , src_h , fb ) ;
if ( ret )
goto out ;
plane - > old_fb = plane - > fb ;
ret = plane - > funcs - > update_plane ( plane , crtc , fb ,
crtc_x , crtc_y , crtc_w , crtc_h ,
src_x , src_y , src_w , src_h ) ;
if ( ! ret ) {
plane - > crtc = crtc ;
plane - > fb = fb ;
fb = NULL ;
} else {
plane - > old_fb = NULL ;
}
out :
if ( fb )
drm_framebuffer_unreference ( fb ) ;
if ( plane - > old_fb )
drm_framebuffer_unreference ( plane - > old_fb ) ;
plane - > old_fb = NULL ;
return ret ;
}
static int setplane_internal ( struct drm_plane * plane ,
struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
int32_t crtc_x , int32_t crtc_y ,
uint32_t crtc_w , uint32_t crtc_h ,
/* src_{x,y,w,h} values are 16.16 fixed point */
uint32_t src_x , uint32_t src_y ,
uint32_t src_w , uint32_t src_h )
{
int ret ;
drm_modeset_lock_all ( plane - > dev ) ;
ret = __setplane_internal ( plane , crtc , fb ,
crtc_x , crtc_y , crtc_w , crtc_h ,
src_x , src_y , src_w , src_h ) ;
drm_modeset_unlock_all ( plane - > dev ) ;
return ret ;
}
int drm_mode_setplane ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_mode_set_plane * plane_req = data ;
struct drm_plane * plane ;
struct drm_crtc * crtc = NULL ;
struct drm_framebuffer * fb = NULL ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
/*
* First , find the plane , crtc , and fb objects . If not available ,
* we don ' t bother to call the driver .
*/
plane = drm_plane_find ( dev , plane_req - > plane_id ) ;
if ( ! plane ) {
DRM_DEBUG_KMS ( " Unknown plane ID %d \n " ,
plane_req - > plane_id ) ;
return - ENOENT ;
}
if ( plane_req - > fb_id ) {
fb = drm_framebuffer_lookup ( dev , plane_req - > fb_id ) ;
if ( ! fb ) {
DRM_DEBUG_KMS ( " Unknown framebuffer ID %d \n " ,
plane_req - > fb_id ) ;
return - ENOENT ;
}
crtc = drm_crtc_find ( dev , plane_req - > crtc_id ) ;
if ( ! crtc ) {
DRM_DEBUG_KMS ( " Unknown crtc ID %d \n " ,
plane_req - > crtc_id ) ;
return - ENOENT ;
}
}
/*
* setplane_internal will take care of deref ' ing either the old or new
* framebuffer depending on success .
*/
return setplane_internal ( plane , crtc , fb ,
plane_req - > crtc_x , plane_req - > crtc_y ,
plane_req - > crtc_w , plane_req - > crtc_h ,
plane_req - > src_x , plane_req - > src_y ,
plane_req - > src_w , plane_req - > src_h ) ;
}
static int drm_mode_cursor_universal ( struct drm_crtc * crtc ,
struct drm_mode_cursor2 * req ,
struct drm_file * file_priv )
{
struct drm_device * dev = crtc - > dev ;
struct drm_framebuffer * fb = NULL ;
struct drm_mode_fb_cmd2 fbreq = {
. width = req - > width ,
. height = req - > height ,
. pixel_format = DRM_FORMAT_ARGB8888 ,
. pitches = { req - > width * 4 } ,
. handles = { req - > handle } ,
} ;
int32_t crtc_x , crtc_y ;
uint32_t crtc_w = 0 , crtc_h = 0 ;
uint32_t src_w = 0 , src_h = 0 ;
int ret = 0 ;
BUG_ON ( ! crtc - > cursor ) ;
WARN_ON ( crtc - > cursor - > crtc ! = crtc & & crtc - > cursor - > crtc ! = NULL ) ;
/*
* Obtain fb we ' ll be using ( either new or existing ) and take an extra
* reference to it if fb ! = null . setplane will take care of dropping
* the reference if the plane update fails .
*/
if ( req - > flags & DRM_MODE_CURSOR_BO ) {
if ( req - > handle ) {
fb = drm_internal_framebuffer_create ( dev , & fbreq , file_priv ) ;
if ( IS_ERR ( fb ) ) {
DRM_DEBUG_KMS ( " failed to wrap cursor buffer in drm framebuffer \n " ) ;
return PTR_ERR ( fb ) ;
}
fb - > hot_x = req - > hot_x ;
fb - > hot_y = req - > hot_y ;
} else {
fb = NULL ;
}
} else {
fb = crtc - > cursor - > fb ;
if ( fb )
drm_framebuffer_reference ( fb ) ;
}
if ( req - > flags & DRM_MODE_CURSOR_MOVE ) {
crtc_x = req - > x ;
crtc_y = req - > y ;
} else {
crtc_x = crtc - > cursor_x ;
crtc_y = crtc - > cursor_y ;
}
if ( fb ) {
crtc_w = fb - > width ;
crtc_h = fb - > height ;
src_w = fb - > width < < 16 ;
src_h = fb - > height < < 16 ;
}
/*
* setplane_internal will take care of deref ' ing either the old or new
* framebuffer depending on success .
*/
ret = __setplane_internal ( crtc - > cursor , crtc , fb ,
crtc_x , crtc_y , crtc_w , crtc_h ,
0 , 0 , src_w , src_h ) ;
/* Update successful; save new cursor position, if necessary */
if ( ret = = 0 & & req - > flags & DRM_MODE_CURSOR_MOVE ) {
crtc - > cursor_x = req - > x ;
crtc - > cursor_y = req - > y ;
}
return ret ;
}
static int drm_mode_cursor_common ( struct drm_device * dev ,
struct drm_mode_cursor2 * req ,
struct drm_file * file_priv )
{
struct drm_crtc * crtc ;
int ret = 0 ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
if ( ! req - > flags | | ( ~ DRM_MODE_CURSOR_FLAGS & req - > flags ) )
return - EINVAL ;
crtc = drm_crtc_find ( dev , req - > crtc_id ) ;
if ( ! crtc ) {
DRM_DEBUG_KMS ( " Unknown CRTC ID %d \n " , req - > crtc_id ) ;
return - ENOENT ;
}
/*
* If this crtc has a universal cursor plane , call that plane ' s update
* handler rather than using legacy cursor handlers .
*/
drm_modeset_lock_crtc ( crtc , crtc - > cursor ) ;
if ( crtc - > cursor ) {
ret = drm_mode_cursor_universal ( crtc , req , file_priv ) ;
goto out ;
}
if ( req - > flags & DRM_MODE_CURSOR_BO ) {
if ( ! crtc - > funcs - > cursor_set & & ! crtc - > funcs - > cursor_set2 ) {
ret = - ENXIO ;
goto out ;
}
/* Turns off the cursor if handle is 0 */
if ( crtc - > funcs - > cursor_set2 )
ret = crtc - > funcs - > cursor_set2 ( crtc , file_priv , req - > handle ,
req - > width , req - > height , req - > hot_x , req - > hot_y ) ;
else
ret = crtc - > funcs - > cursor_set ( crtc , file_priv , req - > handle ,
req - > width , req - > height ) ;
}
if ( req - > flags & DRM_MODE_CURSOR_MOVE ) {
if ( crtc - > funcs - > cursor_move ) {
ret = crtc - > funcs - > cursor_move ( crtc , req - > x , req - > y ) ;
} else {
ret = - EFAULT ;
goto out ;
}
}
out :
drm_modeset_unlock_crtc ( crtc ) ;
return ret ;
}
int drm_mode_cursor_ioctl ( struct drm_device * dev ,
void * data , struct drm_file * file_priv )
{
struct drm_mode_cursor * req = data ;
struct drm_mode_cursor2 new_req ;
memcpy ( & new_req , req , sizeof ( struct drm_mode_cursor ) ) ;
new_req . hot_x = new_req . hot_y = 0 ;
return drm_mode_cursor_common ( dev , & new_req , file_priv ) ;
}
2016-09-21 11:59:25 +03:00
/*
2016-09-21 11:59:24 +03:00
* Set the cursor configuration based on user request . This implements the 2 nd
* version of the cursor ioctl , which allows userspace to additionally specify
* the hotspot of the pointer .
*/
int drm_mode_cursor2_ioctl ( struct drm_device * dev ,
void * data , struct drm_file * file_priv )
{
struct drm_mode_cursor2 * req = data ;
return drm_mode_cursor_common ( dev , req , file_priv ) ;
}
int drm_mode_page_flip_ioctl ( struct drm_device * dev ,
void * data , struct drm_file * file_priv )
{
struct drm_mode_crtc_page_flip_target * page_flip = data ;
struct drm_crtc * crtc ;
struct drm_framebuffer * fb = NULL ;
struct drm_pending_vblank_event * e = NULL ;
u32 target_vblank = page_flip - > sequence ;
int ret = - EINVAL ;
if ( ! drm_core_check_feature ( dev , DRIVER_MODESET ) )
return - EINVAL ;
if ( page_flip - > flags & ~ DRM_MODE_PAGE_FLIP_FLAGS )
return - EINVAL ;
if ( page_flip - > sequence ! = 0 & & ! ( page_flip - > flags & DRM_MODE_PAGE_FLIP_TARGET ) )
return - EINVAL ;
/* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags
* can be specified
*/
if ( ( page_flip - > flags & DRM_MODE_PAGE_FLIP_TARGET ) = = DRM_MODE_PAGE_FLIP_TARGET )
return - EINVAL ;
if ( ( page_flip - > flags & DRM_MODE_PAGE_FLIP_ASYNC ) & & ! dev - > mode_config . async_page_flip )
return - EINVAL ;
crtc = drm_crtc_find ( dev , page_flip - > crtc_id ) ;
if ( ! crtc )
return - ENOENT ;
2016-10-03 11:28:27 +03:00
if ( crtc - > funcs - > page_flip_target ) {
u32 current_vblank ;
int r ;
r = drm_crtc_vblank_get ( crtc ) ;
if ( r )
return r ;
current_vblank = drm_crtc_vblank_count ( crtc ) ;
switch ( page_flip - > flags & DRM_MODE_PAGE_FLIP_TARGET ) {
case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE :
if ( ( int ) ( target_vblank - current_vblank ) > 1 ) {
DRM_DEBUG ( " Invalid absolute flip target %u, "
" must be <= %u \n " , target_vblank ,
current_vblank + 1 ) ;
drm_crtc_vblank_put ( crtc ) ;
return - EINVAL ;
}
break ;
case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE :
if ( target_vblank ! = 0 & & target_vblank ! = 1 ) {
DRM_DEBUG ( " Invalid relative flip target %u, "
" must be 0 or 1 \n " , target_vblank ) ;
drm_crtc_vblank_put ( crtc ) ;
return - EINVAL ;
}
target_vblank + = current_vblank ;
break ;
default :
target_vblank = current_vblank +
! ( page_flip - > flags & DRM_MODE_PAGE_FLIP_ASYNC ) ;
break ;
}
} else if ( crtc - > funcs - > page_flip = = NULL | |
( page_flip - > flags & DRM_MODE_PAGE_FLIP_TARGET ) ) {
return - EINVAL ;
}
2016-09-21 11:59:24 +03:00
drm_modeset_lock_crtc ( crtc , crtc - > primary ) ;
if ( crtc - > primary - > fb = = NULL ) {
/* The framebuffer is currently unbound, presumably
* due to a hotplug event , that userspace has not
* yet discovered .
*/
ret = - EBUSY ;
goto out ;
}
fb = drm_framebuffer_lookup ( dev , page_flip - > fb_id ) ;
if ( ! fb ) {
ret = - ENOENT ;
goto out ;
}
if ( crtc - > state ) {
const struct drm_plane_state * state = crtc - > primary - > state ;
ret = drm_framebuffer_check_src_coords ( state - > src_x ,
state - > src_y ,
state - > src_w ,
state - > src_h ,
fb ) ;
} else {
ret = drm_crtc_check_viewport ( crtc , crtc - > x , crtc - > y , & crtc - > mode , fb ) ;
}
if ( ret )
goto out ;
2016-11-18 22:53:10 +03:00
if ( crtc - > primary - > fb - > format ! = fb - > format ) {
2016-09-21 11:59:24 +03:00
DRM_DEBUG_KMS ( " Page flip is not allowed to change frame buffer format. \n " ) ;
ret = - EINVAL ;
goto out ;
}
if ( page_flip - > flags & DRM_MODE_PAGE_FLIP_EVENT ) {
e = kzalloc ( sizeof * e , GFP_KERNEL ) ;
if ( ! e ) {
ret = - ENOMEM ;
goto out ;
}
e - > event . base . type = DRM_EVENT_FLIP_COMPLETE ;
e - > event . base . length = sizeof ( e - > event ) ;
e - > event . user_data = page_flip - > user_data ;
ret = drm_event_reserve_init ( dev , file_priv , & e - > base , & e - > event . base ) ;
if ( ret ) {
kfree ( e ) ;
goto out ;
}
}
crtc - > primary - > old_fb = crtc - > primary - > fb ;
if ( crtc - > funcs - > page_flip_target )
ret = crtc - > funcs - > page_flip_target ( crtc , fb , e ,
page_flip - > flags ,
target_vblank ) ;
else
ret = crtc - > funcs - > page_flip ( crtc , fb , e , page_flip - > flags ) ;
if ( ret ) {
if ( page_flip - > flags & DRM_MODE_PAGE_FLIP_EVENT )
drm_event_cancel_free ( dev , & e - > base ) ;
/* Keep the old fb, don't unref it. */
crtc - > primary - > old_fb = NULL ;
} else {
crtc - > primary - > fb = fb ;
/* Unref only the old framebuffer. */
fb = NULL ;
}
out :
if ( ret & & crtc - > funcs - > page_flip_target )
drm_crtc_vblank_put ( crtc ) ;
2016-09-29 01:25:00 +03:00
if ( fb )
drm_framebuffer_unreference ( fb ) ;
2016-09-21 11:59:24 +03:00
if ( crtc - > primary - > old_fb )
drm_framebuffer_unreference ( crtc - > primary - > old_fb ) ;
crtc - > primary - > old_fb = NULL ;
drm_modeset_unlock_crtc ( crtc ) ;
return ret ;
}