2008-10-13 12:06:10 +04:00
/*
* LEDs driver for Dialog Semiconductor DA9030 / DA9034
*
* Copyright ( C ) 2008 Compulab , Ltd .
* Mike Rapoport < mike @ compulab . co . il >
*
* Copyright ( C ) 2006 - 2008 Marvell International Ltd .
* Eric Miao < eric . miao @ marvell . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/leds.h>
2008-10-27 12:48:50 +03:00
# include <linux/workqueue.h>
2008-10-13 12:06:10 +04:00
# include <linux/mfd/da903x.h>
# define DA9030_LED1_CONTROL 0x20
# define DA9030_LED2_CONTROL 0x21
# define DA9030_LED3_CONTROL 0x22
# define DA9030_LED4_CONTROL 0x23
# define DA9030_LEDPC_CONTROL 0x24
# define DA9030_MISC_CONTROL_A 0x26 /* Vibrator Control */
# define DA9034_LED1_CONTROL 0x35
# define DA9034_LED2_CONTROL 0x36
# define DA9034_VIBRA 0x40
struct da903x_led {
struct led_classdev cdev ;
struct work_struct work ;
struct device * master ;
enum led_brightness new_brightness ;
int id ;
int flags ;
} ;
# define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1)
# define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1)
static void da903x_led_work ( struct work_struct * work )
{
struct da903x_led * led = container_of ( work , struct da903x_led , work ) ;
uint8_t val ;
int offset ;
switch ( led - > id ) {
case DA9030_ID_LED_1 :
case DA9030_ID_LED_2 :
case DA9030_ID_LED_3 :
case DA9030_ID_LED_4 :
case DA9030_ID_LED_PC :
offset = DA9030_LED_OFFSET ( led - > id ) ;
val = led - > flags & ~ 0x87 ;
val | = ( led - > new_brightness ) ? 0x80 : 0 ; /* EN bit */
2008-10-30 14:05:00 +03:00
val | = ( 0x7 - ( led - > new_brightness > > 5 ) ) & 0x7 ; /* PWM<2:0> */
2008-10-13 12:06:10 +04:00
da903x_write ( led - > master , DA9030_LED1_CONTROL + offset , val ) ;
break ;
case DA9030_ID_VIBRA :
val = led - > flags & ~ 0x80 ;
val | = ( led - > new_brightness ) ? 0x80 : 0 ; /* EN bit */
da903x_write ( led - > master , DA9030_MISC_CONTROL_A , val ) ;
break ;
case DA9034_ID_LED_1 :
case DA9034_ID_LED_2 :
offset = DA9034_LED_OFFSET ( led - > id ) ;
val = ( led - > new_brightness * 0x5f / LED_FULL ) & 0x7f ;
val | = ( led - > flags & DA9034_LED_RAMP ) ? 0x80 : 0 ;
da903x_write ( led - > master , DA9034_LED1_CONTROL + offset , val ) ;
break ;
case DA9034_ID_VIBRA :
val = led - > new_brightness & 0xfe ;
da903x_write ( led - > master , DA9034_VIBRA , val ) ;
break ;
}
}
static void da903x_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
struct da903x_led * led ;
led = container_of ( led_cdev , struct da903x_led , cdev ) ;
led - > new_brightness = value ;
schedule_work ( & led - > work ) ;
}
static int __devinit da903x_led_probe ( struct platform_device * pdev )
{
struct led_info * pdata = pdev - > dev . platform_data ;
struct da903x_led * led ;
int id , ret ;
if ( pdata = = NULL )
return 0 ;
id = pdev - > id ;
if ( ! ( ( id > = DA9030_ID_LED_1 & & id < = DA9030_ID_VIBRA ) | |
( id > = DA9034_ID_LED_1 & & id < = DA9034_ID_VIBRA ) ) ) {
dev_err ( & pdev - > dev , " invalid LED ID (%d) specified \n " , id ) ;
return - EINVAL ;
}
led = kzalloc ( sizeof ( struct da903x_led ) , GFP_KERNEL ) ;
if ( led = = NULL ) {
dev_err ( & pdev - > dev , " failed to alloc memory for LED%d \n " , id ) ;
return - ENOMEM ;
}
led - > cdev . name = pdata - > name ;
led - > cdev . default_trigger = pdata - > default_trigger ;
led - > cdev . brightness_set = da903x_led_set ;
led - > cdev . brightness = LED_OFF ;
led - > id = id ;
led - > flags = pdata - > flags ;
led - > master = pdev - > dev . parent ;
led - > new_brightness = LED_OFF ;
INIT_WORK ( & led - > work , da903x_led_work ) ;
ret = led_classdev_register ( led - > master , & led - > cdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register LED %d \n " , id ) ;
goto err ;
}
platform_set_drvdata ( pdev , led ) ;
return 0 ;
err :
kfree ( led ) ;
return ret ;
}
static int __devexit da903x_led_remove ( struct platform_device * pdev )
{
struct da903x_led * led = platform_get_drvdata ( pdev ) ;
led_classdev_unregister ( & led - > cdev ) ;
kfree ( led ) ;
return 0 ;
}
static struct platform_driver da903x_led_driver = {
. driver = {
. name = " da903x-led " ,
. owner = THIS_MODULE ,
} ,
. probe = da903x_led_probe ,
. remove = __devexit_p ( da903x_led_remove ) ,
} ;
static int __init da903x_led_init ( void )
{
return platform_driver_register ( & da903x_led_driver ) ;
}
module_init ( da903x_led_init ) ;
static void __exit da903x_led_exit ( void )
{
platform_driver_unregister ( & da903x_led_driver ) ;
}
module_exit ( da903x_led_exit ) ;
MODULE_DESCRIPTION ( " LEDs driver for Dialog Semiconductor DA9030/DA9034 " ) ;
MODULE_AUTHOR ( " Eric Miao <eric.miao@marvell.com> "
" Mike Rapoport <mike@compulab.co.il> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:da903x-led " ) ;