2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-11-12 01:05:22 +03:00
/*
2014-05-05 15:46:54 +04:00
* lp5523 . c - LP5523 , LP55231 LED Driver
2010-11-12 01:05:22 +03:00
*
* 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
*/
2024-06-27 01:15:13 +03:00
# include <linux/cleanup.h>
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-09-28 15:38:30 +04:00
# include <linux/of.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-02-05 13:03:08 +04:00
# include "leds-lp55xx-common.h"
2010-11-12 01:05:22 +03:00
2014-01-10 03:14:05 +04:00
/* Memory is used like this:
2020-07-13 18:45:30 +03:00
* 0x00 engine 1 program
* 0x10 engine 2 program
* 0x20 engine 3 program
* 0x30 engine 1 muxing info
* 0x40 engine 2 muxing info
* 0x50 engine 3 muxing info
*/
2024-06-26 19:00:11 +03:00
# define LP5523_PAGES_PER_ENGINE 1
2013-02-05 14:25:26 +04:00
# define LP5523_MAX_LEDS 9
/* Registers */
2010-11-12 01:05:22 +03:00
# define LP5523_REG_ENABLE 0x00
# define LP5523_REG_OP_MODE 0x01
# define LP5523_REG_ENABLE_LEDS_MSB 0x04
# define LP5523_REG_ENABLE_LEDS_LSB 0x05
2015-05-11 22:10:54 +03:00
# define LP5523_REG_LED_CTRL_BASE 0x06
2010-11-12 01:05:22 +03:00
# define LP5523_REG_LED_PWM_BASE 0x16
# define LP5523_REG_LED_CURRENT_BASE 0x26
# define LP5523_REG_CONFIG 0x36
2024-06-26 19:00:08 +03:00
2013-02-05 14:25:26 +04:00
# define LP5523_REG_STATUS 0x3A
2024-06-26 19:00:08 +03:00
# define LP5523_ENGINE_BUSY BIT(4)
2013-02-05 14:25:26 +04:00
# define LP5523_REG_RESET 0x3D
2010-11-12 01:05:22 +03:00
# define LP5523_REG_LED_TEST_CTRL 0x41
# define LP5523_REG_LED_TEST_ADC 0x42
2015-05-11 22:10:54 +03:00
# define LP5523_REG_MASTER_FADER_BASE 0x48
2013-08-08 08:57:35 +04:00
# define LP5523_REG_CH1_PROG_START 0x4C
# define LP5523_REG_CH2_PROG_START 0x4D
# define LP5523_REG_CH3_PROG_START 0x4E
2013-02-05 14:25:26 +04:00
# define LP5523_REG_PROG_PAGE_SEL 0x4F
2010-11-12 01:05:22 +03:00
# define LP5523_REG_PROG_MEM 0x50
2013-02-05 14:25:26 +04:00
/* Bit description in registers */
2010-11-12 01:05:22 +03:00
# define LP5523_ENABLE 0x40
# define LP5523_AUTO_INC 0x40
# define LP5523_PWR_SAVE 0x20
# define LP5523_PWM_PWR_SAVE 0x04
2023-04-21 10:53:05 +03:00
# define LP5523_CP_MODE_MASK 0x18
# define LP5523_CP_MODE_SHIFT 3
2010-11-12 01:05:22 +03:00
# define LP5523_AUTO_CLK 0x02
2023-04-21 10:53:05 +03:00
# define LP5523_DEFAULT_CONFIG \
( LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE )
2013-02-05 14:25:26 +04:00
2010-11-12 01:05:22 +03:00
# define LP5523_EN_LEDTEST 0x80
# define LP5523_LEDTEST_DONE 0x80
2013-02-05 13:08:49 +04:00
# define LP5523_RESET 0xFF
2010-11-12 01:05:22 +03:00
# define LP5523_ADC_SHORTCIRC_LIM 80
# define LP5523_EXT_CLK_USED 0x08
2013-08-08 08:57:35 +04:00
# define LP5523_ENG_STATUS_MASK 0x07
2010-11-12 01:05:22 +03:00
2013-08-08 08:57:35 +04:00
static int lp5523_init_program_engine ( struct lp55xx_chip * chip ) ;
2013-02-05 13:57:36 +04:00
static int lp5523_post_init_device ( struct lp55xx_chip * chip )
2010-11-12 01:05:22 +03:00
{
2013-02-05 13:57:36 +04:00
int ret ;
2023-04-21 10:53:05 +03:00
int val ;
2010-11-12 01:05:22 +03:00
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5523_REG_ENABLE , LP5523_ENABLE ) ;
2013-02-05 13:04:50 +04:00
if ( ret )
return ret ;
2010-11-12 01:05:22 +03:00
2010-11-24 23:57:04 +03:00
/* Chip startup time is 500 us, 1 - 2 ms gives some margin */
usleep_range ( 1000 , 2000 ) ;
2010-11-12 01:05:22 +03:00
2023-04-21 10:53:05 +03:00
val = LP5523_DEFAULT_CONFIG ;
val | = ( chip - > pdata - > charge_pump_mode < < LP5523_CP_MODE_SHIFT ) & LP5523_CP_MODE_MASK ;
ret = lp55xx_write ( chip , LP5523_REG_CONFIG , val ) ;
2013-02-05 13:04:50 +04:00
if ( ret )
return ret ;
2010-11-12 01:05:22 +03:00
/* turn on all leds */
2013-02-05 13:57:36 +04:00
ret = lp55xx_write ( chip , LP5523_REG_ENABLE_LEDS_MSB , 0x01 ) ;
2013-02-05 13:04:50 +04:00
if ( ret )
2012-10-29 05:45:11 +04:00
return ret ;
2013-08-08 08:57:35 +04:00
ret = lp55xx_write ( chip , LP5523_REG_ENABLE_LEDS_LSB , 0xff ) ;
if ( ret )
return ret ;
return lp5523_init_program_engine ( chip ) ;
2010-11-12 01:05:22 +03:00
}
2013-02-05 14:18:51 +04:00
static void lp5523_run_engine ( struct lp55xx_chip * chip , bool start )
2010-11-12 01:05:22 +03:00
{
2013-02-05 14:18:51 +04:00
/* stop engine */
if ( ! start ) {
2024-06-26 19:00:19 +03:00
lp55xx_stop_engine ( chip ) ;
2024-06-26 19:00:18 +03:00
lp55xx_turn_off_channels ( chip ) ;
2013-02-05 14:18:51 +04:00
return ;
}
2010-11-12 01:05:22 +03:00
2024-06-26 19:00:12 +03:00
lp55xx_run_engine_common ( chip ) ;
2010-11-12 01:05:22 +03:00
}
2013-08-08 08:57:35 +04:00
static int lp5523_init_program_engine ( struct lp55xx_chip * chip )
{
int i ;
int j ;
int ret ;
u8 status ;
/* one pattern per engine setting LED MUX start and stop addresses */
2024-06-26 19:00:23 +03:00
static const u8 pattern [ ] [ LP55xx_BYTES_PER_PAGE ] = {
2013-08-08 08:57:35 +04:00
{ 0x9c , 0x30 , 0x9c , 0xb0 , 0x9d , 0x80 , 0xd8 , 0x00 , 0 } ,
{ 0x9c , 0x40 , 0x9c , 0xc0 , 0x9d , 0x80 , 0xd8 , 0x00 , 0 } ,
{ 0x9c , 0x50 , 0x9c , 0xd0 , 0x9d , 0x80 , 0xd8 , 0x00 , 0 } ,
} ;
/* hardcode 32 bytes of memory for each engine from program memory */
ret = lp55xx_write ( chip , LP5523_REG_CH1_PROG_START , 0x00 ) ;
if ( ret )
return ret ;
ret = lp55xx_write ( chip , LP5523_REG_CH2_PROG_START , 0x10 ) ;
if ( ret )
return ret ;
ret = lp55xx_write ( chip , LP5523_REG_CH3_PROG_START , 0x20 ) ;
if ( ret )
return ret ;
/* write LED MUX address space for each engine */
for ( i = LP55XX_ENGINE_1 ; i < = LP55XX_ENGINE_3 ; i + + ) {
chip - > engine_idx = i ;
2024-06-26 19:00:11 +03:00
lp55xx_load_engine ( chip ) ;
2013-08-08 08:57:35 +04:00
2024-06-26 19:00:23 +03:00
for ( j = 0 ; j < LP55xx_BYTES_PER_PAGE ; j + + ) {
2013-08-08 08:57:35 +04:00
ret = lp55xx_write ( chip , LP5523_REG_PROG_MEM + j ,
pattern [ i - 1 ] [ j ] ) ;
if ( ret )
goto out ;
}
}
lp5523_run_engine ( chip , true ) ;
/* Let the programs run for couple of ms and check the engine status */
usleep_range ( 3000 , 6000 ) ;
2021-05-03 14:56:36 +03:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
if ( ret )
goto out ;
2013-08-08 08:57:35 +04:00
status & = LP5523_ENG_STATUS_MASK ;
if ( status ! = LP5523_ENG_STATUS_MASK ) {
dev_err ( & chip - > cl - > dev ,
2017-10-29 08:55:47 +03:00
" could not configure LED engine, status = 0x%.2x \n " ,
2013-08-08 08:57:35 +04:00
status ) ;
ret = - 1 ;
}
out :
2024-06-26 19:00:08 +03:00
lp55xx_stop_all_engine ( chip ) ;
2013-08-08 08:57:35 +04:00
return ret ;
}
2010-11-12 01:05:22 +03:00
static ssize_t lp5523_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 ;
struct lp55xx_platform_data * pdata = chip - > pdata ;
2022-10-28 13:56:43 +03:00
int ret , pos = 0 ;
u8 status , adc , vdd , i ;
2010-11-12 01:05:22 +03:00
2024-06-27 01:15:13 +03:00
guard ( mutex ) ( & chip - > lock ) ;
2010-11-12 01:05:22 +03:00
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
2010-11-12 01:05:22 +03:00
if ( ret < 0 )
2024-06-27 01:15:13 +03:00
return sysfs_emit ( buf , " FAIL \n " ) ;
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 ( pdata - > clock_mode = = LP55XX_CLOCK_EXT ) {
2010-11-12 01:05:22 +03:00
if ( ( status & LP5523_EXT_CLK_USED ) = = 0 )
2024-06-27 01:15:13 +03:00
return sysfs_emit ( buf , " FAIL \n " ) ;
2013-02-05 14:21:43 +04:00
}
2010-11-12 01:05:22 +03:00
/* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_TEST_CTRL , LP5523_EN_LEDTEST | 16 ) ;
2010-11-24 23:57:04 +03:00
usleep_range ( 3000 , 6000 ) ; /* ADC conversion time is typically 2.7 ms */
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
2024-06-27 01:15:13 +03:00
return sysfs_emit ( buf , " FAIL \n " ) ;
2012-10-29 05:45:11 +04:00
2010-11-12 01:05:22 +03:00
if ( ! ( status & LP5523_LEDTEST_DONE ) )
2010-11-24 23:57:04 +03:00
usleep_range ( 3000 , 6000 ) ; /* Was not ready. Wait little bit */
2010-11-12 01:05:22 +03:00
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_LED_TEST_ADC , & vdd ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
2024-06-27 01:15:13 +03:00
return sysfs_emit ( buf , " FAIL \n " ) ;
2012-10-29 05:45:11 +04:00
2010-11-12 01:05:22 +03:00
vdd - - ; /* There may be some fluctuation in measurement */
2022-10-28 13:56:43 +03:00
for ( i = 0 ; i < pdata - > num_channels ; i + + ) {
/* Skip disabled channels */
2013-02-05 14:21:43 +04:00
if ( pdata - > led_config [ i ] . led_current = = 0 )
2010-11-12 01:05:22 +03:00
continue ;
/* Set default current */
2022-10-28 13:56:43 +03:00
lp55xx_write ( chip , LP5523_REG_LED_CURRENT_BASE + led - > chan_nr ,
2013-02-05 14:21:43 +04:00
pdata - > led_config [ i ] . led_current ) ;
2010-11-12 01:05:22 +03:00
2022-10-28 13:56:43 +03:00
lp55xx_write ( chip , LP5523_REG_LED_PWM_BASE + led - > chan_nr ,
0xff ) ;
2010-11-24 23:57:04 +03:00
/* let current stabilize 2 - 4ms before measurements start */
usleep_range ( 2000 , 4000 ) ;
2013-02-05 14:21:43 +04:00
lp55xx_write ( chip , LP5523_REG_LED_TEST_CTRL ,
2022-10-28 13:56:43 +03:00
LP5523_EN_LEDTEST | led - > chan_nr ) ;
2010-11-24 23:57:04 +03:00
/* ADC conversion time is 2.7 ms typically */
usleep_range ( 3000 , 6000 ) ;
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_STATUS , & status ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
2024-06-27 01:15:13 +03:00
return sysfs_emit ( buf , " FAIL \n " ) ;
2012-10-29 05:45:11 +04:00
2010-11-12 01:05:22 +03:00
if ( ! ( status & LP5523_LEDTEST_DONE ) )
2022-10-28 13:56:43 +03:00
usleep_range ( 3000 , 6000 ) ; /* Was not ready. Wait. */
2013-02-05 14:21:43 +04:00
ret = lp55xx_read ( chip , LP5523_REG_LED_TEST_ADC , & adc ) ;
2012-10-29 05:45:11 +04:00
if ( ret < 0 )
2024-06-27 01:15:13 +03:00
return sysfs_emit ( buf , " FAIL \n " ) ;
2010-11-12 01:05:22 +03:00
if ( adc > = vdd | | adc < LP5523_ADC_SHORTCIRC_LIM )
2024-06-27 01:15:12 +03:00
pos + = sysfs_emit_at ( buf , pos , " LED %d FAIL \n " ,
led - > chan_nr ) ;
2010-11-12 01:05:22 +03:00
2022-10-28 13:56:43 +03:00
lp55xx_write ( chip , LP5523_REG_LED_PWM_BASE + led - > chan_nr ,
0x00 ) ;
2010-11-12 01:05:22 +03:00
/* Restore current */
2022-10-28 13:56:43 +03:00
lp55xx_write ( chip , LP5523_REG_LED_CURRENT_BASE + led - > chan_nr ,
led - > led_current ) ;
2010-11-12 01:05:22 +03:00
led + + ;
}
2024-06-27 01:15:13 +03:00
return pos = = 0 ? sysfs_emit ( buf , " OK \n " ) : pos ;
2010-11-12 01:05:22 +03:00
}
2024-06-26 19:00:20 +03:00
LP55XX_DEV_ATTR_ENGINE_MODE ( 1 ) ;
LP55XX_DEV_ATTR_ENGINE_MODE ( 2 ) ;
LP55XX_DEV_ATTR_ENGINE_MODE ( 3 ) ;
2024-06-26 19:00:21 +03:00
LP55XX_DEV_ATTR_ENGINE_LEDS ( 1 ) ;
LP55XX_DEV_ATTR_ENGINE_LEDS ( 2 ) ;
LP55XX_DEV_ATTR_ENGINE_LEDS ( 3 ) ;
2024-06-26 19:00:20 +03:00
LP55XX_DEV_ATTR_ENGINE_LOAD ( 1 ) ;
LP55XX_DEV_ATTR_ENGINE_LOAD ( 2 ) ;
LP55XX_DEV_ATTR_ENGINE_LOAD ( 3 ) ;
2013-08-08 09:02:29 +04:00
static LP55XX_DEV_ATTR_RO ( selftest , lp5523_selftest ) ;
2024-06-26 19:00:22 +03:00
LP55XX_DEV_ATTR_MASTER_FADER ( 1 ) ;
LP55XX_DEV_ATTR_MASTER_FADER ( 2 ) ;
LP55XX_DEV_ATTR_MASTER_FADER ( 3 ) ;
static LP55XX_DEV_ATTR_RW ( master_fader_leds , lp55xx_show_master_fader_leds ,
lp55xx_store_master_fader_leds ) ;
2010-11-12 01:05:22 +03:00
static struct attribute * lp5523_attributes [ ] = {
2013-08-08 09:02:29 +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 ,
& dev_attr_engine1_leds . attr ,
& dev_attr_engine2_leds . attr ,
& dev_attr_engine3_leds . attr ,
2010-11-12 01:05:22 +03:00
& dev_attr_selftest . attr ,
2015-05-11 22:10:54 +03:00
& dev_attr_master_fader1 . attr ,
& dev_attr_master_fader2 . attr ,
& dev_attr_master_fader3 . attr ,
& dev_attr_master_fader_leds . attr ,
2012-09-12 16:16:03 +04:00
NULL ,
2010-11-12 01:05:22 +03:00
} ;
static const struct attribute_group lp5523_group = {
. attrs = lp5523_attributes ,
} ;
2013-02-05 13:08:49 +04:00
/* Chip specific configurations */
static struct lp55xx_device_config lp5523_cfg = {
2024-06-26 19:00:08 +03:00
. reg_op_mode = {
. addr = LP5523_REG_OP_MODE ,
} ,
2024-06-26 19:00:12 +03:00
. reg_exec = {
. addr = LP5523_REG_ENABLE ,
} ,
2024-06-26 19:00:08 +03:00
. engine_busy = {
. addr = LP5523_REG_STATUS ,
. mask = LP5523_ENGINE_BUSY ,
} ,
2013-02-05 13:08:49 +04:00
. reset = {
. addr = LP5523_REG_RESET ,
. val = LP5523_RESET ,
} ,
2013-02-05 13:09:56 +04:00
. enable = {
. addr = LP5523_REG_ENABLE ,
. val = LP5523_ENABLE ,
} ,
2024-06-26 19:00:13 +03:00
. prog_mem_base = {
. addr = LP5523_REG_PROG_MEM ,
} ,
2024-06-26 19:00:15 +03:00
. reg_led_pwm_base = {
. addr = LP5523_REG_LED_PWM_BASE ,
} ,
2024-06-26 19:00:17 +03:00
. reg_led_current_base = {
. addr = LP5523_REG_LED_CURRENT_BASE ,
} ,
2024-06-26 19:00:22 +03:00
. reg_master_fader_base = {
. addr = LP5523_REG_MASTER_FADER_BASE ,
} ,
. reg_led_ctrl_base = {
. addr = LP5523_REG_LED_CTRL_BASE ,
} ,
2024-06-26 19:00:11 +03:00
. pages_per_engine = LP5523_PAGES_PER_ENGINE ,
2013-02-05 14:07:34 +04:00
. max_channel = LP5523_MAX_LEDS ,
2013-02-05 13:57:36 +04:00
. post_init_device = lp5523_post_init_device ,
2024-06-26 19:00:15 +03:00
. brightness_fn = lp55xx_led_brightness ,
2024-06-26 19:00:16 +03:00
. multicolor_brightness_fn = lp55xx_multicolor_brightness ,
2024-06-26 19:00:17 +03:00
. set_led_current = lp55xx_set_led_current ,
2024-06-26 19:00:14 +03:00
. firmware_cb = lp55xx_firmware_loaded_cb ,
2013-02-05 14:18:51 +04:00
. run_engine = lp5523_run_engine ,
2013-02-05 14:20:45 +04:00
. dev_attr_group = & lp5523_group ,
2013-02-05 13:08:49 +04:00
} ;
2010-11-12 01:05:22 +03:00
static const struct i2c_device_id lp5523_id [ ] = {
2024-06-26 19:00:09 +03:00
{ " lp5523 " , . driver_data = ( kernel_ulong_t ) & lp5523_cfg , } ,
{ " lp55231 " , . driver_data = ( kernel_ulong_t ) & lp5523_cfg , } ,
2010-11-12 01:05:22 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lp5523_id ) ;
2013-05-09 08:48:57 +04:00
static const struct of_device_id of_lp5523_leds_match [ ] = {
2024-06-26 19:00:09 +03:00
{ . compatible = " national,lp5523 " , . data = & lp5523_cfg , } ,
{ . compatible = " ti,lp55231 " , . data = & lp5523_cfg , } ,
2013-05-09 08:48:57 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_lp5523_leds_match ) ;
2010-11-12 01:05:22 +03:00
static struct i2c_driver lp5523_driver = {
. driver = {
2012-09-04 11:06:18 +04:00
. name = " lp5523x " ,
2023-08-08 14:11:08 +03:00
. of_match_table = of_lp5523_leds_match ,
2010-11-12 01:05:22 +03:00
} ,
2024-06-26 19:00:09 +03:00
. probe = lp55xx_probe ,
. remove = lp55xx_remove ,
2010-11-12 01:05:22 +03:00
. id_table = lp5523_id ,
} ;
2012-01-11 03:09:27 +04:00
module_i2c_driver ( lp5523_driver ) ;
2010-11-12 01:05:22 +03:00
MODULE_AUTHOR ( " Mathias Nyman <mathias.nyman@nokia.com> " ) ;
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 ( " LP5523 LED engine " ) ;
MODULE_LICENSE ( " GPL " ) ;