Skip to content

Commit a18983b

Browse files
zonquelag-linaro
authored andcommitted
leds: is31f132xx: Add support for is31fl3293
This chip supports 3 LED channels with 4096 possible PWM values. Extend the driver to support this variant: * Make brightness steps configurable per device type * Handle dual-register brightness updates * Allow to specify values to write into the PWM update register * Add custom init and shutdown function for 3293 variant * Init registers after parsing DT properties Signed-off-by: Daniel Mack <daniel@zonque.org> Link: https://patch.msgid.link/20251219154521.643312-4-daniel@zonque.org Signed-off-by: Lee Jones <lee@kernel.org>
1 parent 6f1bc45 commit a18983b

1 file changed

Lines changed: 130 additions & 8 deletions

File tree

drivers/leds/leds-is31fl32xx.c

Lines changed: 130 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,26 @@
3434

3535
#define IS31FL32XX_PWM_FREQUENCY_22KHZ 0x01
3636

37+
/* Registers for IS31FL3293 */
38+
#define IS31FL3293_SHUTDOWN_REG 0x01
39+
#define IS31FL3293_SHUTDOWN_SSD_DISABLE BIT(0)
40+
#define IS31FL3293_SHUTDOWN_EN1 BIT(4)
41+
#define IS31FL3293_SHUTDOWN_EN2 BIT(5)
42+
#define IS31FL3293_SHUTDOWN_EN3 BIT(6)
43+
#define IS31FL3293_GCC_REG 0x03
44+
#define IS31FL3293_GCC_LEVEL_MAX 0x3f
45+
#define IS31FL3293_CL_REG 0x10
46+
#define IS31FL3293_COLOR_UPDATE_REG 0x27
47+
#define IS31FL3293_COLOR_UPDATE_MAGIC 0xc5
48+
#define IS31FL3293_RESET_REG 0x3c
49+
#define IS31FL3293_RESET_MAGIC 0xc5
50+
#define IS31FL3293_MAX_MICROAMP 20000
51+
3752
struct is31fl32xx_priv;
3853
struct is31fl32xx_led_data {
3954
struct led_classdev cdev;
4055
u8 channel; /* 1-based, max priv->cdef->channels */
56+
u32 max_microamp;
4157
struct is31fl32xx_priv *priv;
4258
};
4359

@@ -53,13 +69,15 @@ struct is31fl32xx_priv {
5369
* @channels : Number of LED channels
5470
* @shutdown_reg : address of Shutdown register (optional)
5571
* @pwm_update_reg : address of PWM Update register
72+
* @pwm_update_value : value to write to PWM Update register
5673
* @global_control_reg : address of Global Control register (optional)
5774
* @reset_reg : address of Reset register (optional)
5875
* @output_frequency_setting_reg: address of output frequency register (optional)
5976
* @pwm_register_base : address of first PWM register
6077
* @pwm_registers_reversed: : true if PWM registers count down instead of up
6178
* @led_control_register_base : address of first LED control register (optional)
6279
* @enable_bits_per_led_control_register: number of LEDs enable bits in each
80+
* @brightness_steps : number of brightness steps supported by the chip
6381
* @reset_func : pointer to reset function
6482
* @sw_shutdown_func : pointer to software shutdown function
6583
*
@@ -77,13 +95,15 @@ struct is31fl32xx_chipdef {
7795
u8 channels;
7896
u8 shutdown_reg;
7997
u8 pwm_update_reg;
98+
u8 pwm_update_value;
8099
u8 global_control_reg;
81100
u8 reset_reg;
82101
u8 output_frequency_setting_reg;
83102
u8 pwm_register_base;
84103
bool pwm_registers_reversed;
85104
u8 led_control_register_base;
86105
u8 enable_bits_per_led_control_register;
106+
u16 brightness_steps;
87107
int (*reset_func)(struct is31fl32xx_priv *priv);
88108
int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable);
89109
};
@@ -152,6 +172,62 @@ static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
152172
return is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, value);
153173
}
154174

