2005-04-16 15:20:36 -07:00
/*
* LCD Lowlevel Control Abstraction
*
* Copyright ( C ) 2003 , 2004 Hewlett - Packard Company
*
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/device.h>
# include <linux/lcd.h>
# include <linux/notifier.h>
# include <linux/ctype.h>
# include <linux/err.h>
# include <linux/fb.h>
2006-12-08 02:40:47 -08:00
# if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
defined ( CONFIG_LCD_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 lcd power as well . . .
*/
static int fb_notifier_callback ( struct notifier_block * self ,
unsigned long event , void * data )
{
struct lcd_device * ld ;
struct fb_event * evdata = data ;
/* If we aren't interested in this event, skip it immediately ... */
2008-08-29 04:18:43 +08:00
switch ( event ) {
case FB_EVENT_BLANK :
case FB_EVENT_MODE_CHANGE :
case FB_EVENT_MODE_CHANGE_ALL :
break ;
default :
2006-12-08 02:40:47 -08:00
return 0 ;
2008-08-29 04:18:43 +08:00
}
2006-12-08 02:40:47 -08:00
ld = container_of ( self , struct lcd_device , fb_notif ) ;
2008-08-29 04:18:43 +08:00
if ( ! ld - > ops )
return 0 ;
2007-02-10 23:07:48 +00:00
mutex_lock ( & ld - > ops_lock ) ;
2008-08-29 04:18:43 +08:00
if ( ! ld - > ops - > check_fb | | ld - > ops - > check_fb ( ld , evdata - > info ) ) {
2008-11-19 15:36:25 -08:00
if ( event = = FB_EVENT_BLANK ) {
if ( ld - > ops - > set_power )
ld - > ops - > set_power ( ld , * ( int * ) evdata - > data ) ;
} else {
if ( ld - > ops - > set_mode )
ld - > ops - > set_mode ( ld , evdata - > data ) ;
}
2008-08-29 04:18:43 +08:00
}
2007-02-10 23:07:48 +00:00
mutex_unlock ( & ld - > ops_lock ) ;
2006-12-08 02:40:47 -08:00
return 0 ;
}
static int lcd_register_fb ( struct lcd_device * ld )
{
2009-10-02 11:28:18 +02:00
memset ( & ld - > fb_notif , 0 , sizeof ( ld - > fb_notif ) ) ;
2006-12-08 02:40:47 -08:00
ld - > fb_notif . notifier_call = fb_notifier_callback ;
return fb_register_client ( & ld - > fb_notif ) ;
}
static void lcd_unregister_fb ( struct lcd_device * ld )
{
fb_unregister_client ( & ld - > fb_notif ) ;
}
# else
static int lcd_register_fb ( struct lcd_device * ld )
{
return 0 ;
}
static inline void lcd_unregister_fb ( struct lcd_device * ld )
{
}
# endif /* CONFIG_FB */
2007-07-09 12:17:24 +01:00
static ssize_t lcd_show_power ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-16 15:20:36 -07:00
{
int rc ;
2007-07-09 12:17:24 +01:00
struct lcd_device * ld = to_lcd_device ( dev ) ;
2005-04-16 15:20:36 -07:00
2007-02-10 23:07:48 +00:00
mutex_lock ( & ld - > ops_lock ) ;
if ( ld - > ops & & ld - > ops - > get_power )
rc = sprintf ( buf , " %d \n " , ld - > ops - > get_power ( ld ) ) ;
2005-04-16 15:20:36 -07:00
else
rc = - ENXIO ;
2007-02-10 23:07:48 +00:00
mutex_unlock ( & ld - > ops_lock ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2007-07-09 12:17:24 +01:00
static ssize_t lcd_store_power ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-16 15:20:36 -07:00
{
2006-05-15 09:44:15 -07:00
int rc = - ENXIO ;
2005-04-16 15:20:36 -07:00
char * endp ;
2007-07-09 12:17:24 +01:00
struct lcd_device * ld = to_lcd_device ( dev ) ;
2006-05-15 09:44:15 -07:00
int power = simple_strtoul ( buf , & endp , 0 ) ;
size_t size = endp - buf ;
2005-04-16 15:20:36 -07:00
2009-12-14 18:01:06 -08:00
if ( isspace ( * endp ) )
2006-05-15 09:44:15 -07:00
size + + ;
if ( size ! = count )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2007-02-10 23:07:48 +00:00
mutex_lock ( & ld - > ops_lock ) ;
if ( ld - > ops & & ld - > ops - > set_power ) {
2005-04-16 15:20:36 -07:00
pr_debug ( " lcd: set power to %d \n " , power ) ;
2007-02-10 23:07:48 +00:00
ld - > ops - > set_power ( ld , power ) ;
2005-04-16 15:20:36 -07:00
rc = count ;
2006-05-15 09:44:15 -07:00
}
2007-02-10 23:07:48 +00:00
mutex_unlock ( & ld - > ops_lock ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2007-07-09 12:17:24 +01:00
static ssize_t lcd_show_contrast ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
2006-05-15 09:44:15 -07:00
int rc = - ENXIO ;
2007-07-09 12:17:24 +01:00
struct lcd_device * ld = to_lcd_device ( dev ) ;
2005-04-16 15:20:36 -07:00
2007-02-10 23:07:48 +00:00
mutex_lock ( & ld - > ops_lock ) ;
if ( ld - > ops & & ld - > ops - > get_contrast )
rc = sprintf ( buf , " %d \n " , ld - > ops - > get_contrast ( ld ) ) ;
mutex_unlock ( & ld - > ops_lock ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2007-07-09 12:17:24 +01:00
static ssize_t lcd_store_contrast ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-16 15:20:36 -07:00
{
2006-05-15 09:44:15 -07:00
int rc = - ENXIO ;
2005-04-16 15:20:36 -07:00
char * endp ;
2007-07-09 12:17:24 +01:00
struct lcd_device * ld = to_lcd_device ( dev ) ;
2006-05-15 09:44:15 -07:00
int contrast = simple_strtoul ( buf , & endp , 0 ) ;
size_t size = endp - buf ;
2005-04-16 15:20:36 -07:00
2009-12-14 18:01:06 -08:00
if ( isspace ( * endp ) )
2006-05-15 09:44:15 -07:00
size + + ;
if ( size ! = count )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2007-02-10 23:07:48 +00:00
mutex_lock ( & ld - > ops_lock ) ;
if ( ld - > ops & & ld - > ops - > set_contrast ) {
2005-04-16 15:20:36 -07:00
pr_debug ( " lcd: set contrast to %d \n " , contrast ) ;
2007-02-10 23:07:48 +00:00
ld - > ops - > set_contrast ( ld , contrast ) ;
2005-04-16 15:20:36 -07:00
rc = count ;
2006-05-15 09:44:15 -07:00
}
2007-02-10 23:07:48 +00:00
mutex_unlock ( & ld - > ops_lock ) ;
2005-04-16 15:20:36 -07:00
return rc ;
}
2007-07-09 12:17:24 +01:00
static ssize_t lcd_show_max_contrast ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
2007-07-09 12:17:24 +01:00
struct lcd_device * ld = to_lcd_device ( dev ) ;
2005-04-16 15:20:36 -07:00
2007-02-10 23:07:48 +00:00
return sprintf ( buf , " %d \n " , ld - > props . max_contrast ) ;
2005-04-16 15:20:36 -07:00
}
2007-08-11 10:27:19 +01:00
static struct class * lcd_class ;
2007-07-09 12:17:24 +01:00
static void lcd_device_release ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
struct lcd_device * ld = to_lcd_device ( dev ) ;
kfree ( ld ) ;
}
2007-07-09 12:17:24 +01:00
static struct device_attribute lcd_device_attributes [ ] = {
__ATTR ( lcd_power , 0644 , lcd_show_power , lcd_store_power ) ,
__ATTR ( contrast , 0644 , lcd_show_contrast , lcd_store_contrast ) ,
__ATTR ( max_contrast , 0444 , lcd_show_max_contrast , NULL ) ,
__ATTR_NULL ,
2005-04-16 15:20:36 -07:00
} ;
/**
* lcd_device_register - register a new object of lcd_device class .
* @ name : the name of the new object ( must be the same as the name of the
* respective framebuffer device ) .
2007-07-09 12:17:24 +01:00
* @ devdata : an optional pointer to be stored in the device . The
* methods may retrieve it by using lcd_get_data ( ld ) .
2007-02-10 23:07:48 +00:00
* @ ops : the lcd operations structure .
2005-04-16 15:20:36 -07:00
*
2007-07-09 12:17:24 +01:00
* Creates and registers a new lcd device . Returns either an ERR_PTR ( )
2005-04-16 15:20:36 -07:00
* or a pointer to the newly allocated device .
*/
2007-07-09 12:17:24 +01:00
struct lcd_device * lcd_device_register ( const char * name , struct device * parent ,
void * devdata , struct lcd_ops * ops )
2005-04-16 15:20:36 -07:00
{
struct lcd_device * new_ld ;
2007-07-09 12:17:24 +01:00
int rc ;
2005-04-16 15:20:36 -07:00
pr_debug ( " lcd_device_register: name=%s \n " , name ) ;
2007-02-10 23:07:48 +00:00
new_ld = kzalloc ( sizeof ( struct lcd_device ) , GFP_KERNEL ) ;
2007-02-08 00:12:28 +00:00
if ( ! new_ld )
2006-03-09 17:33:36 -08:00
return ERR_PTR ( - ENOMEM ) ;
2005-04-16 15:20:36 -07:00
2007-02-10 23:07:48 +00:00
mutex_init ( & new_ld - > ops_lock ) ;
2007-02-08 22:25:09 +00:00
mutex_init ( & new_ld - > update_lock ) ;
2005-04-16 15:20:36 -07:00
2007-07-09 12:17:24 +01:00
new_ld - > dev . class = lcd_class ;
new_ld - > dev . parent = parent ;
new_ld - > dev . release = lcd_device_release ;
2009-01-06 10:44:35 -08:00
dev_set_name ( & new_ld - > dev , name ) ;
2007-07-09 12:17:24 +01:00
dev_set_drvdata ( & new_ld - > dev , devdata ) ;
rc = device_register ( & new_ld - > dev ) ;
2007-02-08 00:12:28 +00:00
if ( rc ) {
2007-02-07 22:25:50 +00:00
kfree ( new_ld ) ;
2005-04-16 15:20:36 -07:00
return ERR_PTR ( rc ) ;
}
2006-12-08 02:40:47 -08:00
rc = lcd_register_fb ( new_ld ) ;
2007-02-07 22:25:50 +00:00
if ( rc ) {
2007-07-09 12:17:24 +01:00
device_unregister ( & new_ld - > dev ) ;
2007-02-07 22:25:50 +00:00
return ERR_PTR ( rc ) ;
}
2005-04-16 15:20:36 -07:00
2007-07-09 12:17:24 +01:00
new_ld - > ops = ops ;
2005-04-16 15:20:36 -07:00
return new_ld ;
}
EXPORT_SYMBOL ( lcd_device_register ) ;
/**
* lcd_device_unregister - unregisters a object of lcd_device class .
* @ ld : the lcd device object to be unregistered and freed .
*
* Unregisters a previously registered via lcd_device_register object .
*/
void lcd_device_unregister ( struct lcd_device * ld )
{
if ( ! ld )
return ;
2007-02-10 23:07:48 +00:00
mutex_lock ( & ld - > ops_lock ) ;
ld - > ops = NULL ;
mutex_unlock ( & ld - > ops_lock ) ;
2006-12-08 02:40:47 -08:00
lcd_unregister_fb ( ld ) ;
2007-07-09 12:17:24 +01:00
device_unregister ( & ld - > dev ) ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( lcd_device_unregister ) ;
static void __exit lcd_class_exit ( void )
{
2007-07-09 12:17:24 +01:00
class_destroy ( lcd_class ) ;
2005-04-16 15:20:36 -07:00
}
static int __init lcd_class_init ( void )
{
2007-07-09 12:17:24 +01:00
lcd_class = class_create ( THIS_MODULE , " lcd " ) ;
if ( IS_ERR ( lcd_class ) ) {
printk ( KERN_WARNING " Unable to create backlight class; errno = %ld \n " ,
PTR_ERR ( lcd_class ) ) ;
return PTR_ERR ( lcd_class ) ;
}
lcd_class - > dev_attrs = lcd_device_attributes ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
/*
* 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 ( lcd_class_init ) ;
module_exit ( lcd_class_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru> " ) ;
MODULE_DESCRIPTION ( " LCD Lowlevel Control Abstraction " ) ;