2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2012-08-15 13:59:49 +01:00
/*
* Copyright ( C ) 2012 Russell King
* Written from the i915 driver .
*/
2019-08-04 11:41:31 +02:00
2012-08-15 13:59:49 +01:00
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <drm/drm_fb_helper.h>
2019-08-04 11:41:31 +02:00
# include <drm/drm_fourcc.h>
2012-08-15 13:59:49 +01:00
# include "armada_crtc.h"
# include "armada_drm.h"
# include "armada_fb.h"
# include "armada_gem.h"
2019-12-03 18:38:48 +02:00
static const struct fb_ops armada_fb_ops = {
2012-08-15 13:59:49 +01:00
. owner = THIS_MODULE ,
2016-11-14 00:03:14 +01:00
DRM_FB_HELPER_DEFAULT_OPS ,
2015-07-22 14:58:04 +05:30
. fb_fillrect = drm_fb_helper_cfb_fillrect ,
. fb_copyarea = drm_fb_helper_cfb_copyarea ,
. fb_imageblit = drm_fb_helper_cfb_imageblit ,
2012-08-15 13:59:49 +01:00
} ;
2018-07-30 11:52:34 +01:00
static int armada_fbdev_create ( struct drm_fb_helper * fbh ,
2012-08-15 13:59:49 +01:00
struct drm_fb_helper_surface_size * sizes )
{
struct drm_device * dev = fbh - > dev ;
struct drm_mode_fb_cmd2 mode ;
struct armada_framebuffer * dfb ;
struct armada_gem_object * obj ;
struct fb_info * info ;
int size , ret ;
void * ptr ;
memset ( & mode , 0 , sizeof ( mode ) ) ;
mode . width = sizes - > surface_width ;
mode . height = sizes - > surface_height ;
mode . pitches [ 0 ] = armada_pitch ( mode . width , sizes - > surface_bpp ) ;
mode . pixel_format = drm_mode_legacy_fb_format ( sizes - > surface_bpp ,
sizes - > surface_depth ) ;
size = mode . pitches [ 0 ] * mode . height ;
obj = armada_gem_alloc_private_object ( dev , size ) ;
if ( ! obj ) {
DRM_ERROR ( " failed to allocate fb memory \n " ) ;
return - ENOMEM ;
}
ret = armada_gem_linear_back ( dev , obj ) ;
if ( ret ) {
2020-05-15 10:50:56 +01:00
drm_gem_object_put ( & obj - > obj ) ;
2012-08-15 13:59:49 +01:00
return ret ;
}
ptr = armada_gem_map_object ( dev , obj ) ;
if ( ! ptr ) {
2020-05-15 10:50:56 +01:00
drm_gem_object_put ( & obj - > obj ) ;
2012-08-15 13:59:49 +01:00
return - ENOMEM ;
}
dfb = armada_framebuffer_create ( dev , & mode , obj ) ;
/*
* A reference is now held by the framebuffer object if
* successful , otherwise this drops the ref for the error path .
*/
2020-05-15 10:50:56 +01:00
drm_gem_object_put ( & obj - > obj ) ;
2012-08-15 13:59:49 +01:00
if ( IS_ERR ( dfb ) )
return PTR_ERR ( dfb ) ;
2015-07-22 14:58:04 +05:30
info = drm_fb_helper_alloc_fbi ( fbh ) ;
if ( IS_ERR ( info ) ) {
ret = PTR_ERR ( info ) ;
2012-08-15 13:59:49 +01:00
goto err_fballoc ;
}
info - > fbops = & armada_fb_ops ;
info - > fix . smem_start = obj - > phys_addr ;
info - > fix . smem_len = obj - > obj . size ;
info - > screen_size = obj - > obj . size ;
info - > screen_base = ptr ;
fbh - > fb = & dfb - > fb ;
2015-07-22 14:58:04 +05:30
2019-03-26 14:19:52 +01:00
drm_fb_helper_fill_info ( info , fbh , sizes ) ;
2012-08-15 13:59:49 +01:00
2013-11-27 15:46:55 +00:00
DRM_DEBUG_KMS ( " allocated %dx%d %dbpp fb: 0x%08llx \n " ,
2016-12-14 23:32:20 +02:00
dfb - > fb . width , dfb - > fb . height , dfb - > fb . format - > cpp [ 0 ] * 8 ,
2013-11-27 15:46:55 +00:00
( unsigned long long ) obj - > phys_addr ) ;
2012-08-15 13:59:49 +01:00
return 0 ;
err_fballoc :
dfb - > fb . funcs - > destroy ( & dfb - > fb ) ;
return ret ;
}
static int armada_fb_probe ( struct drm_fb_helper * fbh ,
struct drm_fb_helper_surface_size * sizes )
{
int ret = 0 ;
if ( ! fbh - > fb ) {
2018-07-30 11:52:34 +01:00
ret = armada_fbdev_create ( fbh , sizes ) ;
2012-08-15 13:59:49 +01:00
if ( ret = = 0 )
ret = 1 ;
}
return ret ;
}
2014-06-27 17:19:23 +02:00
static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
2012-08-15 13:59:49 +01:00
. fb_probe = armada_fb_probe ,
} ;
int armada_fbdev_init ( struct drm_device * dev )
{
2020-09-04 16:39:19 +02:00
struct armada_private * priv = drm_to_armada_dev ( dev ) ;
2012-08-15 13:59:49 +01:00
struct drm_fb_helper * fbh ;
int ret ;
fbh = devm_kzalloc ( dev - > dev , sizeof ( * fbh ) , GFP_KERNEL ) ;
if ( ! fbh )
return - ENOMEM ;
priv - > fbdev = fbh ;
2014-06-27 17:19:24 +02:00
drm_fb_helper_prepare ( dev , fbh , & armada_fb_helper_funcs ) ;
2012-08-15 13:59:49 +01:00
2020-03-05 17:34:28 +05:30
ret = drm_fb_helper_init ( dev , fbh ) ;
2012-08-15 13:59:49 +01:00
if ( ret ) {
DRM_ERROR ( " failed to initialize drm fb helper \n " ) ;
goto err_fb_helper ;
}
ret = drm_fb_helper_initial_config ( fbh , 32 ) ;
if ( ret ) {
DRM_ERROR ( " failed to set initial config \n " ) ;
goto err_fb_setup ;
}
return 0 ;
err_fb_setup :
drm_fb_helper_fini ( fbh ) ;
err_fb_helper :
priv - > fbdev = NULL ;
return ret ;
}
void armada_fbdev_fini ( struct drm_device * dev )
{
2020-09-04 16:39:19 +02:00
struct armada_private * priv = drm_to_armada_dev ( dev ) ;
2012-08-15 13:59:49 +01:00
struct drm_fb_helper * fbh = priv - > fbdev ;
if ( fbh ) {
2015-07-22 14:58:04 +05:30
drm_fb_helper_unregister_fbi ( fbh ) ;
2012-08-15 13:59:49 +01:00
2013-10-27 15:35:27 +00:00
drm_fb_helper_fini ( fbh ) ;
2012-08-15 13:59:49 +01:00
if ( fbh - > fb )
fbh - > fb - > funcs - > destroy ( fbh - > fb ) ;
priv - > fbdev = NULL ;
}
}