175+
/*
176+
* Custom Reset function for IS31FL3293. We need to set the global current limit
177+
* and write to the color update register once.
178+
*/
179+
static int is31fl3293_reset(struct is31fl32xx_priv *priv)
180+
{
181+
int i, ret;
182+
183+
ret = is31fl32xx_write(priv, IS31FL3293_RESET_REG,
184+
IS31FL3293_RESET_MAGIC);
185+
if (ret)
186+
return ret;
187+
188+
/* Set the global current limit to maximum */
189+
ret = is31fl32xx_write(priv, IS31FL3293_GCC_REG,
190+
IS31FL3293_GCC_LEVEL_MAX);
191+
if (ret)
192+
return ret;
193+
194+
for (i = 0; i < priv->num_leds; i++) {
195+
struct is31fl32xx_led_data *led_data = &priv->leds[i];
196+
int current_level_reg = IS31FL3293_CL_REG + led_data->channel - 1;
197+
int microamp = max(led_data->max_microamp, IS31FL3293_MAX_MICROAMP);
198+
int current_level = (microamp * 0xff) / IS31FL3293_MAX_MICROAMP;
199+
200+
ret = is31fl32xx_write(priv, current_level_reg, current_level);
201+
if (ret)
202+
return ret;
203+
}
204+
205+
ret = is31fl32xx_write(priv, IS31FL3293_COLOR_UPDATE_REG,
206+
IS31FL3293_COLOR_UPDATE_MAGIC);
207+
if (ret)
208+
return ret;
209+
210+
return 0;
211+
}
212+
213+
/*
214+
* Custom Software-Shutdown function for IS31FL3293 because the SHUTDOWN
215+
* register of this device also has bits to enable the channels.
216+
*/
217+
static int is31fl3293_software_shutdown(struct is31fl32xx_priv *priv,
218+
bool enable)
219+
{
220+
u8 value = 0;
221+
222+
if (!enable)
223+
value = IS31FL3293_SHUTDOWN_SSD_DISABLE |
224+
IS31FL3293_SHUTDOWN_EN1 |
225+
IS31FL3293_SHUTDOWN_EN2 |
226+
IS31FL3293_SHUTDOWN_EN3;
227+
228+
return is31fl32xx_write(priv, IS31FL3293_SHUTDOWN_REG, value);
229+
}
230+
155231
/*
156232
* NOTE: A mutex is not needed in this function because:
157233
* - All referenced data is read-only after probe()
@@ -190,13 +266,36 @@ static int is31fl32xx_brightness_set(struct led_classdev *led_cdev,
190266
else
191267
pwm_register_offset = led_data->channel - 1;
192268

193-
ret = is31fl32xx_write(led_data->priv,
194-
cdef->pwm_register_base + pwm_register_offset,
195-
brightness);
196-
if (ret)
197-
return ret;
269+
switch (cdef->brightness_steps) {
270+
case 256:
271+
ret = is31fl32xx_write(led_data->priv,
272+
cdef->pwm_register_base + pwm_register_offset,
273+
brightness);
274+
if (ret)
275+
return ret;
276+
277+
break;
278+
case 4096:
279+
/* IS31FL329x devices use two registers to store 12 bits of brightness */
280+
pwm_register_offset *= 2;
198281

199-
return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0);
282+
ret = is31fl32xx_write(led_data->priv,
283+
cdef->pwm_register_base + pwm_register_offset,
284+
brightness & 0xff);
285+
if (ret)
286+
return ret;
287+
288+
ret = is31fl32xx_write(led_data->priv,
289+
cdef->pwm_register_base + pwm_register_offset + 1,
290+
(brightness >> 8) & 0xf);
291+
if (ret)
292+
return ret;
293+
294+
break;
295+
}
296+
297+
return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg,
298+
cdef->pwm_update_value);
200299
}
201300

202301
static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv)
@@ -295,6 +394,8 @@ static int is31fl32xx_parse_child_dt(const struct device *dev,
295394
}
296395
led_data->channel = reg;
297396

