2014-08-22 18:36:26 +08:00
/*
* Copyright ( C ) Fuzhou Rockchip Electronics Co . Ltd
* Author : Mark Yao < mark . yao @ rock - chips . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/kernel.h>
# include <drm/drm.h>
# include <drm/drmP.h>
2015-11-30 18:22:42 +08:00
# include <drm/drm_atomic.h>
2014-08-22 18:36:26 +08:00
# include <drm/drm_fb_helper.h>
2018-03-30 15:11:18 +01:00
# include <drm/drm_gem_framebuffer_helper.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2014-08-22 18:36:26 +08:00
# include "rockchip_drm_drv.h"
2016-05-10 17:03:55 +01:00
# include "rockchip_drm_fb.h"
2014-08-22 18:36:26 +08:00
# include "rockchip_drm_gem.h"
2016-07-24 14:57:44 +08:00
# include "rockchip_drm_psr.h"
2014-08-22 18:36:26 +08:00
2016-07-24 14:57:44 +08:00
static int rockchip_drm_fb_dirty ( struct drm_framebuffer * fb ,
struct drm_file * file ,
unsigned int flags , unsigned int color ,
struct drm_clip_rect * clips ,
unsigned int num_clips )
{
2016-08-18 12:01:46 -07:00
rockchip_drm_psr_flush_all ( fb - > dev ) ;
2016-07-24 14:57:44 +08:00
return 0 ;
}
2015-12-15 12:21:12 +01:00
static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
2018-03-30 15:11:18 +01:00
. destroy = drm_gem_fb_destroy ,
. create_handle = drm_gem_fb_create_handle ,
. dirty = rockchip_drm_fb_dirty ,
2014-08-22 18:36:26 +08:00
} ;
2018-03-30 15:11:19 +01:00
static struct drm_framebuffer *
2015-11-11 19:11:29 +02:00
rockchip_fb_alloc ( struct drm_device * dev , const struct drm_mode_fb_cmd2 * mode_cmd ,
2014-08-22 18:36:26 +08:00
struct drm_gem_object * * obj , unsigned int num_planes )
{
2018-03-30 15:11:19 +01:00
struct drm_framebuffer * fb ;
2014-08-22 18:36:26 +08:00
int ret ;
int i ;
2018-03-30 15:11:19 +01:00
fb = kzalloc ( sizeof ( * fb ) , GFP_KERNEL ) ;
if ( ! fb )
2014-08-22 18:36:26 +08:00
return ERR_PTR ( - ENOMEM ) ;
2018-03-30 15:11:19 +01:00
drm_helper_mode_fill_fb_struct ( dev , fb , mode_cmd ) ;
2014-08-22 18:36:26 +08:00
for ( i = 0 ; i < num_planes ; i + + )
2018-03-30 15:11:19 +01:00
fb - > obj [ i ] = obj [ i ] ;
2014-08-22 18:36:26 +08:00
2018-03-30 15:11:19 +01:00
ret = drm_framebuffer_init ( dev , fb , & rockchip_drm_fb_funcs ) ;
2014-08-22 18:36:26 +08:00
if ( ret ) {
2017-09-15 02:36:03 -06:00
DRM_DEV_ERROR ( dev - > dev ,
" Failed to initialize framebuffer: %d \n " ,
ret ) ;
2018-03-30 15:11:19 +01:00
kfree ( fb ) ;
2014-08-22 18:36:26 +08:00
return ERR_PTR ( ret ) ;
}
2018-03-30 15:11:19 +01:00
return fb ;
2014-08-22 18:36:26 +08:00
}
static struct drm_framebuffer *
rockchip_user_fb_create ( struct drm_device * dev , struct drm_file * file_priv ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd )
2014-08-22 18:36:26 +08:00
{
2018-03-30 15:11:19 +01:00
struct drm_framebuffer * fb ;
2014-08-22 18:36:26 +08:00
struct drm_gem_object * objs [ ROCKCHIP_MAX_FB_BUFFER ] ;
struct drm_gem_object * obj ;
unsigned int hsub ;
unsigned int vsub ;
int num_planes ;
int ret ;
int i ;
hsub = drm_format_horz_chroma_subsampling ( mode_cmd - > pixel_format ) ;
vsub = drm_format_vert_chroma_subsampling ( mode_cmd - > pixel_format ) ;
num_planes = min ( drm_format_num_planes ( mode_cmd - > pixel_format ) ,
ROCKCHIP_MAX_FB_BUFFER ) ;
for ( i = 0 ; i < num_planes ; i + + ) {
unsigned int width = mode_cmd - > width / ( i ? hsub : 1 ) ;
unsigned int height = mode_cmd - > height / ( i ? vsub : 1 ) ;
unsigned int min_size ;
2016-05-09 11:04:54 +01:00
obj = drm_gem_object_lookup ( file_priv , mode_cmd - > handles [ i ] ) ;
2014-08-22 18:36:26 +08:00
if ( ! obj ) {
2017-09-15 02:36:03 -06:00
DRM_DEV_ERROR ( dev - > dev ,
" Failed to lookup GEM object \n " ) ;
2014-08-22 18:36:26 +08:00
ret = - ENXIO ;
goto err_gem_object_unreference ;
}
min_size = ( height - 1 ) * mode_cmd - > pitches [ i ] +
mode_cmd - > offsets [ i ] +
width * drm_format_plane_cpp ( mode_cmd - > pixel_format , i ) ;
if ( obj - > size < min_size ) {
2017-08-11 15:33:06 +03:00
drm_gem_object_put_unlocked ( obj ) ;
2014-08-22 18:36:26 +08:00
ret = - EINVAL ;
goto err_gem_object_unreference ;
}
objs [ i ] = obj ;
}
2018-03-30 15:11:19 +01:00
fb = rockchip_fb_alloc ( dev , mode_cmd , objs , i ) ;
if ( IS_ERR ( fb ) ) {
ret = PTR_ERR ( fb ) ;
2014-08-22 18:36:26 +08:00
goto err_gem_object_unreference ;
}
2018-03-30 15:11:19 +01:00
return fb ;
2014-08-22 18:36:26 +08:00
err_gem_object_unreference :
for ( i - - ; i > = 0 ; i - - )
2017-08-11 15:33:06 +03:00
drm_gem_object_put_unlocked ( objs [ i ] ) ;
2014-08-22 18:36:26 +08:00
return ERR_PTR ( ret ) ;
}
2018-04-23 12:50:02 +02:00
static void
rockchip_atomic_helper_commit_tail_rpm ( struct drm_atomic_state * old_state )
{
struct drm_device * dev = old_state - > dev ;
rockchip_drm_psr_inhibit_get_state ( old_state ) ;
drm_atomic_helper_commit_modeset_disables ( dev , old_state ) ;
drm_atomic_helper_commit_modeset_enables ( dev , old_state ) ;
drm_atomic_helper_commit_planes ( dev , old_state ,
DRM_PLANE_COMMIT_ACTIVE_ONLY ) ;
rockchip_drm_psr_inhibit_put_state ( old_state ) ;
drm_atomic_helper_commit_hw_done ( old_state ) ;
drm_atomic_helper_wait_for_vblanks ( dev , old_state ) ;
drm_atomic_helper_cleanup_planes ( dev , old_state ) ;
}
2017-01-02 11:16:13 +02:00
static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
2018-04-23 12:50:02 +02:00
. atomic_commit_tail = rockchip_atomic_helper_commit_tail_rpm ,
2016-06-08 14:19:12 +02:00
} ;
2015-11-30 18:22:42 +08:00
2014-08-22 18:36:26 +08:00
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
. fb_create = rockchip_user_fb_create ,
2017-12-05 19:25:03 +01:00
. output_poll_changed = drm_fb_helper_output_poll_changed ,
2015-11-30 18:22:42 +08:00
. atomic_check = drm_atomic_helper_check ,
2016-06-08 14:19:12 +02:00
. atomic_commit = drm_atomic_helper_commit ,
2014-08-22 18:36:26 +08:00
} ;
struct drm_framebuffer *
rockchip_drm_framebuffer_init ( struct drm_device * dev ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd ,
2014-08-22 18:36:26 +08:00
struct drm_gem_object * obj )
{
2018-03-30 15:11:19 +01:00
struct drm_framebuffer * fb ;
2014-08-22 18:36:26 +08:00
2018-03-30 15:11:19 +01:00
fb = rockchip_fb_alloc ( dev , mode_cmd , & obj , 1 ) ;
if ( IS_ERR ( fb ) )
return ERR_CAST ( fb ) ;
2014-08-22 18:36:26 +08:00
2018-03-30 15:11:19 +01:00
return fb ;
2014-08-22 18:36:26 +08:00
}
void rockchip_drm_mode_config_init ( struct drm_device * dev )
{
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
/*
* set max width and height as default value ( 4096 x4096 ) .
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb ( ) .
*/
dev - > mode_config . max_width = 4096 ;
dev - > mode_config . max_height = 4096 ;
dev - > mode_config . funcs = & rockchip_drm_mode_config_funcs ;
2016-06-08 14:19:12 +02:00
dev - > mode_config . helper_private = & rockchip_mode_config_helpers ;
2014-08-22 18:36:26 +08:00
}