2013-12-17 18:04:46 +01: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 .
*/
# include "bochs.h"
/* ---------------------------------------------------------------------- */
2014-11-19 12:28:11 +01:00
static int bochsfb_mmap ( struct fb_info * info ,
struct vm_area_struct * vma )
{
struct drm_fb_helper * fb_helper = info - > par ;
struct bochs_device * bochs =
container_of ( fb_helper , struct bochs_device , fb . helper ) ;
struct bochs_bo * bo = gem_to_bochs_bo ( bochs - > fb . gfb . obj ) ;
return ttm_fbdev_mmap ( vma , & bo - > bo ) ;
}
2013-12-17 18:04:46 +01:00
static struct fb_ops bochsfb_ops = {
. owner = THIS_MODULE ,
2016-11-14 00:03:15 +01:00
DRM_FB_HELPER_DEFAULT_OPS ,
2015-07-31 16:21:59 +05:30
. fb_fillrect = drm_fb_helper_sys_fillrect ,
. fb_copyarea = drm_fb_helper_sys_copyarea ,
. fb_imageblit = drm_fb_helper_sys_imageblit ,
2014-11-19 12:28:11 +01:00
. fb_mmap = bochsfb_mmap ,
2013-12-17 18:04:46 +01:00
} ;
static int bochsfb_create_object ( struct bochs_device * bochs ,
2015-11-11 19:11:29 +02:00
const struct drm_mode_fb_cmd2 * mode_cmd ,
2013-12-17 18:04:46 +01:00
struct drm_gem_object * * gobj_p )
{
struct drm_device * dev = bochs - > dev ;
struct drm_gem_object * gobj ;
u32 size ;
int ret = 0 ;
size = mode_cmd - > pitches [ 0 ] * mode_cmd - > height ;
ret = bochs_gem_create ( dev , size , true , & gobj ) ;
if ( ret )
return ret ;
* gobj_p = gobj ;
return ret ;
}
static int bochsfb_create ( struct drm_fb_helper * helper ,
struct drm_fb_helper_surface_size * sizes )
{
struct bochs_device * bochs =
container_of ( helper , struct bochs_device , fb . helper ) ;
struct fb_info * info ;
struct drm_framebuffer * fb ;
struct drm_mode_fb_cmd2 mode_cmd ;
struct drm_gem_object * gobj = NULL ;
struct bochs_bo * bo = NULL ;
int size , ret ;
if ( sizes - > surface_bpp ! = 32 )
return - EINVAL ;
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 ;
/* alloc, pin & map bo */
ret = bochsfb_create_object ( bochs , & mode_cmd , & gobj ) ;
if ( ret ) {
DRM_ERROR ( " failed to create fbcon backing object %d \n " , ret ) ;
return ret ;
}
bo = gem_to_bochs_bo ( gobj ) ;
2016-04-06 11:12:03 +02:00
ret = ttm_bo_reserve ( & bo - > bo , true , false , NULL ) ;
2013-12-17 18:04:46 +01:00
if ( ret )
return ret ;
ret = bochs_bo_pin ( bo , TTM_PL_FLAG_VRAM , NULL ) ;
if ( ret ) {
DRM_ERROR ( " failed to pin fbcon \n " ) ;
ttm_bo_unreserve ( & bo - > bo ) ;
return ret ;
}
ret = ttm_bo_kmap ( & bo - > bo , 0 , bo - > bo . num_pages ,
& bo - > kmap ) ;
if ( ret ) {
DRM_ERROR ( " failed to kmap fbcon \n " ) ;
ttm_bo_unreserve ( & bo - > bo ) ;
return ret ;
}
ttm_bo_unreserve ( & bo - > bo ) ;
/* init fb device */
2015-07-31 16:21:59 +05:30
info = drm_fb_helper_alloc_fbi ( helper ) ;
if ( IS_ERR ( info ) )
return PTR_ERR ( info ) ;
2013-12-17 18:04:46 +01:00
info - > par = & bochs - > fb . helper ;
ret = bochs_framebuffer_init ( bochs - > dev , & bochs - > fb . gfb , & mode_cmd , gobj ) ;
2015-07-31 16:21:59 +05:30
if ( ret ) {
drm_fb_helper_release_fbi ( helper ) ;
2013-12-17 18:04:46 +01:00
return ret ;
2015-07-31 16:21:59 +05:30
}
2013-12-17 18:04:46 +01:00
bochs - > fb . size = size ;
/* setup helper */
fb = & bochs - > fb . gfb . base ;
bochs - > fb . helper . fb = fb ;
strcpy ( info - > fix . id , " bochsdrmfb " ) ;
info - > flags = FBINFO_DEFAULT ;
info - > fbops = & bochsfb_ops ;
drm_fb_helper_fill_fix ( info , fb - > pitches [ 0 ] , fb - > depth ) ;
drm_fb_helper_fill_var ( info , & bochs - > fb . helper , sizes - > fb_width ,
sizes - > fb_height ) ;
info - > screen_base = bo - > kmap . virtual ;
info - > screen_size = size ;
2014-11-19 12:28:11 +01:00
drm_vma_offset_remove ( & bo - > bo . bdev - > vma_manager , & bo - > bo . vma_node ) ;
info - > fix . smem_start = 0 ;
2013-12-17 18:04:46 +01:00
info - > fix . smem_len = size ;
return 0 ;
}
static int bochs_fbdev_destroy ( struct bochs_device * bochs )
{
struct bochs_framebuffer * gfb = & bochs - > fb . gfb ;
DRM_DEBUG_DRIVER ( " \n " ) ;
2015-07-31 16:21:59 +05:30
drm_fb_helper_unregister_fbi ( & bochs - > fb . helper ) ;
drm_fb_helper_release_fbi ( & bochs - > fb . helper ) ;
2013-12-17 18:04:46 +01:00
if ( gfb - > obj ) {
drm_gem_object_unreference_unlocked ( gfb - > obj ) ;
gfb - > obj = NULL ;
}
drm_fb_helper_fini ( & bochs - > fb . helper ) ;
drm_framebuffer_unregister_private ( & gfb - > base ) ;
drm_framebuffer_cleanup ( & gfb - > base ) ;
return 0 ;
}
2014-06-27 17:19:23 +02:00
static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
2013-12-17 18:04:46 +01:00
. fb_probe = bochsfb_create ,
} ;
int bochs_fbdev_init ( struct bochs_device * bochs )
{
int ret ;
2014-06-27 17:19:24 +02:00
drm_fb_helper_prepare ( bochs - > dev , & bochs - > fb . helper ,
& bochs_fb_helper_funcs ) ;
2013-12-17 18:04:46 +01:00
ret = drm_fb_helper_init ( bochs - > dev , & bochs - > fb . helper ,
1 , 1 ) ;
if ( ret )
return ret ;
2014-12-19 11:21:32 +01:00
ret = drm_fb_helper_single_add_all_connectors ( & bochs - > fb . helper ) ;
if ( ret )
goto fini ;
2013-12-17 18:04:46 +01:00
drm_helper_disable_unused_functions ( bochs - > dev ) ;
2014-12-19 11:21:32 +01:00
ret = drm_fb_helper_initial_config ( & bochs - > fb . helper , 32 ) ;
if ( ret )
goto fini ;
2013-12-17 18:04:46 +01:00
bochs - > fb . initialized = true ;
return 0 ;
2014-12-19 11:21:32 +01:00
fini :
drm_fb_helper_fini ( & bochs - > fb . helper ) ;
return ret ;
2013-12-17 18:04:46 +01:00
}
void bochs_fbdev_fini ( struct bochs_device * bochs )
{
if ( ! bochs - > fb . initialized )
return ;
bochs_fbdev_destroy ( bochs ) ;
bochs - > fb . initialized = false ;
}