Skip to content

Commit 771898d

Browse files
author
Jeppe Odgaard
committed
drivers: led: add led_dac
Add LED driver support for DAC based LED drivers. Signed-off-by: Jeppe Odgaard <[email protected]>
1 parent d8cd8dd commit 771898d

File tree

8 files changed

+277
-0
lines changed

8 files changed

+277
-0
lines changed

drivers/led/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ zephyr_library()
66

77
zephyr_library_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
88
zephyr_library_sources_ifdef(CONFIG_IS31FL3216A is31fl3216a.c)
9+
zephyr_library_sources_ifdef(CONFIG_LED_DAC led_dac.c)
910
zephyr_library_sources_ifdef(CONFIG_LED_GPIO led_gpio.c)
1011
zephyr_library_sources_ifdef(CONFIG_LED_NPM1300 led_npm1300.c)
1112
zephyr_library_sources_ifdef(CONFIG_LED_PWM led_pwm.c)

drivers/led/Kconfig

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ config LED_SHELL
2626
help
2727
Enable LED shell for testing.
2828

29+
source "drivers/led/Kconfig.dac"
2930
source "drivers/led/Kconfig.gpio"
3031
source "drivers/led/Kconfig.ht16k33"
3132
source "drivers/led/Kconfig.is31fl3216a"

drivers/led/Kconfig.dac

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 Prevas A/S
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config LED_DAC
5+
bool "DAC LED driver"
6+
default y
7+
depends on DT_HAS_DAC_LEDS_ENABLED
8+
select DAC
9+
help
10+
Enable DAC LED driver.

drivers/led/led_dac.c

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright (c) 2025 Prevas A/S
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include <errno.h>
7+
#include <stdint.h>
8+
#include <zephyr/device.h>
9+
#include <zephyr/drivers/dac.h>
10+
#include <zephyr/drivers/led.h>
11+
#include <zephyr/sys/math_extras.h>
12+
13+
struct led_dac_leds {
14+
const struct device *dac;
15+
struct dac_channel_cfg chan_cfg;
16+
uint32_t dac_max;
17+
uint32_t dac_min;
18+
};
19+
20+
struct led_dac_config {
21+
const struct led_dac_leds *leds;
22+
uint8_t num_leds;
23+
};
24+
25+
static int led_dac_set_raw(const struct device *dev, uint32_t led, uint32_t value)
26+
{
27+
const struct led_dac_config *config = dev->config;
28+
29+
return dac_write_value(config->leds[led].dac, config->leds[led].chan_cfg.channel_id, value);
30+
}
31+
32+
static int led_dac_set_brightness(const struct device *dev, uint32_t led, uint8_t pct)
33+
{
34+
const struct led_dac_config *config = dev->config;
35+
uint32_t value;
36+
37+
if (led >= config->num_leds) {
38+
return -EINVAL;
39+
}
40+
41+
value = (uint64_t)(config->leds[led].dac_max - config->leds[led].dac_min) * pct / 100;
42+
value += config->leds[led].dac_min;
43+
44+
return led_dac_set_raw(dev, led, value);
45+
}
46+
47+
static inline int led_dac_on(const struct device *dev, uint32_t led)
48+
{
49+
const struct led_dac_config *config = dev->config;
50+
51+
if (led >= config->num_leds) {
52+
return -EINVAL;
53+
}
54+
55+
return led_dac_set_raw(dev, led, config->leds[led].dac_max);
56+
}
57+
58+
static inline int led_dac_off(const struct device *dev, uint32_t led)
59+
{
60+
const struct led_dac_config *config = dev->config;
61+
62+
if (led >= config->num_leds) {
63+
return -EINVAL;
64+
}
65+
66+
return led_dac_set_raw(dev, led, 0);
67+
}
68+
69+
static DEVICE_API(led, led_dac_api) = {
70+
.on = led_dac_on,
71+
.off = led_dac_off,
72+
.set_brightness = led_dac_set_brightness,
73+
};
74+
75+
static int led_dac_init(const struct device *dev)
76+
{
77+
const struct led_dac_config *config = dev->config;
78+
int ret;
79+
80+
for (uint8_t i = 0; i < config->num_leds; ++i) {
81+
const struct led_dac_leds *led = &config->leds[i];
82+
83+
if (!device_is_ready(led->dac)) {
84+
return -ENODEV;
85+
}
86+
87+
ret = dac_channel_setup(led->dac, &led->chan_cfg);
88+
if (ret != 0) {
89+
return ret;
90+
}
91+
}
92+
93+
return 0;
94+
}
95+
96+
#define LED_DAC_MAX(n) (BIT(DT_PROP(n, resolution)) - 1)
97+
98+
#define LED_DAC_DT_GET(n) \
99+
{ \
100+
.dac = DEVICE_DT_GET(DT_PHANDLE(n, dac)), \
101+
.chan_cfg = {.channel_id = DT_PROP(n, channel), \
102+
.resolution = DT_PROP(n, resolution), \
103+
.buffered = false, \
104+
.internal = false}, \
105+
.dac_max = LED_DAC_MAX(n), .dac_min = LED_DAC_MIN(n) \
106+
}
107+
108+
#define LED_DAC_DEFINE(n, compat) \
109+
static const struct led_dac_leds led_##compat##_##n[] = { \
110+
DT_INST_FOREACH_CHILD_SEP(n, LED_DAC_DT_GET, (, ))}; \
111+
\
112+
static const struct led_dac_config led_##compat##_config_##n = { \
113+
.leds = led_##compat##_##n, \
114+
.num_leds = ARRAY_SIZE(led_##compat##_##n), \
115+
}; \
116+
\
117+
DEVICE_DT_INST_DEFINE(n, &led_dac_init, NULL, NULL, &led_##compat##_config_##n, \
118+
POST_KERNEL, CONFIG_LED_INIT_PRIORITY, &led_dac_api);
119+
120+
#define DT_DRV_COMPAT dac_leds
121+
#define LED_DAC_MIN(n) \
122+
((uint64_t)LED_DAC_MAX(n) * \
123+
((uint64_t)DT_PROP(n, forward_voltage_mv) + DT_PROP(n, resistor_forward_voltage_mv)) / \
124+
DT_PROP(n, max_voltage_mv))
125+
DT_INST_FOREACH_STATUS_OKAY_VARGS(LED_DAC_DEFINE, DT_DRV_COMPAT);
126+
127+
#undef DT_DRV_COMPAT
128+
#define DT_DRV_COMPAT dac_leds_vccs
129+
#undef LED_DAC_MIN
130+
#define LED_DAC_MIN(n) 0
131+
132+
DT_INST_FOREACH_STATUS_OKAY_VARGS(LED_DAC_DEFINE, DT_DRV_COMPAT);