397+
of_property_read_u32(child, "led-max-microamp", &led_data->max_microamp);
398+
298399
cdev->brightness_set_blocking = is31fl32xx_brightness_set;
299400

300401
return 0;
@@ -339,6 +440,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
339440
const struct is31fl32xx_led_data *other_led_data;
340441

341442
led_data->priv = priv;
443+
led_data->cdev.max_brightness = priv->cdef->brightness_steps - 1;
342444

343445
ret = is31fl32xx_parse_child_dt(dev, child, led_data);
344446
if (ret)
@@ -391,6 +493,7 @@ static const struct is31fl32xx_chipdef is31fl3236a_cdef = {
391493
.pwm_register_base = 0x01,
392494
.led_control_register_base = 0x26,
393495
.enable_bits_per_led_control_register = 1,
496+
.brightness_steps = 256,
394497
};
395498

396499
static const struct is31fl32xx_chipdef is31fl3235_cdef = {
@@ -403,6 +506,7 @@ static const struct is31fl32xx_chipdef is31fl3235_cdef = {
403506
.pwm_register_base = 0x05,
404507
.led_control_register_base = 0x2a,
405508
.enable_bits_per_led_control_register = 1,
509+
.brightness_steps = 256,
406510
};
407511

408512
static const struct is31fl32xx_chipdef is31fl3218_cdef = {
@@ -415,6 +519,7 @@ static const struct is31fl32xx_chipdef is31fl3218_cdef = {
415519
.pwm_register_base = 0x01,
416520
.led_control_register_base = 0x13,
417521
.enable_bits_per_led_control_register = 6,
522+
.brightness_steps = 256,
418523
};
419524

420525
static const struct is31fl32xx_chipdef is31fl3216_cdef = {
@@ -430,9 +535,25 @@ static const struct is31fl32xx_chipdef is31fl3216_cdef = {
430535
.enable_bits_per_led_control_register = 8,
431536
.reset_func = is31fl3216_reset,
432537
.sw_shutdown_func = is31fl3216_software_shutdown,
538+
.brightness_steps = 256,
539+
};
540+
541+
static const struct is31fl32xx_chipdef is31fl3293_cdef = {
542+
.channels = 3,
543+
.shutdown_reg = IS31FL32XX_REG_NONE,
544+
.pwm_update_reg = 0x28,
545+
.pwm_update_value = 0xc5,
546+
.global_control_reg = IS31FL32XX_REG_NONE,
547+
.reset_reg = IS31FL32XX_REG_NONE,
548+
.pwm_register_base = 0x19,
549+
.led_control_register_base = IS31FL32XX_REG_NONE,
550+
.brightness_steps = 4096,
551+
.reset_func = is31fl3293_reset,
552+
.sw_shutdown_func = is31fl3293_software_shutdown,
433553
};
434554

435555
static const struct of_device_id of_is31fl32xx_match[] = {
556+
{ .compatible = "issi,is31fl3293", .data = &is31fl3293_cdef, },
436557
{ .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
437558
{ .compatible = "issi,is31fl3236a", .data = &is31fl3236a_cdef, },
438559
{ .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
@@ -468,11 +589,11 @@ static int is31fl32xx_probe(struct i2c_client *client)
468589
priv->cdef = cdef;
469590
i2c_set_clientdata(client, priv);
470591

471-
ret = is31fl32xx_init_regs(priv);
592+
ret = is31fl32xx_parse_dt(dev, priv);
472593
if (ret)
473594
return ret;
474595

475-
ret = is31fl32xx_parse_dt(dev, priv);
596+
ret = is31fl32xx_init_regs(priv);
476597
if (ret)
477598
return ret;
478599

@@ -495,6 +616,7 @@ static void is31fl32xx_remove(struct i2c_client *client)
495616
* even though it is not used for DeviceTree based instantiation.
496617
*/
497618
static const struct i2c_device_id is31fl32xx_id[] = {
619+
{ "is31fl3293" },
498620
{ "is31fl3236" },
499621
{ "is31fl3236a" },
500622
{ "is31fl3235" },

0 commit comments

Comments
 (0)