2009-11-09 20:41:07 +03:00
/*
* Backlight driver for Marvell Semiconductor 88 PM8606
*
* Copyright ( C ) 2009 Marvell International Ltd .
* Haojian Zhuang < haojian . zhuang @ 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/init.h>
# include <linux/kernel.h>
2012-09-21 14:06:52 +04:00
# include <linux/of.h>
2009-11-09 20:41:07 +03:00
# include <linux/platform_device.h>
2011-03-07 18:43:09 +03:00
# include <linux/slab.h>
2009-11-09 20:41:07 +03:00
# include <linux/fb.h>
# include <linux/i2c.h>
# include <linux/backlight.h>
# include <linux/mfd/88pm860x.h>
2011-07-04 00:17:28 +04:00
# include <linux/module.h>
2009-11-09 20:41:07 +03:00
# define MAX_BRIGHTNESS (0xFF)
# define MIN_BRIGHTNESS (0)
2011-01-21 01:44:31 +03:00
# define CURRENT_BITMASK (0x1F << 1)
2009-11-09 20:41:07 +03:00
struct pm860x_backlight_data {
struct pm860x_chip * chip ;
struct i2c_client * i2c ;
int current_brightness ;
int port ;
int pwm ;
int iset ;
2012-08-08 19:17:26 +04:00
int reg_duty_cycle ;
int reg_always_on ;
int reg_current ;
2009-11-09 20:41:07 +03:00
} ;
2012-02-28 06:59:08 +04:00
static int backlight_power_set ( struct pm860x_chip * chip , int port ,
int on )
{
int ret = - EINVAL ;
switch ( port ) {
2012-08-08 19:17:26 +04:00
case 0 :
2012-02-28 06:59:08 +04:00
ret = on ? pm8606_osc_enable ( chip , WLED1_DUTY ) :
pm8606_osc_disable ( chip , WLED1_DUTY ) ;
break ;
2012-08-08 19:17:26 +04:00
case 1 :
2012-02-28 06:59:08 +04:00
ret = on ? pm8606_osc_enable ( chip , WLED2_DUTY ) :
pm8606_osc_disable ( chip , WLED2_DUTY ) ;
break ;
2012-08-08 19:17:26 +04:00
case 2 :
2012-02-28 06:59:08 +04:00
ret = on ? pm8606_osc_enable ( chip , WLED3_DUTY ) :
pm8606_osc_disable ( chip , WLED3_DUTY ) ;
break ;
}
return ret ;
}
2009-11-09 20:41:07 +03:00
static int pm860x_backlight_set ( struct backlight_device * bl , int brightness )
{
struct pm860x_backlight_data * data = bl_get_data ( bl ) ;
struct pm860x_chip * chip = data - > chip ;
unsigned char value ;
int ret ;
if ( brightness > MAX_BRIGHTNESS )
value = MAX_BRIGHTNESS ;
else
value = brightness ;
2012-02-28 06:59:08 +04:00
if ( brightness )
backlight_power_set ( chip , data - > port , 1 ) ;
2012-08-08 19:17:26 +04:00
ret = pm860x_reg_write ( data - > i2c , data - > reg_duty_cycle , value ) ;
2009-11-09 20:41:07 +03:00
if ( ret < 0 )
goto out ;
if ( ( data - > current_brightness = = 0 ) & & brightness ) {
if ( data - > iset ) {
2012-08-08 19:17:26 +04:00
ret = pm860x_set_bits ( data - > i2c , data - > reg_current ,
2011-01-21 01:44:31 +03:00
CURRENT_BITMASK , data - > iset ) ;
2009-11-09 20:41:07 +03:00
if ( ret < 0 )
goto out ;
}
if ( data - > pwm ) {
ret = pm860x_set_bits ( data - > i2c , PM8606_PWM ,
PM8606_PWM_FREQ_MASK , data - > pwm ) ;
if ( ret < 0 )
goto out ;
}
if ( brightness = = MAX_BRIGHTNESS ) {
/* set WLED_ON bit as 100% */
2012-08-08 19:17:26 +04:00
ret = pm860x_set_bits ( data - > i2c , data - > reg_always_on ,
2009-11-09 20:41:07 +03:00
PM8606_WLED_ON , PM8606_WLED_ON ) ;
}
} else {
if ( brightness = = MAX_BRIGHTNESS ) {
/* set WLED_ON bit as 100% */
2012-08-08 19:17:26 +04:00
ret = pm860x_set_bits ( data - > i2c , data - > reg_always_on ,
2009-11-09 20:41:07 +03:00
PM8606_WLED_ON , PM8606_WLED_ON ) ;
} else {
/* clear WLED_ON bit since it's not 100% */
2012-08-08 19:17:26 +04:00
ret = pm860x_set_bits ( data - > i2c , data - > reg_always_on ,
2009-11-09 20:41:07 +03:00
PM8606_WLED_ON , 0 ) ;
}
}
if ( ret < 0 )
goto out ;
2012-02-28 06:59:08 +04:00
if ( brightness = = 0 )
backlight_power_set ( chip , data - > port , 0 ) ;
2009-11-09 20:41:07 +03:00
dev_dbg ( chip - > dev , " set brightness %d \n " , value ) ;
data - > current_brightness = value ;
return 0 ;
out :
2012-12-18 04:00:11 +04:00
dev_dbg ( chip - > dev , " set brightness %d failure with return value: %d \n " ,
value , ret ) ;
2009-11-09 20:41:07 +03:00
return ret ;
}
static int pm860x_backlight_update_status ( struct backlight_device * bl )
{
int brightness = bl - > props . brightness ;
if ( bl - > props . power ! = FB_BLANK_UNBLANK )
brightness = 0 ;
if ( bl - > props . fb_blank ! = FB_BLANK_UNBLANK )
brightness = 0 ;
if ( bl - > props . state & BL_CORE_SUSPENDED )
brightness = 0 ;
return pm860x_backlight_set ( bl , brightness ) ;
}
static int pm860x_backlight_get_brightness ( struct backlight_device * bl )
{
struct pm860x_backlight_data * data = bl_get_data ( bl ) ;
struct pm860x_chip * chip = data - > chip ;
int ret ;
2012-08-08 19:17:26 +04:00
ret = pm860x_reg_read ( data - > i2c , data - > reg_duty_cycle ) ;
2009-11-09 20:41:07 +03:00
if ( ret < 0 )
goto out ;
data - > current_brightness = ret ;
dev_dbg ( chip - > dev , " get brightness %d \n " , data - > current_brightness ) ;
return data - > current_brightness ;
out :
return - EINVAL ;
}
2010-11-16 16:14:02 +03:00
static const struct backlight_ops pm860x_backlight_ops = {
2009-11-09 20:41:07 +03:00
. options = BL_CORE_SUSPENDRESUME ,
. update_status = pm860x_backlight_update_status ,
. get_brightness = pm860x_backlight_get_brightness ,
} ;
2012-09-21 14:06:52 +04:00
# ifdef CONFIG_OF
static int pm860x_backlight_dt_init ( struct platform_device * pdev ,
struct pm860x_backlight_data * data ,
char * name )
{
2013-02-22 04:44:00 +04:00
struct device_node * nproot , * np ;
2012-09-21 14:06:52 +04:00
int iset = 0 ;
2013-02-22 04:44:00 +04:00
nproot = of_node_get ( pdev - > dev . parent - > of_node ) ;
2012-09-21 14:06:52 +04:00
if ( ! nproot )
return - ENODEV ;
nproot = of_find_node_by_name ( nproot , " backlights " ) ;
if ( ! nproot ) {
dev_err ( & pdev - > dev , " failed to find backlights node \n " ) ;
return - ENODEV ;
}
for_each_child_of_node ( nproot , np ) {
if ( ! of_node_cmp ( np - > name , name ) ) {
of_property_read_u32 ( np , " marvell,88pm860x-iset " ,
& iset ) ;
data - > iset = PM8606_WLED_CURRENT ( iset ) ;
of_property_read_u32 ( np , " marvell,88pm860x-pwm " ,
& data - > pwm ) ;
break ;
}
}
2013-02-22 04:44:00 +04:00
of_node_put ( nproot ) ;
2012-09-21 14:06:52 +04:00
return 0 ;
}
# else
# define pm860x_backlight_dt_init(x, y, z) (-1)
# endif
2009-11-09 20:41:07 +03:00
static int pm860x_backlight_probe ( struct platform_device * pdev )
{
struct pm860x_chip * chip = dev_get_drvdata ( pdev - > dev . parent ) ;
2012-08-08 19:17:26 +04:00
struct pm860x_backlight_pdata * pdata = pdev - > dev . platform_data ;
2009-11-09 20:41:07 +03:00
struct pm860x_backlight_data * data ;
struct backlight_device * bl ;
struct resource * res ;
2010-02-18 00:39:44 +03:00
struct backlight_properties props ;
2009-11-09 20:41:07 +03:00
char name [ MFD_NAME_SIZE ] ;
2012-08-08 19:17:26 +04:00
int ret = 0 ;
2009-11-09 20:41:07 +03:00
2012-03-24 02:02:00 +04:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( struct pm860x_backlight_data ) ,
GFP_KERNEL ) ;
2009-11-09 20:41:07 +03:00
if ( data = = NULL )
return - ENOMEM ;
2012-08-08 19:17:26 +04:00
res = platform_get_resource_byname ( pdev , IORESOURCE_REG , " duty cycle " ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " No REG resource for duty cycle \n " ) ;
2012-12-18 04:00:57 +04:00
return - ENXIO ;
2012-08-08 19:17:26 +04:00
}
data - > reg_duty_cycle = res - > start ;
res = platform_get_resource_byname ( pdev , IORESOURCE_REG , " always on " ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " No REG resorce for always on \n " ) ;
2012-12-18 04:00:57 +04:00
return - ENXIO ;
2012-08-08 19:17:26 +04:00
}
data - > reg_always_on = res - > start ;
res = platform_get_resource_byname ( pdev , IORESOURCE_REG , " current " ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " No REG resource for current \n " ) ;
2012-12-18 04:00:57 +04:00
return - ENXIO ;
2012-08-08 19:17:26 +04:00
}
data - > reg_current = res - > start ;
memset ( name , 0 , MFD_NAME_SIZE ) ;
sprintf ( name , " backlight-%d " , pdev - > id ) ;
data - > port = pdev - > id ;
2009-11-09 20:41:07 +03:00
data - > chip = chip ;
2012-12-18 04:01:09 +04:00
data - > i2c = ( chip - > id = = CHIP_PM8606 ) ? chip - > client : chip - > companion ;
2009-11-09 20:41:07 +03:00
data - > current_brightness = MAX_BRIGHTNESS ;
2012-09-21 14:06:52 +04:00
if ( pm860x_backlight_dt_init ( pdev , data , name ) ) {
if ( pdata ) {
data - > pwm = pdata - > pwm ;
data - > iset = pdata - > iset ;
}
2009-11-09 20:41:07 +03:00
}
2010-02-18 00:39:44 +03:00
memset ( & props , 0 , sizeof ( struct backlight_properties ) ) ;
2011-03-23 02:30:21 +03:00
props . type = BACKLIGHT_RAW ;
2010-02-18 00:39:44 +03:00
props . max_brightness = MAX_BRIGHTNESS ;
2009-11-09 20:41:07 +03:00
bl = backlight_device_register ( name , & pdev - > dev , data ,
2010-02-18 00:39:44 +03:00
& pm860x_backlight_ops , & props ) ;
2009-11-09 20:41:07 +03:00
if ( IS_ERR ( bl ) ) {
dev_err ( & pdev - > dev , " failed to register backlight \n " ) ;
return PTR_ERR ( bl ) ;
}
bl - > props . brightness = MAX_BRIGHTNESS ;
platform_set_drvdata ( pdev , bl ) ;
/* read current backlight */
ret = pm860x_backlight_get_brightness ( bl ) ;
if ( ret < 0 )
2012-08-08 19:17:26 +04:00
goto out_brt ;
2009-11-09 20:41:07 +03:00
backlight_update_status ( bl ) ;
return 0 ;
2012-08-08 19:17:26 +04:00
out_brt :
2010-05-10 05:29:58 +04:00
backlight_device_unregister ( bl ) ;
2009-11-09 20:41:07 +03:00
return ret ;
}
static int pm860x_backlight_remove ( struct platform_device * pdev )
{
struct backlight_device * bl = platform_get_drvdata ( pdev ) ;
backlight_device_unregister ( bl ) ;
return 0 ;
}
static struct platform_driver pm860x_backlight_driver = {
. driver = {
. name = " 88pm860x-backlight " ,
. owner = THIS_MODULE ,
} ,
. probe = pm860x_backlight_probe ,
. remove = pm860x_backlight_remove ,
} ;
2012-01-11 03:09:11 +04:00
module_platform_driver ( pm860x_backlight_driver ) ;
2009-11-09 20:41:07 +03:00
MODULE_DESCRIPTION ( " Backlight Driver for Marvell Semiconductor 88PM8606 " ) ;
MODULE_AUTHOR ( " Haojian Zhuang <haojian.zhuang@marvell.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:88pm860x-backlight " ) ;