dts/bindings/led/dac-leds-common.yaml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) 2025, Prevas A/S
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
Common fields for DAC LEDs
6+
7+
child-binding:
8+
description: Common DAC LED child node
9+
properties:
10+
dac:
11+
type: phandle
12+
required: true
13+
description: |
14+
Property containing phandle to DAC e.g. &dac.
15+
channel:
16+
type: int
17+
required: true
18+
description: |
19+
The DAC channel.
20+
resolution:
21+
type: int
22+
required: true
23+
description: |
24+
The DAC resolution to use.

dts/bindings/led/dac-leds-vccs.yaml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright (c) 2018, Linaro Limited
2+
# Copyright (c) 2025, Prevas A/S
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
description: |
6+
Group of DAC-controlled LEDs using voltage control current source (vccs) circuit.
7+
8+
Each LED is defined in a child node of the dac-leds node.
9+
10+
Here is an example which defines an LED in the node /leds:
11+
12+
/ {
13+
leds {
14+
compatible = "dac-leds-vccs";
15+
led_0 {
16+
dac = <&dac1>;
17+
channel = <0>;
18+
resolution = <12>;
19+
};
20+
};
21+
};
22+
23+
Above:
24+
25+
- led_0 uses dac1 channel 0 with 12 bit resolution.
26+
27+
compatible: "dac-leds-vccs"
28+
29+
include: ["dac-leds-common.yaml"]

dts/bindings/led/dac-leds.yaml

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright (c) 2018, Linaro Limited
2+
# Copyright (c) 2025, Prevas A/S
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
description: |
6+
Group of DAC-controlled LEDs.
7+
8+
Each LED is defined in a child node of the dac-leds node.
9+
10+
Here is an example which defines an LED in the node /leds:
11+
12+
/ {
13+
leds {
14+
compatible = "dac-leds";
15+
led_0 {
16+
dac = <&dac1>;
17+
channel = <0>;
18+
resolution = <12>;
19+
max-voltage-mv = <3300>;
20+
forward-voltage-mv = <2100>;
21+
resistor-forward-voltage-mv = <(5 * 120)>;
22+
};
23+
};
24+
};
25+
26+
Above:
27+
28+
- led_0 uses dac1 channel 0 with 12 bit resolution.
29+
30+
compatible: "dac-leds"
31+
32+
include: ["dac-leds-common.yaml"]
33+
34+
child-binding:
35+
description: DAC LED child node
36+
properties:
37+
max-voltage-mv:
38+
type: int
39+
required: true
40+
description: |
41+
Maximum voltage in millivolt (the DAC's maximum output voltage).
42+
forward-voltage-mv:
43+
type: int
44+
required: true
45+
description: |
46+
LED forward voltage in millivolt.
47+
resistor-forward-voltage-mv:
48+
type: int
49+
required: true
50+
description: |
51+
Current limiting resistor voltage in millivolt at LED forward voltage.
52+
LED forward current [mA] * resistor resistance [Ohms]

tests/drivers/build_all/led/app.overlay

+28
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@
1515
#address-cells = <1>;
1616
#size-cells = <1>;
1717

18+
test_dac: dac@dac0dac0 {
19+
compatible = "vnd,dac";
20+
reg = <0xdac0dac0 0x1000>;
21+
status = "okay";
22+
#io-channel-cells = <1>;
23+
};
24+
25+
test_dac_leds {
26+
compatible = "dac-leds";
27+
test_dac_led0: test_dac_led_0 {
28+
dac = <&test_dac>;
29+
channel = <0>;
30+
resolution = <16>;
31+
max-voltage-mv = <5000>;
32+
forward-voltage-mv = <1800>;
33+
resistor-forward-voltage-mv = <(2 * 150)>;
34+
};
35+
};
36+
37+
test_dac_leds_vccs {
38+
compatible = "dac-leds-vccs";
39+
test_dac_vccs_led0: test_dac_vccs_led0 {
40+
dac = <&test_dac>;
41+
channel = <0>;
42+
resolution = <16>;
43+
};
44+
};
45+
1846
test_gpio: gpio@deadbeef {
1947
compatible = "vnd,gpio";
2048
gpio-controller;

0 commit comments

Comments
 (0)