mbox series

[v9,0/4] leds: add new LED driver for TI LP5812

Message ID 20250610174319.183375-1-trannamatk@gmail.com
Headers show
Series leds: add new LED driver for TI LP5812 | expand

Message

Nam Tran June 10, 2025, 5:43 p.m. UTC
This patch series adds support for the TI/National Semiconductor LP5812
4x3 matrix RGB LED driver. The driver supports features such as autonomous
animation and time-cross-multiplexing (TCM) for dynamic LED effects.

Following feedback from both the LED and auxdisplay subsystem maintainers,
the driver has been moved back to the LED subsystem, under drivers/leds/rgb/.
This version integrates with the existing multicolor LED APIs, avoiding custom
sysfs where standard LED interfaces are sufficient.

Signed-off-by: Nam Tran <trannamatk@gmail.com>
---
Changes in v9:
- Move driver back to drivers/leds/rgb/
- Integrate with LED multicolor framework
- Refactor and simplify custom sysfs handling
- Extend Device Tree binding to support multi-led@ nodes using leds-class-multicolor.yaml
- Update documentation to reflect the updated sysfs
- Link to v8: https://lore.kernel.org/lkml/20250427082447.138359-1-trannamatk@gmail.com/

Changes in v8:
- Move driver to drivers/auxdisplay/ instead of drivers/leds/.
- Rename files from leds-lp5812.c/.h to lp5812.c/.h.
- Move ti,lp5812.yaml binding to auxdisplay/ directory,
  and update the title and $id to match new path.
- No functional changes to the binding itself (keep Reviewed-by).
- Update commit messages and patch titles to reflect the move.
- Link to v7: https://lore.kernel.org/linux-leds/20250422190121.46839-1-trannamatk@gmail.com/

Changes in v7:
- Mark `chip_leds_map` as const.
- Use consistent `ret` initialization.
- Simplify the function `set_mix_sel_led()`.
- Refactor `dev_config_show()` and `led_auto_animation_show()` to avoid temp buffer, malloc/free.
- Simplify the code and ensure consistent use of mutex lock/unlock in show/store functions.
- Remove `total_leds` and `total_aeu`.
- Link to v6: https://lore.kernel.org/linux-leds/20250419184333.56617-1-trannamatk@gmail.com/

Changes in v6:
- Add `vcc-supply` property to describe the LP5812 power supply.
- Remove `chan-name` property and entire LED subnodes, as they are not needed.
- Update LP5812 LED driver node to Raspberry Pi 4 B Device Tree, based on updated binding.
- Link to v5: https://lore.kernel.org/linux-leds/20250414145742.35713-1-trannamatk@gmail.com/

Changes in v5:
- Rebase on v6.15-rc2
- Removed unused functions (lp5812_dump_regs, lp5812_update_bit).
- Address Krzysztof's review comments
- Link to v4: https://lore.kernel.org/linux-leds/20250405183246.198568-1-trannamatk@gmail.com/
---

Nam Tran (4):
  dt-bindings: leds: add TI/National Semiconductor LP5812 LED Driver
  leds: add TI/National Semiconductor LP5812 LED Driver
  docs: ABI: Document LP5812 LED sysfs interfaces
  docs: leds: Document TI LP5812 LED driver

 .../ABI/testing/sysfs-bus-i2c-devices-lp5812  |   40 +
 .../ABI/testing/sysfs-class-led-lp5812        |  120 +
 .../devicetree/bindings/leds/ti,lp5812.yaml   |  264 +++
 Documentation/leds/index.rst                  |    1 +
 Documentation/leds/leds-lp5812.rst            |   84 +
 MAINTAINERS                                   |   13 +
 drivers/leds/rgb/Kconfig                      |   13 +
 drivers/leds/rgb/Makefile                     |    1 +
 drivers/leds/rgb/leds-lp5812.c                | 1946 +++++++++++++++++
 drivers/leds/rgb/leds-lp5812.h                |  228 ++
 10 files changed, 2710 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-lp5812
 create mode 100644 Documentation/ABI/testing/sysfs-class-led-lp5812
 create mode 100644 Documentation/devicetree/bindings/leds/ti,lp5812.yaml
 create mode 100644 Documentation/leds/leds-lp5812.rst
 create mode 100644 drivers/leds/rgb/leds-lp5812.c
 create mode 100644 drivers/leds/rgb/leds-lp5812.h


