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>
# include <drm/drm_fb_helper.h>
# include <drm/drm_crtc_helper.h>
# include "rockchip_drm_drv.h"
# include "rockchip_drm_gem.h"
# define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
struct rockchip_drm_fb {
struct drm_framebuffer fb ;
struct drm_gem_object * obj [ ROCKCHIP_MAX_FB_BUFFER ] ;
} ;
struct drm_gem_object * rockchip_fb_get_gem_obj ( struct drm_framebuffer * fb ,
unsigned int plane )
{
struct rockchip_drm_fb * rk_fb = to_rockchip_fb ( fb ) ;
if ( plane > = ROCKCHIP_MAX_FB_BUFFER )
return NULL ;
return rk_fb - > obj [ plane ] ;
}
EXPORT_SYMBOL_GPL ( rockchip_fb_get_gem_obj ) ;
static void rockchip_drm_fb_destroy ( struct drm_framebuffer * fb )
{
struct rockchip_drm_fb * rockchip_fb = to_rockchip_fb ( fb ) ;
struct drm_gem_object * obj ;
int i ;
for ( i = 0 ; i < ROCKCHIP_MAX_FB_BUFFER ; i + + ) {
obj = rockchip_fb - > obj [ i ] ;
if ( obj )
drm_gem_object_unreference_unlocked ( obj ) ;
}
drm_framebuffer_cleanup ( fb ) ;
kfree ( rockchip_fb ) ;
}
static int rockchip_drm_fb_create_handle ( struct drm_framebuffer * fb ,
struct drm_file * file_priv ,
unsigned int * handle )
{
struct rockchip_drm_fb * rockchip_fb = to_rockchip_fb ( fb ) ;
return drm_gem_handle_create ( file_priv ,
rockchip_fb - > obj [ 0 ] , handle ) ;
}
static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
. destroy = rockchip_drm_fb_destroy ,
. create_handle = rockchip_drm_fb_create_handle ,
} ;
static struct rockchip_drm_fb *
rockchip_fb_alloc ( struct drm_device * dev , struct drm_mode_fb_cmd2 * mode_cmd ,
struct drm_gem_object * * obj , unsigned int num_planes )
{
struct rockchip_drm_fb * rockchip_fb ;
int ret ;
int i ;
rockchip_fb = kzalloc ( sizeof ( * rockchip_fb ) , GFP_KERNEL ) ;
if ( ! rockchip_fb )
return ERR_PTR ( - ENOMEM ) ;
drm_helper_mode_fill_fb_struct ( & rockchip_fb - > fb , mode_cmd ) ;
for ( i = 0 ; i < num_planes ; i + + )
rockchip_fb - > obj [ i ] = obj [ i ] ;
ret = drm_framebuffer_init ( dev , & rockchip_fb - > fb ,
& rockchip_drm_fb_funcs ) ;
if ( ret ) {
dev_err ( dev - > dev , " Failed to initialize framebuffer: %d \n " ,
ret ) ;
kfree ( rockchip_fb ) ;
return ERR_PTR ( ret ) ;
}
return rockchip_fb ;
}
static struct drm_framebuffer *
rockchip_user_fb_create ( struct drm_device * dev , struct drm_file * file_priv ,
struct drm_mode_fb_cmd2 * mode_cmd )
{
struct rockchip_drm_fb * rockchip_fb ;
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 ;
obj = drm_gem_object_lookup ( dev , file_priv ,
mode_cmd - > handles [ i ] ) ;
if ( ! obj ) {
dev_err ( dev - > dev , " Failed to lookup GEM object \n " ) ;
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 ) {
drm_gem_object_unreference_unlocked ( obj ) ;
ret = - EINVAL ;
goto err_gem_object_unreference ;
}
objs [ i ] = obj ;
}
rockchip_fb = rockchip_fb_alloc ( dev , mode_cmd , objs , i ) ;
if ( IS_ERR ( rockchip_fb ) ) {
ret = PTR_ERR ( rockchip_fb ) ;
goto err_gem_object_unreference ;
}
return & rockchip_fb - > fb ;
err_gem_object_unreference :
for ( i - - ; i > = 0 ; i - - )
drm_gem_object_unreference_unlocked ( objs [ i ] ) ;
return ERR_PTR ( ret ) ;
}
static void rockchip_drm_output_poll_changed ( struct drm_device * dev )
{
struct rockchip_drm_private * private = dev - > dev_private ;
struct drm_fb_helper * fb_helper = & private - > fbdev_helper ;
2015-06-02 16:41:45 +02:00
if ( fb_helper )
drm_fb_helper_hotplug_event ( fb_helper ) ;
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 ,
. output_poll_changed = rockchip_drm_output_poll_changed ,
} ;
struct drm_framebuffer *
rockchip_drm_framebuffer_init ( struct drm_device * dev ,
struct drm_mode_fb_cmd2 * mode_cmd ,
struct drm_gem_object * obj )
{
struct rockchip_drm_fb * rockchip_fb ;
rockchip_fb = rockchip_fb_alloc ( dev , mode_cmd , & obj , 1 ) ;
if ( IS_ERR ( rockchip_fb ) )
return NULL ;
return & rockchip_fb - > fb ;
}
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 ;
}