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 >
*
2012-12-18 02:30:17 +09:00
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
2011-10-04 19:19:01 +09:00
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_fb_helper.h>
# include <drm/drm_crtc_helper.h>
2013-08-06 17:22:04 +05:30
# include <drm/exynos_drm.h>
2011-10-04 19:19:01 +09:00
2017-09-14 14:01:01 +02:00
# include <linux/console.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"
2012-12-21 17:59:20 +09:00
# include "exynos_drm_iommu.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
unsigned long vm_size ;
int ret ;
vma - > vm_flags | = VM_IO | VM_DONTEXPAND | VM_DONTDUMP ;
vm_size = vma - > vm_end - vma - > vm_start ;
2015-10-02 09:33:47 +09:00
if ( vm_size > exynos_gem - > size )
2012-11-19 13:55:28 +05:30
return - EINVAL ;
2016-02-29 17:50:53 +09:00
ret = dma_mmap_attrs ( to_dma_dev ( helper - > dev ) , vma , exynos_gem - > cookie ,
2015-10-02 09:33:47 +09:00
exynos_gem - > dma_addr , exynos_gem - > size ,
2016-08-03 13:46:00 -07:00
exynos_gem - > dma_attrs ) ;
2012-11-19 13:55:28 +05:30
if ( ret < 0 ) {
DRM_ERROR ( " failed to mmap. \n " ) ;
return ret ;
}
return 0 ;
}
2011-10-04 19:19:01 +09:00
static struct fb_ops exynos_drm_fb_ops = {
. 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 ] ;
2015-02-04 10:23:19 +01:00
unsigned int nr_pages ;
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 ) ) {
DRM_ERROR ( " failed to allocate fb info. \n " ) ;
return PTR_ERR ( fbi ) ;
}
fbi - > par = helper ;
fbi - > flags = FBINFO_FLAG_DEFAULT ;
fbi - > fbops = & exynos_drm_fb_ops ;
2016-12-14 23:31:35 +02:00
drm_fb_helper_fill_fix ( fbi , fb - > pitches [ 0 ] , fb - > format - > depth ) ;
2015-03-11 10:23:11 -04:00
drm_fb_helper_fill_var ( fbi , helper , sizes - > fb_width , sizes - > fb_height ) ;
2011-10-04 19:19:01 +09:00
2015-10-02 09:33:47 +09:00
nr_pages = exynos_gem - > size > > PAGE_SHIFT ;
2015-02-04 10:23:19 +01:00
2015-10-02 09:33:47 +09:00
exynos_gem - > kvaddr = ( void __iomem * ) vmap ( exynos_gem - > pages , nr_pages ,
VM_MAP , pgprot_writecombine ( PAGE_KERNEL ) ) ;
if ( ! exynos_gem - > kvaddr ) {
2015-02-04 10:23:19 +01:00
DRM_ERROR ( " failed to map pages to kernel space. \n " ) ;
return - EIO ;
2012-12-07 17:51:27 +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
2015-10-02 09:33:47 +09:00
fbi - > screen_base = 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 ;
DRM_DEBUG_KMS ( " surface width(%d), height(%d) and bpp(%d \n " ,
sizes - > surface_width , sizes - > surface_height ,
sizes - > surface_bpp ) ;
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
2015-10-02 09:33:47 +09:00
exynos_gem = exynos_drm_gem_create ( dev , EXYNOS_BO_CONTIG , size ) ;
2013-08-06 17:22:04 +05:30
/*
* If physically contiguous memory allocation fails and if IOMMU is
* supported then try to get buffer from non physically contiguous
* memory area .
*/
2015-10-02 09:33:47 +09:00
if ( IS_ERR ( exynos_gem ) & & is_drm_iommu_supported ( dev ) ) {
2016-12-12 11:28:47 +02:00
dev_warn ( dev - > dev , " contiguous FB allocation failed, falling back to non-contiguous \n " ) ;
2015-10-02 09:33:47 +09:00
exynos_gem = exynos_drm_gem_create ( dev , EXYNOS_BO_NONCONTIG ,
size ) ;
2013-08-06 17:22:04 +05:30
}
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 ) ) {
2011-10-04 19:19:01 +09:00
DRM_ERROR ( " 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 ;
if ( ! dev - > mode_config . num_crtc | | ! dev - > mode_config . num_connector )
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
drm: Rely on mode_config data for fb_helper initialization
Instead of receiving the num_crts as a parameter, we can read it
directly from the mode_config structure. I audited the drivers that
invoke this helper and I believe all of them initialize the mode_config
struct accordingly, prior to calling the fb_helper.
I used the following coccinelle hack to make this transformation, except
for the function headers and comment updates. The first and second
rules are split because I couldn't find a way to remove the unused
temporary variables at the same time I removed the parameter.
// <smpl>
@r@
expression A,B,D,E;
identifier C;
@@
(
- drm_fb_helper_init(A,B,C,D)
+ drm_fb_helper_init(A,B,D)
|
- drm_fbdev_cma_init_with_funcs(A,B,C,D,E)
+ drm_fbdev_cma_init_with_funcs(A,B,D,E)
|
- drm_fbdev_cma_init(A,B,C,D)
+ drm_fbdev_cma_init(A,B,D)
)
@@
expression A,B,C,D,E;
@@
(
- drm_fb_helper_init(A,B,C,D)
+ drm_fb_helper_init(A,B,D)
|
- drm_fbdev_cma_init_with_funcs(A,B,C,D,E)
+ drm_fbdev_cma_init_with_funcs(A,B,D,E)
|
- drm_fbdev_cma_init(A,B,C,D)
+ drm_fbdev_cma_init(A,B,D)
)
@@
identifier r.C;
type T;
expression V;
@@
- T C;
<...
when != C
- C = V;
...>
// </smpl>
Changes since v1:
- Rebased on top of the tip of drm-misc-next.
- Remove mention to sti since a proper fix got merged.
Suggested-by: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
Reviewed-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20170202162640.27261-1-krisman@collabora.co.uk
2017-02-02 14:26:40 -02:00
ret = drm_fb_helper_init ( dev , helper , MAX_CONNECTOR ) ;
2011-10-04 19:19:01 +09:00
if ( ret < 0 ) {
DRM_ERROR ( " failed to initialize drm fb helper. \n " ) ;
goto err_init ;
}
ret = drm_fb_helper_single_add_all_connectors ( helper ) ;
if ( ret < 0 ) {
DRM_ERROR ( " failed to register drm_fb_helper_connector. \n " ) ;
goto err_setup ;
}
ret = drm_fb_helper_initial_config ( helper , PREFERRED_BPP ) ;
if ( ret < 0 ) {
DRM_ERROR ( " failed to set up hw configuration. \n " ) ;
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 )
{
2012-12-07 17:51:27 +09:00
struct exynos_drm_fbdev * exynos_fbd = to_exynos_fbdev ( fb_helper ) ;
2015-10-02 09:33:47 +09:00
struct exynos_drm_gem * exynos_gem = exynos_fbd - > exynos_gem ;
2011-10-04 19:19:01 +09:00
struct drm_framebuffer * fb ;
2016-07-21 19:23:25 +02:00
vunmap ( exynos_gem - > kvaddr ) ;
2012-12-07 17:51:27 +09:00
2011-10-04 19:19:01 +09:00
/* 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 ;
}
2017-09-14 14:01:01 +02:00
void exynos_drm_fbdev_suspend ( struct drm_device * dev )
{
struct exynos_drm_private * private = dev - > dev_private ;
console_lock ( ) ;
drm_fb_helper_set_suspend ( private - > fb_helper , 1 ) ;
console_unlock ( ) ;
}
void exynos_drm_fbdev_resume ( struct drm_device * dev )
{
struct exynos_drm_private * private = dev - > dev_private ;
console_lock ( ) ;
drm_fb_helper_set_suspend ( private - > fb_helper , 0 ) ;
console_unlock ( ) ;
}