Skip to content

Add support for multi-color LEDs #92473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/led/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ zephyr_library_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
zephyr_library_sources_ifdef(CONFIG_IS31FL3194 is31fl3194.c)
zephyr_library_sources_ifdef(CONFIG_IS31FL3216A is31fl3216a.c)
zephyr_library_sources_ifdef(CONFIG_IS31FL3733 is31fl3733.c)
zephyr_library_sources_ifdef(CONFIG_LEDS_GROUP_MULTICOLOR leds_group_multicolor.c)
zephyr_library_sources_ifdef(CONFIG_LED_AXP192_AXP2101 led_axp192.c)
zephyr_library_sources_ifdef(CONFIG_LED_DAC led_dac.c)
zephyr_library_sources_ifdef(CONFIG_LED_GPIO led_gpio.c)
Expand Down
1 change: 1 addition & 0 deletions drivers/led/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ source "drivers/led/Kconfig.ht16k33"
source "drivers/led/Kconfig.is31fl3194"
source "drivers/led/Kconfig.is31fl3216a"
source "drivers/led/Kconfig.is31fl3733"
source "drivers/led/Kconfig.leds-group-multicolor"
source "drivers/led/Kconfig.lp3943"
source "drivers/led/Kconfig.lp50xx"
source "drivers/led/Kconfig.lp5562"
Expand Down
12 changes: 12 additions & 0 deletions drivers/led/Kconfig.leds-group-multicolor
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2025 Seagate Technology LLC
# SPDX-License-Identifier: Apache-2.0

config LEDS_GROUP_MULTICOLOR
bool "LEDs group multi-color driver"
default y
depends on DT_HAS_LEDS_GROUP_MULTICOLOR_ENABLED
Copy link
Preview

Copilot AI Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multi-color LED driver relies on the underlying PWM LED driver (LED_PWM). Add depends on LED_PWM to the Kconfig so that the necessary driver is enabled when multi-color support is selected.

Suggested change
depends on DT_HAS_LEDS_GROUP_MULTICOLOR_ENABLED
depends on DT_HAS_LEDS_GROUP_MULTICOLOR_ENABLED && LED_PWM

Copilot uses AI. Check for mistakes.

help
Enable this driver to supports multi-color LEDs built with several
monochromatic LEDs.

See the leds-group-multicolor DT binding for details.
3 changes: 2 additions & 1 deletion drivers/led/Kconfig.pwm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
config LED_PWM
bool "PWM LED driver"
default y
depends on PWM && DT_HAS_PWM_LEDS_ENABLED
depends on DT_HAS_PWM_LEDS_ENABLED
select PWM
Copy link
Preview

Copilot AI Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using select PWM is discouraged because it implicitly enables PWM and can lead to conflicting configurations. Prefer depends on PWM to correctly express the dependency.

Suggested change
select PWM
depends on PWM

Copilot uses AI. Check for mistakes.

help
Enable driver for PWM LEDs.
94 changes: 94 additions & 0 deletions drivers/led/leds_group_multicolor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2025 Seagate Technology LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT leds_group_multicolor

/**
* @file
* @brief Driver for multi-color LED built from monochromatic LEDs.
*/

#include <zephyr/drivers/led.h>
#include <zephyr/device.h>
#include <zephyr/kernel.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(leds_group_multicolor, CONFIG_LED_LOG_LEVEL);

struct leds_group_multicolor_config {
uint8_t num_leds;
const struct led_dt_spec *led;
};

static int leds_group_multicolor_set_color(const struct device *dev, uint32_t led,
uint8_t num_colors, const uint8_t *color)
{
const struct leds_group_multicolor_config *config = dev->config;

if (led != 0) {
return -EINVAL;
}
if (num_colors != config->num_leds) {
return -EINVAL;
}

for (uint8_t i = 0; i < num_colors; i++) {
int err;

err = led_set_brightness_dt(&config->led[i], color[i]);
if (err) {
return err;
}
}

return 0;
}

static int leds_group_multicolor_init(const struct device *dev)
{
const struct leds_group_multicolor_config *config = dev->config;

for (uint8_t i = 0; i < config->num_leds; i++) {
const struct led_dt_spec *led = &config->led[i];

if (!led_is_ready_dt(led)) {
LOG_ERR("%s: LED device %s is not ready", dev->name, led->dev->name);
return -ENODEV;
}
}

return 0;
}

static DEVICE_API(led, leds_group_multicolor_api) = {
.set_color = leds_group_multicolor_set_color,
};

#define LED_DT_SPEC_GET_BY_PHANDLE_IDX(node_id, prop, idx) \
LED_DT_SPEC_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx))

#define LEDS_GROUP_MULTICOLOR_DEVICE(inst) \
\
BUILD_ASSERT(DT_INST_PROP_LEN(inst, leds) > 0, \
"at least one LED phandle must be present"); \
\
static const struct led_dt_spec led_group_multicolor_##inst[] = { \
DT_INST_FOREACH_PROP_ELEM_SEP( \
inst, leds, LED_DT_SPEC_GET_BY_PHANDLE_IDX, (,)) \
}; \
\
static const struct leds_group_multicolor_config \
leds_group_multicolor_config_##inst = { \
.num_leds = ARRAY_SIZE(led_group_multicolor_##inst), \
.led = led_group_multicolor_##inst, \
}; \
\
DEVICE_DT_INST_DEFINE(inst, &leds_group_multicolor_init, NULL, \
NULL, &leds_group_multicolor_config_##inst, \
POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
&leds_group_multicolor_api);

