2005-04-17 02:20:36 +04:00
/*
* Backlight Lowlevel Control Abstraction
*
* Copyright ( C ) 2003 , 2004 Hewlett - Packard Company
*
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/device.h>
# include <linux/backlight.h>
# include <linux/notifier.h>
# include <linux/ctype.h>
# include <linux/err.h>
# include <linux/fb.h>
2007-02-10 18:04:08 +03:00
# ifdef CONFIG_PMAC_BACKLIGHT
# include <asm/backlight.h>
# endif
2006-12-08 13:40:47 +03:00
# if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
defined ( CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE ) )
/* This callback gets called when something important happens inside a
* framebuffer driver . We ' re looking if that important event is blanking ,
* and if it is , we ' re switching backlight power as well . . .
*/
static int fb_notifier_callback ( struct notifier_block * self ,
unsigned long event , void * data )
{
struct backlight_device * bd ;
struct fb_event * evdata = data ;
/* If we aren't interested in this event, skip it immediately ... */
2007-02-09 12:46:45 +03:00
if ( event ! = FB_EVENT_BLANK & & event ! = FB_EVENT_CONBLANK )
2006-12-08 13:40:47 +03:00
return 0 ;
bd = container_of ( self , struct backlight_device , fb_notif ) ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2006-12-08 13:40:47 +03:00
if ( bd - > props )
if ( ! bd - > props - > check_fb | |
bd - > props - > check_fb ( evdata - > info ) ) {
bd - > props - > fb_blank = * ( int * ) evdata - > data ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( bd ) ;
2006-12-08 13:40:47 +03:00
}
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2006-12-08 13:40:47 +03:00
return 0 ;
}
static int backlight_register_fb ( struct backlight_device * bd )
{
memset ( & bd - > fb_notif , 0 , sizeof ( bd - > fb_notif ) ) ;
bd - > fb_notif . notifier_call = fb_notifier_callback ;
return fb_register_client ( & bd - > fb_notif ) ;
}
static void backlight_unregister_fb ( struct backlight_device * bd )
{
fb_unregister_client ( & bd - > fb_notif ) ;
}
# else
static inline int backlight_register_fb ( struct backlight_device * bd )
{
return 0 ;
}
static inline void backlight_unregister_fb ( struct backlight_device * bd )
{
}
# endif /* CONFIG_FB */
2005-04-17 02:20:36 +04:00
static ssize_t backlight_show_power ( struct class_device * cdev , char * buf )
{
2006-03-31 14:31:49 +04:00
int rc = - ENXIO ;
2005-04-17 02:20:36 +04:00
struct backlight_device * bd = to_backlight_device ( cdev ) ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2007-02-08 03:12:28 +03:00
if ( bd - > props )
2006-03-31 14:31:49 +04:00
rc = sprintf ( buf , " %d \n " , bd - > props - > power ) ;
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static ssize_t backlight_store_power ( struct class_device * cdev , const char * buf , size_t count )
{
2006-05-15 20:44:15 +04:00
int rc = - ENXIO ;
2005-04-17 02:20:36 +04:00
char * endp ;
struct backlight_device * bd = to_backlight_device ( cdev ) ;
2006-05-15 20:44:15 +04:00
int power = simple_strtoul ( buf , & endp , 0 ) ;
size_t size = endp - buf ;
2005-04-17 02:20:36 +04:00
2006-05-15 20:44:15 +04:00
if ( * endp & & isspace ( * endp ) )
size + + ;
if ( size ! = count )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2007-02-08 03:12:28 +03:00
if ( bd - > props ) {
2005-04-17 02:20:36 +04:00
pr_debug ( " backlight: set power to %d \n " , power ) ;
2006-03-31 14:31:49 +04:00
bd - > props - > power = power ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( bd ) ;
2005-04-17 02:20:36 +04:00
rc = count ;
2006-03-31 14:31:49 +04:00
}
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static ssize_t backlight_show_brightness ( struct class_device * cdev , char * buf )
{
2006-03-31 14:31:49 +04:00
int rc = - ENXIO ;
2005-04-17 02:20:36 +04:00
struct backlight_device * bd = to_backlight_device ( cdev ) ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2007-02-08 03:12:28 +03:00
if ( bd - > props )
2006-03-31 14:31:49 +04:00
rc = sprintf ( buf , " %d \n " , bd - > props - > brightness ) ;
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static ssize_t backlight_store_brightness ( struct class_device * cdev , const char * buf , size_t count )
{
2006-05-15 20:44:15 +04:00
int rc = - ENXIO ;
2005-04-17 02:20:36 +04:00
char * endp ;
struct backlight_device * bd = to_backlight_device ( cdev ) ;
2006-05-15 20:44:15 +04:00
int brightness = simple_strtoul ( buf , & endp , 0 ) ;
size_t size = endp - buf ;
2005-04-17 02:20:36 +04:00
2006-05-15 20:44:15 +04:00
if ( * endp & & isspace ( * endp ) )
size + + ;
if ( size ! = count )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2007-02-08 03:12:28 +03:00
if ( bd - > props ) {
2006-03-31 14:31:49 +04:00
if ( brightness > bd - > props - > max_brightness )
rc = - EINVAL ;
else {
pr_debug ( " backlight: set brightness to %d \n " ,
brightness ) ;
bd - > props - > brightness = brightness ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( bd ) ;
2006-03-31 14:31:49 +04:00
rc = count ;
}
}
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static ssize_t backlight_show_max_brightness ( struct class_device * cdev , char * buf )
{
2006-03-31 14:31:49 +04:00
int rc = - ENXIO ;
2005-04-17 02:20:36 +04:00
struct backlight_device * bd = to_backlight_device ( cdev ) ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2007-02-08 03:12:28 +03:00
if ( bd - > props )
2005-04-17 02:20:36 +04:00
rc = sprintf ( buf , " %d \n " , bd - > props - > max_brightness ) ;
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2006-03-31 14:31:49 +04:00
return rc ;
}
static ssize_t backlight_show_actual_brightness ( struct class_device * cdev ,
char * buf )
{
int rc = - ENXIO ;
struct backlight_device * bd = to_backlight_device ( cdev ) ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2007-02-08 03:12:28 +03:00
if ( bd - > props & & bd - > props - > get_brightness )
2006-03-31 14:31:49 +04:00
rc = sprintf ( buf , " %d \n " , bd - > props - > get_brightness ( bd ) ) ;
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
static void backlight_class_release ( struct class_device * dev )
{
struct backlight_device * bd = to_backlight_device ( dev ) ;
kfree ( bd ) ;
}
static struct class backlight_class = {
. name = " backlight " ,
. release = backlight_class_release ,
} ;
# define DECLARE_ATTR(_name,_mode,_show,_store) \
{ \
. attr = { . name = __stringify ( _name ) , . mode = _mode , . owner = THIS_MODULE } , \
. show = _show , \
. store = _store , \
}
2006-12-08 13:40:28 +03:00
static const struct class_device_attribute bl_class_device_attributes [ ] = {
2005-04-17 02:20:36 +04:00
DECLARE_ATTR ( power , 0644 , backlight_show_power , backlight_store_power ) ,
2006-03-31 14:31:49 +04:00
DECLARE_ATTR ( brightness , 0644 , backlight_show_brightness ,
backlight_store_brightness ) ,
DECLARE_ATTR ( actual_brightness , 0444 , backlight_show_actual_brightness ,
NULL ) ,
2005-04-17 02:20:36 +04:00
DECLARE_ATTR ( max_brightness , 0444 , backlight_show_max_brightness , NULL ) ,
} ;
/**
* backlight_device_register - create and register a new object of
* backlight_device class .
* @ name : the name of the new object ( must be the same as the name of the
* respective framebuffer device ) .
* @ devdata : an optional pointer to be stored in the class_device . The
* methods may retrieve it by using class_get_devdata ( & bd - > class_dev ) .
* @ bp : the backlight properties structure .
*
* Creates and registers new backlight class_device . Returns either an
* ERR_PTR ( ) or a pointer to the newly allocated device .
*/
2006-12-19 23:56:15 +03:00
struct backlight_device * backlight_device_register ( const char * name ,
struct device * dev ,
void * devdata ,
struct backlight_properties * bp )
2005-04-17 02:20:36 +04:00
{
int i , rc ;
struct backlight_device * new_bd ;
pr_debug ( " backlight_device_alloc: name=%s \n " , name ) ;
new_bd = kmalloc ( sizeof ( struct backlight_device ) , GFP_KERNEL ) ;
2007-02-08 03:12:28 +03:00
if ( ! new_bd )
2006-03-10 04:33:36 +03:00
return ERR_PTR ( - ENOMEM ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 01:25:09 +03:00
mutex_init ( & new_bd - > update_lock ) ;
2007-02-09 01:53:55 +03:00
mutex_init ( & new_bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
new_bd - > props = bp ;
memset ( & new_bd - > class_dev , 0 , sizeof ( new_bd - > class_dev ) ) ;
new_bd - > class_dev . class = & backlight_class ;
2006-12-19 23:56:15 +03:00
new_bd - > class_dev . dev = dev ;
2005-04-17 02:20:36 +04:00
strlcpy ( new_bd - > class_dev . class_id , name , KOBJ_NAME_LEN ) ;
class_set_devdata ( & new_bd - > class_dev , devdata ) ;
rc = class_device_register ( & new_bd - > class_dev ) ;
2007-02-08 03:12:28 +03:00
if ( rc ) {
2007-02-08 01:25:50 +03:00
kfree ( new_bd ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( rc ) ;
}
2006-12-08 13:40:47 +03:00
rc = backlight_register_fb ( new_bd ) ;
2007-02-08 01:25:50 +03:00
if ( rc ) {
class_device_unregister ( & new_bd - > class_dev ) ;
return ERR_PTR ( rc ) ;
}
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( bl_class_device_attributes ) ; i + + ) {
rc = class_device_create_file ( & new_bd - > class_dev ,
& bl_class_device_attributes [ i ] ) ;
2007-02-08 03:12:28 +03:00
if ( rc ) {
2005-04-17 02:20:36 +04:00
while ( - - i > = 0 )
class_device_remove_file ( & new_bd - > class_dev ,
& bl_class_device_attributes [ i ] ) ;
class_device_unregister ( & new_bd - > class_dev ) ;
/* No need to kfree(new_bd) since release() method was called */
return ERR_PTR ( rc ) ;
}
}
2007-02-10 18:04:08 +03:00
# ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock ( & pmac_backlight_mutex ) ;
if ( ! pmac_backlight )
pmac_backlight = new_bd ;
mutex_unlock ( & pmac_backlight_mutex ) ;
# endif
2005-04-17 02:20:36 +04:00
return new_bd ;
}
EXPORT_SYMBOL ( backlight_device_register ) ;
/**
* backlight_device_unregister - unregisters a backlight device object .
* @ bd : the backlight device object to be unregistered and freed .
*
* Unregisters a previously registered via backlight_device_register object .
*/
void backlight_device_unregister ( struct backlight_device * bd )
{
int i ;
if ( ! bd )
return ;
pr_debug ( " backlight_device_unregister: name=%s \n " , bd - > class_dev . class_id ) ;
2007-02-10 18:04:08 +03:00
# ifdef CONFIG_PMAC_BACKLIGHT
mutex_lock ( & pmac_backlight_mutex ) ;
if ( pmac_backlight = = bd )
pmac_backlight = NULL ;
mutex_unlock ( & pmac_backlight_mutex ) ;
# endif
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( bl_class_device_attributes ) ; i + + )
class_device_remove_file ( & bd - > class_dev ,
& bl_class_device_attributes [ i ] ) ;
2007-02-09 01:53:55 +03:00
mutex_lock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
bd - > props = NULL ;
2007-02-09 01:53:55 +03:00
mutex_unlock ( & bd - > props_lock ) ;
2005-04-17 02:20:36 +04:00
2006-12-08 13:40:47 +03:00
backlight_unregister_fb ( bd ) ;
2005-04-17 02:20:36 +04:00
class_device_unregister ( & bd - > class_dev ) ;
}
EXPORT_SYMBOL ( backlight_device_unregister ) ;
static void __exit backlight_class_exit ( void )
{
class_unregister ( & backlight_class ) ;
}
static int __init backlight_class_init ( void )
{
return class_register ( & backlight_class ) ;
}
/*
* if this is compiled into the kernel , we need to ensure that the
* class is registered before users of the class try to register lcd ' s
*/
postcore_initcall ( backlight_class_init ) ;
module_exit ( backlight_class_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru> " ) ;
MODULE_DESCRIPTION ( " Backlight Lowlevel Control Abstraction " ) ;