base-commit: f09079bd04a924c72d555cd97942d5f8d7eca98c

Comments

Nam Tran June 10, 2025, 5:55 p.m. UTC | #1
Please disregard the mistakenly sent patch named "[PATCH v5] test"
— it was sent in error and is not part of this series.

Apologies for the noise.

--
Nam Tran
Randy Dunlap June 10, 2025, 6:13 p.m. UTC | #2
On 6/10/25 10:43 AM, Nam Tran wrote:
> diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig
> index 222d943d826a..becee5c1d21c 100644
> --- a/drivers/leds/rgb/Kconfig
> +++ b/drivers/leds/rgb/Kconfig
> @@ -26,6 +26,19 @@ config LEDS_KTD202X
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called leds-ktd202x.
>  
> +config LEDS_LP5812
> +	tristate "LED support for Texas Instruments LP5812"
> +	depends on I2C
> +	help
> +	  If you say Y here you get support for TI LP5812 LED driver.
> +	  The LP5812 is a 4 × 3 matrix RGB LED driver with autonomous

	The '×' character does not display well (not at all) in menuconfig
	or nconfig. The graphical configs (gconfig, xconfig) can display it.
	I would change it to 4x3 (letter 'x') but it's up to you.


> +	  animation engine control.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called leds-lp5812.
> +
> +	  If unsure, say N.
Christophe JAILLET June 10, 2025, 9:07 p.m. UTC | #3
Le 10/06/2025 à 19:43, Nam Tran a écrit :
> The LP5812 is a 4×3 matrix RGB LED driver with an autonomous animation
> engine and time-cross-multiplexing (TCM) support for up to 12 LEDs or
> 4 RGB LEDs. Each LED can be configured through the related registers
> to realize vivid and fancy lighting effects.

Hi,

...

