2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-07-11 13:48:49 +03:00
/*
* LED driver for TI lp3952 controller
*
* Copyright ( C ) 2016 , DAQRI , LLC .
* Author : Tony Makkiel < tony . makkiel @ daqri . com >
*/
# include <linux/delay.h>
2020-04-15 15:35:08 +03:00
# include <linux/gpio/consumer.h>
2016-07-11 13:48:49 +03:00
# include <linux/i2c.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/leds.h>
# include <linux/leds-lp3952.h>
# include <linux/module.h>
# include <linux/notifier.h>
# include <linux/platform_device.h>
# include <linux/pm.h>
# include <linux/reboot.h>
# include <linux/regmap.h>
static int lp3952_register_write ( struct i2c_client * client , u8 reg , u8 val )
{
int ret ;
struct lp3952_led_array * priv = i2c_get_clientdata ( client ) ;
ret = regmap_write ( priv - > regmap , reg , val ) ;
if ( ret )
dev_err ( & client - > dev , " %s: reg 0x%x, val 0x%x, err %d \n " ,
__func__ , reg , val , ret ) ;
return ret ;
}
static void lp3952_on_off ( struct lp3952_led_array * priv ,
enum lp3952_leds led_id , bool on )
{
int ret , val ;
dev_dbg ( & priv - > client - > dev , " %s LED %d to %d \n " , __func__ , led_id , on ) ;
val = 1 < < led_id ;
if ( led_id = = LP3952_LED_ALL )
val = LP3952_LED_MASK_ALL ;
ret = regmap_update_bits ( priv - > regmap , LP3952_REG_LED_CTRL , val ,
on ? val : 0 ) ;
if ( ret )
dev_err ( & priv - > client - > dev , " %s, Error %d \n " , __func__ , ret ) ;
}
/*
* Using Imax to control brightness . There are 4 possible
* setting 25 , 50 , 75 and 100 % of Imax . Possible values are
* values 0 - 4. 0 meaning turn off .
*/
static int lp3952_set_brightness ( struct led_classdev * cdev ,
enum led_brightness value )
{
unsigned int reg , shift_val ;
struct lp3952_ctrl_hdl * led = container_of ( cdev ,
struct lp3952_ctrl_hdl ,
cdev ) ;
struct lp3952_led_array * priv = ( struct lp3952_led_array * ) led - > priv ;
dev_dbg ( cdev - > dev , " Brightness request: %d on %d \n " , value ,
led - > channel ) ;
if ( value = = LED_OFF ) {
lp3952_on_off ( priv , led - > channel , false ) ;
return 0 ;
}
if ( led - > channel > LP3952_RED_1 ) {
dev_err ( cdev - > dev , " %s Invalid LED requested " , __func__ ) ;
return - EINVAL ;
}
if ( led - > channel > = LP3952_BLUE_1 ) {
reg = LP3952_REG_RGB1_MAX_I_CTRL ;
shift_val = ( led - > channel - LP3952_BLUE_1 ) * 2 ;
} else {
reg = LP3952_REG_RGB2_MAX_I_CTRL ;
shift_val = led - > channel * 2 ;
}
/* Enable the LED in case it is not enabled already */
lp3952_on_off ( priv , led - > channel , true ) ;
return regmap_update_bits ( priv - > regmap , reg , 3 < < shift_val ,
- - value < < shift_val ) ;
}
static int lp3952_get_label ( struct device * dev , const char * label , char * dest )
{
int ret ;
const char * str ;
ret = device_property_read_string ( dev , label , & str ) ;
2017-03-23 22:17:49 +03:00
if ( ret )
return ret ;
2016-07-11 13:48:49 +03:00
2017-03-23 22:17:49 +03:00
strncpy ( dest , str , LP3952_LABEL_MAX_LEN ) ;
return 0 ;
2016-07-11 13:48:49 +03:00
}
static int lp3952_register_led_classdev ( struct lp3952_led_array * priv )
{
int i , acpi_ret , ret = - ENODEV ;
static const char * led_name_hdl [ LP3952_LED_ALL ] = {
" blue2 " ,
" green2 " ,
" red2 " ,
" blue1 " ,
" green1 " ,
" red1 "
} ;
for ( i = 0 ; i < LP3952_LED_ALL ; i + + ) {
acpi_ret = lp3952_get_label ( & priv - > client - > dev , led_name_hdl [ i ] ,
priv - > leds [ i ] . name ) ;
if ( acpi_ret )
continue ;
priv - > leds [ i ] . cdev . name = priv - > leds [ i ] . name ;
priv - > leds [ i ] . cdev . brightness = LED_OFF ;
priv - > leds [ i ] . cdev . max_brightness = LP3952_BRIGHT_MAX ;
priv - > leds [ i ] . cdev . brightness_set_blocking =
lp3952_set_brightness ;
priv - > leds [ i ] . channel = i ;
priv - > leds [ i ] . priv = priv ;
ret = devm_led_classdev_register ( & priv - > client - > dev ,
& priv - > leds [ i ] . cdev ) ;
if ( ret < 0 ) {
dev_err ( & priv - > client - > dev ,
" couldn't register LED %s \n " ,
priv - > leds [ i ] . cdev . name ) ;
break ;
}
}
return ret ;
}
static int lp3952_set_pattern_gen_cmd ( struct lp3952_led_array * priv ,
u8 cmd_index , u8 r , u8 g , u8 b ,
enum lp3952_tt tt , enum lp3952_cet cet )
{
int ret ;
struct ptrn_gen_cmd line = {
{
{
. r = r ,
. g = g ,
. b = b ,
. cet = cet ,
. tt = tt
}
}
} ;
if ( cmd_index > = LP3952_CMD_REG_COUNT )
return - EINVAL ;
ret = lp3952_register_write ( priv - > client ,
LP3952_REG_CMD_0 + cmd_index * 2 ,
line . bytes . msb ) ;
if ( ret )
return ret ;
return lp3952_register_write ( priv - > client ,
LP3952_REG_CMD_0 + cmd_index * 2 + 1 ,
line . bytes . lsb ) ;
}
static int lp3952_configure ( struct lp3952_led_array * priv )
{
int ret ;
/* Disable any LEDs on from any previous conf. */
ret = lp3952_register_write ( priv - > client , LP3952_REG_LED_CTRL , 0 ) ;
if ( ret )
return ret ;
/* enable rgb patter, loop */
ret = lp3952_register_write ( priv - > client , LP3952_REG_PAT_GEN_CTRL ,
LP3952_PATRN_LOOP | LP3952_PATRN_GEN_EN ) ;
if ( ret )
return ret ;
/* Update Bit 6 (Active mode), Select both Led sets, Bit [1:0] */
ret = lp3952_register_write ( priv - > client , LP3952_REG_ENABLES ,
LP3952_ACTIVE_MODE | LP3952_INT_B00ST_LDR ) ;
if ( ret )
return ret ;
/* Set Cmd1 for RGB intensity,cmd and transition time */
return lp3952_set_pattern_gen_cmd ( priv , 0 , I46 , I71 , I100 , TT0 ,
CET197 ) ;
}
static const struct regmap_config lp3952_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = REG_MAX ,
. cache_type = REGCACHE_RBTREE ,
} ;
static int lp3952_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int status ;
struct lp3952_led_array * priv ;
priv = devm_kzalloc ( & client - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > client = client ;
priv - > enable_gpio = devm_gpiod_get ( & client - > dev , " nrst " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( priv - > enable_gpio ) ) {
status = PTR_ERR ( priv - > enable_gpio ) ;
dev_err ( & client - > dev , " Failed to enable gpio: %d \n " , status ) ;
return status ;
}
priv - > regmap = devm_regmap_init_i2c ( client , & lp3952_regmap ) ;
if ( IS_ERR ( priv - > regmap ) ) {
int err = PTR_ERR ( priv - > regmap ) ;
dev_err ( & client - > dev , " Failed to allocate register map: %d \n " ,
err ) ;
return err ;
}
i2c_set_clientdata ( client , priv ) ;
status = lp3952_configure ( priv ) ;
if ( status ) {
dev_err ( & client - > dev , " Probe failed. Device not found (%d) \n " ,
status ) ;
return status ;
}
status = lp3952_register_led_classdev ( priv ) ;
if ( status ) {
dev_err ( & client - > dev , " Unable to register led_classdev: %d \n " ,
status ) ;
return status ;
}
return 0 ;
}
static int lp3952_remove ( struct i2c_client * client )
{
struct lp3952_led_array * priv ;
priv = i2c_get_clientdata ( client ) ;
lp3952_on_off ( priv , LP3952_LED_ALL , false ) ;
gpiod_set_value ( priv - > enable_gpio , 0 ) ;
return 0 ;
}
static const struct i2c_device_id lp3952_id [ ] = {
{ LP3952_NAME , 0 } ,
{ }
} ;
2016-10-14 15:23:34 +03:00
MODULE_DEVICE_TABLE ( i2c , lp3952_id ) ;
2016-07-11 13:48:49 +03:00
static struct i2c_driver lp3952_i2c_driver = {
. driver = {
. name = LP3952_NAME ,
} ,
. probe = lp3952_probe ,
. remove = lp3952_remove ,
. id_table = lp3952_id ,
} ;
module_i2c_driver ( lp3952_i2c_driver ) ;
MODULE_AUTHOR ( " Tony Makkiel <tony.makkiel@daqri.com> " ) ;
MODULE_DESCRIPTION ( " lp3952 I2C LED controller driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;