2005-04-17 02:20:36 +04:00
/*
* linux / drivers / video / fbcmap . c - - Colormap handling for frame buffer devices
*
* Created 15 Jun 1997 by Geert Uytterhoeven
*
* 2001 - Documented with DocBook
* - Brad Douglas < brad @ neruo . com >
*
* 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/string.h>
# include <linux/module.h>
# include <linux/fb.h>
# include <linux/slab.h>
2007-10-16 12:29:04 +04:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
2006-12-08 13:40:27 +03:00
static u16 red2 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa
} ;
2006-12-08 13:40:27 +03:00
static u16 green2 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa
} ;
2006-12-08 13:40:27 +03:00
static u16 blue2 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa
} ;
2006-12-08 13:40:27 +03:00
static u16 red4 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa , 0x5555 , 0xffff
} ;
2006-12-08 13:40:27 +03:00
static u16 green4 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa , 0x5555 , 0xffff
} ;
2006-12-08 13:40:27 +03:00
static u16 blue4 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa , 0x5555 , 0xffff
} ;
2006-12-08 13:40:27 +03:00
static u16 red8 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0x0000 , 0x0000 , 0x0000 , 0xaaaa , 0xaaaa , 0xaaaa , 0xaaaa
} ;
2006-12-08 13:40:27 +03:00
static u16 green8 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0x0000 , 0xaaaa , 0xaaaa , 0x0000 , 0x0000 , 0x5555 , 0xaaaa
} ;
2006-12-08 13:40:27 +03:00
static u16 blue8 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa , 0x0000 , 0xaaaa , 0x0000 , 0xaaaa , 0x0000 , 0xaaaa
} ;
2006-12-08 13:40:27 +03:00
static u16 red16 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0x0000 , 0x0000 , 0x0000 , 0xaaaa , 0xaaaa , 0xaaaa , 0xaaaa ,
0x5555 , 0x5555 , 0x5555 , 0x5555 , 0xffff , 0xffff , 0xffff , 0xffff
} ;
2006-12-08 13:40:27 +03:00
static u16 green16 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0x0000 , 0xaaaa , 0xaaaa , 0x0000 , 0x0000 , 0x5555 , 0xaaaa ,
0x5555 , 0x5555 , 0xffff , 0xffff , 0x5555 , 0x5555 , 0xffff , 0xffff
} ;
2006-12-08 13:40:27 +03:00
static u16 blue16 [ ] __read_mostly = {
2005-04-17 02:20:36 +04:00
0x0000 , 0xaaaa , 0x0000 , 0xaaaa , 0x0000 , 0xaaaa , 0x0000 , 0xaaaa ,
0x5555 , 0xffff , 0x5555 , 0xffff , 0x5555 , 0xffff , 0x5555 , 0xffff
} ;
2006-12-08 13:40:27 +03:00
static const struct fb_cmap default_2_colors = {
. len = 2 , . red = red2 , . green = green2 , . blue = blue2
2005-04-17 02:20:36 +04:00
} ;
2006-12-08 13:40:27 +03:00
static const struct fb_cmap default_8_colors = {
. len = 8 , . red = red8 , . green = green8 , . blue = blue8
2005-04-17 02:20:36 +04:00
} ;
2006-12-08 13:40:27 +03:00
static const struct fb_cmap default_4_colors = {
. len = 4 , . red = red4 , . green = green4 , . blue = blue4
2005-04-17 02:20:36 +04:00
} ;
2006-12-08 13:40:27 +03:00
static const struct fb_cmap default_16_colors = {
. len = 16 , . red = red16 , . green = green16 , . blue = blue16
2005-04-17 02:20:36 +04:00
} ;
2006-12-08 13:40:27 +03:00
2005-04-17 02:20:36 +04:00
/**
* fb_alloc_cmap - allocate a colormap
* @ cmap : frame buffer colormap structure
* @ len : length of @ cmap
* @ transp : boolean , 1 if there is transparency , 0 otherwise
2010-11-26 08:20:25 +03:00
* @ flags : flags for kmalloc memory allocation
2005-04-17 02:20:36 +04:00
*
* Allocates memory for a colormap @ cmap . @ len is the
* number of entries in the palette .
*
2006-03-27 13:17:30 +04:00
* Returns negative errno on error , or zero on success .
2005-04-17 02:20:36 +04:00
*
*/
2010-11-16 12:11:02 +03:00
int fb_alloc_cmap_gfp ( struct fb_cmap * cmap , int len , int transp , gfp_t flags )
2005-04-17 02:20:36 +04:00
{
2010-11-13 13:06:38 +03:00
int size = len * sizeof ( u16 ) ;
2010-11-16 12:11:02 +03:00
int ret = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2010-11-13 13:06:38 +03:00
if ( cmap - > len ! = len ) {
fb_dealloc_cmap ( cmap ) ;
if ( ! len )
return 0 ;
2010-11-16 12:11:02 +03:00
cmap - > red = kmalloc ( size , flags ) ;
2010-11-13 13:06:38 +03:00
if ( ! cmap - > red )
goto fail ;
2010-11-16 12:11:02 +03:00
cmap - > green = kmalloc ( size , flags ) ;
2010-11-13 13:06:38 +03:00
if ( ! cmap - > green )
goto fail ;
2010-11-16 12:11:02 +03:00
cmap - > blue = kmalloc ( size , flags ) ;
2010-11-13 13:06:38 +03:00
if ( ! cmap - > blue )
goto fail ;
if ( transp ) {
2010-11-16 12:11:02 +03:00
cmap - > transp = kmalloc ( size , flags ) ;
2010-11-13 13:06:38 +03:00
if ( ! cmap - > transp )
goto fail ;
} else {
cmap - > transp = NULL ;
}
}
cmap - > start = 0 ;
cmap - > len = len ;
2010-11-16 12:11:02 +03:00
ret = fb_copy_cmap ( fb_default_cmap ( len ) , cmap ) ;
if ( ret )
goto fail ;
2010-11-13 13:06:38 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
fail :
2010-11-13 13:06:38 +03:00
fb_dealloc_cmap ( cmap ) ;
2010-11-16 12:11:02 +03:00
return ret ;
}
int fb_alloc_cmap ( struct fb_cmap * cmap , int len , int transp )
{
return fb_alloc_cmap_gfp ( cmap , len , transp , GFP_ATOMIC ) ;
2005-04-17 02:20:36 +04:00
}
/**
* fb_dealloc_cmap - deallocate a colormap
* @ cmap : frame buffer colormap structure
*
* Deallocates a colormap that was previously allocated with
* fb_alloc_cmap ( ) .
*
*/
void fb_dealloc_cmap ( struct fb_cmap * cmap )
{
kfree ( cmap - > red ) ;
kfree ( cmap - > green ) ;
kfree ( cmap - > blue ) ;
kfree ( cmap - > transp ) ;
cmap - > red = cmap - > green = cmap - > blue = cmap - > transp = NULL ;
cmap - > len = 0 ;
}
/**
* fb_copy_cmap - copy a colormap
* @ from : frame buffer colormap structure
* @ to : frame buffer colormap structure
*
* Copy contents of colormap from @ from to @ to .
*/
2006-12-08 13:40:27 +03:00
int fb_copy_cmap ( const struct fb_cmap * from , struct fb_cmap * to )
2005-04-17 02:20:36 +04:00
{
int tooff = 0 , fromoff = 0 ;
int size ;
if ( to - > start > from - > start )
fromoff = to - > start - from - > start ;
else
tooff = from - > start - to - > start ;
size = to - > len - tooff ;
if ( size > ( int ) ( from - > len - fromoff ) )
size = from - > len - fromoff ;
if ( size < = 0 )
return - EINVAL ;
size * = sizeof ( u16 ) ;
memcpy ( to - > red + tooff , from - > red + fromoff , size ) ;
memcpy ( to - > green + tooff , from - > green + fromoff , size ) ;
memcpy ( to - > blue + tooff , from - > blue + fromoff , size ) ;
if ( from - > transp & & to - > transp )
memcpy ( to - > transp + tooff , from - > transp + fromoff , size ) ;
return 0 ;
}
2006-12-08 13:40:27 +03:00
int fb_cmap_to_user ( const struct fb_cmap * from , struct fb_cmap_user * to )
2005-04-17 02:20:36 +04:00
{
int tooff = 0 , fromoff = 0 ;
int size ;
if ( to - > start > from - > start )
fromoff = to - > start - from - > start ;
else
tooff = from - > start - to - > start ;
size = to - > len - tooff ;
if ( size > ( int ) ( from - > len - fromoff ) )
size = from - > len - fromoff ;
if ( size < = 0 )
return - EINVAL ;
size * = sizeof ( u16 ) ;
if ( copy_to_user ( to - > red + tooff , from - > red + fromoff , size ) )
return - EFAULT ;
if ( copy_to_user ( to - > green + tooff , from - > green + fromoff , size ) )
return - EFAULT ;
if ( copy_to_user ( to - > blue + tooff , from - > blue + fromoff , size ) )
return - EFAULT ;
if ( from - > transp & & to - > transp )
if ( copy_to_user ( to - > transp + tooff , from - > transp + fromoff , size ) )
return - EFAULT ;
return 0 ;
}
/**
* fb_set_cmap - set the colormap
* @ cmap : frame buffer colormap structure
* @ info : frame buffer info structure
*
* Sets the colormap @ cmap for a screen of device @ info .
*
* Returns negative errno on error , or zero on success .
*
*/
int fb_set_cmap ( struct fb_cmap * cmap , struct fb_info * info )
{
2005-07-27 22:46:08 +04:00
int i , start , rc = 0 ;
2005-04-17 02:20:36 +04:00
u16 * red , * green , * blue , * transp ;
u_int hred , hgreen , hblue , htransp = 0xffff ;
red = cmap - > red ;
green = cmap - > green ;
blue = cmap - > blue ;
transp = cmap - > transp ;
start = cmap - > start ;
2005-05-01 19:59:22 +04:00
if ( start < 0 | | ( ! info - > fbops - > fb_setcolreg & &
! info - > fbops - > fb_setcmap ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-07-27 22:46:08 +04:00
if ( info - > fbops - > fb_setcmap ) {
rc = info - > fbops - > fb_setcmap ( cmap , info ) ;
} else {
for ( i = 0 ; i < cmap - > len ; i + + ) {
hred = * red + + ;
hgreen = * green + + ;
hblue = * blue + + ;
if ( transp )
htransp = * transp + + ;
if ( info - > fbops - > fb_setcolreg ( start + + ,
hred , hgreen , hblue ,
htransp , info ) )
break ;
}
2005-04-17 02:20:36 +04:00
}
2005-07-27 22:46:08 +04:00
if ( rc = = 0 )
fb_copy_cmap ( cmap , & info - > cmap ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
int fb_set_user_cmap ( struct fb_cmap_user * cmap , struct fb_info * info )
{
2005-07-27 22:46:08 +04:00
int rc , size = cmap - > len * sizeof ( u16 ) ;
struct fb_cmap umap ;
2005-04-17 02:20:36 +04:00
2010-11-16 12:11:02 +03:00
if ( size < 0 | | size < cmap - > len )
return - E2BIG ;
2005-07-27 22:46:08 +04:00
memset ( & umap , 0 , sizeof ( struct fb_cmap ) ) ;
2010-11-16 12:11:02 +03:00
rc = fb_alloc_cmap_gfp ( & umap , cmap - > len , cmap - > transp ! = NULL ,
GFP_KERNEL ) ;
2005-07-27 22:46:08 +04:00
if ( rc )
2005-05-01 19:59:22 +04:00
return rc ;
2005-07-27 22:46:08 +04:00
if ( copy_from_user ( umap . red , cmap - > red , size ) | |
copy_from_user ( umap . green , cmap - > green , size ) | |
copy_from_user ( umap . blue , cmap - > blue , size ) | |
( cmap - > transp & & copy_from_user ( umap . transp , cmap - > transp , size ) ) ) {
2009-02-05 02:12:03 +03:00
rc = - EFAULT ;
goto out ;
2005-05-01 19:59:22 +04:00
}
2005-07-27 22:46:08 +04:00
umap . start = cmap - > start ;
2009-02-05 02:12:03 +03:00
if ( ! lock_fb_info ( info ) ) {
rc = - ENODEV ;
goto out ;
}
if ( cmap - > start < 0 | | ( ! info - > fbops - > fb_setcolreg & &
! info - > fbops - > fb_setcmap ) ) {
rc = - EINVAL ;
goto out1 ;
}
2005-07-27 22:46:08 +04:00
rc = fb_set_cmap ( & umap , info ) ;
2009-02-05 02:12:03 +03:00
out1 :
unlock_fb_info ( info ) ;
out :
2005-07-27 22:46:08 +04:00
fb_dealloc_cmap ( & umap ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
/**
* fb_default_cmap - get default colormap
* @ len : size of palette for a depth
*
* Gets the default colormap for a specific screen depth . @ len
* is the size of the palette for a particular screen depth .
*
* Returns pointer to a frame buffer colormap structure .
*
*/
2006-12-08 13:40:27 +03:00
const struct fb_cmap * fb_default_cmap ( int len )
2005-04-17 02:20:36 +04:00
{
if ( len < = 2 )
return & default_2_colors ;
if ( len < = 4 )
return & default_4_colors ;
if ( len < = 8 )
return & default_8_colors ;
return & default_16_colors ;
}
/**
* fb_invert_cmaps - invert all defaults colormaps
*
* Invert all default colormaps .
*
*/
void fb_invert_cmaps ( void )
{
u_int i ;
2006-12-08 13:40:27 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( red2 ) ; i + + ) {
2005-04-17 02:20:36 +04:00
red2 [ i ] = ~ red2 [ i ] ;
green2 [ i ] = ~ green2 [ i ] ;
blue2 [ i ] = ~ blue2 [ i ] ;
}
2006-12-08 13:40:27 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( red4 ) ; i + + ) {
2005-04-17 02:20:36 +04:00
red4 [ i ] = ~ red4 [ i ] ;
green4 [ i ] = ~ green4 [ i ] ;
blue4 [ i ] = ~ blue4 [ i ] ;
}
2006-12-08 13:40:27 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( red8 ) ; i + + ) {
2005-04-17 02:20:36 +04:00
red8 [ i ] = ~ red8 [ i ] ;
green8 [ i ] = ~ green8 [ i ] ;
blue8 [ i ] = ~ blue8 [ i ] ;
}
2006-12-08 13:40:27 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( red16 ) ; i + + ) {
2005-04-17 02:20:36 +04:00
red16 [ i ] = ~ red16 [ i ] ;
green16 [ i ] = ~ green16 [ i ] ;
blue16 [ i ] = ~ blue16 [ i ] ;
}
}
/*
* Visible symbols for modules
*/
EXPORT_SYMBOL ( fb_alloc_cmap ) ;
EXPORT_SYMBOL ( fb_dealloc_cmap ) ;
EXPORT_SYMBOL ( fb_copy_cmap ) ;
EXPORT_SYMBOL ( fb_set_cmap ) ;
EXPORT_SYMBOL ( fb_default_cmap ) ;
EXPORT_SYMBOL ( fb_invert_cmaps ) ;