2023-04-19 14:18:06 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2023 Andreas Kemnade
*
* Datasheet :
* https : //fscdn.rohm.com/en/products/databook/datasheet/ic/power/led_driver/bd2606mvv_1-e.pdf
*
* If LED brightness cannot be controlled independently due to shared
* brightness registers , max_brightness is set to 1 and only on / off
* is possible for the affected LED pair .
*/
# include <linux/i2c.h>
# include <linux/leds.h>
# include <linux/module.h>
# include <linux/mod_devicetable.h>
# include <linux/property.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# define BD2606_MAX_LEDS 6
# define BD2606_MAX_BRIGHTNESS 63
# define BD2606_REG_PWRCNT 3
# define ldev_to_led(c) container_of(c, struct bd2606mvv_led, ldev)
struct bd2606mvv_led {
unsigned int led_no ;
struct led_classdev ldev ;
struct bd2606mvv_priv * priv ;
} ;
struct bd2606mvv_priv {
struct bd2606mvv_led leds [ BD2606_MAX_LEDS ] ;
struct regmap * regmap ;
} ;
static int
bd2606mvv_brightness_set ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
struct bd2606mvv_led * led = ldev_to_led ( led_cdev ) ;
struct bd2606mvv_priv * priv = led - > priv ;
int err ;
if ( brightness = = 0 )
return regmap_update_bits ( priv - > regmap ,
BD2606_REG_PWRCNT ,
1 < < led - > led_no ,
0 ) ;
/* shared brightness register */
err = regmap_write ( priv - > regmap , led - > led_no / 2 ,
led_cdev - > max_brightness = = 1 ?
BD2606_MAX_BRIGHTNESS : brightness ) ;
if ( err )
return err ;
return regmap_update_bits ( priv - > regmap ,
BD2606_REG_PWRCNT ,
1 < < led - > led_no ,
1 < < led - > led_no ) ;
}
static const struct regmap_config bd2606mvv_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0x3 ,
} ;
static int bd2606mvv_probe ( struct i2c_client * client )
{
struct fwnode_handle * np , * child ;
struct device * dev = & client - > dev ;
struct bd2606mvv_priv * priv ;
struct fwnode_handle * led_fwnodes [ BD2606_MAX_LEDS ] = { 0 } ;
int active_pairs [ BD2606_MAX_LEDS / 2 ] = { 0 } ;
int err , reg ;
int i ;
np = dev_fwnode ( dev ) ;
if ( ! np )
return - ENODEV ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > regmap = devm_regmap_init_i2c ( client , & bd2606mvv_regmap ) ;
if ( IS_ERR ( priv - > regmap ) ) {
err = PTR_ERR ( priv - > regmap ) ;
dev_err ( dev , " Failed to allocate register map: %d \n " , err ) ;
return err ;
}
i2c_set_clientdata ( client , priv ) ;
fwnode_for_each_available_child_node ( np , child ) {
struct bd2606mvv_led * led ;
err = fwnode_property_read_u32 ( child , " reg " , & reg ) ;
if ( err ) {
fwnode_handle_put ( child ) ;
return err ;
}
if ( reg < 0 | | reg > = BD2606_MAX_LEDS | | led_fwnodes [ reg ] ) {
fwnode_handle_put ( child ) ;
return - EINVAL ;
}
led = & priv - > leds [ reg ] ;
led_fwnodes [ reg ] = child ;
active_pairs [ reg / 2 ] + + ;
led - > priv = priv ;
led - > led_no = reg ;
led - > ldev . brightness_set_blocking = bd2606mvv_brightness_set ;
led - > ldev . max_brightness = BD2606_MAX_BRIGHTNESS ;
}
for ( i = 0 ; i < BD2606_MAX_LEDS ; i + + ) {
struct led_init_data init_data = { } ;
if ( ! led_fwnodes [ i ] )
continue ;
init_data . fwnode = led_fwnodes [ i ] ;
/* Check whether brightness can be independently adjusted. */
if ( active_pairs [ i / 2 ] = = 2 )
priv - > leds [ i ] . ldev . max_brightness = 1 ;
err = devm_led_classdev_register_ext ( dev ,
& priv - > leds [ i ] . ldev ,
& init_data ) ;
if ( err < 0 ) {
fwnode_handle_put ( child ) ;
return dev_err_probe ( dev , err ,
" couldn't register LED %s \n " ,
priv - > leds [ i ] . ldev . name ) ;
}
}
return 0 ;
}
static const struct of_device_id __maybe_unused of_bd2606mvv_leds_match [ ] = {
{ . compatible = " rohm,bd2606mvv " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_bd2606mvv_leds_match ) ;
static struct i2c_driver bd2606mvv_driver = {
. driver = {
. name = " leds-bd2606mvv " ,
. of_match_table = of_match_ptr ( of_bd2606mvv_leds_match ) ,
} ,
2023-05-17 21:05:59 +03:00
. probe = bd2606mvv_probe ,
2023-04-19 14:18:06 +03:00
} ;
module_i2c_driver ( bd2606mvv_driver ) ;
MODULE_AUTHOR ( " Andreas Kemnade <andreas@kemnade.info> " ) ;
MODULE_DESCRIPTION ( " BD2606 LED driver " ) ;
MODULE_LICENSE ( " GPL " ) ;