2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-11-12 01:05:22 +03:00
/*
* LP5521 LED chip driver .
*
* Copyright ( C ) 2010 Nokia Corporation
2013-02-05 14:28:00 +04:00
* Copyright ( C ) 2012 Texas Instruments
2010-11-12 01:05:22 +03:00
*
* Contact : Samu Onkalo < samu . p . onkalo @ nokia . com >
2013-02-05 14:28:00 +04:00
* Milo ( Woogyom ) Kim < milo . kim @ ti . com >
2010-11-12 01:05:22 +03:00
*/
# include <linux/delay.h>
2013-02-05 14:26:14 +04:00
# include <linux/firmware.h>
# include <linux/i2c.h>
2010-11-12 01:05:22 +03:00
# include <linux/leds.h>
2013-02-05 14:26:14 +04:00
# include <linux/module.h>
# include <linux/mutex.h>
2013-02-05 13:03:08 +04:00
# include <linux/platform_data/leds-lp55xx.h>
2013-02-05 14:26:14 +04:00
# include <linux/slab.h>
2013-04-23 15:52:59 +04:00
# include <linux/of.h>
2013-02-05 13:03:08 +04:00
# include "leds-lp55xx-common.h"
2010-11-12 01:05:22 +03:00
2013-02-05 14:25:26 +04:00
# define LP5521_PROGRAM_LENGTH 32
# define LP5521_MAX_LEDS 3
# define LP5521_CMD_DIRECT 0x3F
2010-11-12 01:05:22 +03:00
/* Registers */
# define LP5521_REG_ENABLE 0x00
# define LP5521_REG_OP_MODE 0x01
# define LP5521_REG_R_PWM 0x02
# define LP5521_REG_G_PWM 0x03
# define LP5521_REG_B_PWM 0x04
# define LP5521_REG_R_CURRENT 0x05
# define LP5521_REG_G_CURRENT 0x06
# define LP5521_REG_B_CURRENT 0x07
# define LP5521_REG_CONFIG 0x08
# define LP5521_REG_STATUS 0x0C
# define LP5521_REG_RESET 0x0D
# define LP5521_REG_R_PROG_MEM 0x10
# define LP5521_REG_G_PROG_MEM 0x30
# define LP5521_REG_B_PROG_MEM 0x50
/* Base register to set LED current */
# define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT
/* Base register to set the brightness */
# define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM
/* Bits in ENABLE register */
# define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */
# define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
# define LP5521_EXEC_RUN 0x2A
2012-03-24 02:02:09 +04:00
# define LP5521_ENABLE_DEFAULT \
( LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM )
# define LP5521_ENABLE_RUN_PROGRAM \
( LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN )
2010-11-12 01:05:22 +03:00
2013-03-21 04:37:04 +04:00
/* CONFIG register */
# define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
# define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
# define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
# define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
# define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
# define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
# define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */
# define LP5521_CLK_INT 0x01 /* Internal clock */
# define LP5521_DEFAULT_CFG \
( LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO )
2010-11-12 01:05:22 +03:00
/* Status */
# define LP5521_EXT_CLK_USED 0x08
2011-11-01 04:12:24 +04:00
/* default R channel current register value */
# define LP5521_REG_R_CURR_DEFAULT 0xAF
2013-02-05 13:08:49 +04:00
/* Reset register value */
# define LP5521_RESET 0xFF
2013-02-05 14:18:10 +04:00
/* Program Memory Operations */
# define LP5521_MODE_R_M 0x30 /* Operation Mode Register */
# define LP5521_MODE_G_M 0x0C
# define LP5521_MODE_B_M 0x03
# define LP5521_LOAD_R 0x10
# define LP5521_LOAD_G 0x04
# define LP5521_LOAD_B 0x01
# define LP5521_R_IS_LOADING(mode) \
( ( mode & LP5521_MODE_R_M ) = = LP5521_LOAD_R )
# define LP5521_G_IS_LOADING(mode) \
( ( mode & LP5521_MODE_G_M ) = = LP5521_LOAD_G )
# define LP5521_B_IS_LOADING(mode) \
( ( mode & LP5521_MODE_B_M ) = = LP5521_LOAD_B )
# define LP5521_EXEC_R_M 0x30 /* Enable Register */
# define LP5521_EXEC_G_M 0x0C
# define LP5521_EXEC_B_M 0x03
# define LP5521_EXEC_M 0x3F
# define LP5521_RUN_R 0x20
# define LP5521_RUN_G 0x08
# define LP5521_RUN_B 0x02
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:10 +04:00
static inline void lp5521_wait_opmode_done ( void )
{
/* operation mode change needs to be longer than 153 us */
usleep_range ( 200 , 300 ) ;
}
2013-02-05 13:03:55 +04:00
static inline void lp5521_wait_enable_done ( void )
{
/* it takes more 488 us to update ENABLE register */
usleep_range ( 500 , 600 ) ;
}
2013-02-05 14:09:32 +04:00
static void lp5521_set_led_current ( struct lp55xx_led * led , u8 led_current )
{
led - > led_current = led_current ;
lp55xx_write ( led - > chip , LP5521_REG_LED_CURRENT_BASE + led - > chan_nr ,
led_current ) ;
}
2013-02-05 14:18:10 +04:00
static void lp5521_load_engine ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:10 +04:00
enum lp55xx_engine_index idx = chip - > engine_idx ;
2017-08-24 16:00:53 +03:00
static const u8 mask [ ] = {
2013-02-05 14:18:10 +04:00
[ LP55XX_ENGINE_1 ] = LP5521_MODE_R_M ,
[ LP55XX_ENGINE_2 ] = LP5521_MODE_G_M ,
[ LP55XX_ENGINE_3 ] = LP5521_MODE_B_M ,
} ;
2010-11-12 01:05:22 +03:00
2017-08-24 16:00:53 +03:00
static const u8 val [ ] = {
2013-02-05 14:18:10 +04:00
[ LP55XX_ENGINE_1 ] = LP5521_LOAD_R ,
[ LP55XX_ENGINE_2 ] = LP5521_LOAD_G ,
[ LP55XX_ENGINE_3 ] = LP5521_LOAD_B ,
} ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:10 +04:00
lp55xx_update_bits ( chip , LP5521_REG_OP_MODE , mask [ idx ] , val [ idx ] ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:10 +04:00
lp5521_wait_opmode_done ( ) ;
}
2010-11-12 01:05:22 +03:00
2013-11-21 10:13:34 +04:00
static void lp5521_stop_all_engines ( struct lp55xx_chip * chip )
2013-02-05 14:18:10 +04:00
{
lp55xx_write ( chip , LP5521_REG_OP_MODE , 0 ) ;
lp5521_wait_opmode_done ( ) ;
2010-11-12 01:05:22 +03:00
}
2013-11-21 10:13:34 +04:00
static void lp5521_stop_engine ( struct lp55xx_chip * chip )
{
enum lp55xx_engine_index idx = chip - > engine_idx ;
2017-08-24 16:00:53 +03:00
static const u8 mask [ ] = {
2013-11-21 10:13:34 +04:00
[ LP55XX_ENGINE_1 ] = LP5521_MODE_R_M ,
[ LP55XX_ENGINE_2 ] = LP5521_MODE_G_M ,
[ LP55XX_ENGINE_3 ] = LP5521_MODE_B_M ,
} ;
lp55xx_update_bits ( chip , LP5521_REG_OP_MODE , mask [ idx ] , 0 ) ;
lp5521_wait_opmode_done ( ) ;
}
2013-02-05 14:18:10 +04:00
static void lp5521_run_engine ( struct lp55xx_chip * chip , bool start )
2010-11-12 01:05:22 +03:00
{
int ret ;
u8 mode ;
2013-02-05 14:18:10 +04:00
u8 exec ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:10 +04:00
/* stop engine */
if ( ! start ) {
lp5521_stop_engine ( chip ) ;
lp55xx_write ( chip , LP5521_REG_OP_MODE , LP5521_CMD_DIRECT ) ;
lp5521_wait_opmode_done ( ) ;
return ;
}
/*
* To run the engine ,
* operation mode and enable register should updated at the same time
*/
ret = lp55xx_read ( chip , LP5521_REG_OP_MODE , & mode ) ;
2012-05-30 02:07:26 +04:00
if ( ret )
2013-02-05 14:18:10 +04:00
return ;
2012-05-30 02:07:26 +04:00
2013-02-05 14:18:10 +04:00
ret = lp55xx_read ( chip , LP5521_REG_ENABLE , & exec ) ;
2012-05-30 02:07:26 +04:00
if ( ret )
2013-02-05 14:18:10 +04:00
return ;
/* change operation mode to RUN only when each engine is loading */
if ( LP5521_R_IS_LOADING ( mode ) ) {
mode = ( mode & ~ LP5521_MODE_R_M ) | LP5521_RUN_R ;
exec = ( exec & ~ LP5521_EXEC_R_M ) | LP5521_RUN_R ;
}
if ( LP5521_G_IS_LOADING ( mode ) ) {
mode = ( mode & ~ LP5521_MODE_G_M ) | LP5521_RUN_G ;
exec = ( exec & ~ LP5521_EXEC_G_M ) | LP5521_RUN_G ;
}
if ( LP5521_B_IS_LOADING ( mode ) ) {
mode = ( mode & ~ LP5521_MODE_B_M ) | LP5521_RUN_B ;
exec = ( exec & ~ LP5521_EXEC_B_M ) | LP5521_RUN_B ;
}
lp55xx_write ( chip , LP5521_REG_OP_MODE , mode ) ;
lp5521_wait_opmode_done ( ) ;
lp55xx_update_bits ( chip , LP5521_REG_ENABLE , LP5521_EXEC_M , exec ) ;
lp5521_wait_enable_done ( ) ;
}
static int lp5521_update_program_memory ( struct lp55xx_chip * chip ,
const u8 * data , size_t size )
{
enum lp55xx_engine_index idx = chip - > engine_idx ;
u8 pattern [ LP5521_PROGRAM_LENGTH ] = { 0 } ;
2017-08-24 16:00:53 +03:00
static const u8 addr [ ] = {
2013-02-05 14:18:10 +04:00
[ LP55XX_ENGINE_1 ] = LP5521_REG_R_PROG_MEM ,
[ LP55XX_ENGINE_2 ] = LP5521_REG_G_PROG_MEM ,
[ LP55XX_ENGINE_3 ] = LP5521_REG_B_PROG_MEM ,
} ;
unsigned cmd ;
char c [ 3 ] ;
int nrchars ;
int ret ;
2013-08-08 08:45:45 +04:00
int offset = 0 ;
int i = 0 ;
2013-02-05 14:18:10 +04:00
while ( ( offset < size - 1 ) & & ( i < LP5521_PROGRAM_LENGTH ) ) {
/* separate sscanfs because length is working only for %s */
ret = sscanf ( data + offset , " %2s%n " , c , & nrchars ) ;
if ( ret ! = 1 )
goto err ;
ret = sscanf ( c , " %2x " , & cmd ) ;
if ( ret ! = 1 )
goto err ;
pattern [ i ] = ( u8 ) cmd ;
offset + = nrchars ;
i + + ;
}
/* Each instruction is 16bit long. Check that length is even */
if ( i % 2 )
goto err ;
2013-08-08 08:45:45 +04:00
for ( i = 0 ; i < LP5521_PROGRAM_LENGTH ; i + + ) {
2013-08-08 08:41:40 +04:00
ret = lp55xx_write ( chip , addr [ idx ] + i , pattern [ i ] ) ;
2013-12-03 05:21:44 +04:00
if ( ret )
2013-08-08 08:41:40 +04:00
return - EINVAL ;
}
return size ;
2013-02-05 14:18:10 +04:00
err :
dev_err ( & chip - > cl - > dev , " wrong pattern format \n " ) ;
return - EINVAL ;
}
static void lp5521_firmware_loaded ( struct lp55xx_chip * chip )
{
const struct firmware * fw = chip - > fw ;
if ( fw - > size > LP5521_PROGRAM_LENGTH ) {
dev_err ( & chip - > cl - > dev , " firmware data size overflow: %zu \n " ,
fw - > size ) ;
return ;
}
2010-11-12 01:05:22 +03:00
2013-02-05 14:18:10 +04:00
/*
2017-05-09 01:57:47 +03:00
* Program memory sequence
2013-02-05 14:18:10 +04:00
* 1 ) set engine mode to " LOAD "
* 2 ) write firmware data into program memory
*/
lp5521_load_engine ( chip ) ;
lp5521_update_program_memory ( chip , fw - > data , fw - > size ) ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 13:57:36 +04:00
static int lp5521_post_init_device ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
int ret ;
2013-02-05 13:03:55 +04:00
u8 val ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:03:55 +04:00
/*
* Make sure that the chip is reset by reading back the r channel
* current reg . This is dummy read is required on some platforms -
* otherwise further access to the R G B channels in the
* LP5521_REG_ENABLE register will not have any effect - strange !
*/
2013-02-05 13:57:36 +04:00
ret = lp55xx_read ( chip , LP5521_REG_R_CURRENT , & val ) ;
2013-02-05 13:03:55 +04:00
if ( ret ) {
2013-02-05 13:57:36 +04:00
dev_err ( & chip - > cl - > dev , " error in resetting chip \n " ) ;
2013-02-05 13:03:55 +04:00
return ret ;
}
if ( val ! = LP5521_REG_R_CURR_DEFAULT ) {
2013-02-05 13:57:36 +04:00
dev_err ( & chip - > cl - > dev ,
2013-02-05 13:03:55 +04:00
" unexpected data in register (expected 0x%x got 0x%x) \n " ,
LP5521_REG_R_CURR_DEFAULT , val ) ;
ret = - EINVAL ;
return ret ;
}
usleep_range ( 10000 , 20000 ) ;
2010-11-12 01:05:22 +03:00
/* Set all PWMs to direct control mode */
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5521_REG_OP_MODE , LP5521_CMD_DIRECT ) ;
2010-11-12 01:05:22 +03:00
2013-03-21 04:37:04 +04:00
/* Update configuration for the clock setting */
val = LP5521_DEFAULT_CFG ;
if ( ! lp55xx_is_extclk_used ( chip ) )
val | = LP5521_CLK_INT ;
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5521_REG_CONFIG , val ) ;
2013-02-05 13:03:55 +04:00
if ( ret )
return ret ;
2010-11-12 01:05:22 +03:00
/* Initialize all channels PWM to zero -> leds off */
2013-02-05 13:57:36 +04:00
lp55xx_write ( chip , LP5521_REG_R_PWM , 0 ) ;
lp55xx_write ( chip , LP5521_REG_G_PWM , 0 ) ;
lp55xx_write ( chip , LP5521_REG_B_PWM , 0 ) ;
2010-11-12 01:05:22 +03:00
/* Set engines are set to run state when OP_MODE enables engines */
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5521_REG_ENABLE , LP5521_ENABLE_RUN_PROGRAM ) ;
2013-02-05 13:03:55 +04:00
if ( ret )
return ret ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:03:55 +04:00
lp5521_wait_enable_done ( ) ;
return 0 ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:21:43 +04:00
static int lp5521_run_selftest ( struct lp55xx_chip * chip , char * buf )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:21:43 +04:00
struct lp55xx_platform_data * pdata = chip - > pdata ;
2010-11-12 01:05:22 +03:00
int ret ;
u8 status ;
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5521_REG_STATUS , & status ) ;
2010-11-12 01:05:22 +03:00
if ( ret < 0 )
return ret ;
2013-02-05 14:21:43 +04:00
if ( pdata - > clock_mode ! = LP55XX_CLOCK_EXT )
return 0 ;
2010-11-12 01:05:22 +03:00
/* Check that ext clock is really in use if requested */
2013-02-05 14:21:43 +04:00
if ( ( status & LP5521_EXT_CLK_USED ) = = 0 )
return - EIO ;
2010-11-12 01:05:22 +03:00
return 0 ;
}
2020-07-16 21:20:04 +03:00
static int lp5521_multicolor_brightness ( struct lp55xx_led * led )
{
struct lp55xx_chip * chip = led - > chip ;
int ret ;
int i ;
mutex_lock ( & chip - > lock ) ;
for ( i = 0 ; i < led - > mc_cdev . num_colors ; i + + ) {
ret = lp55xx_write ( chip ,
LP5521_REG_LED_PWM_BASE +
led - > mc_cdev . subled_info [ i ] . channel ,
led - > mc_cdev . subled_info [ i ] . brightness ) ;
if ( ret )
break ;
}
mutex_unlock ( & chip - > lock ) ;
return ret ;
}
2015-08-20 13:22:57 +03:00
static int lp5521_led_brightness ( struct lp55xx_led * led )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:08:40 +04:00
struct lp55xx_chip * chip = led - > chip ;
2015-08-20 13:22:57 +03:00
int ret ;
2010-11-12 01:05:22 +03:00
mutex_lock ( & chip - > lock ) ;
2015-08-20 13:22:57 +03:00
ret = lp55xx_write ( chip , LP5521_REG_LED_PWM_BASE + led - > chan_nr ,
2010-11-12 01:05:22 +03:00
led - > brightness ) ;
mutex_unlock ( & chip - > lock ) ;
2015-08-20 13:22:57 +03:00
return ret ;
2010-11-12 01:05:22 +03:00
}
2013-08-08 08:41:40 +04:00
static ssize_t show_engine_mode ( struct device * dev ,
struct device_attribute * attr ,
char * buf , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
enum lp55xx_engine_mode mode = chip - > engines [ nr - 1 ] . mode ;
switch ( mode ) {
case LP55XX_ENGINE_RUN :
return sprintf ( buf , " run \n " ) ;
case LP55XX_ENGINE_LOAD :
return sprintf ( buf , " load \n " ) ;
case LP55XX_ENGINE_DISABLED :
default :
return sprintf ( buf , " disabled \n " ) ;
}
}
show_mode ( 1 )
show_mode ( 2 )
show_mode ( 3 )
static ssize_t store_engine_mode ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
struct lp55xx_engine * engine = & chip - > engines [ nr - 1 ] ;
mutex_lock ( & chip - > lock ) ;
chip - > engine_idx = nr ;
if ( ! strncmp ( buf , " run " , 3 ) ) {
lp5521_run_engine ( chip , true ) ;
engine - > mode = LP55XX_ENGINE_RUN ;
} else if ( ! strncmp ( buf , " load " , 4 ) ) {
lp5521_stop_engine ( chip ) ;
lp5521_load_engine ( chip ) ;
engine - > mode = LP55XX_ENGINE_LOAD ;
} else if ( ! strncmp ( buf , " disabled " , 8 ) ) {
lp5521_stop_engine ( chip ) ;
engine - > mode = LP55XX_ENGINE_DISABLED ;
}
mutex_unlock ( & chip - > lock ) ;
return len ;
}
store_mode ( 1 )
store_mode ( 2 )
store_mode ( 3 )
static ssize_t store_engine_load ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len , int nr )
{
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
2013-12-03 05:21:44 +04:00
int ret ;
2013-08-08 08:41:40 +04:00
mutex_lock ( & chip - > lock ) ;
chip - > engine_idx = nr ;
lp5521_load_engine ( chip ) ;
2013-12-03 05:21:44 +04:00
ret = lp5521_update_program_memory ( chip , buf , len ) ;
2013-08-08 08:41:40 +04:00
mutex_unlock ( & chip - > lock ) ;
2013-12-03 05:21:44 +04:00
return ret ;
2013-08-08 08:41:40 +04:00
}
store_load ( 1 )
store_load ( 2 )
store_load ( 3 )
2010-11-12 01:05:22 +03:00
static ssize_t lp5521_selftest ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2013-02-05 14:21:43 +04:00
struct lp55xx_led * led = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lp55xx_chip * chip = led - > chip ;
2010-11-12 01:05:22 +03:00
int ret ;
mutex_lock ( & chip - > lock ) ;
ret = lp5521_run_selftest ( chip , buf ) ;
mutex_unlock ( & chip - > lock ) ;
2013-03-15 04:19:36 +04:00
return scnprintf ( buf , PAGE_SIZE , " %s \n " , ret ? " FAIL " : " OK " ) ;
2010-11-12 01:05:22 +03:00
}
/* device attributes */
2013-08-08 08:41:40 +04:00
static LP55XX_DEV_ATTR_RW ( engine1_mode , show_engine1_mode , store_engine1_mode ) ;
static LP55XX_DEV_ATTR_RW ( engine2_mode , show_engine2_mode , store_engine2_mode ) ;
static LP55XX_DEV_ATTR_RW ( engine3_mode , show_engine3_mode , store_engine3_mode ) ;
static LP55XX_DEV_ATTR_WO ( engine1_load , store_engine1_load ) ;
static LP55XX_DEV_ATTR_WO ( engine2_load , store_engine2_load ) ;
static LP55XX_DEV_ATTR_WO ( engine3_load , store_engine3_load ) ;
static LP55XX_DEV_ATTR_RO ( selftest , lp5521_selftest ) ;
2010-11-12 01:05:22 +03:00
static struct attribute * lp5521_attributes [ ] = {
2013-08-08 08:41:40 +04:00
& dev_attr_engine1_mode . attr ,
& dev_attr_engine2_mode . attr ,
& dev_attr_engine3_mode . attr ,
& dev_attr_engine1_load . attr ,
& dev_attr_engine2_load . attr ,
& dev_attr_engine3_load . attr ,
2010-11-12 01:05:22 +03:00
& dev_attr_selftest . attr ,
NULL
} ;
static const struct attribute_group lp5521_group = {
. attrs = lp5521_attributes ,
} ;
2013-02-05 13:08:49 +04:00
/* Chip specific configurations */
static struct lp55xx_device_config lp5521_cfg = {
. reset = {
. addr = LP5521_REG_RESET ,
. val = LP5521_RESET ,
} ,
2013-02-05 13:09:56 +04:00
. enable = {
. addr = LP5521_REG_ENABLE ,
. val = LP5521_ENABLE_DEFAULT ,
} ,
2013-02-05 14:07:34 +04:00
. max_channel = LP5521_MAX_LEDS ,
2013-02-05 13:57:36 +04:00
. post_init_device = lp5521_post_init_device ,
2015-08-20 13:22:57 +03:00
. brightness_fn = lp5521_led_brightness ,
2020-07-16 21:20:04 +03:00
. multicolor_brightness_fn = lp5521_multicolor_brightness ,
2013-02-05 14:09:32 +04:00
. set_led_current = lp5521_set_led_current ,
2013-02-05 14:18:10 +04:00
. firmware_cb = lp5521_firmware_loaded ,
. run_engine = lp5521_run_engine ,
2013-02-05 14:20:45 +04:00
. dev_attr_group = & lp5521_group ,
2013-02-05 13:08:49 +04:00
} ;
2012-11-19 22:23:02 +04:00
static int lp5521_probe ( struct i2c_client * client ,
2010-11-12 01:05:22 +03:00
const struct i2c_device_id * id )
{
2013-02-05 12:56:23 +04:00
int ret ;
2013-02-05 13:03:08 +04:00
struct lp55xx_chip * chip ;
struct lp55xx_led * led ;
2015-08-24 10:09:55 +03:00
struct lp55xx_platform_data * pdata = dev_get_platdata ( & client - > dev ) ;
2020-09-18 01:32:54 +03:00
struct device_node * np = dev_of_node ( & client - > dev ) ;
2013-04-23 15:52:59 +04:00
2020-07-16 21:20:01 +03:00
chip = devm_kzalloc ( & client - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
chip - > cfg = & lp5521_cfg ;
2015-08-24 10:09:55 +03:00
if ( ! pdata ) {
2013-04-23 15:52:59 +04:00
if ( np ) {
2020-07-16 21:20:01 +03:00
pdata = lp55xx_of_populate_pdata ( & client - > dev , np ,
chip ) ;
2015-08-24 10:09:55 +03:00
if ( IS_ERR ( pdata ) )
return PTR_ERR ( pdata ) ;
2013-04-23 15:52:59 +04:00
} else {
dev_err ( & client - > dev , " no platform data \n " ) ;
return - EINVAL ;
}
2010-11-12 01:05:22 +03:00
}
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
led = devm_kcalloc ( & client - > dev ,
pdata - > num_channels , sizeof ( * led ) , GFP_KERNEL ) ;
2013-02-05 13:03:08 +04:00
if ( ! led )
return - ENOMEM ;
chip - > cl = client ;
chip - > pdata = pdata ;
mutex_init ( & chip - > lock ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:03:08 +04:00
i2c_set_clientdata ( client , led ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:58:35 +04:00
ret = lp55xx_init_device ( chip ) ;
2013-02-05 12:49:46 +04:00
if ( ret )
2013-02-05 12:58:01 +04:00
goto err_init ;
2010-11-12 01:05:22 +03:00
dev_info ( & client - > dev , " %s programmable led chip found \n " , id - > name ) ;
2013-02-05 14:06:27 +04:00
ret = lp55xx_register_leds ( led , chip ) ;
2013-02-05 12:53:40 +04:00
if ( ret )
2020-07-16 21:20:00 +03:00
goto err_out ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:20:45 +04:00
ret = lp55xx_register_sysfs ( chip ) ;
2010-11-12 01:05:22 +03:00
if ( ret ) {
dev_err ( & client - > dev , " registering sysfs failed \n " ) ;
2020-07-16 21:20:00 +03:00
goto err_out ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:20:45 +04:00
return 0 ;
2020-07-16 21:20:00 +03:00
err_out :
2013-02-05 14:03:02 +04:00
lp55xx_deinit_device ( chip ) ;
2013-02-05 12:58:01 +04:00
err_init :
2010-11-12 01:05:22 +03:00
return ret ;
}
2012-11-19 22:26:00 +04:00
static int lp5521_remove ( struct i2c_client * client )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:03:02 +04:00
struct lp55xx_led * led = i2c_get_clientdata ( client ) ;
struct lp55xx_chip * chip = led - > chip ;
2010-11-12 01:05:22 +03:00
2013-11-21 10:13:34 +04:00
lp5521_stop_all_engines ( chip ) ;
2013-02-05 14:23:51 +04:00
lp55xx_unregister_sysfs ( chip ) ;
2013-02-05 14:03:02 +04:00
lp55xx_deinit_device ( chip ) ;
2010-11-12 01:05:22 +03:00
return 0 ;
}
static const struct i2c_device_id lp5521_id [ ] = {
{ " lp5521 " , 0 } , /* Three channel chip */
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lp5521_id ) ;
2013-05-09 08:48:07 +04:00
# ifdef CONFIG_OF
static const struct of_device_id of_lp5521_leds_match [ ] = {
{ . compatible = " national,lp5521 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_lp5521_leds_match ) ;
# endif
2010-11-12 01:05:22 +03:00
static struct i2c_driver lp5521_driver = {
. driver = {
. name = " lp5521 " ,
2013-05-09 08:48:07 +04:00
. of_match_table = of_match_ptr ( of_lp5521_leds_match ) ,
2010-11-12 01:05:22 +03:00
} ,
. probe = lp5521_probe ,
2012-11-19 22:20:20 +04:00
. remove = lp5521_remove ,
2010-11-12 01:05:22 +03:00
. id_table = lp5521_id ,
} ;
2012-01-11 03:09:27 +04:00
module_i2c_driver ( lp5521_driver ) ;
2010-11-12 01:05:22 +03:00
MODULE_AUTHOR ( " Mathias Nyman, Yuri Zaporozhets, Samu Onkalo " ) ;
2013-02-05 14:28:00 +04:00
MODULE_AUTHOR ( " Milo Kim <milo.kim@ti.com> " ) ;
2010-11-12 01:05:22 +03:00
MODULE_DESCRIPTION ( " LP5521 LED engine " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;