2012-04-17 17:12:29 +04:00
/*
* Copyright 2012 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details .
*
* Authors : Matthew Garrett
* Dave Airlie
*/
# include <linux/module.h>
2012-10-02 21:01:07 +04:00
# include <drm/drmP.h>
# include <drm/drm_fb_helper.h>
2013-01-21 02:12:54 +04:00
# include <drm/drm_crtc_helper.h>
2012-04-17 17:12:29 +04:00
# include "cirrus_drv.h"
static void cirrus_dirty_update ( struct cirrus_fbdev * afbdev ,
int x , int y , int width , int height )
{
int i ;
struct drm_gem_object * obj ;
struct cirrus_bo * bo ;
int src_offset , dst_offset ;
2018-07-20 14:27:43 +03:00
int bpp = afbdev - > gfb - > format - > cpp [ 0 ] ;
2013-06-27 15:38:24 +04:00
int ret = - EBUSY ;
2012-04-17 17:12:29 +04:00
bool unmap = false ;
2013-05-02 10:45:02 +04:00
bool store_for_later = false ;
int x2 , y2 ;
unsigned long flags ;
2012-04-17 17:12:29 +04:00
2018-07-20 14:27:43 +03:00
obj = afbdev - > gfb - > obj [ 0 ] ;
2012-04-17 17:12:29 +04:00
bo = gem_to_cirrus_bo ( obj ) ;
2013-05-02 10:45:02 +04:00
/*
* try and reserve the BO , if we fail with busy
* then the BO is being moved and we should
* store up the damage until later .
*/
2014-02-05 08:47:45 +04:00
if ( drm_can_sleep ( ) )
2013-06-27 15:38:24 +04:00
ret = cirrus_bo_reserve ( bo , true ) ;
2012-04-17 17:12:29 +04:00
if ( ret ) {
2013-05-02 10:45:02 +04:00
if ( ret ! = - EBUSY )
return ;
store_for_later = true ;
}
x2 = x + width - 1 ;
y2 = y + height - 1 ;
spin_lock_irqsave ( & afbdev - > dirty_lock , flags ) ;
if ( afbdev - > y1 < y )
y = afbdev - > y1 ;
if ( afbdev - > y2 > y2 )
y2 = afbdev - > y2 ;
if ( afbdev - > x1 < x )
x = afbdev - > x1 ;
if ( afbdev - > x2 > x2 )
x2 = afbdev - > x2 ;
if ( store_for_later ) {
afbdev - > x1 = x ;
afbdev - > x2 = x2 ;
afbdev - > y1 = y ;
afbdev - > y2 = y2 ;
spin_unlock_irqrestore ( & afbdev - > dirty_lock , flags ) ;
2012-04-17 17:12:29 +04:00
return ;
}
2013-05-02 10:45:02 +04:00
afbdev - > x1 = afbdev - > y1 = INT_MAX ;
afbdev - > x2 = afbdev - > y2 = 0 ;
spin_unlock_irqrestore ( & afbdev - > dirty_lock , flags ) ;
2012-04-17 17:12:29 +04:00
if ( ! bo - > kmap . virtual ) {
ret = ttm_bo_kmap ( & bo - > bo , 0 , bo - > bo . num_pages , & bo - > kmap ) ;
if ( ret ) {
DRM_ERROR ( " failed to kmap fb updates \n " ) ;
cirrus_bo_unreserve ( bo ) ;
return ;
}
unmap = true ;
}
for ( i = y ; i < y + height ; i + + ) {
/* assume equal stride for now */
2018-07-20 14:27:43 +03:00
src_offset = dst_offset = i * afbdev - > gfb - > pitches [ 0 ] + ( x * bpp ) ;
2012-04-17 17:12:29 +04:00
memcpy_toio ( bo - > kmap . virtual + src_offset , afbdev - > sysram + src_offset , width * bpp ) ;
}
if ( unmap )
ttm_bo_kunmap ( & bo - > kmap ) ;
cirrus_bo_unreserve ( bo ) ;
}
static void cirrus_fillrect ( struct fb_info * info ,
const struct fb_fillrect * rect )
{
struct cirrus_fbdev * afbdev = info - > par ;
2015-07-31 13:51:44 +03:00
drm_fb_helper_sys_fillrect ( info , rect ) ;
2012-04-17 17:12:29 +04:00
cirrus_dirty_update ( afbdev , rect - > dx , rect - > dy , rect - > width ,
rect - > height ) ;
}
static void cirrus_copyarea ( struct fb_info * info ,
const struct fb_copyarea * area )
{
struct cirrus_fbdev * afbdev = info - > par ;
2015-07-31 13:51:44 +03:00
drm_fb_helper_sys_copyarea ( info , area ) ;
2012-04-17 17:12:29 +04:00
cirrus_dirty_update ( afbdev , area - > dx , area - > dy , area - > width ,
area - > height ) ;
}
static void cirrus_imageblit ( struct fb_info * info ,
const struct fb_image * image )
{
struct cirrus_fbdev * afbdev = info - > par ;
2015-07-31 13:51:44 +03:00
drm_fb_helper_sys_imageblit ( info , image ) ;
2012-04-17 17:12:29 +04:00
cirrus_dirty_update ( afbdev , image - > dx , image - > dy , image - > width ,
image - > height ) ;
}
static struct fb_ops cirrusfb_ops = {
. owner = THIS_MODULE ,
. fb_check_var = drm_fb_helper_check_var ,
. fb_set_par = drm_fb_helper_set_par ,
. fb_fillrect = cirrus_fillrect ,
. fb_copyarea = cirrus_copyarea ,
. fb_imageblit = cirrus_imageblit ,
. fb_pan_display = drm_fb_helper_pan_display ,
. fb_blank = drm_fb_helper_blank ,
. fb_setcmap = drm_fb_helper_setcmap ,
} ;
static int cirrusfb_create_object ( struct cirrus_fbdev * afbdev ,
2015-11-11 20:11:29 +03:00
const struct drm_mode_fb_cmd2 * mode_cmd ,
2012-04-17 17:12:29 +04:00
struct drm_gem_object * * gobj_p )
{
struct drm_device * dev = afbdev - > helper . dev ;
2014-10-29 21:04:24 +03:00
struct cirrus_device * cdev = dev - > dev_private ;
2016-10-18 01:41:15 +03:00
u32 bpp ;
2012-04-17 17:12:29 +04:00
u32 size ;
struct drm_gem_object * gobj ;
int ret = 0 ;
2016-10-18 01:41:15 +03:00
bpp = drm_format_plane_cpp ( mode_cmd - > pixel_format , 0 ) * 8 ;
2012-04-17 17:12:29 +04:00
2014-10-29 21:04:24 +03:00
if ( ! cirrus_check_framebuffer ( cdev , mode_cmd - > width , mode_cmd - > height ,
bpp , mode_cmd - > pitches [ 0 ] ) )
2012-04-17 17:12:29 +04:00
return - EINVAL ;
2014-10-29 21:04:24 +03:00
2012-04-17 17:12:29 +04:00
size = mode_cmd - > pitches [ 0 ] * mode_cmd - > height ;
ret = cirrus_gem_create ( dev , size , true , & gobj ) ;
if ( ret )
return ret ;
* gobj_p = gobj ;
return ret ;
}
2013-01-22 02:42:49 +04:00
static int cirrusfb_create ( struct drm_fb_helper * helper ,
2012-04-17 17:12:29 +04:00
struct drm_fb_helper_surface_size * sizes )
{
2014-09-14 20:40:14 +04:00
struct cirrus_fbdev * gfbdev =
container_of ( helper , struct cirrus_fbdev , helper ) ;
2012-04-17 17:12:29 +04:00
struct cirrus_device * cdev = gfbdev - > helper . dev - > dev_private ;
struct fb_info * info ;
struct drm_framebuffer * fb ;
struct drm_mode_fb_cmd2 mode_cmd ;
void * sysram ;
struct drm_gem_object * gobj = NULL ;
struct cirrus_bo * bo = NULL ;
int size , ret ;
mode_cmd . width = sizes - > surface_width ;
mode_cmd . height = sizes - > surface_height ;
mode_cmd . pitches [ 0 ] = mode_cmd . width * ( ( sizes - > surface_bpp + 7 ) / 8 ) ;
mode_cmd . pixel_format = drm_mode_legacy_fb_format ( sizes - > surface_bpp ,
sizes - > surface_depth ) ;
size = mode_cmd . pitches [ 0 ] * mode_cmd . height ;
ret = cirrusfb_create_object ( gfbdev , & mode_cmd , & gobj ) ;
if ( ret ) {
DRM_ERROR ( " failed to create fbcon backing object %d \n " , ret ) ;
return ret ;
}
bo = gem_to_cirrus_bo ( gobj ) ;
sysram = vmalloc ( size ) ;
if ( ! sysram )
return - ENOMEM ;
2015-07-31 13:51:44 +03:00
info = drm_fb_helper_alloc_fbi ( helper ) ;
2018-07-20 14:27:43 +03:00
if ( IS_ERR ( info ) ) {
ret = PTR_ERR ( info ) ;
goto err_vfree ;
}
2012-04-17 17:12:29 +04:00
info - > par = gfbdev ;
2018-07-20 14:27:43 +03:00
fb = kzalloc ( sizeof ( * fb ) , GFP_KERNEL ) ;
if ( ! fb ) {
ret = - ENOMEM ;
goto err_drm_gem_object_put_unlocked ;
}
ret = cirrus_framebuffer_init ( cdev - > dev , fb , & mode_cmd , gobj ) ;
2012-04-17 17:12:29 +04:00
if ( ret )
2018-07-20 14:27:43 +03:00
goto err_kfree ;
2012-04-17 17:12:29 +04:00
gfbdev - > sysram = sysram ;
gfbdev - > size = size ;
2018-07-20 14:27:43 +03:00
gfbdev - > gfb = fb ;
2012-04-17 17:12:29 +04:00
/* setup helper */
gfbdev - > helper . fb = fb ;
strcpy ( info - > fix . id , " cirrusdrmfb " ) ;
info - > fbops = & cirrusfb_ops ;
2016-12-15 00:31:35 +03:00
drm_fb_helper_fill_fix ( info , fb - > pitches [ 0 ] , fb - > format - > depth ) ;
2012-04-17 17:12:29 +04:00
drm_fb_helper_fill_var ( info , & gfbdev - > helper , sizes - > fb_width ,
sizes - > fb_height ) ;
/* setup aperture base/size for vesafb takeover */
info - > apertures - > ranges [ 0 ] . base = cdev - > dev - > mode_config . fb_base ;
info - > apertures - > ranges [ 0 ] . size = cdev - > mc . vram_size ;
2014-01-09 13:05:07 +04:00
info - > fix . smem_start = cdev - > dev - > mode_config . fb_base ;
info - > fix . smem_len = cdev - > mc . vram_size ;
2012-04-17 17:12:29 +04:00
info - > screen_base = sysram ;
info - > screen_size = size ;
info - > fix . mmio_start = 0 ;
info - > fix . mmio_len = 0 ;
DRM_INFO ( " fb mappable at 0x%lX \n " , info - > fix . smem_start ) ;
DRM_INFO ( " vram aper at 0x%lX \n " , ( unsigned long ) info - > fix . smem_start ) ;
DRM_INFO ( " size %lu \n " , ( unsigned long ) info - > fix . smem_len ) ;
2016-12-15 00:31:35 +03:00
DRM_INFO ( " fb depth is %d \n " , fb - > format - > depth ) ;
2012-04-17 17:12:29 +04:00
DRM_INFO ( " pitch is %d \n " , fb - > pitches [ 0 ] ) ;
return 0 ;
2018-07-20 14:27:43 +03:00
err_kfree :
kfree ( fb ) ;
err_drm_gem_object_put_unlocked :
drm_gem_object_put_unlocked ( gobj ) ;
err_vfree :
vfree ( sysram ) ;
return ret ;
2012-04-17 17:12:29 +04:00
}
static int cirrus_fbdev_destroy ( struct drm_device * dev ,
struct cirrus_fbdev * gfbdev )
{
2018-07-20 14:27:43 +03:00
struct drm_framebuffer * gfb = gfbdev - > gfb ;
2012-04-17 17:12:29 +04:00
2015-07-31 13:51:44 +03:00
drm_fb_helper_unregister_fbi ( & gfbdev - > helper ) ;
2012-04-17 17:12:29 +04:00
vfree ( gfbdev - > sysram ) ;
drm_fb_helper_fini ( & gfbdev - > helper ) ;
2018-07-20 14:27:43 +03:00
if ( gfb )
drm_framebuffer_put ( gfb ) ;
2012-04-17 17:12:29 +04:00
return 0 ;
}
2014-06-27 19:19:23 +04:00
static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
2013-01-22 02:42:49 +04:00
. fb_probe = cirrusfb_create ,
2012-04-17 17:12:29 +04:00
} ;
int cirrus_fbdev_init ( struct cirrus_device * cdev )
{
struct cirrus_fbdev * gfbdev ;
int ret ;
/*bpp_sel = 8;*/
gfbdev = kzalloc ( sizeof ( struct cirrus_fbdev ) , GFP_KERNEL ) ;
if ( ! gfbdev )
return - ENOMEM ;
cdev - > mode_info . gfbdev = gfbdev ;
2013-05-02 10:45:02 +04:00
spin_lock_init ( & gfbdev - > dirty_lock ) ;
2012-04-17 17:12:29 +04:00
2014-06-27 19:19:24 +04:00
drm_fb_helper_prepare ( cdev - > dev , & gfbdev - > helper ,
& cirrus_fb_helper_funcs ) ;
2012-04-17 17:12:29 +04:00
ret = drm_fb_helper_init ( cdev - > dev , & gfbdev - > helper ,
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 19:26:40 +03:00
CIRRUSFB_CONN_LIMIT ) ;
2014-12-19 13:21:32 +03:00
if ( ret )
return ret ;
ret = drm_fb_helper_single_add_all_connectors ( & gfbdev - > helper ) ;
if ( ret )
2012-04-17 17:12:29 +04:00
return ret ;
2013-01-21 02:12:54 +04:00
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions ( cdev - > dev ) ;
2012-04-17 17:12:29 +04:00
2018-08-08 14:13:11 +03:00
return drm_fb_helper_initial_config ( & gfbdev - > helper , cirrus_bpp ) ;
2012-04-17 17:12:29 +04:00
}
void cirrus_fbdev_fini ( struct cirrus_device * cdev )
{
if ( ! cdev - > mode_info . gfbdev )
return ;
cirrus_fbdev_destroy ( cdev - > dev , cdev - > mode_info . gfbdev ) ;
kfree ( cdev - > mode_info . gfbdev ) ;
cdev - > mode_info . gfbdev = NULL ;
}