2009-12-10 00:19:58 +00:00
/**************************************************************************
*
* Copyright © 2007 David Airlie
2015-07-29 12:38:02 -07:00
* Copyright © 2009 - 2015 VMware , Inc . , Palo Alto , CA . , USA
2009-12-10 00:19:58 +00:00
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sub license , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial portions
* of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NON - INFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS , AUTHORS AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM ,
* DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR
* OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-06-23 12:23:34 +02:00
# include <linux/pci.h>
# include <drm/drm_fourcc.h>
# include <drm/ttm/ttm_placement.h>
2011-08-30 18:16:33 -04:00
2009-12-10 00:19:58 +00:00
# include "vmwgfx_drv.h"
2015-06-29 12:55:07 -07:00
# include "vmwgfx_kms.h"
2009-12-10 00:19:58 +00:00
# define VMW_DIRTY_DELAY (HZ / 30)
struct vmw_fb_par {
struct vmw_private * vmw_priv ;
void * vmalloc ;
2015-06-29 12:55:07 -07:00
struct mutex bo_mutex ;
2018-06-19 15:02:16 +02:00
struct vmw_buffer_object * vmw_bo ;
2015-06-29 12:55:07 -07:00
unsigned bo_size ;
struct drm_framebuffer * set_fb ;
struct drm_display_mode * set_mode ;
u32 fb_x ;
u32 fb_y ;
bool bo_iowrite ;
2009-12-10 00:19:58 +00:00
u32 pseudo_palette [ 17 ] ;
unsigned max_width ;
unsigned max_height ;
struct {
spinlock_t lock ;
bool active ;
unsigned x1 ;
unsigned y1 ;
unsigned x2 ;
unsigned y2 ;
} dirty ;
2015-06-29 12:55:07 -07:00
struct drm_crtc * crtc ;
struct drm_connector * con ;
2015-08-18 09:07:38 -07:00
struct delayed_work local_work ;
2009-12-10 00:19:58 +00:00
} ;
static int vmw_fb_setcolreg ( unsigned regno , unsigned red , unsigned green ,
unsigned blue , unsigned transp ,
struct fb_info * info )
{
struct vmw_fb_par * par = info - > par ;
u32 * pal = par - > pseudo_palette ;
if ( regno > 15 ) {
DRM_ERROR ( " Bad regno %u. \n " , regno ) ;
return 1 ;
}
2016-12-14 23:31:35 +02:00
switch ( par - > set_fb - > format - > depth ) {
2009-12-10 00:19:58 +00:00
case 24 :
case 32 :
pal [ regno ] = ( ( red & 0xff00 ) < < 8 ) |
( green & 0xff00 ) |
( ( blue & 0xff00 ) > > 8 ) ;
break ;
default :
2016-12-14 23:31:35 +02:00
DRM_ERROR ( " Bad depth %u, bpp %u. \n " ,
par - > set_fb - > format - > depth ,
2016-12-14 23:32:20 +02:00
par - > set_fb - > format - > cpp [ 0 ] * 8 ) ;
2009-12-10 00:19:58 +00:00
return 1 ;
}
return 0 ;
}
static int vmw_fb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
int depth = var - > bits_per_pixel ;
struct vmw_fb_par * par = info - > par ;
struct vmw_private * vmw_priv = par - > vmw_priv ;
switch ( var - > bits_per_pixel ) {
case 32 :
depth = ( var - > transp . length > 0 ) ? 32 : 24 ;
break ;
default :
DRM_ERROR ( " Bad bpp %u. \n " , var - > bits_per_pixel ) ;
return - EINVAL ;
}
switch ( depth ) {
case 24 :
var - > red . offset = 16 ;
var - > green . offset = 8 ;
var - > blue . offset = 0 ;
var - > red . length = 8 ;
var - > green . length = 8 ;
var - > blue . length = 8 ;
var - > transp . length = 0 ;
var - > transp . offset = 0 ;
break ;
case 32 :
var - > red . offset = 16 ;
var - > green . offset = 8 ;
var - > blue . offset = 0 ;
var - > red . length = 8 ;
var - > green . length = 8 ;
var - > blue . length = 8 ;
var - > transp . length = 8 ;
var - > transp . offset = 24 ;
break ;
default :
DRM_ERROR ( " Bad depth %u. \n " , depth ) ;
return - EINVAL ;
}
2010-05-28 11:21:59 +02:00
if ( ( var - > xoffset + var - > xres ) > par - > max_width | |
( var - > yoffset + var - > yres ) > par - > max_height ) {
2009-12-10 00:19:58 +00:00
DRM_ERROR ( " Requested geom can not fit in framebuffer \n " ) ;
return - EINVAL ;
}
2010-10-05 12:43:04 +02:00
if ( ! vmw_kms_validate_mode_vram ( vmw_priv ,
2014-02-01 10:01:15 -05:00
var - > xres * var - > bits_per_pixel / 8 ,
2010-10-05 12:43:04 +02:00
var - > yoffset + var - > yres ) ) {
DRM_ERROR ( " Requested geom can not fit in framebuffer \n " ) ;
return - EINVAL ;
}
2009-12-10 00:19:58 +00:00
return 0 ;
}
static int vmw_fb_blank ( int blank , struct fb_info * info )
{
return 0 ;
}
2018-03-22 10:26:37 +01:00
/**
* vmw_fb_dirty_flush - flush dirty regions to the kms framebuffer
*
* @ work : The struct work_struct associated with this task .
*
* This function flushes the dirty regions of the vmalloc framebuffer to the
* kms framebuffer , and if the kms framebuffer is visible , also updated the
* corresponding displays . Note that this function runs even if the kms
* framebuffer is not bound to a crtc and thus not visible , but it ' s turned
* off during hibernation using the par - > dirty . active bool .
2009-12-10 00:19:58 +00:00
*/
2015-08-18 09:07:38 -07:00
static void vmw_fb_dirty_flush ( struct work_struct * work )
2009-12-10 00:19:58 +00:00
{
2015-08-18 09:07:38 -07:00
struct vmw_fb_par * par = container_of ( work , struct vmw_fb_par ,
local_work . work ) ;
2009-12-10 00:19:58 +00:00
struct vmw_private * vmw_priv = par - > vmw_priv ;
struct fb_info * info = vmw_priv - > fb_info ;
2015-06-29 12:55:07 -07:00
unsigned long irq_flags ;
2018-03-22 10:19:01 +01:00
s32 dst_x1 , dst_x2 , dst_y1 , dst_y2 , w = 0 , h = 0 ;
2015-06-29 12:55:07 -07:00
u32 cpp , max_x , max_y ;
struct drm_clip_rect clip ;
struct drm_framebuffer * cur_fb ;
u8 * src_ptr , * dst_ptr ;
2018-06-19 15:02:16 +02:00
struct vmw_buffer_object * vbo = par - > vmw_bo ;
2018-03-22 10:19:01 +01:00
void * virtual ;
2009-12-10 00:19:58 +00:00
2018-03-22 10:30:19 +01:00
if ( ! READ_ONCE ( par - > dirty . active ) )
2010-10-05 12:43:05 +02:00
return ;
2015-06-29 12:55:07 -07:00
mutex_lock ( & par - > bo_mutex ) ;
cur_fb = par - > set_fb ;
if ( ! cur_fb )
goto out_unlock ;
2009-12-10 00:19:58 +00:00
2018-03-22 10:19:01 +01:00
( void ) ttm_bo_reserve ( & vbo - > base , false , false , NULL ) ;
2018-06-19 15:33:53 +02:00
virtual = vmw_bo_map_and_cache ( vbo ) ;
2018-03-22 10:19:01 +01:00
if ( ! virtual )
goto out_unreserve ;
2015-06-29 12:55:07 -07:00
spin_lock_irqsave ( & par - > dirty . lock , irq_flags ) ;
if ( ! par - > dirty . active ) {
spin_unlock_irqrestore ( & par - > dirty . lock , irq_flags ) ;
2018-03-22 10:19:01 +01:00
goto out_unreserve ;
2009-12-10 00:19:58 +00:00
}
2015-06-29 12:55:07 -07:00
/*
* Handle panning when copying from vmalloc to framebuffer .
* Clip dirty area to framebuffer .
*/
2016-12-14 23:32:20 +02:00
cpp = cur_fb - > format - > cpp [ 0 ] ;
2015-06-29 12:55:07 -07:00
max_x = par - > fb_x + cur_fb - > width ;
max_y = par - > fb_y + cur_fb - > height ;
dst_x1 = par - > dirty . x1 - par - > fb_x ;
dst_y1 = par - > dirty . y1 - par - > fb_y ;
dst_x1 = max_t ( s32 , dst_x1 , 0 ) ;
dst_y1 = max_t ( s32 , dst_y1 , 0 ) ;
dst_x2 = par - > dirty . x2 - par - > fb_x ;
dst_y2 = par - > dirty . y2 - par - > fb_y ;
dst_x2 = min_t ( s32 , dst_x2 , max_x ) ;
dst_y2 = min_t ( s32 , dst_y2 , max_y ) ;
w = dst_x2 - dst_x1 ;
h = dst_y2 - dst_y1 ;
w = max_t ( s32 , 0 , w ) ;
h = max_t ( s32 , 0 , h ) ;
2009-12-10 00:19:58 +00:00
2015-06-29 12:55:07 -07:00
par - > dirty . x1 = par - > dirty . x2 = 0 ;
par - > dirty . y1 = par - > dirty . y2 = 0 ;
spin_unlock_irqrestore ( & par - > dirty . lock , irq_flags ) ;
if ( w & & h ) {
2018-03-22 10:19:01 +01:00
dst_ptr = ( u8 * ) virtual +
2015-06-29 12:55:07 -07:00
( dst_y1 * par - > set_fb - > pitches [ 0 ] + dst_x1 * cpp ) ;
src_ptr = ( u8 * ) par - > vmalloc +
( ( dst_y1 + par - > fb_y ) * info - > fix . line_length +
( dst_x1 + par - > fb_x ) * cpp ) ;
while ( h - - > 0 ) {
memcpy ( dst_ptr , src_ptr , w * cpp ) ;
dst_ptr + = par - > set_fb - > pitches [ 0 ] ;
src_ptr + = info - > fix . line_length ;
}
clip . x1 = dst_x1 ;
clip . x2 = dst_x2 ;
clip . y1 = dst_y1 ;
clip . y2 = dst_y2 ;
2018-03-22 10:19:01 +01:00
}
2015-06-29 12:55:07 -07:00
2018-03-22 10:19:01 +01:00
out_unreserve :
ttm_bo_unreserve ( & vbo - > base ) ;
if ( w & & h ) {
2015-06-29 12:55:07 -07:00
WARN_ON_ONCE ( par - > set_fb - > funcs - > dirty ( cur_fb , NULL , 0 , 0 ,
& clip , 1 ) ) ;
2020-11-18 12:54:19 -05:00
vmw_cmd_flush ( vmw_priv , false ) ;
2009-12-10 00:19:58 +00:00
}
2015-06-29 12:55:07 -07:00
out_unlock :
mutex_unlock ( & par - > bo_mutex ) ;
2009-12-10 00:19:58 +00:00
}
static void vmw_fb_dirty_mark ( struct vmw_fb_par * par ,
unsigned x1 , unsigned y1 ,
unsigned width , unsigned height )
{
unsigned long flags ;
unsigned x2 = x1 + width ;
unsigned y2 = y1 + height ;
spin_lock_irqsave ( & par - > dirty . lock , flags ) ;
if ( par - > dirty . x1 = = par - > dirty . x2 ) {
par - > dirty . x1 = x1 ;
par - > dirty . y1 = y1 ;
par - > dirty . x2 = x2 ;
par - > dirty . y2 = y2 ;
/* if we are active start the dirty work
* we share the work with the defio system */
if ( par - > dirty . active )
2015-08-18 09:07:38 -07:00
schedule_delayed_work ( & par - > local_work ,
VMW_DIRTY_DELAY ) ;
2009-12-10 00:19:58 +00:00
} else {
if ( x1 < par - > dirty . x1 )
par - > dirty . x1 = x1 ;
if ( y1 < par - > dirty . y1 )
par - > dirty . y1 = y1 ;
if ( x2 > par - > dirty . x2 )
par - > dirty . x2 = x2 ;
if ( y2 > par - > dirty . y2 )
par - > dirty . y2 = y2 ;
}
spin_unlock_irqrestore ( & par - > dirty . lock , flags ) ;
}
2015-06-29 12:55:07 -07:00
static int vmw_fb_pan_display ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
struct vmw_fb_par * par = info - > par ;
if ( ( var - > xoffset + var - > xres ) > var - > xres_virtual | |
( var - > yoffset + var - > yres ) > var - > yres_virtual ) {
DRM_ERROR ( " Requested panning can not fit in framebuffer \n " ) ;
return - EINVAL ;
}
mutex_lock ( & par - > bo_mutex ) ;
par - > fb_x = var - > xoffset ;
par - > fb_y = var - > yoffset ;
if ( par - > set_fb )
vmw_fb_dirty_mark ( par , par - > fb_x , par - > fb_y , par - > set_fb - > width ,
par - > set_fb - > height ) ;
mutex_unlock ( & par - > bo_mutex ) ;
return 0 ;
}
2009-12-10 00:19:58 +00:00
static void vmw_deferred_io ( struct fb_info * info ,
struct list_head * pagelist )
{
struct vmw_fb_par * par = info - > par ;
unsigned long start , end , min , max ;
unsigned long flags ;
struct page * page ;
int y1 , y2 ;
min = ULONG_MAX ;
max = 0 ;
list_for_each_entry ( page , pagelist , lru ) {
start = page - > index < < PAGE_SHIFT ;
end = start + PAGE_SIZE - 1 ;
min = min ( min , start ) ;
max = max ( max , end ) ;
}
if ( min < max ) {
y1 = min / info - > fix . line_length ;
y2 = ( max / info - > fix . line_length ) + 1 ;
spin_lock_irqsave ( & par - > dirty . lock , flags ) ;
par - > dirty . x1 = 0 ;
par - > dirty . y1 = y1 ;
par - > dirty . x2 = info - > var . xres ;
par - > dirty . y2 = y2 ;
spin_unlock_irqrestore ( & par - > dirty . lock , flags ) ;
2015-08-18 09:07:38 -07:00
/*
* Since we ' ve already waited on this work once , try to
* execute asap .
*/
cancel_delayed_work ( & par - > local_work ) ;
schedule_delayed_work ( & par - > local_work , 0 ) ;
}
2009-12-10 00:19:58 +00:00
} ;
2015-04-02 02:39:45 -07:00
static struct fb_deferred_io vmw_defio = {
2009-12-10 00:19:58 +00:00
. delay = VMW_DIRTY_DELAY ,
. deferred_io = vmw_deferred_io ,
} ;
/*
* Draw code
*/
static void vmw_fb_fillrect ( struct fb_info * info , const struct fb_fillrect * rect )
{
cfb_fillrect ( info , rect ) ;
vmw_fb_dirty_mark ( info - > par , rect - > dx , rect - > dy ,
rect - > width , rect - > height ) ;
}
static void vmw_fb_copyarea ( struct fb_info * info , const struct fb_copyarea * region )
{
cfb_copyarea ( info , region ) ;
vmw_fb_dirty_mark ( info - > par , region - > dx , region - > dy ,
region - > width , region - > height ) ;
}
static void vmw_fb_imageblit ( struct fb_info * info , const struct fb_image * image )
{
cfb_imageblit ( info , image ) ;
vmw_fb_dirty_mark ( info - > par , image - > dx , image - > dy ,
image - > width , image - > height ) ;
}
/*
* Bring up code
*/
static int vmw_fb_create_bo ( struct vmw_private * vmw_priv ,
2018-06-19 15:02:16 +02:00
size_t size , struct vmw_buffer_object * * out )
2009-12-10 00:19:58 +00:00
{
2018-06-19 15:02:16 +02:00
struct vmw_buffer_object * vmw_bo ;
2009-12-10 00:19:58 +00:00
int ret ;
vmw_bo = kmalloc ( sizeof ( * vmw_bo ) , GFP_KERNEL ) ;
2014-02-27 12:34:51 +01:00
if ( ! vmw_bo ) {
ret = - ENOMEM ;
2009-12-10 00:19:58 +00:00
goto err_unlock ;
2014-02-27 12:34:51 +01:00
}
2009-12-10 00:19:58 +00:00
2018-06-19 15:02:16 +02:00
ret = vmw_bo_init ( vmw_priv , vmw_bo , size ,
2015-06-29 12:55:07 -07:00
& vmw_sys_placement ,
2020-09-21 14:37:25 +02:00
false , false ,
2018-06-19 15:02:16 +02:00
& vmw_bo_bo_free ) ;
2009-12-10 00:19:58 +00:00
if ( unlikely ( ret ! = 0 ) )
goto err_unlock ; /* init frees the buffer on failure */
* out = vmw_bo ;
err_unlock :
return ret ;
}
2015-06-29 12:55:07 -07:00
static int vmw_fb_compute_depth ( struct fb_var_screeninfo * var ,
int * depth )
{
switch ( var - > bits_per_pixel ) {
case 32 :
* depth = ( var - > transp . length > 0 ) ? 32 : 24 ;
break ;
default :
DRM_ERROR ( " Bad bpp %u. \n " , var - > bits_per_pixel ) ;
return - EINVAL ;
}
return 0 ;
}
2017-04-06 22:02:56 +02:00
static int vmwgfx_set_config_internal ( struct drm_mode_set * set )
{
struct drm_crtc * crtc = set - > crtc ;
2018-04-26 09:48:55 +02:00
struct drm_modeset_acquire_ctx ctx ;
2017-04-06 22:02:56 +02:00
int ret ;
2018-04-26 09:48:55 +02:00
drm_modeset_acquire_init ( & ctx , 0 ) ;
2017-04-06 22:02:56 +02:00
restart :
2018-04-26 09:48:55 +02:00
ret = crtc - > funcs - > set_config ( set , & ctx ) ;
2017-04-06 22:02:56 +02:00
if ( ret = = - EDEADLK ) {
2018-04-26 09:48:55 +02:00
drm_modeset_backoff ( & ctx ) ;
2017-04-06 22:02:56 +02:00
goto restart ;
}
2018-04-26 09:48:55 +02:00
drm_modeset_drop_locks ( & ctx ) ;
drm_modeset_acquire_fini ( & ctx ) ;
2017-04-06 22:02:56 +02:00
return ret ;
}
2015-06-29 12:55:07 -07:00
static int vmw_fb_kms_detach ( struct vmw_fb_par * par ,
bool detach_bo ,
bool unref_bo )
{
struct drm_framebuffer * cur_fb = par - > set_fb ;
int ret ;
/* Detach the KMS framebuffer from crtcs */
if ( par - > set_mode ) {
struct drm_mode_set set ;
set . crtc = par - > crtc ;
set . x = 0 ;
set . y = 0 ;
set . mode = NULL ;
set . fb = NULL ;
2017-03-23 14:28:21 -07:00
set . num_connectors = 0 ;
2015-06-29 12:55:07 -07:00
set . connectors = & par - > con ;
2017-04-06 22:02:56 +02:00
ret = vmwgfx_set_config_internal ( & set ) ;
2015-06-29 12:55:07 -07:00
if ( ret ) {
DRM_ERROR ( " Could not unset a mode. \n " ) ;
return ret ;
}
2020-11-03 22:21:34 -05:00
drm_mode_destroy ( & par - > vmw_priv - > drm , par - > set_mode ) ;
2015-06-29 12:55:07 -07:00
par - > set_mode = NULL ;
}
if ( cur_fb ) {
2018-03-11 17:33:13 -06:00
drm_framebuffer_put ( cur_fb ) ;
2015-06-29 12:55:07 -07:00
par - > set_fb = NULL ;
}
2018-03-22 10:19:01 +01:00
if ( par - > vmw_bo & & detach_bo & & unref_bo )
2018-06-19 15:02:16 +02:00
vmw_bo_unreference ( & par - > vmw_bo ) ;
2015-06-29 12:55:07 -07:00
return 0 ;
}
static int vmw_fb_kms_framebuffer ( struct fb_info * info )
{
2016-12-02 08:07:40 +01:00
struct drm_mode_fb_cmd2 mode_cmd ;
2015-06-29 12:55:07 -07:00
struct vmw_fb_par * par = info - > par ;
struct fb_var_screeninfo * var = & info - > var ;
struct drm_framebuffer * cur_fb ;
struct vmw_framebuffer * vfb ;
2016-12-02 08:07:40 +01:00
int ret = 0 , depth ;
2015-06-29 12:55:07 -07:00
size_t new_bo_size ;
2016-12-02 08:07:40 +01:00
ret = vmw_fb_compute_depth ( var , & depth ) ;
2015-06-29 12:55:07 -07:00
if ( ret )
return ret ;
mode_cmd . width = var - > xres ;
mode_cmd . height = var - > yres ;
2016-12-02 08:07:40 +01:00
mode_cmd . pitches [ 0 ] = ( ( var - > bits_per_pixel + 7 ) / 8 ) * mode_cmd . width ;
mode_cmd . pixel_format =
2017-01-18 14:14:01 -08:00
drm_mode_legacy_fb_format ( var - > bits_per_pixel , depth ) ;
2015-06-29 12:55:07 -07:00
cur_fb = par - > set_fb ;
if ( cur_fb & & cur_fb - > width = = mode_cmd . width & &
cur_fb - > height = = mode_cmd . height & &
2016-12-14 23:32:55 +02:00
cur_fb - > format - > format = = mode_cmd . pixel_format & &
2016-12-02 08:07:40 +01:00
cur_fb - > pitches [ 0 ] = = mode_cmd . pitches [ 0 ] )
2015-06-29 12:55:07 -07:00
return 0 ;
/* Need new buffer object ? */
2016-12-02 08:07:40 +01:00
new_bo_size = ( size_t ) mode_cmd . pitches [ 0 ] * ( size_t ) mode_cmd . height ;
2015-06-29 12:55:07 -07:00
ret = vmw_fb_kms_detach ( par ,
par - > bo_size < new_bo_size | |
par - > bo_size > 2 * new_bo_size ,
true ) ;
if ( ret )
return ret ;
if ( ! par - > vmw_bo ) {
ret = vmw_fb_create_bo ( par - > vmw_priv , new_bo_size ,
& par - > vmw_bo ) ;
if ( ret ) {
DRM_ERROR ( " Failed creating a buffer object for "
" fbdev. \n " ) ;
return ret ;
}
par - > bo_size = new_bo_size ;
}
vfb = vmw_kms_new_framebuffer ( par - > vmw_priv , par - > vmw_bo , NULL ,
true , & mode_cmd ) ;
if ( IS_ERR ( vfb ) )
return PTR_ERR ( vfb ) ;
par - > set_fb = & vfb - > base ;
return 0 ;
}
static int vmw_fb_set_par ( struct fb_info * info )
{
struct vmw_fb_par * par = info - > par ;
struct vmw_private * vmw_priv = par - > vmw_priv ;
struct drm_mode_set set ;
struct fb_var_screeninfo * var = & info - > var ;
struct drm_display_mode new_mode = { DRM_MODE ( " fb_mode " ,
DRM_MODE_TYPE_DRIVER ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC )
} ;
struct drm_display_mode * mode ;
int ret ;
2020-11-03 22:21:34 -05:00
mode = drm_mode_duplicate ( & vmw_priv - > drm , & new_mode ) ;
2015-06-29 12:55:07 -07:00
if ( ! mode ) {
DRM_ERROR ( " Could not create new fb mode. \n " ) ;
return - ENOMEM ;
}
mode - > hdisplay = var - > xres ;
mode - > vdisplay = var - > yres ;
vmw_guess_mode_timing ( mode ) ;
2019-03-18 15:47:58 +01:00
if ( ! vmw_kms_validate_mode_vram ( vmw_priv ,
2016-04-21 11:29:31 -07:00
mode - > hdisplay *
DIV_ROUND_UP ( var - > bits_per_pixel , 8 ) ,
mode - > vdisplay ) ) {
2020-11-03 22:21:34 -05:00
drm_mode_destroy ( & vmw_priv - > drm , mode ) ;
2015-06-29 12:55:07 -07:00
return - EINVAL ;
}
mutex_lock ( & par - > bo_mutex ) ;
ret = vmw_fb_kms_framebuffer ( info ) ;
if ( ret )
goto out_unlock ;
par - > fb_x = var - > xoffset ;
par - > fb_y = var - > yoffset ;
set . crtc = par - > crtc ;
set . x = 0 ;
set . y = 0 ;
set . mode = mode ;
set . fb = par - > set_fb ;
set . num_connectors = 1 ;
set . connectors = & par - > con ;
2017-04-06 22:02:56 +02:00
ret = vmwgfx_set_config_internal ( & set ) ;
2015-06-29 12:55:07 -07:00
if ( ret )
goto out_unlock ;
vmw_fb_dirty_mark ( par , par - > fb_x , par - > fb_y ,
par - > set_fb - > width , par - > set_fb - > height ) ;
/* If there already was stuff dirty we wont
* schedule a new work , so lets do it now */
2015-08-18 09:07:38 -07:00
schedule_delayed_work ( & par - > local_work , 0 ) ;
2015-06-29 12:55:07 -07:00
out_unlock :
2019-03-18 15:47:58 +01:00
if ( par - > set_mode )
2020-11-03 22:21:34 -05:00
drm_mode_destroy ( & vmw_priv - > drm , par - > set_mode ) ;
2015-06-29 12:55:07 -07:00
par - > set_mode = mode ;
mutex_unlock ( & par - > bo_mutex ) ;
return ret ;
}
2019-12-03 18:38:48 +02:00
static const struct fb_ops vmw_fb_ops = {
2015-06-29 12:55:07 -07:00
. owner = THIS_MODULE ,
. fb_check_var = vmw_fb_check_var ,
. fb_set_par = vmw_fb_set_par ,
. fb_setcolreg = vmw_fb_setcolreg ,
. fb_fillrect = vmw_fb_fillrect ,
. fb_copyarea = vmw_fb_copyarea ,
. fb_imageblit = vmw_fb_imageblit ,
. fb_pan_display = vmw_fb_pan_display ,
. fb_blank = vmw_fb_blank ,
} ;
2009-12-10 00:19:58 +00:00
int vmw_fb_init ( struct vmw_private * vmw_priv )
{
2020-11-03 22:21:34 -05:00
struct device * device = vmw_priv - > drm . dev ;
2009-12-10 00:19:58 +00:00
struct vmw_fb_par * par ;
struct fb_info * info ;
unsigned fb_width , fb_height ;
2019-03-25 10:32:17 -07:00
unsigned int fb_bpp , fb_pitch , fb_size ;
2015-06-29 12:55:07 -07:00
struct drm_display_mode * init_mode ;
2009-12-10 00:19:58 +00:00
int ret ;
2011-08-31 07:42:49 +00:00
fb_bpp = 32 ;
2009-12-10 00:19:58 +00:00
2010-05-28 11:21:59 +02:00
/* XXX As shouldn't these be as well. */
fb_width = min ( vmw_priv - > fb_max_width , ( unsigned ) 2048 ) ;
fb_height = min ( vmw_priv - > fb_max_height , ( unsigned ) 2048 ) ;
2009-12-10 00:19:58 +00:00
2011-08-31 07:42:49 +00:00
fb_pitch = fb_width * fb_bpp / 8 ;
2010-05-28 11:21:59 +02:00
fb_size = fb_pitch * fb_height ;
2009-12-10 00:19:58 +00:00
info = framebuffer_alloc ( sizeof ( * par ) , device ) ;
if ( ! info )
return - ENOMEM ;
/*
* Par
*/
vmw_priv - > fb_info = info ;
par = info - > par ;
2015-06-29 12:55:07 -07:00
memset ( par , 0 , sizeof ( * par ) ) ;
2015-08-18 09:07:38 -07:00
INIT_DELAYED_WORK ( & par - > local_work , & vmw_fb_dirty_flush ) ;
2009-12-10 00:19:58 +00:00
par - > vmw_priv = vmw_priv ;
par - > vmalloc = NULL ;
par - > max_width = fb_width ;
par - > max_height = fb_height ;
2015-06-29 12:55:07 -07:00
ret = vmw_kms_fbdev_init_data ( vmw_priv , 0 , par - > max_width ,
par - > max_height , & par - > con ,
& par - > crtc , & init_mode ) ;
2018-04-26 09:48:55 +02:00
if ( ret )
2015-06-29 12:55:07 -07:00
goto err_kms ;
info - > var . xres = init_mode - > hdisplay ;
info - > var . yres = init_mode - > vdisplay ;
2009-12-10 00:19:58 +00:00
/*
* Create buffers and alloc memory
*/
2015-06-29 12:55:07 -07:00
par - > vmalloc = vzalloc ( fb_size ) ;
2009-12-10 00:19:58 +00:00
if ( unlikely ( par - > vmalloc = = NULL ) ) {
ret = - ENOMEM ;
goto err_free ;
}
/*
* Fixed and var
*/
strcpy ( info - > fix . id , " svgadrmfb " ) ;
info - > fix . type = FB_TYPE_PACKED_PIXELS ;
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
info - > fix . type_aux = 0 ;
info - > fix . xpanstep = 1 ; /* doing it in hw */
info - > fix . ypanstep = 1 ; /* doing it in hw */
info - > fix . ywrapstep = 0 ;
info - > fix . accel = FB_ACCEL_NONE ;
info - > fix . line_length = fb_pitch ;
info - > fix . smem_start = 0 ;
info - > fix . smem_len = fb_size ;
info - > pseudo_palette = par - > pseudo_palette ;
2015-04-02 02:39:45 -07:00
info - > screen_base = ( char __iomem * ) par - > vmalloc ;
2009-12-10 00:19:58 +00:00
info - > screen_size = fb_size ;
info - > fbops = & vmw_fb_ops ;
/* 24 depth per default */
info - > var . red . offset = 16 ;
info - > var . green . offset = 8 ;
info - > var . blue . offset = 0 ;
info - > var . red . length = 8 ;
info - > var . green . length = 8 ;
info - > var . blue . length = 8 ;
info - > var . transp . offset = 0 ;
info - > var . transp . length = 0 ;
info - > var . xres_virtual = fb_width ;
info - > var . yres_virtual = fb_height ;
2015-06-29 12:55:07 -07:00
info - > var . bits_per_pixel = fb_bpp ;
2009-12-10 00:19:58 +00:00
info - > var . xoffset = 0 ;
info - > var . yoffset = 0 ;
info - > var . activate = FB_ACTIVATE_NOW ;
info - > var . height = - 1 ;
info - > var . width = - 1 ;
2012-02-06 10:58:19 +01:00
/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
2010-05-16 17:27:03 +02:00
info - > apertures = alloc_apertures ( 1 ) ;
if ( ! info - > apertures ) {
ret = - ENOMEM ;
goto err_aper ;
}
info - > apertures - > ranges [ 0 ] . base = vmw_priv - > vram_start ;
info - > apertures - > ranges [ 0 ] . size = vmw_priv - > vram_size ;
2010-02-15 14:45:22 +00:00
2009-12-10 00:19:58 +00:00
/*
* Dirty & Deferred IO
*/
par - > dirty . x1 = par - > dirty . x2 = 0 ;
2010-07-24 17:15:11 +01:00
par - > dirty . y1 = par - > dirty . y2 = 0 ;
2009-12-10 00:19:58 +00:00
par - > dirty . active = true ;
spin_lock_init ( & par - > dirty . lock ) ;
2015-06-29 12:55:07 -07:00
mutex_init ( & par - > bo_mutex ) ;
2009-12-10 00:19:58 +00:00
info - > fbdefio = & vmw_defio ;
fb_deferred_io_init ( info ) ;
ret = register_framebuffer ( info ) ;
if ( unlikely ( ret ! = 0 ) )
goto err_defio ;
2015-06-29 12:55:07 -07:00
vmw_fb_set_par ( info ) ;
2009-12-10 00:19:58 +00:00
return 0 ;
err_defio :
fb_deferred_io_cleanup ( info ) ;
2010-05-16 17:27:03 +02:00
err_aper :
2009-12-10 00:19:58 +00:00
err_free :
vfree ( par - > vmalloc ) ;
2015-06-29 12:55:07 -07:00
err_kms :
2009-12-10 00:19:58 +00:00
framebuffer_release ( info ) ;
vmw_priv - > fb_info = NULL ;
return ret ;
}
int vmw_fb_close ( struct vmw_private * vmw_priv )
{
struct fb_info * info ;
struct vmw_fb_par * par ;
if ( ! vmw_priv - > fb_info )
return 0 ;
info = vmw_priv - > fb_info ;
par = info - > par ;
/* ??? order */
fb_deferred_io_cleanup ( info ) ;
2015-08-18 09:07:38 -07:00
cancel_delayed_work_sync ( & par - > local_work ) ;
2009-12-10 00:19:58 +00:00
unregister_framebuffer ( info ) ;
2018-04-26 09:48:55 +02:00
mutex_lock ( & par - > bo_mutex ) ;
2015-06-29 12:55:07 -07:00
( void ) vmw_fb_kms_detach ( par , true , true ) ;
2018-04-26 09:48:55 +02:00
mutex_unlock ( & par - > bo_mutex ) ;
2009-12-10 00:19:58 +00:00
vfree ( par - > vmalloc ) ;
framebuffer_release ( info ) ;
return 0 ;
}
int vmw_fb_off ( struct vmw_private * vmw_priv )
{
struct fb_info * info ;
struct vmw_fb_par * par ;
unsigned long flags ;
if ( ! vmw_priv - > fb_info )
return - EINVAL ;
info = vmw_priv - > fb_info ;
par = info - > par ;
spin_lock_irqsave ( & par - > dirty . lock , flags ) ;
par - > dirty . active = false ;
spin_unlock_irqrestore ( & par - > dirty . lock , flags ) ;
2012-08-20 14:51:24 -07:00
flush_delayed_work ( & info - > deferred_work ) ;
2015-08-18 09:07:38 -07:00
flush_delayed_work ( & par - > local_work ) ;
2009-12-10 00:19:58 +00:00
return 0 ;
}
int vmw_fb_on ( struct vmw_private * vmw_priv )
{
struct fb_info * info ;
struct vmw_fb_par * par ;
unsigned long flags ;
if ( ! vmw_priv - > fb_info )
return - EINVAL ;
info = vmw_priv - > fb_info ;
par = info - > par ;
spin_lock_irqsave ( & par - > dirty . lock , flags ) ;
par - > dirty . active = true ;
spin_unlock_irqrestore ( & par - > dirty . lock , flags ) ;
2018-03-22 10:26:37 +01:00
2018-05-23 16:14:54 +02:00
/*
* Need to reschedule a dirty update , because otherwise that ' s
* only done in dirty_mark ( ) if the previous coalesced
* dirty region was empty .
*/
schedule_delayed_work ( & par - > local_work , 0 ) ;
2018-03-22 10:26:37 +01:00
2018-05-23 16:14:54 +02:00
return 0 ;
2018-03-22 10:26:37 +01:00
}