DT_INST_FOREACH_STATUS_OKAY(LEDS_GROUP_MULTICOLOR_DEVICE)
12 changes: 6 additions & 6 deletions dts/bindings/led/gpio-leds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ compatible: "gpio-leds"

child-binding:
description: GPIO LED child node

include:
- name: led-node.yaml
property-allowlist:
- label

properties:
gpios:
type: phandle-array
required: true
label:
type: string
description: |
Human readable string describing the LED. It can be used by an
application to identify this LED or to retrieve its number/index
(i.e. child node number) on the parent device.
19 changes: 3 additions & 16 deletions dts/bindings/led/led-controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

child-binding:
description: LED child node

include: led-node.yaml

properties:
label:
type: string
description: Human readable string describing the LED
index:
type: int
description: |
Expand All @@ -17,16 +17,3 @@ child-binding:
controller. For example, this allows to handle boards where the
LEDs in an array/strip are not wired following the LED order of
the controller.
color-mapping:
type: array
description: |
Channel to color mapping of a multicolor LED. If a LED supports
several colors, then the color-mapping property can be used to
describe how the hardware channels and the colors are mapped.

For example the channel to color mapping of RGB LEDs would be

color-mapping =
<LED_COLOR_ID_RED>,
<LED_COLOR_ID_GREEN>,
<LED_COLOR_ID_BLUE>;
25 changes: 25 additions & 0 deletions dts/bindings/led/led-node.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2025 Seagate Technology LLC
# SPDX-License-Identifier: Apache-2.0

# Common fields for LED nodes

properties:
label:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks redundant :-o

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean redundant with the node name ?

There is a discussion here: LED.#92473 (comment). Feel free to join.

type: string
description: |
Human readable string describing the LED. It can be used by an
application to identify this LED or to retrieve its number/index
(i.e. child node number) on the parent device.
color-mapping:
type: array
description: |
Channel to color mapping of a multicolor LED. If a LED supports
several colors, then the color-mapping property can be used to
describe how the hardware channels and the colors are mapped.

For example the channel to color mapping of RGB LEDs would be

color-mapping =
<LED_COLOR_ID_RED>,
<LED_COLOR_ID_GREEN>,
<LED_COLOR_ID_BLUE>;
49 changes: 49 additions & 0 deletions dts/bindings/led/leds-group-multicolor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright (c) 2025 Seagate Technology LLC
# SPDX-License-Identifier: Apache-2.0

title: Combines several monochromatic LEDs into one multi-color LED.

description: |
Here is an example that defines an RGB LED with a group of monochromatic
PWM LEDs. Note that the pwms property definition handle depends on the PWM
controller model. In this example, an STM32 PWM controller is assumed.

#include <zephyr/dt-bindings/led/led.h>

/ {
monochromatic-leds {
compatible = "pwm-leds";

red_pwm_led: led-0 {
pwms = <&pwm1 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
};

green_pwm_led: led-1 {
pwms = <&pwm2 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
};

blue_pwm_led: led-2 {
pwms = <&pwm3 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
};
};

rgb-led {
compatible = "leds-group-multicolor";

leds = <&red_pwm_led>, <&green_pwm_led>, <&blue_pwm_led>;
color-mapping = <LED_COLOR_ID_RED>,
<LED_COLOR_ID_GREEN>,
<LED_COLOR_ID_BLUE>;
};
};

compatible: "leds-group-multicolor"

include: led-node.yaml

properties:
leds:
required: true
type: phandles
description: |
References to monochromatic LED nodes.
13 changes: 6 additions & 7 deletions dts/bindings/led/pwm-leds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ compatible: "pwm-leds"

child-binding:
description: PWM LED child node

include:
- name: led-node.yaml
property-allowlist:
- label

properties:
pwms:
required: true
Expand All @@ -22,10 +28,3 @@ child-binding:
brigtness granularity) and lesser than 50 milliseconds (average visual
Copy link
Preview

Copilot AI Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a typo in brigtness; it should be spelled brightness.

Suggested change
brigtness granularity) and lesser than 50 milliseconds (average visual
brightness granularity) and lesser than 50 milliseconds (average visual

Copilot uses AI. Check for mistakes.

persistence time of the human eye). Typical values for the PWM period
are 10 or 20 milliseconds.

label:
type: string
description: |
Human readable string describing the LED. It can be used by an
application to identify this LED or to retrieve its number/index
(i.e. child node number) on the parent device.
2 changes: 0 additions & 2 deletions samples/drivers/led/pwm/prj.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
CONFIG_LOG=y

CONFIG_PWM=y

CONFIG_LED=y

CONFIG_LOG_MODE_IMMEDIATE=y
8 changes: 8 additions & 0 deletions tests/drivers/build_all/led/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@
test_pwm_led0: test_pwm_led_0 {
pwms = <&test_pwm 0 0 0>;
};
test_pwm_led1: test_pwm_led_1 {
pwms = <&test_pwm 1 0 0>;
};
};

multi-led {
compatible = "leds-group-multicolor";
leds = <&test_pwm_led0>, <&test_pwm_led1>;
};
};
};
1 change: 0 additions & 1 deletion tests/drivers/build_all/led/prj.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
CONFIG_TEST=y
CONFIG_TEST_USERSPACE=y
CONFIG_LED=y
CONFIG_PWM=y