2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-10-04 19:19:01 +09:00
/* exynos_drm_fbdev.c
*
* Copyright ( c ) 2011 Samsung Electronics Co . , Ltd .
* Authors :
* Inki Dae < inki . dae @ samsung . com >
* Joonyoung Shim < jy0922 . shim @ samsung . com >
* Seung - Woo Kim < sw0312 . kim @ samsung . com >
*/
2019-06-24 22:06:28 +09:00
# include <linux/console.h>
# include <linux/dma-mapping.h>
2019-06-27 20:00:54 +09:00
# include <linux/vmalloc.h>
2019-06-24 22:06:28 +09:00
2012-10-02 18:01:07 +01:00
# include <drm/drm_crtc.h>
# include <drm/drm_fb_helper.h>
2019-06-24 22:06:28 +09:00
# include <drm/drm_fourcc.h>
2022-06-14 12:54:49 +03:00
# include <drm/drm_framebuffer.h>
2021-11-08 11:28:44 +01:00
# include <drm/drm_prime.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2013-08-06 17:22:04 +05:30
# include <drm/exynos_drm.h>
2011-10-04 19:19:01 +09:00
# include "exynos_drm_drv.h"
# include "exynos_drm_fb.h"
2013-08-13 00:46:40 +01:00
# include "exynos_drm_fbdev.h"
2011-10-04 19:19:01 +09:00
# define MAX_CONNECTOR 4
# define PREFERRED_BPP 32
# define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\
drm_fb_helper )
struct exynos_drm_fbdev {
2015-10-02 09:33:47 +09:00
struct drm_fb_helper drm_fb_helper ;
struct exynos_drm_gem * exynos_gem ;
2011-10-04 19:19:01 +09:00
} ;
2012-11-19 13:55:28 +05:30
static int exynos_drm_fb_mmap ( struct fb_info * info ,
struct vm_area_struct * vma )
{
struct drm_fb_helper * helper = info - > par ;
struct exynos_drm_fbdev * exynos_fbd = to_exynos_fbdev ( helper ) ;
2015-10-02 09:33:47 +09:00
struct exynos_drm_gem * exynos_gem = exynos_fbd - > exynos_gem ;
2012-11-19 13:55:28 +05:30
2021-11-08 11:28:44 +01:00
return drm_gem_prime_mmap ( & exynos_gem - > base , vma ) ;
2012-11-19 13:55:28 +05:30
}
2019-12-03 18:38:48 +02:00
static const struct fb_ops exynos_drm_fb_ops = {
2011-10-04 19:19:01 +09:00
. owner = THIS_MODULE ,
2016-11-14 00:03:17 +01:00
DRM_FB_HELPER_DEFAULT_OPS ,
2012-11-19 13:55:28 +05:30
. fb_mmap = exynos_drm_fb_mmap ,
2015-07-22 14:58:09 +05:30
. fb_fillrect = drm_fb_helper_cfb_fillrect ,
. fb_copyarea = drm_fb_helper_cfb_copyarea ,
. fb_imageblit = drm_fb_helper_cfb_imageblit ,
2011-10-04 19:19:01 +09:00
} ;
2011-10-14 13:29:46 +09:00
static int exynos_drm_fbdev_update ( struct drm_fb_helper * helper ,
2015-09-01 16:22:49 +09:00
struct drm_fb_helper_surface_size * sizes ,
2015-10-02 09:33:47 +09:00
struct exynos_drm_gem * exynos_gem )
2011-10-04 19:19:01 +09:00
{
2015-09-01 16:22:50 +09:00
struct fb_info * fbi ;
2015-09-01 16:22:49 +09:00
struct drm_framebuffer * fb = helper - > fb ;
2016-12-14 23:32:20 +02:00
unsigned int size = fb - > width * fb - > height * fb - > format - > cpp [ 0 ] ;
2011-10-14 13:29:46 +09:00
unsigned long offset ;
2011-10-04 19:19:01 +09:00
2015-09-01 16:22:50 +09:00
fbi = drm_fb_helper_alloc_fbi ( helper ) ;
if ( IS_ERR ( fbi ) ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( to_dma_dev ( helper - > dev ) ,
" failed to allocate fb info. \n " ) ;
2015-09-01 16:22:50 +09:00
return PTR_ERR ( fbi ) ;
}
fbi - > fbops = & exynos_drm_fb_ops ;
2019-03-26 14:19:55 +01:00
drm_fb_helper_fill_info ( fbi , helper , sizes ) ;
2011-10-04 19:19:01 +09:00
2016-12-14 23:32:20 +02:00
offset = fbi - > var . xoffset * fb - > format - > cpp [ 0 ] ;
2011-12-20 00:06:49 +02:00
offset + = fbi - > var . yoffset * fb - > pitches [ 0 ] ;
2011-10-04 19:19:01 +09:00
2020-07-13 09:07:08 +02:00
fbi - > screen_buffer = exynos_gem - > kvaddr + offset ;
2011-10-04 19:19:01 +09:00
fbi - > screen_size = size ;
2014-09-01 21:28:00 +09:00
fbi - > fix . smem_len = size ;
2011-10-14 13:29:46 +09:00
return 0 ;
2011-10-04 19:19:01 +09:00
}
static int exynos_drm_fbdev_create ( struct drm_fb_helper * helper ,
struct drm_fb_helper_surface_size * sizes )
{
struct exynos_drm_fbdev * exynos_fbdev = to_exynos_fbdev ( helper ) ;
2015-10-02 09:33:47 +09:00
struct exynos_drm_gem * exynos_gem ;
2011-10-04 19:19:01 +09:00
struct drm_device * dev = helper - > dev ;
2011-12-08 15:05:19 +09:00
struct drm_mode_fb_cmd2 mode_cmd = { 0 } ;
2011-12-13 14:46:57 +09:00
unsigned long size ;
2011-10-04 19:19:01 +09:00
int ret ;
2019-04-15 16:25:12 +09:00
DRM_DEV_DEBUG_KMS ( dev - > dev ,
" surface width(%d), height(%d) and bpp(%d \n " ,
sizes - > surface_width , sizes - > surface_height ,
sizes - > surface_bpp ) ;
2011-10-04 19:19:01 +09:00
mode_cmd . width = sizes - > surface_width ;
mode_cmd . height = sizes - > surface_height ;
2011-12-08 15:05:19 +09:00
mode_cmd . pitches [ 0 ] = sizes - > surface_width * ( sizes - > surface_bpp > > 3 ) ;
mode_cmd . pixel_format = drm_mode_legacy_fb_format ( sizes - > surface_bpp ,
sizes - > surface_depth ) ;
2011-10-04 19:19:01 +09:00
2011-12-13 14:46:57 +09:00
size = mode_cmd . pitches [ 0 ] * mode_cmd . height ;
2012-03-16 18:47:05 +09:00
2020-04-23 14:43:49 +09:00
exynos_gem = exynos_drm_gem_create ( dev , EXYNOS_BO_WC , size , true ) ;
2016-03-30 11:40:48 +02:00
if ( IS_ERR ( exynos_gem ) )
return PTR_ERR ( exynos_gem ) ;
2011-12-13 14:46:57 +09:00
2015-10-02 09:33:47 +09:00
exynos_fbdev - > exynos_gem = exynos_gem ;
2011-12-13 14:46:57 +09:00
2015-10-02 09:33:47 +09:00
helper - > fb =
exynos_drm_framebuffer_init ( dev , & mode_cmd , & exynos_gem , 1 ) ;
2013-04-29 12:27:05 +05:30
if ( IS_ERR ( helper - > fb ) ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dev - > dev , " failed to create drm framebuffer. \n " ) ;
2011-12-13 14:46:57 +09:00
ret = PTR_ERR ( helper - > fb ) ;
2012-12-07 18:06:43 +09:00
goto err_destroy_gem ;
2011-10-04 19:19:01 +09:00
}
2015-10-02 09:33:47 +09:00
ret = exynos_drm_fbdev_update ( helper , sizes , exynos_gem ) ;
2012-12-07 18:06:43 +09:00
if ( ret < 0 )
2015-07-22 14:58:09 +05:30
goto err_destroy_framebuffer ;
2012-12-07 18:06:43 +09:00
return ret ;
err_destroy_framebuffer :
drm_framebuffer_cleanup ( helper - > fb ) ;
err_destroy_gem :
2015-10-02 09:33:47 +09:00
exynos_drm_gem_destroy ( exynos_gem ) ;
2011-10-04 19:19:01 +09:00
2016-03-30 11:40:48 +02:00
/*
* if failed , all resources allocated above would be released by
* drm_mode_config_cleanup ( ) when drm_load ( ) had been called prior
* to any specific driver such as fimd or hdmi driver .
*/
2011-10-04 19:19:01 +09:00
return ret ;
}
2014-06-27 17:19:23 +02:00
static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
2013-01-21 23:42:49 +01:00
. fb_probe = exynos_drm_fbdev_create ,
2011-10-04 19:19:01 +09:00
} ;
int exynos_drm_fbdev_init ( struct drm_device * dev )
{
struct exynos_drm_fbdev * fbdev ;
struct exynos_drm_private * private = dev - > dev_private ;
struct drm_fb_helper * helper ;
int ret ;
2018-10-26 12:13:28 +02:00
if ( ! dev - > mode_config . num_crtc )
2011-10-04 19:19:01 +09:00
return 0 ;
fbdev = kzalloc ( sizeof ( * fbdev ) , GFP_KERNEL ) ;
2013-08-19 19:04:55 +09:00
if ( ! fbdev )
2011-10-04 19:19:01 +09:00
return - ENOMEM ;
private - > fb_helper = helper = & fbdev - > drm_fb_helper ;
2014-06-27 17:19:24 +02:00
drm_fb_helper_prepare ( dev , helper , & exynos_drm_fb_helper_funcs ) ;
2011-10-04 19:19:01 +09:00
2020-03-05 17:34:28 +05:30
ret = drm_fb_helper_init ( dev , helper ) ;
2011-10-04 19:19:01 +09:00
if ( ret < 0 ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dev - > dev ,
" failed to initialize drm fb helper. \n " ) ;
2011-10-04 19:19:01 +09:00
goto err_init ;
}
ret = drm_fb_helper_initial_config ( helper , PREFERRED_BPP ) ;
if ( ret < 0 ) {
2019-04-15 14:24:36 +09:00
DRM_DEV_ERROR ( dev - > dev ,
" failed to set up hw configuration. \n " ) ;
2011-10-04 19:19:01 +09:00
goto err_setup ;
}
return 0 ;
err_setup :
drm_fb_helper_fini ( helper ) ;
err_init :
private - > fb_helper = NULL ;
kfree ( fbdev ) ;
return ret ;
}
static void exynos_drm_fbdev_destroy ( struct drm_device * dev ,
struct drm_fb_helper * fb_helper )
{
struct drm_framebuffer * fb ;
/* release drm framebuffer and real buffer */
if ( fb_helper - > fb & & fb_helper - > fb - > funcs ) {
fb = fb_helper - > fb ;
2016-12-27 11:49:23 +01:00
if ( fb )
2012-09-05 21:48:38 +00:00
drm_framebuffer_remove ( fb ) ;
2011-10-04 19:19:01 +09:00
}
2015-07-22 14:58:09 +05:30
drm_fb_helper_unregister_fbi ( fb_helper ) ;
2011-10-04 19:19:01 +09:00
drm_fb_helper_fini ( fb_helper ) ;
}
void exynos_drm_fbdev_fini ( struct drm_device * dev )
{
struct exynos_drm_private * private = dev - > dev_private ;
struct exynos_drm_fbdev * fbdev ;
if ( ! private | | ! private - > fb_helper )
return ;
fbdev = to_exynos_fbdev ( private - > fb_helper ) ;
exynos_drm_fbdev_destroy ( dev , private - > fb_helper ) ;
kfree ( fbdev ) ;
private - > fb_helper = NULL ;
}