> +static struct lp5812_data *lp5812_of_populate_pdata(struct device *dev,
> +						    struct device_node *np,
> +						    struct lp5812_chip *chip)
> +{
> +	struct device_node *child;
> +	struct lp5812_data *pdata;
> +	struct lp5812_led_config *cfg;
> +	int num_channels, i = 0, ret;
> +
> +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return ERR_PTR(-ENOMEM);
> +
> +	num_channels = of_get_available_child_count(np);
> +	if (num_channels == 0) {
> +		dev_err(dev, "no LED channels\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL);
> +	if (!cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pdata->led_config = &cfg[0];
> +	pdata->num_channels = num_channels;
> +
> +	for_each_available_child_of_node(np, child) {

Maybe for_each_available_child_of_node_scoped() to slihtly simplify the 
code?

> +		ret = lp5812_parse_logical_led(child, cfg, i);
> +		if (ret) {
> +			of_node_put(child);
> +			return ERR_PTR(-EINVAL);
> +		}
> +		i++;
> +	}
> +
> +	of_property_read_string(np, "label", &pdata->label);
> +
> +	return pdata;
> +}

...

> +static ssize_t lp5812_aeu_slope_time(struct device *dev,
> +				     struct device_attribute *attr,
> +				     enum slope_time_num slope_chan,
> +				     const char *buf, size_t len)
> +{
> +	struct lp5812_led *led;
> +	struct lp5812_chip *chip;
> +	struct lp5812_led_config *led_cfg;
> +	const char *name = dev->platform_data;
> +	int val[LED_COLOR_ID_MAX];
> +	u8 chan_nr = 0;
> +	char *sub_str, *str = (char *)buf;
> +	int i, ret, aeu;
> +	union slope_time slope_time_val;
> +	u16 reg;
> +
> +	if (strcmp(name, LP5812_SC_LED) == 0)
> +		led = dev_to_lp5812_led(dev);
> +	else
> +		led = dev_to_lp5812_led_mc(dev);
> +
> +	chan_nr = led->chan_nr;
> +	chip = led->chip;
> +	led_cfg = &chip->pdata->led_config[chan_nr];
> +
> +	sub_str = strsep(&str, ":");
> +	if (!sub_str)
> +		return -EINVAL;
> +	if (kstrtoint(&sub_str[3], 0, &aeu))
> +		return -EINVAL;
> +
> +	pr_info("AEU = %d", aeu);
> +
> +	guard(mutex)(&chip->lock);
> +	for (i = 0; i < led_cfg->num_colors; i++) {
> +		sub_str = strsep(&str, " ");
> +		if (!sub_str)
> +			return -EINVAL;
> +		if (kstrtoint(sub_str, 0, &val[i]))
> +			return -EINVAL;
> +		if (val[i] < 0 || val[i] > 15)
> +			return -EINVAL;
> +
> +		reg = LP5812_AEU_SLOPE_TIME_ADDR(led_cfg->led_id[i], aeu, slope_chan);
> +
> +		/* get original value of slope time */
> +		ret = lp5812_read(chip, reg, &slope_time_val.time_val);
> +		if (ret)
> +			return ret;
> +
> +		/* Update new value for slope time*/
> +		if (slope_chan == LP5812_SLOPE_TIME_T1 || slope_chan == LP5812_SLOPE_TIME_T3)
> +			slope_time_val.s_time.first = val[i];
> +		if (slope_chan == LP5812_SLOPE_TIME_T2 || slope_chan == LP5812_SLOPE_TIME_T4)
> +			slope_time_val.s_time.second = val[i];
> +
> +		/* Save updated value to hardware */
> +		ret = lp5812_write(chip, reg, slope_time_val.time_val);

Should we do something if ret != 0?

> +	}
> +
> +	return len;
> +}

...

> +static struct attribute *lp5812_led_attrs[] = {
> +	&dev_attr_led_current.attr,
> +	&dev_attr_max_current.attr,
> +	&dev_attr_mode.attr,
> +	&dev_attr_activate.attr,
> +	&dev_attr_pwm_dimming_scale.attr,
> +	&dev_attr_pwm_phase_align.attr,
> +	&dev_attr_auto_time_pause_at_start.attr,
> +	&dev_attr_auto_time_pause_at_stop.attr,
> +	&dev_attr_auto_playback_eau_number.attr,
> +	&dev_attr_auto_playback_time.attr,
> +	&dev_attr_aeu_playback_time.attr,
> +	&dev_attr_aeu_pwm1.attr,
> +	&dev_attr_aeu_pwm2.attr,
> +	&dev_attr_aeu_pwm3.attr,
> +	&dev_attr_aeu_pwm4.attr,
> +	&dev_attr_aeu_pwm5.attr,
> +	&dev_attr_aeu_slop_time_t1.attr,
> +	&dev_attr_aeu_slop_time_t2.attr,
> +	&dev_attr_aeu_slop_time_t3.attr,
> +	&dev_attr_aeu_slop_time_t4.attr,
> +	&dev_attr_lod_lsd.attr,
> +	NULL,

Unneeded trailing comma after a terminator.

> +};
> +ATTRIBUTE_GROUPS(lp5812_led);

...

> +static int lp5812_init_led(struct lp5812_led *led, struct lp5812_chip *chip, int chan)
> +{
> +	struct lp5812_data *pdata = chip->pdata;
> +	struct device *dev = &chip->i2c_cl->dev;
> +	struct mc_subled *mc_led_info;
> +	struct led_classdev *led_cdev;
> +	char name[32];
> +	int i, ret = 0;
> +
> +	if (pdata->led_config[chan].name) {
> +		led->cdev.name = pdata->led_config[chan].name;
> +	} else {
> +		snprintf(name, sizeof(name), "%s:channel%d",
> +			 pdata->label ? : chip->i2c_cl->name, chan);
> +		led->cdev.name = name;

Is it fine below when 'name' is defined on the stack and is used...

> +	}
> +
> +	if (pdata->led_config[chan].is_sc_led == 0) {
> +		mc_led_info = devm_kcalloc(dev,
> +					   pdata->led_config[chan].num_colors,
> +					   sizeof(*mc_led_info), GFP_KERNEL);
> +		if (!mc_led_info)
> +			return -ENOMEM;
> +
> +		led_cdev = &led->mc_cdev.led_cdev;
> +		led_cdev->name = led->cdev.name;

...here?

> +		led_cdev->brightness_set_blocking = lp5812_set_mc_brightness;
> +		led->mc_cdev.num_colors = pdata->led_config[chan].num_colors;
> +		for (i = 0; i < led->mc_cdev.num_colors; i++) {
> +			mc_led_info[i].color_index =
> +				pdata->led_config[chan].color_id[i];
> +			mc_led_info[i].channel =
> +					pdata->led_config[chan].led_id[i];
> +		}
> +
> +		led->mc_cdev.subled_info = mc_led_info;
> +	} else {
> +		led->cdev.brightness_set_blocking = lp5812_set_brightness;
> +	}
> +
> +	led->cdev.groups = lp5812_led_groups;
> +	led->chan_nr = chan;
> +
> +	if (pdata->led_config[chan].is_sc_led) {
> +		ret = devm_led_classdev_register(dev, &led->cdev);
> +		if (ret == 0) {
> +			led->cdev.dev->platform_data = devm_kstrdup(dev, LP5812_SC_LED, GFP_KERNEL);
> +			if (!led->cdev.dev->platform_data)
> +				return -ENOMEM;
> +		}
> +	} else {
> +		ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev);
> +		if (ret == 0) {
> +			led->mc_cdev.led_cdev.dev->platform_data =
> +				devm_kstrdup(dev, LP5812_MC_LED, GFP_KERNEL);
> +			if (!led->mc_cdev.led_cdev.dev->platform_data)
> +				return -ENOMEM;
> +
> +			ret = sysfs_create_groups(&led->mc_cdev.led_cdev.dev->kobj,
> +						  lp5812_led_groups);
> +			if (ret)
> +				dev_err(dev, "sysfs_create_groups failed\n");
> +		}
> +	}
> +
> +	if (ret) {
> +		dev_err(dev, "led register err: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}

...

CJ
Krzysztof Kozlowski June 11, 2025, 6:59 a.m. UTC | #4
On 10/06/2025 19:43, Nam Tran wrote:
> This patch series adds support for the TI/National Semiconductor LP5812
> 4x3 matrix RGB LED driver. The driver supports features such as autonomous
> animation and time-cross-multiplexing (TCM) for dynamic LED effects.
> 
> Following feedback from both the LED and auxdisplay subsystem maintainers,
> the driver has been moved back to the LED subsystem, under drivers/leds/rgb/.
> This version integrates with the existing multicolor LED APIs, avoiding custom
> sysfs where standard LED interfaces are sufficient.
> 
> Signed-off-by: Nam Tran <trannamatk@gmail.com>
> ---
> Changes in v9:
> - Move driver back to drivers/leds/rgb/
> - Integrate with LED multicolor framework
> - Refactor and simplify custom sysfs handling
> - Extend Device Tree binding to support multi-led@ nodes using leds-class-multicolor.yaml

You need to provide reason why you dropped reviews.

Best regards,
Krzysztof
Krzysztof Kozlowski June 11, 2025, 8:24 a.m. UTC | #5
On Wed, Jun 11, 2025 at 12:43:15AM GMT, Nam Tran wrote:
> +patternProperties:
> +  "^led@[0-3]$":
> +    type: object
> +    $ref: common.yaml#
> +    unevaluatedProperties: false
> +
> +    properties:
> +      led-cur:
> +        $ref: /schemas/types.yaml#/definitions/uint8
> +        description: |
> +          LED current in 0.1 mA steps (e.g., 150 = 15.0 mA; 0 if not connected)
> +        minimum: 0
> +        maximum: 255
> +
> +      max-cur:
> +        $ref: /schemas/types.yaml#/definitions/uint8
> +        description: Maximum allowed current in 0.1 mA steps
> +
> +      reg:
> +        minimum: 0
> +        maximum: 3

Place properties according to DTS coding style.

> +
> +  '^multi-led@[4-7]$':
> +    type: object
> +    $ref: leds-class-multicolor.yaml#
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        minimum: 4
> +        maximum: 7
> +
> +      '#address-cells':

Don't mix quotes. Either ' or "

> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
> +    patternProperties:
> +      "^led@[4-9a-f]$":
> +        type: object
> +        $ref: common.yaml#
> +        unevaluatedProperties: false
> +
> +        properties:
> +          led-cur:
> +            $ref: /schemas/types.yaml#/definitions/uint8

No, use existing led common properties. Also observe the units - this is
not uint8 but a defined type for microamp, see property-units in
dtschema.

> +            description: |
> +              LED current in 0.1 mA steps (e.g., 150 = 15.0 mA; 0 if not connected)
> +            minimum: 0
> +            maximum: 255
> +
> +          max-cur:
> +            $ref: /schemas/types.yaml#/definitions/uint8

No, use existing led common properties. Same everywhere.

> +            description: Maximum allowed current in 0.1 mA steps
> +
> +          reg:
> +            minimum: 4
> +            maximum: 15
> +
> +        required:
> +          - reg
> +
> +required:
> +  - compatible
> +  - reg
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/leds/common.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        led-controller@1b {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            compatible = "ti,lp5812";
> +            reg = <0x1b>;
> +            vcc-supply = <&vdd_3v3_reg>;
> +
> +            led@0 {
> +              reg = <0x0>;


Messed/mixed indentation.

BTW, such significant binding change at v9, invalidting reviews and
rewriting the binding completely, is surprising.

Best regards,
Krzysztof
Nam Tran June 17, 2025, 3:40 p.m. UTC | #6
On Wed, 11 Jun 2025, Krzysztof Kozlowski wrote:

> On 10/06/2025 19:43, Nam Tran wrote:
> > This patch series adds support for the TI/National Semiconductor LP5812
> > 4x3 matrix RGB LED driver. The driver supports features such as autonomous
> > animation and time-cross-multiplexing (TCM) for dynamic LED effects.
> > 
> > Following feedback from both the LED and auxdisplay subsystem maintainers,
> > the driver has been moved back to the LED subsystem, under drivers/leds/rgb/.
> > This version integrates with the existing multicolor LED APIs, avoiding custom
> > sysfs where standard LED interfaces are sufficient.
> > 
> > Signed-off-by: Nam Tran <trannamatk@gmail.com>
> > ---
> > Changes in v9:
> > - Move driver back to drivers/leds/rgb/
> > - Integrate with LED multicolor framework
> > - Refactor and simplify custom sysfs handling
> > - Extend Device Tree binding to support multi-led@ nodes using leds-class-multicolor.yaml
> 
> You need to provide reason why you dropped reviews.

In v9, the Device Tree binding was restructured to integrate with the standard
leds-class-multicolor.yaml schema and support multi-led@ nodes with nested led@
subnodes. This change introduced a new patternProperties hierarchy and removed
the previous flat led@ layout used in the earlier versions.

Due to this substantial structural change — even though the intent and top-level
properties remained similar — I decided to drop the Reviewed-by tag to avoid
misrepresenting prior review coverage.

I will include this explanation in the changelog of the next version (v10).

Best regards,
Nam Tran
Nam Tran June 17, 2025, 4:29 p.m. UTC | #7
On Wed, 11 Jun 2025, Krzysztof Kozlowski wrote:

> > +patternProperties:
> > +  "^led@[0-3]$":
> > +    type: object
> > +    $ref: common.yaml#
> > +    unevaluatedProperties: false
> > +
> > +    properties:
> > +      led-cur:
> > +        $ref: /schemas/types.yaml#/definitions/uint8
> > +        description: |
> > +          LED current in 0.1 mA steps (e.g., 150 = 15.0 mA; 0 if not connected)
> > +        minimum: 0
> > +        maximum: 255
> > +
> > +      max-cur:
> > +        $ref: /schemas/types.yaml#/definitions/uint8
> > +        description: Maximum allowed current in 0.1 mA steps
> > +
> > +      reg:
> > +        minimum: 0
> > +        maximum: 3
> 
> Place properties according to DTS coding style.

Got it! I'll update the property order accordingly.

> > +  '^multi-led@[4-7]$':
> > +    type: object
> > +    $ref: leds-class-multicolor.yaml#
> > +    unevaluatedProperties: false
> > +
> > +    properties:
> > +      reg:
> > +        minimum: 4
> > +        maximum: 7
> > +
> > +      '#address-cells':
> 
> Don't mix quotes. Either ' or "

I'll use consistent ".

> > +        const: 1
> > +
> > +      '#size-cells':
> > +        const: 0
> > +
> > +    patternProperties:
> > +      "^led@[4-9a-f]$":
> > +        type: object
> > +        $ref: common.yaml#
> > +        unevaluatedProperties: false
> > +
> > +        properties:
> > +          led-cur:
> > +            $ref: /schemas/types.yaml#/definitions/uint8
> 
> No, use existing led common properties. Also observe the units - this is
> not uint8 but a defined type for microamp, see property-units in
> dtschema.
> 
> > +            description: |
> > +              LED current in 0.1 mA steps (e.g., 150 = 15.0 mA; 0 if not connected)
> > +            minimum: 0
> > +            maximum: 255
> > +
> > +          max-cur:
> > +            $ref: /schemas/types.yaml#/definitions/uint8
> 
> No, use existing led common properties. Same everywhere.

I'll replace max-cur with the standard led-max-microamp.
I'll remove led-cur as there's no equivalent LED common property to represent it.
The LED current can be configured runtime via the led_current sysfs.

> > +examples:
> > +  - |
> > +    #include <dt-bindings/leds/common.h>
> > +
> > +    i2c {
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +
> > +        led-controller@1b {
> > +            #address-cells = <1>;
> > +            #size-cells = <0>;
> > +            compatible = "ti,lp5812";
> > +            reg = <0x1b>;
> > +            vcc-supply = <&vdd_3v3_reg>;
> > +
> > +            led@0 {
> > +              reg = <0x0>;
> 
> 
> Messed/mixed indentation.

I'll fix it.

> BTW, such significant binding change at v9, invalidting reviews and
> rewriting the binding completely, is surprising.

Understood. I restructured the binding in v9 to align with leds-class-multicolor.yaml
and better represent the LP5812 hierarchy.
I'll make sure to highlight such major changes more clearly in future revisions.

Appreciate your time and feedback.

Best regards,
Nam Tran
Nam Tran June 17, 2025, 5:30 p.m. UTC | #8
On Tue, 10 Jun 2025, Christophe JAILLET wrote:

> > +static struct lp5812_data *lp5812_of_populate_pdata(struct device *dev,
> > +						    struct device_node *np,
> > +						    struct lp5812_chip *chip)
> > +{
> > +	struct device_node *child;
> > +	struct lp5812_data *pdata;
> > +	struct lp5812_led_config *cfg;
> > +	int num_channels, i = 0, ret;
> > +
> > +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
> > +	if (!pdata)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	num_channels = of_get_available_child_count(np);
> > +	if (num_channels == 0) {
> > +		dev_err(dev, "no LED channels\n");
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	cfg = devm_kcalloc(dev, num_channels, sizeof(*cfg), GFP_KERNEL);
> > +	if (!cfg)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	pdata->led_config = &cfg[0];
> > +	pdata->num_channels = num_channels;
> > +
> > +	for_each_available_child_of_node(np, child) {
> 
> Maybe for_each_available_child_of_node_scoped() to slihtly simplify the 
> code?

Thanks, I'll switch to for_each_available_child_of_node_scoped().

> > +static ssize_t lp5812_aeu_slope_time(struct device *dev,
> > +				     struct device_attribute *attr,
> > +				     enum slope_time_num slope_chan,
> > +				     const char *buf, size_t len)
> > +{
> > +	struct lp5812_led *led;
> > +	struct lp5812_chip *chip;
> > +	struct lp5812_led_config *led_cfg;
> > +	const char *name = dev->platform_data;
> > +	int val[LED_COLOR_ID_MAX];
> > +	u8 chan_nr = 0;
> > +	char *sub_str, *str = (char *)buf;
> > +	int i, ret, aeu;
> > +	union slope_time slope_time_val;
> > +	u16 reg;
> > +
> > +	if (strcmp(name, LP5812_SC_LED) == 0)
> > +		led = dev_to_lp5812_led(dev);
> > +	else
> > +		led = dev_to_lp5812_led_mc(dev);
> > +
> > +	chan_nr = led->chan_nr;
> > +	chip = led->chip;
> > +	led_cfg = &chip->pdata->led_config[chan_nr];
> > +
> > +	sub_str = strsep(&str, ":");
> > +	if (!sub_str)
> > +		return -EINVAL;
> > +	if (kstrtoint(&sub_str[3], 0, &aeu))
> > +		return -EINVAL;
> > +
> > +	pr_info("AEU = %d", aeu);
> > +
> > +	guard(mutex)(&chip->lock);
> > +	for (i = 0; i < led_cfg->num_colors; i++) {
> > +		sub_str = strsep(&str, " ");
> > +		if (!sub_str)
> > +			return -EINVAL;
> > +		if (kstrtoint(sub_str, 0, &val[i]))
> > +			return -EINVAL;
> > +		if (val[i] < 0 || val[i] > 15)
> > +			return -EINVAL;
> > +
> > +		reg = LP5812_AEU_SLOPE_TIME_ADDR(led_cfg->led_id[i], aeu, slope_chan);
> > +
> > +		/* get original value of slope time */
> > +		ret = lp5812_read(chip, reg, &slope_time_val.time_val);
> > +		if (ret)
> > +			return ret;
> > +
> > +		/* Update new value for slope time*/
> > +		if (slope_chan == LP5812_SLOPE_TIME_T1 || slope_chan == LP5812_SLOPE_TIME_T3)
> > +			slope_time_val.s_time.first = val[i];
> > +		if (slope_chan == LP5812_SLOPE_TIME_T2 || slope_chan == LP5812_SLOPE_TIME_T4)
> > +			slope_time_val.s_time.second = val[i];
> > +
> > +		/* Save updated value to hardware */
> > +		ret = lp5812_write(chip, reg, slope_time_val.time_val);
> 
> Should we do something if ret != 0?

Yes. I'll add a return check to handle possible write errors.

> > +static struct attribute *lp5812_led_attrs[] = {
> > +	&dev_attr_led_current.attr,
> > +	&dev_attr_max_current.attr,
> > +	&dev_attr_mode.attr,
> > +	&dev_attr_activate.attr,
> > +	&dev_attr_pwm_dimming_scale.attr,
> > +	&dev_attr_pwm_phase_align.attr,
> > +	&dev_attr_auto_time_pause_at_start.attr,
> > +	&dev_attr_auto_time_pause_at_stop.attr,
> > +	&dev_attr_auto_playback_eau_number.attr,
> > +	&dev_attr_auto_playback_time.attr,
> > +	&dev_attr_aeu_playback_time.attr,
> > +	&dev_attr_aeu_pwm1.attr,
> > +	&dev_attr_aeu_pwm2.attr,
> > +	&dev_attr_aeu_pwm3.attr,
> > +	&dev_attr_aeu_pwm4.attr,
> > +	&dev_attr_aeu_pwm5.attr,
> > +	&dev_attr_aeu_slop_time_t1.attr,
> > +	&dev_attr_aeu_slop_time_t2.attr,
> > +	&dev_attr_aeu_slop_time_t3.attr,
> > +	&dev_attr_aeu_slop_time_t4.attr,
> > +	&dev_attr_lod_lsd.attr,
> > +	NULL,
> 
> Unneeded trailing comma after a terminator.

I'll remove it.

> > +static int lp5812_init_led(struct lp5812_led *led, struct lp5812_chip *chip, int chan)
> > +{
> > +	struct lp5812_data *pdata = chip->pdata;
> > +	struct device *dev = &chip->i2c_cl->dev;
> > +	struct mc_subled *mc_led_info;
> > +	struct led_classdev *led_cdev;
> > +	char name[32];
> > +	int i, ret = 0;
> > +
> > +	if (pdata->led_config[chan].name) {
> > +		led->cdev.name = pdata->led_config[chan].name;
> > +	} else {
> > +		snprintf(name, sizeof(name), "%s:channel%d",
> > +			 pdata->label ? : chip->i2c_cl->name, chan);
> > +		led->cdev.name = name;
> 
> Is it fine below when 'name' is defined on the stack and is used...
> 
> > +	}
> > +
> > +	if (pdata->led_config[chan].is_sc_led == 0) {
> > +		mc_led_info = devm_kcalloc(dev,
> > +					   pdata->led_config[chan].num_colors,
> > +					   sizeof(*mc_led_info), GFP_KERNEL);
> > +		if (!mc_led_info)
> > +			return -ENOMEM;
> > +
> > +		led_cdev = &led->mc_cdev.led_cdev;
> > +		led_cdev->name = led->cdev.name;
> 
> ...here?

You're right, name was stack-allocated and unsafe to use after the function returns.
I'll replace it with a devm_kasprintf() allocation.

Appreciate your time and feedback.

Best regards,
Nam Tran