2010-05-01 01:09:51 +04:00
/*
* Backlight driver for Analog Devices ADP8860 Backlight Devices
*
* Copyright 2009 - 2010 Analog Devices Inc .
*
* Licensed under the GPL - 2 or later .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/pm.h>
# include <linux/platform_device.h>
# include <linux/i2c.h>
# include <linux/fb.h>
# include <linux/backlight.h>
# include <linux/leds.h>
2010-05-13 00:49:12 +04:00
# include <linux/slab.h>
2010-05-01 01:09:51 +04:00
# include <linux/workqueue.h>
2017-05-22 01:09:52 +03:00
# include <linux/platform_data/adp8860.h>
2010-05-01 01:09:51 +04:00
# define ADP8860_EXT_FEATURES
# define ADP8860_USE_LEDS
# define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */
# define ADP8860_MDCR 0x01 /* Device mode and status */
# define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */
# define ADP8860_INTR_EN 0x03 /* Interrupts enable */
# define ADP8860_CFGR 0x04 /* Configuration register */
# define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */
# define ADP8860_BLOFF 0x06 /* Backlight off timeout */
# define ADP8860_BLDIM 0x07 /* Backlight dim timeout */
# define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */
# define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */
# define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */
# define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */
# define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */
# define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */
# define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */
# define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */
# define ADP8860_ISCC 0x10 /* Independent sink current control register */
# define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */
# define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */
# define ADP8860_ISCF 0x13 /* Independent sink current fade register */
# define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */
# define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */
# define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */
# define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */
# define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */
# define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */
# define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */
# define ADP8860_CCFG 0x1B /* Comparator configuration */
# define ADP8860_CCFG2 0x1C /* Second comparator configuration */
# define ADP8860_L2_TRP 0x1D /* L2 comparator reference */
# define ADP8860_L2_HYS 0x1E /* L2 hysteresis */
# define ADP8860_L3_TRP 0x1F /* L3 comparator reference */
# define ADP8860_L3_HYS 0x20 /* L3 hysteresis */
# define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */
# define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */
# define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */
# define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */
# define ADP8860_MANUFID 0x0 /* Analog Devices ADP8860 Manufacturer ID */
2010-05-26 11:00:59 +04:00
# define ADP8861_MANUFID 0x4 /* Analog Devices ADP8861 Manufacturer ID */
# define ADP8863_MANUFID 0x2 /* Analog Devices ADP8863 Manufacturer ID */
2010-05-01 01:09:51 +04:00
# define ADP8860_DEVID(x) ((x) & 0xF)
# define ADP8860_MANID(x) ((x) >> 4)
/* MDCR Device mode and status */
# define INT_CFG (1 << 6)
# define NSTBY (1 << 5)
# define DIM_EN (1 << 4)
2010-05-26 11:00:59 +04:00
# define GDWN_DIS (1 << 3)
2010-05-01 01:09:51 +04:00
# define SIS_EN (1 << 2)
# define CMP_AUTOEN (1 << 1)
# define BLEN (1 << 0)
/* ADP8860_CCFG Main ALS comparator level enable */
# define L3_EN (1 << 1)
# define L2_EN (1 << 0)
# define CFGR_BLV_SHIFT 3
# define CFGR_BLV_MASK 0x3
# define ADP8860_FLAG_LED_MASK 0xFF
# define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4))
# define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1))
# define ALS_CCFG_VAL(filt) ((0x7 & filt) << 5)
2010-05-26 11:00:59 +04:00
enum {
adp8860 ,
adp8861 ,
adp8863
} ;
2010-05-01 01:09:51 +04:00
struct adp8860_led {
struct led_classdev cdev ;
struct work_struct work ;
struct i2c_client * client ;
enum led_brightness new_brightness ;
int id ;
int flags ;
} ;
struct adp8860_bl {
struct i2c_client * client ;
struct backlight_device * bl ;
struct adp8860_led * led ;
struct adp8860_backlight_platform_data * pdata ;
struct mutex lock ;
unsigned long cached_daylight_max ;
int id ;
int revid ;
int current_brightness ;
2010-05-26 11:00:59 +04:00
unsigned en_ambl_sens : 1 ;
unsigned gdwn_dis : 1 ;
2010-05-01 01:09:51 +04:00
} ;
static int adp8860_read ( struct i2c_client * client , int reg , uint8_t * val )
{
int ret ;
ret = i2c_smbus_read_byte_data ( client , reg ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " failed reading at 0x%02x \n " , reg ) ;
return ret ;
}
* val = ( uint8_t ) ret ;
return 0 ;
}
static int adp8860_write ( struct i2c_client * client , u8 reg , u8 val )
{
return i2c_smbus_write_byte_data ( client , reg , val ) ;
}
static int adp8860_set_bits ( struct i2c_client * client , int reg , uint8_t bit_mask )
{
struct adp8860_bl * data = i2c_get_clientdata ( client ) ;
uint8_t reg_val ;
int ret ;
mutex_lock ( & data - > lock ) ;
ret = adp8860_read ( client , reg , & reg_val ) ;
2012-01-21 02:34:05 +04:00
if ( ! ret & & ( ( reg_val & bit_mask ) ! = bit_mask ) ) {
2010-05-01 01:09:51 +04:00
reg_val | = bit_mask ;
ret = adp8860_write ( client , reg , reg_val ) ;
}
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static int adp8860_clr_bits ( struct i2c_client * client , int reg , uint8_t bit_mask )
{
struct adp8860_bl * data = i2c_get_clientdata ( client ) ;
uint8_t reg_val ;
int ret ;
mutex_lock ( & data - > lock ) ;
ret = adp8860_read ( client , reg , & reg_val ) ;
if ( ! ret & & ( reg_val & bit_mask ) ) {
reg_val & = ~ bit_mask ;
ret = adp8860_write ( client , reg , reg_val ) ;
}
mutex_unlock ( & data - > lock ) ;
return ret ;
}
/*
* Independent sink / LED
*/
# if defined(ADP8860_USE_LEDS)
static void adp8860_led_work ( struct work_struct * work )
{
struct adp8860_led * led = container_of ( work , struct adp8860_led , work ) ;
2014-08-27 05:09:52 +04:00
2010-05-01 01:09:51 +04:00
adp8860_write ( led - > client , ADP8860_ISC1 - led - > id + 1 ,
led - > new_brightness > > 1 ) ;
}
static void adp8860_led_set ( struct led_classdev * led_cdev ,
enum led_brightness value )
{
struct adp8860_led * led ;
led = container_of ( led_cdev , struct adp8860_led , cdev ) ;
led - > new_brightness = value ;
schedule_work ( & led - > work ) ;
}
static int adp8860_led_setup ( struct adp8860_led * led )
{
struct i2c_client * client = led - > client ;
int ret = 0 ;
ret = adp8860_write ( client , ADP8860_ISC1 - led - > id + 1 , 0 ) ;
ret | = adp8860_set_bits ( client , ADP8860_ISCC , 1 < < ( led - > id - 1 ) ) ;
if ( led - > id > 4 )
ret | = adp8860_set_bits ( client , ADP8860_ISCT1 ,
( led - > flags & 0x3 ) < < ( ( led - > id - 5 ) * 2 ) ) ;
else
ret | = adp8860_set_bits ( client , ADP8860_ISCT2 ,
( led - > flags & 0x3 ) < < ( ( led - > id - 1 ) * 2 ) ) ;
return ret ;
}
2012-11-19 22:21:46 +04:00
static int adp8860_led_probe ( struct i2c_client * client )
2010-05-01 01:09:51 +04:00
{
struct adp8860_backlight_platform_data * pdata =
2013-11-13 03:09:04 +04:00
dev_get_platdata ( & client - > dev ) ;
2010-05-01 01:09:51 +04:00
struct adp8860_bl * data = i2c_get_clientdata ( client ) ;
struct adp8860_led * led , * led_dat ;
struct led_info * cur_led ;
int ret , i ;
2012-05-30 02:07:21 +04:00
led = devm_kzalloc ( & client - > dev , sizeof ( * led ) * pdata - > num_leds ,
GFP_KERNEL ) ;
2014-04-04 01:48:57 +04:00
if ( led = = NULL )
2010-05-01 01:09:51 +04:00
return - ENOMEM ;
ret = adp8860_write ( client , ADP8860_ISCFR , pdata - > led_fade_law ) ;
ret = adp8860_write ( client , ADP8860_ISCT1 ,
( pdata - > led_on_time & 0x3 ) < < 6 ) ;
ret | = adp8860_write ( client , ADP8860_ISCF ,
FADE_VAL ( pdata - > led_fade_in , pdata - > led_fade_out ) ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to write \n " ) ;
2012-05-30 02:07:21 +04:00
return ret ;
2010-05-01 01:09:51 +04:00
}
for ( i = 0 ; i < pdata - > num_leds ; + + i ) {
cur_led = & pdata - > leds [ i ] ;
led_dat = & led [ i ] ;
led_dat - > id = cur_led - > flags & ADP8860_FLAG_LED_MASK ;
if ( led_dat - > id > 7 | | led_dat - > id < 1 ) {
dev_err ( & client - > dev , " Invalid LED ID %d \n " ,
led_dat - > id ) ;
2013-04-30 03:17:53 +04:00
ret = - EINVAL ;
2010-05-01 01:09:51 +04:00
goto err ;
}
if ( pdata - > bl_led_assign & ( 1 < < ( led_dat - > id - 1 ) ) ) {
dev_err ( & client - > dev , " LED %d used by Backlight \n " ,
led_dat - > id ) ;
2013-04-30 03:17:53 +04:00
ret = - EBUSY ;
2010-05-01 01:09:51 +04:00
goto err ;
}
led_dat - > cdev . name = cur_led - > name ;
led_dat - > cdev . default_trigger = cur_led - > default_trigger ;
led_dat - > cdev . brightness_set = adp8860_led_set ;
led_dat - > cdev . brightness = LED_OFF ;
led_dat - > flags = cur_led - > flags > > FLAG_OFFT_SHIFT ;
led_dat - > client = client ;
led_dat - > new_brightness = LED_OFF ;
INIT_WORK ( & led_dat - > work , adp8860_led_work ) ;
ret = led_classdev_register ( & client - > dev , & led_dat - > cdev ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to register LED %d \n " ,
led_dat - > id ) ;
goto err ;
}
ret = adp8860_led_setup ( led_dat ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to write \n " ) ;
i + + ;
goto err ;
}
}
data - > led = led ;
return 0 ;
err :
for ( i = i - 1 ; i > = 0 ; - - i ) {
led_classdev_unregister ( & led [ i ] . cdev ) ;
cancel_work_sync ( & led [ i ] . work ) ;
}
return ret ;
}
2012-11-19 22:26:34 +04:00
static int adp8860_led_remove ( struct i2c_client * client )
2010-05-01 01:09:51 +04:00
{
struct adp8860_backlight_platform_data * pdata =
2013-11-13 03:09:04 +04:00
dev_get_platdata ( & client - > dev ) ;
2010-05-01 01:09:51 +04:00
struct adp8860_bl * data = i2c_get_clientdata ( client ) ;
int i ;
for ( i = 0 ; i < pdata - > num_leds ; i + + ) {
led_classdev_unregister ( & data - > led [ i ] . cdev ) ;
cancel_work_sync ( & data - > led [ i ] . work ) ;
}
return 0 ;
}
# else
2012-11-19 22:21:46 +04:00
static int adp8860_led_probe ( struct i2c_client * client )
2010-05-01 01:09:51 +04:00
{
return 0 ;
}
2012-11-19 22:26:34 +04:00
static int adp8860_led_remove ( struct i2c_client * client )
2010-05-01 01:09:51 +04:00
{
return 0 ;
}
# endif
static int adp8860_bl_set ( struct backlight_device * bl , int brightness )
{
struct adp8860_bl * data = bl_get_data ( bl ) ;
struct i2c_client * client = data - > client ;
int ret = 0 ;
2010-05-26 11:00:59 +04:00
if ( data - > en_ambl_sens ) {
2010-05-01 01:09:51 +04:00
if ( ( brightness > 0 ) & & ( brightness < ADP8860_MAX_BRIGHTNESS ) ) {
/* Disable Ambient Light auto adjust */
ret | = adp8860_clr_bits ( client , ADP8860_MDCR ,
CMP_AUTOEN ) ;
ret | = adp8860_write ( client , ADP8860_BLMX1 , brightness ) ;
} else {
/*
* MAX_BRIGHTNESS - > Enable Ambient Light auto adjust
* restore daylight l1 sysfs brightness
*/
ret | = adp8860_write ( client , ADP8860_BLMX1 ,
data - > cached_daylight_max ) ;
ret | = adp8860_set_bits ( client , ADP8860_MDCR ,
CMP_AUTOEN ) ;
}
} else
ret | = adp8860_write ( client , ADP8860_BLMX1 , brightness ) ;
if ( data - > current_brightness & & brightness = = 0 )
ret | = adp8860_set_bits ( client ,
ADP8860_MDCR , DIM_EN ) ;
else if ( data - > current_brightness = = 0 & & brightness )
ret | = adp8860_clr_bits ( client ,
ADP8860_MDCR , DIM_EN ) ;
if ( ! ret )
data - > current_brightness = brightness ;
return ret ;
}
static int adp8860_bl_update_status ( struct backlight_device * bl )
{
int brightness = bl - > props . brightness ;
2014-08-27 05:09:52 +04:00
2010-05-01 01:09:51 +04:00
if ( bl - > props . power ! = FB_BLANK_UNBLANK )
brightness = 0 ;
if ( bl - > props . fb_blank ! = FB_BLANK_UNBLANK )
brightness = 0 ;
return adp8860_bl_set ( bl , brightness ) ;
}
static int adp8860_bl_get_brightness ( struct backlight_device * bl )
{
struct adp8860_bl * data = bl_get_data ( bl ) ;
return data - > current_brightness ;
}
static const struct backlight_ops adp8860_bl_ops = {
. update_status = adp8860_bl_update_status ,
. get_brightness = adp8860_bl_get_brightness ,
} ;
static int adp8860_bl_setup ( struct backlight_device * bl )
{
struct adp8860_bl * data = bl_get_data ( bl ) ;
struct i2c_client * client = data - > client ;
struct adp8860_backlight_platform_data * pdata = data - > pdata ;
int ret = 0 ;
ret | = adp8860_write ( client , ADP8860_BLSEN , ~ pdata - > bl_led_assign ) ;
ret | = adp8860_write ( client , ADP8860_BLMX1 , pdata - > l1_daylight_max ) ;
ret | = adp8860_write ( client , ADP8860_BLDM1 , pdata - > l1_daylight_dim ) ;
2010-05-26 11:00:59 +04:00
if ( data - > en_ambl_sens ) {
2010-05-01 01:09:51 +04:00
data - > cached_daylight_max = pdata - > l1_daylight_max ;
ret | = adp8860_write ( client , ADP8860_BLMX2 ,
pdata - > l2_office_max ) ;
ret | = adp8860_write ( client , ADP8860_BLDM2 ,
pdata - > l2_office_dim ) ;
ret | = adp8860_write ( client , ADP8860_BLMX3 ,
pdata - > l3_dark_max ) ;
ret | = adp8860_write ( client , ADP8860_BLDM3 ,
pdata - > l3_dark_dim ) ;
ret | = adp8860_write ( client , ADP8860_L2_TRP , pdata - > l2_trip ) ;
ret | = adp8860_write ( client , ADP8860_L2_HYS , pdata - > l2_hyst ) ;
ret | = adp8860_write ( client , ADP8860_L3_TRP , pdata - > l3_trip ) ;
ret | = adp8860_write ( client , ADP8860_L3_HYS , pdata - > l3_hyst ) ;
ret | = adp8860_write ( client , ADP8860_CCFG , L2_EN | L3_EN |
ALS_CCFG_VAL ( pdata - > abml_filt ) ) ;
}
ret | = adp8860_write ( client , ADP8860_CFGR ,
BL_CFGR_VAL ( pdata - > bl_fade_law , 0 ) ) ;
ret | = adp8860_write ( client , ADP8860_BLFR , FADE_VAL ( pdata - > bl_fade_in ,
pdata - > bl_fade_out ) ) ;
2010-05-26 11:00:59 +04:00
ret | = adp8860_set_bits ( client , ADP8860_MDCR , BLEN | DIM_EN | NSTBY |
( data - > gdwn_dis ? GDWN_DIS : 0 ) ) ;
2010-05-01 01:09:51 +04:00
return ret ;
}
static ssize_t adp8860_show ( struct device * dev , char * buf , int reg )
{
struct adp8860_bl * data = dev_get_drvdata ( dev ) ;
int error ;
uint8_t reg_val ;
mutex_lock ( & data - > lock ) ;
error = adp8860_read ( data - > client , reg , & reg_val ) ;
mutex_unlock ( & data - > lock ) ;
if ( error < 0 )
return error ;
return sprintf ( buf , " %u \n " , reg_val ) ;
}
static ssize_t adp8860_store ( struct device * dev , const char * buf ,
size_t count , int reg )
{
struct adp8860_bl * data = dev_get_drvdata ( dev ) ;
unsigned long val ;
int ret ;
2012-05-30 02:07:15 +04:00
ret = kstrtoul ( buf , 10 , & val ) ;
2010-05-01 01:09:51 +04:00
if ( ret )
return ret ;
mutex_lock ( & data - > lock ) ;
adp8860_write ( data - > client , reg , val ) ;
mutex_unlock ( & data - > lock ) ;
return count ;
}
static ssize_t adp8860_bl_l3_dark_max_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return adp8860_show ( dev , buf , ADP8860_BLMX3 ) ;
}
static ssize_t adp8860_bl_l3_dark_max_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
return adp8860_store ( dev , buf , count , ADP8860_BLMX3 ) ;
}
static DEVICE_ATTR ( l3_dark_max , 0664 , adp8860_bl_l3_dark_max_show ,
adp8860_bl_l3_dark_max_store ) ;
static ssize_t adp8860_bl_l2_office_max_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return adp8860_show ( dev , buf , ADP8860_BLMX2 ) ;
}
static ssize_t adp8860_bl_l2_office_max_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
return adp8860_store ( dev , buf , count , ADP8860_BLMX2 ) ;
}
static DEVICE_ATTR ( l2_office_max , 0664 , adp8860_bl_l2_office_max_show ,
adp8860_bl_l2_office_max_store ) ;
static ssize_t adp8860_bl_l1_daylight_max_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return adp8860_show ( dev , buf , ADP8860_BLMX1 ) ;
}
static ssize_t adp8860_bl_l1_daylight_max_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct adp8860_bl * data = dev_get_drvdata ( dev ) ;
2012-05-30 02:07:15 +04:00
int ret = kstrtoul ( buf , 10 , & data - > cached_daylight_max ) ;
2014-08-27 05:09:52 +04:00
2010-11-12 01:05:29 +03:00
if ( ret )
return ret ;
2010-05-01 01:09:51 +04:00
return adp8860_store ( dev , buf , count , ADP8860_BLMX1 ) ;
}
static DEVICE_ATTR ( l1_daylight_max , 0664 , adp8860_bl_l1_daylight_max_show ,
adp8860_bl_l1_daylight_max_store ) ;
static ssize_t adp8860_bl_l3_dark_dim_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return adp8860_show ( dev , buf , ADP8860_BLDM3 ) ;
}
static ssize_t adp8860_bl_l3_dark_dim_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
return adp8860_store ( dev , buf , count , ADP8860_BLDM3 ) ;
}
static DEVICE_ATTR ( l3_dark_dim , 0664 , adp8860_bl_l3_dark_dim_show ,
adp8860_bl_l3_dark_dim_store ) ;
static ssize_t adp8860_bl_l2_office_dim_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return adp8860_show ( dev , buf , ADP8860_BLDM2 ) ;
}
static ssize_t adp8860_bl_l2_office_dim_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
return adp8860_store ( dev , buf , count , ADP8860_BLDM2 ) ;
}
static DEVICE_ATTR ( l2_office_dim , 0664 , adp8860_bl_l2_office_dim_show ,
adp8860_bl_l2_office_dim_store ) ;
static ssize_t adp8860_bl_l1_daylight_dim_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return adp8860_show ( dev , buf , ADP8860_BLDM1 ) ;
}
static ssize_t adp8860_bl_l1_daylight_dim_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
return adp8860_store ( dev , buf , count , ADP8860_BLDM1 ) ;
}
static DEVICE_ATTR ( l1_daylight_dim , 0664 , adp8860_bl_l1_daylight_dim_show ,
adp8860_bl_l1_daylight_dim_store ) ;
# ifdef ADP8860_EXT_FEATURES
static ssize_t adp8860_bl_ambient_light_level_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct adp8860_bl * data = dev_get_drvdata ( dev ) ;
int error ;
uint8_t reg_val ;
uint16_t ret_val ;
mutex_lock ( & data - > lock ) ;
error = adp8860_read ( data - > client , ADP8860_PH1LEVL , & reg_val ) ;
2015-11-30 14:24:23 +03:00
if ( ! error ) {
ret_val = reg_val ;
error = adp8860_read ( data - > client , ADP8860_PH1LEVH , & reg_val ) ;
}
2010-05-01 01:09:51 +04:00
mutex_unlock ( & data - > lock ) ;
2015-11-30 14:24:23 +03:00
if ( error )
2010-05-01 01:09:51 +04:00
return error ;
/* Return 13-bit conversion value for the first light sensor */
ret_val + = ( reg_val & 0x1F ) < < 8 ;
return sprintf ( buf , " %u \n " , ret_val ) ;
}
static DEVICE_ATTR ( ambient_light_level , 0444 ,
adp8860_bl_ambient_light_level_show , NULL ) ;
static ssize_t adp8860_bl_ambient_light_zone_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct adp8860_bl * data = dev_get_drvdata ( dev ) ;
int error ;
uint8_t reg_val ;
mutex_lock ( & data - > lock ) ;
error = adp8860_read ( data - > client , ADP8860_CFGR , & reg_val ) ;
mutex_unlock ( & data - > lock ) ;
if ( error < 0 )
return error ;
return sprintf ( buf , " %u \n " ,
( ( reg_val > > CFGR_BLV_SHIFT ) & CFGR_BLV_MASK ) + 1 ) ;
}
static ssize_t adp8860_bl_ambient_light_zone_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct adp8860_bl * data = dev_get_drvdata ( dev ) ;
unsigned long val ;
uint8_t reg_val ;
int ret ;
2012-05-30 02:07:15 +04:00
ret = kstrtoul ( buf , 10 , & val ) ;
2010-05-01 01:09:51 +04:00
if ( ret )
return ret ;
if ( val = = 0 ) {
/* Enable automatic ambient light sensing */
adp8860_set_bits ( data - > client , ADP8860_MDCR , CMP_AUTOEN ) ;
2010-11-12 01:05:29 +03:00
} else if ( ( val > 0 ) & & ( val < = 3 ) ) {
2010-05-01 01:09:51 +04:00
/* Disable automatic ambient light sensing */
adp8860_clr_bits ( data - > client , ADP8860_MDCR , CMP_AUTOEN ) ;
/* Set user supplied ambient light zone */
mutex_lock ( & data - > lock ) ;
2015-11-23 16:44:13 +03:00
ret = adp8860_read ( data - > client , ADP8860_CFGR , & reg_val ) ;
if ( ! ret ) {
reg_val & = ~ ( CFGR_BLV_MASK < < CFGR_BLV_SHIFT ) ;
reg_val | = ( val - 1 ) < < CFGR_BLV_SHIFT ;
adp8860_write ( data - > client , ADP8860_CFGR , reg_val ) ;
}
2010-05-01 01:09:51 +04:00
mutex_unlock ( & data - > lock ) ;
}
return count ;
}
static DEVICE_ATTR ( ambient_light_zone , 0664 ,
adp8860_bl_ambient_light_zone_show ,
adp8860_bl_ambient_light_zone_store ) ;
# endif
static struct attribute * adp8860_bl_attributes [ ] = {
& dev_attr_l3_dark_max . attr ,
& dev_attr_l3_dark_dim . attr ,
& dev_attr_l2_office_max . attr ,
& dev_attr_l2_office_dim . attr ,
& dev_attr_l1_daylight_max . attr ,
& dev_attr_l1_daylight_dim . attr ,
# ifdef ADP8860_EXT_FEATURES
& dev_attr_ambient_light_level . attr ,
& dev_attr_ambient_light_zone . attr ,
# endif
NULL
} ;
static const struct attribute_group adp8860_bl_attr_group = {
. attrs = adp8860_bl_attributes ,
} ;
2012-11-19 22:21:46 +04:00
static int adp8860_probe ( struct i2c_client * client ,
2010-05-01 01:09:51 +04:00
const struct i2c_device_id * id )
{
struct backlight_device * bl ;
struct adp8860_bl * data ;
struct adp8860_backlight_platform_data * pdata =
2013-11-13 03:09:04 +04:00
dev_get_platdata ( & client - > dev ) ;
2010-05-13 00:49:12 +04:00
struct backlight_properties props ;
2010-05-01 01:09:51 +04:00
uint8_t reg_val ;
int ret ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE_DATA ) ) {
dev_err ( & client - > dev , " SMBUS Byte Data not Supported \n " ) ;
return - EIO ;
}
if ( ! pdata ) {
dev_err ( & client - > dev , " no platform data? \n " ) ;
return - EINVAL ;
}
2012-05-30 02:07:21 +04:00
data = devm_kzalloc ( & client - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
2010-05-26 11:00:59 +04:00
if ( data = = NULL )
return - ENOMEM ;
2010-05-01 01:09:51 +04:00
ret = adp8860_read ( client , ADP8860_MFDVID , & reg_val ) ;
if ( ret < 0 )
2012-05-30 02:07:21 +04:00
return ret ;
2010-05-01 01:09:51 +04:00
2010-05-26 11:00:59 +04:00
switch ( ADP8860_MANID ( reg_val ) ) {
case ADP8863_MANUFID :
data - > gdwn_dis = ! ! pdata - > gdwn_dis ;
case ADP8860_MANUFID :
data - > en_ambl_sens = ! ! pdata - > en_ambl_sens ;
break ;
case ADP8861_MANUFID :
data - > gdwn_dis = ! ! pdata - > gdwn_dis ;
break ;
default :
2010-05-01 01:09:51 +04:00
dev_err ( & client - > dev , " failed to probe \n " ) ;
2012-05-30 02:07:21 +04:00
return - ENODEV ;
2010-05-01 01:09:51 +04:00
}
/* It's confirmed that the DEVID field is actually a REVID */
data - > revid = ADP8860_DEVID ( reg_val ) ;
data - > client = client ;
data - > pdata = pdata ;
data - > id = id - > driver_data ;
data - > current_brightness = 0 ;
i2c_set_clientdata ( client , data ) ;
2010-05-13 00:49:12 +04:00
memset ( & props , 0 , sizeof ( props ) ) ;
2011-03-23 02:30:21 +03:00
props . type = BACKLIGHT_RAW ;
2010-05-13 00:49:12 +04:00
props . max_brightness = ADP8860_MAX_BRIGHTNESS ;
2010-05-01 01:09:51 +04:00
mutex_init ( & data - > lock ) ;
2013-11-13 03:09:08 +04:00
bl = devm_backlight_device_register ( & client - > dev ,
dev_driver_string ( & client - > dev ) ,
& client - > dev , data , & adp8860_bl_ops , & props ) ;
2010-05-01 01:09:51 +04:00
if ( IS_ERR ( bl ) ) {
dev_err ( & client - > dev , " failed to register backlight \n " ) ;
2012-05-30 02:07:21 +04:00
return PTR_ERR ( bl ) ;
2010-05-01 01:09:51 +04:00
}
2011-07-26 04:11:59 +04:00
bl - > props . brightness = ADP8860_MAX_BRIGHTNESS ;
2010-05-01 01:09:51 +04:00
data - > bl = bl ;
2010-05-26 11:00:59 +04:00
if ( data - > en_ambl_sens )
2010-05-01 01:09:51 +04:00
ret = sysfs_create_group ( & bl - > dev . kobj ,
& adp8860_bl_attr_group ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to register sysfs \n " ) ;
2013-11-13 03:09:08 +04:00
return ret ;
2010-05-01 01:09:51 +04:00
}
ret = adp8860_bl_setup ( bl ) ;
if ( ret ) {
ret = - EIO ;
goto out ;
}
backlight_update_status ( bl ) ;
2010-05-26 11:00:59 +04:00
dev_info ( & client - > dev , " %s Rev.%d Backlight \n " ,
client - > name , data - > revid ) ;
2010-05-01 01:09:51 +04:00
if ( pdata - > num_leds )
adp8860_led_probe ( client ) ;
return 0 ;
out :
2010-05-26 11:00:59 +04:00
if ( data - > en_ambl_sens )
2010-05-01 01:09:51 +04:00
sysfs_remove_group ( & data - > bl - > dev . kobj ,
& adp8860_bl_attr_group ) ;
return ret ;
}
2012-11-19 22:26:34 +04:00
static int adp8860_remove ( struct i2c_client * client )
2010-05-01 01:09:51 +04:00
{
struct adp8860_bl * data = i2c_get_clientdata ( client ) ;
adp8860_clr_bits ( client , ADP8860_MDCR , NSTBY ) ;
if ( data - > led )
adp8860_led_remove ( client ) ;
2010-05-26 11:00:59 +04:00
if ( data - > en_ambl_sens )
2010-05-01 01:09:51 +04:00
sysfs_remove_group ( & data - > bl - > dev . kobj ,
& adp8860_bl_attr_group ) ;
return 0 ;
}
2013-04-30 03:17:39 +04:00
# ifdef CONFIG_PM_SLEEP
static int adp8860_i2c_suspend ( struct device * dev )
2010-05-01 01:09:51 +04:00
{
2013-04-30 03:17:39 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2010-05-01 01:09:51 +04:00
adp8860_clr_bits ( client , ADP8860_MDCR , NSTBY ) ;
return 0 ;
}
2013-04-30 03:17:39 +04:00
static int adp8860_i2c_resume ( struct device * dev )
2010-05-01 01:09:51 +04:00
{
2013-04-30 03:17:39 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2013-02-22 04:44:04 +04:00
adp8860_set_bits ( client , ADP8860_MDCR , NSTBY | BLEN ) ;
2010-05-01 01:09:51 +04:00
return 0 ;
}
# endif
2013-04-30 03:17:39 +04:00
static SIMPLE_DEV_PM_OPS ( adp8860_i2c_pm_ops , adp8860_i2c_suspend ,
adp8860_i2c_resume ) ;
2010-05-01 01:09:51 +04:00
static const struct i2c_device_id adp8860_id [ ] = {
2010-05-26 11:00:59 +04:00
{ " adp8860 " , adp8860 } ,
{ " adp8861 " , adp8861 } ,
{ " adp8863 " , adp8863 } ,
2010-05-01 01:09:51 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , adp8860_id ) ;
static struct i2c_driver adp8860_driver = {
. driver = {
2013-04-30 03:17:39 +04:00
. name = KBUILD_MODNAME ,
. pm = & adp8860_i2c_pm_ops ,
2010-05-01 01:09:51 +04:00
} ,
. probe = adp8860_probe ,
2012-11-19 22:21:09 +04:00
. remove = adp8860_remove ,
2010-05-01 01:09:51 +04:00
. id_table = adp8860_id ,
} ;
2012-03-24 02:01:59 +04:00
module_i2c_driver ( adp8860_driver ) ;
2010-05-01 01:09:51 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Michael Hennerich <hennerich@blackfin.uclinux.org> " ) ;
MODULE_DESCRIPTION ( " ADP8860 Backlight driver " ) ;