2005-04-17 02:20:36 +04:00
/*
* linux / drivers / video / vfb . c - - Virtual frame buffer device
*
* Copyright ( C ) 2002 James Simmons
*
* Copyright ( C ) 1997 Geert Uytterhoeven
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive for
* more details .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/vmalloc.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/fb.h>
# include <linux/init.h>
/*
* RAM we reserve for the frame buffer . This defines the maximum screen
* size
*
* The default can be overridden if the driver is compiled as a module
*/
# define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */
static void * videomemory ;
static u_long videomemorysize = VIDEOMEMSIZE ;
module_param ( videomemorysize , ulong , 0 ) ;
2016-08-15 13:10:30 +03:00
MODULE_PARM_DESC ( videomemorysize , " RAM available to frame buffer (in bytes) " ) ;
2005-04-17 02:20:36 +04:00
2016-08-15 13:10:31 +03:00
static char * mode_option = NULL ;
module_param ( mode_option , charp , 0 ) ;
MODULE_PARM_DESC ( mode_option , " Preferred video mode (e.g. 640x480-8@60) " ) ;
static const struct fb_videomode vfb_default = {
2005-04-17 02:20:36 +04:00
. xres = 640 ,
. yres = 480 ,
2016-08-15 13:10:31 +03:00
. pixclock = 20000 ,
. left_margin = 64 ,
. right_margin = 64 ,
. upper_margin = 32 ,
. lower_margin = 32 ,
. hsync_len = 64 ,
. vsync_len = 2 ,
. vmode = FB_VMODE_NONINTERLACED ,
2005-04-17 02:20:36 +04:00
} ;
2012-12-22 01:07:39 +04:00
static struct fb_fix_screeninfo vfb_fix = {
2005-04-17 02:20:36 +04:00
. id = " Virtual FB " ,
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_PSEUDOCOLOR ,
. xpanstep = 1 ,
. ypanstep = 1 ,
. ywrapstep = 1 ,
. accel = FB_ACCEL_NONE ,
} ;
2012-01-13 03:02:20 +04:00
static bool vfb_enable __initdata = 0 ; /* disabled by default */
2005-04-17 02:20:36 +04:00
module_param ( vfb_enable , bool , 0 ) ;
2016-08-15 13:10:30 +03:00
MODULE_PARM_DESC ( vfb_enable , " Enable Virtual FB driver " ) ;
2005-04-17 02:20:36 +04:00
static int vfb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info ) ;
static int vfb_set_par ( struct fb_info * info ) ;
static int vfb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int transp , struct fb_info * info ) ;
static int vfb_pan_display ( struct fb_var_screeninfo * var ,
struct fb_info * info ) ;
2006-01-15 00:21:25 +03:00
static int vfb_mmap ( struct fb_info * info ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * vma ) ;
static struct fb_ops vfb_ops = {
2007-05-08 11:39:07 +04:00
. fb_read = fb_sys_read ,
. fb_write = fb_sys_write ,
2005-04-17 02:20:36 +04:00
. fb_check_var = vfb_check_var ,
. fb_set_par = vfb_set_par ,
. fb_setcolreg = vfb_setcolreg ,
. fb_pan_display = vfb_pan_display ,
2007-05-08 11:39:01 +04:00
. fb_fillrect = sys_fillrect ,
. fb_copyarea = sys_copyarea ,
. fb_imageblit = sys_imageblit ,
2005-04-17 02:20:36 +04:00
. fb_mmap = vfb_mmap ,
} ;
/*
* Internal routines
*/
static u_long get_line_length ( int xres_virtual , int bpp )
{
u_long length ;
length = xres_virtual * bpp ;
length = ( length + 31 ) & ~ 31 ;
length > > = 3 ;
return ( length ) ;
}
/*
* Setting the video mode has been split into two parts .
* First part , xxxfb_check_var , must not write anything
* to hardware , it should only verify and adjust var .
* This means it doesn ' t alter par but it does use hardware
* data from it to check this var .
*/
static int vfb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
u_long line_length ;
/*
* FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal !
* as FB_VMODE_SMOOTH_XPAN is only used internally
*/
if ( var - > vmode & FB_VMODE_CONUPDATE ) {
var - > vmode | = FB_VMODE_YWRAP ;
var - > xoffset = info - > var . xoffset ;
var - > yoffset = info - > var . yoffset ;
}
/*
* Some very basic checks
*/
if ( ! var - > xres )
var - > xres = 1 ;
if ( ! var - > yres )
var - > yres = 1 ;
if ( var - > xres > var - > xres_virtual )
var - > xres_virtual = var - > xres ;
if ( var - > yres > var - > yres_virtual )
var - > yres_virtual = var - > yres ;
if ( var - > bits_per_pixel < = 1 )
var - > bits_per_pixel = 1 ;
else if ( var - > bits_per_pixel < = 8 )
var - > bits_per_pixel = 8 ;
else if ( var - > bits_per_pixel < = 16 )
var - > bits_per_pixel = 16 ;
else if ( var - > bits_per_pixel < = 24 )
var - > bits_per_pixel = 24 ;
else if ( var - > bits_per_pixel < = 32 )
var - > bits_per_pixel = 32 ;
else
return - EINVAL ;
if ( var - > xres_virtual < var - > xoffset + var - > xres )
var - > xres_virtual = var - > xoffset + var - > xres ;
if ( var - > yres_virtual < var - > yoffset + var - > yres )
var - > yres_virtual = var - > yoffset + var - > yres ;
/*
* Memory limit
*/
line_length =
get_line_length ( var - > xres_virtual , var - > bits_per_pixel ) ;
if ( line_length * var - > yres_virtual > videomemorysize )
return - ENOMEM ;
/*
* Now that we checked it we alter var . The reason being is that the video
* mode passed in might not work but slight changes to it might make it
* work . This way we let the user know what is acceptable .
*/
switch ( var - > bits_per_pixel ) {
case 1 :
case 8 :
var - > red . offset = 0 ;
var - > red . length = 8 ;
var - > green . offset = 0 ;
var - > green . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . length = 8 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 16 : /* RGBA 5551 */
if ( var - > transp . length ) {
var - > red . offset = 0 ;
var - > red . length = 5 ;
var - > green . offset = 5 ;
var - > green . length = 5 ;
var - > blue . offset = 10 ;
var - > blue . length = 5 ;
var - > transp . offset = 15 ;
var - > transp . length = 1 ;
} else { /* RGB 565 */
var - > red . offset = 0 ;
var - > red . length = 5 ;
var - > green . offset = 5 ;
var - > green . length = 6 ;
var - > blue . offset = 11 ;
var - > blue . length = 5 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
}
break ;
case 24 : /* RGB 888 */
var - > red . offset = 0 ;
var - > red . length = 8 ;
var - > green . offset = 8 ;
var - > green . length = 8 ;
var - > blue . offset = 16 ;
var - > blue . length = 8 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 32 : /* RGBA 8888 */
var - > red . offset = 0 ;
var - > red . length = 8 ;
var - > green . offset = 8 ;
var - > green . length = 8 ;
var - > blue . offset = 16 ;
var - > blue . length = 8 ;
var - > transp . offset = 24 ;
var - > transp . length = 8 ;
break ;
}
var - > red . msb_right = 0 ;
var - > green . msb_right = 0 ;
var - > blue . msb_right = 0 ;
var - > transp . msb_right = 0 ;
return 0 ;
}
/* This routine actually sets the video mode. It's in here where we
* the hardware state info - > par and fix which can be affected by the
* change in par . For this driver it doesn ' t do much .
*/
static int vfb_set_par ( struct fb_info * info )
{
info - > fix . line_length = get_line_length ( info - > var . xres_virtual ,
info - > var . bits_per_pixel ) ;
return 0 ;
}
/*
* Set a single color register . The values supplied are already
* rounded down to the hardware ' s capabilities ( according to the
* entries in the var structure ) . Return ! = 0 for invalid regno .
*/
static int vfb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int transp , struct fb_info * info )
{
if ( regno > = 256 ) /* no. of hw registers */
return 1 ;
/*
* Program hardware . . . do anything you want with transp
*/
/* grayscale works only partially under directcolor */
if ( info - > var . grayscale ) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
red = green = blue =
( red * 77 + green * 151 + blue * 28 ) > > 8 ;
}
/* Directcolor:
* var - > { color } . offset contains start of bitfield
* var - > { color } . length contains length of bitfield
* { hardwarespecific } contains width of RAMDAC
* cmap [ X ] is programmed to ( X < < red . offset ) | ( X < < green . offset ) | ( X < < blue . offset )
* RAMDAC [ X ] is programmed to ( red , green , blue )
2009-04-14 01:39:41 +04:00
*
2005-04-17 02:20:36 +04:00
* Pseudocolor :
2009-04-14 01:39:41 +04:00
* var - > { color } . offset is 0 unless the palette index takes less than
* bits_per_pixel bits and is stored in the upper
* bits of the pixel value
* var - > { color } . length is set so that 1 < < length is the number of available
* palette entries
2005-04-17 02:20:36 +04:00
* cmap is not used
* RAMDAC [ X ] is programmed to ( red , green , blue )
2009-04-14 01:39:41 +04:00
*
2005-04-17 02:20:36 +04:00
* Truecolor :
* does not use DAC . Usually 3 are present .
* var - > { color } . offset contains start of bitfield
* var - > { color } . length contains length of bitfield
* cmap is programmed to ( red < < red . offset ) | ( green < < green . offset ) |
* ( blue < < blue . offset ) | ( transp < < transp . offset )
* RAMDAC does not exist
*/
# define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
switch ( info - > fix . visual ) {
case FB_VISUAL_TRUECOLOR :
case FB_VISUAL_PSEUDOCOLOR :
red = CNVT_TOHW ( red , info - > var . red . length ) ;
green = CNVT_TOHW ( green , info - > var . green . length ) ;
blue = CNVT_TOHW ( blue , info - > var . blue . length ) ;
transp = CNVT_TOHW ( transp , info - > var . transp . length ) ;
break ;
case FB_VISUAL_DIRECTCOLOR :
red = CNVT_TOHW ( red , 8 ) ; /* expect 8 bit DAC */
green = CNVT_TOHW ( green , 8 ) ;
blue = CNVT_TOHW ( blue , 8 ) ;
/* hey, there is bug in transp handling... */
transp = CNVT_TOHW ( transp , 8 ) ;
break ;
}
# undef CNVT_TOHW
/* Truecolor has hardware independent palette */
if ( info - > fix . visual = = FB_VISUAL_TRUECOLOR ) {
u32 v ;
if ( regno > = 16 )
return 1 ;
v = ( red < < info - > var . red . offset ) |
( green < < info - > var . green . offset ) |
( blue < < info - > var . blue . offset ) |
( transp < < info - > var . transp . offset ) ;
switch ( info - > var . bits_per_pixel ) {
case 8 :
break ;
case 16 :
( ( u32 * ) ( info - > pseudo_palette ) ) [ regno ] = v ;
break ;
case 24 :
case 32 :
( ( u32 * ) ( info - > pseudo_palette ) ) [ regno ] = v ;
break ;
}
return 0 ;
}
return 0 ;
}
/*
* Pan or Wrap the Display
*
* This call looks only at xoffset , yoffset and the FB_VMODE_YWRAP flag
*/
static int vfb_pan_display ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
if ( var - > vmode & FB_VMODE_YWRAP ) {
2013-09-25 16:02:56 +04:00
if ( var - > yoffset > = info - > var . yres_virtual | |
var - > xoffset )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
} else {
2011-05-25 13:34:52 +04:00
if ( var - > xoffset + info - > var . xres > info - > var . xres_virtual | |
var - > yoffset + info - > var . yres > info - > var . yres_virtual )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
info - > var . xoffset = var - > xoffset ;
info - > var . yoffset = var - > yoffset ;
if ( var - > vmode & FB_VMODE_YWRAP )
info - > var . vmode | = FB_VMODE_YWRAP ;
else
info - > var . vmode & = ~ FB_VMODE_YWRAP ;
return 0 ;
}
/*
* Most drivers don ' t need their own mmap function
*/
2006-01-15 00:21:25 +03:00
static int vfb_mmap ( struct fb_info * info ,
2005-04-17 02:20:36 +04:00
struct vm_area_struct * vma )
{
2016-08-15 13:10:32 +03:00
return remap_vmalloc_range ( vma , ( void * ) info - > fix . smem_start , vma - > vm_pgoff ) ;
2005-04-17 02:20:36 +04:00
}
# ifndef MODULE
2008-07-24 08:31:26 +04:00
/*
* The virtual framebuffer driver is only enabled if explicitly
* requested by passing ' video = vfb : ' ( or any actual options ) .
*/
2005-04-17 02:20:36 +04:00
static int __init vfb_setup ( char * options )
{
char * this_opt ;
2008-07-24 08:31:26 +04:00
vfb_enable = 0 ;
if ( ! options )
return 1 ;
2005-04-17 02:20:36 +04:00
vfb_enable = 1 ;
2008-07-24 08:31:26 +04:00
if ( ! * options )
2005-04-17 02:20:36 +04:00
return 1 ;
while ( ( this_opt = strsep ( & options , " , " ) ) ! = NULL ) {
if ( ! * this_opt )
continue ;
2008-07-24 08:31:26 +04:00
/* Test disable for backwards compatibility */
if ( ! strcmp ( this_opt , " disable " ) )
2005-04-17 02:20:36 +04:00
vfb_enable = 0 ;
2016-08-15 13:10:31 +03:00
else
mode_option = this_opt ;
2005-04-17 02:20:36 +04:00
}
return 1 ;
}
# endif /* MODULE */
/*
* Initialisation
*/
2012-12-22 01:07:39 +04:00
static int vfb_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
struct fb_info * info ;
2016-08-15 13:10:32 +03:00
unsigned int size = PAGE_ALIGN ( videomemorysize ) ;
2005-04-17 02:20:36 +04:00
int retval = - ENOMEM ;
/*
* For real video cards we use ioremap .
*/
2016-08-15 13:10:32 +03:00
if ( ! ( videomemory = vmalloc_32_user ( size ) ) )
2005-04-17 02:20:36 +04:00
return retval ;
info = framebuffer_alloc ( sizeof ( u32 ) * 256 , & dev - > dev ) ;
if ( ! info )
goto err ;
info - > screen_base = ( char __iomem * ) videomemory ;
info - > fbops = & vfb_ops ;
2016-08-15 13:10:31 +03:00
if ( ! fb_find_mode ( & info - > var , info , mode_option ,
NULL , 0 , & vfb_default , 8 ) ) {
fb_err ( info , " Unable to find usable video mode. \n " ) ;
retval = - EINVAL ;
goto err1 ;
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:29:17 +04:00
vfb_fix . smem_start = ( unsigned long ) videomemory ;
vfb_fix . smem_len = videomemorysize ;
2005-04-17 02:20:36 +04:00
info - > fix = vfb_fix ;
info - > pseudo_palette = info - > par ;
info - > par = NULL ;
info - > flags = FBINFO_FLAG_DEFAULT ;
retval = fb_alloc_cmap ( & info - > cmap , 256 , 0 ) ;
if ( retval < 0 )
goto err1 ;
retval = register_framebuffer ( info ) ;
if ( retval < 0 )
goto err2 ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , info ) ;
2005-04-17 02:20:36 +04:00
2013-09-20 05:35:55 +04:00
fb_info ( info , " Virtual frame buffer device, using %ldK of video memory \n " ,
videomemorysize > > 10 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
err2 :
fb_dealloc_cmap ( & info - > cmap ) ;
err1 :
framebuffer_release ( info ) ;
err :
2016-08-15 13:10:32 +03:00
vfree ( videomemory ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2005-11-10 01:32:44 +03:00
static int vfb_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct fb_info * info = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( info ) {
unregister_framebuffer ( info ) ;
2016-08-15 13:10:32 +03:00
vfree ( videomemory ) ;
2009-04-01 02:25:22 +04:00
fb_dealloc_cmap ( & info - > cmap ) ;
2005-04-17 02:20:36 +04:00
framebuffer_release ( info ) ;
}
return 0 ;
}
2005-11-10 01:32:44 +03:00
static struct platform_driver vfb_driver = {
2005-04-17 02:20:36 +04:00
. probe = vfb_probe ,
. remove = vfb_remove ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " vfb " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-06-26 11:26:34 +04:00
static struct platform_device * vfb_device ;
2005-04-17 02:20:36 +04:00
static int __init vfb_init ( void )
{
int ret = 0 ;
# ifndef MODULE
char * option = NULL ;
if ( fb_get_options ( " vfb " , & option ) )
return - ENODEV ;
vfb_setup ( option ) ;
# endif
if ( ! vfb_enable )
return - ENXIO ;
2005-11-10 01:32:44 +03:00
ret = platform_driver_register ( & vfb_driver ) ;
2005-04-17 02:20:36 +04:00
if ( ! ret ) {
2006-06-26 11:26:34 +04:00
vfb_device = platform_device_alloc ( " vfb " , 0 ) ;
if ( vfb_device )
ret = platform_device_add ( vfb_device ) ;
else
ret = - ENOMEM ;
if ( ret ) {
platform_device_put ( vfb_device ) ;
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & vfb_driver ) ;
2006-06-26 11:26:34 +04:00
}
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:34 +04:00
2005-04-17 02:20:36 +04:00
return ret ;
}
module_init ( vfb_init ) ;
# ifdef MODULE
static void __exit vfb_exit ( void )
{
2006-06-26 11:26:34 +04:00
platform_device_unregister ( vfb_device ) ;
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & vfb_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_exit ( vfb_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
# endif /* MODULE */