* [PATCH 0/2] Add Support for LTC3220 18 Channel LED Driver
@ 2026-01-06 5:27 Edelweise Escala
2026-01-06 5:27 ` [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel " Edelweise Escala
2026-01-06 5:27 ` [PATCH 2/2] leds: ltc3220: add driver Edelweise Escala
0 siblings, 2 replies; 11+ messages in thread
From: Edelweise Escala @ 2026-01-06 5:27 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-leds, devicetree, linux-kernel, Edelweise Escala
The LTC3220/LTC3220-1 is a multi-display LED driver, which contains a high-efficiency,
low-noise charge pump to provide power to up to 18 LED current sources.
The LEDs are individually configurable to 64-step linear brightness control,
blinking and gradation control via 2-wire I2C interface.
The blinking and gradation configuration is shared across all LED.
LTC3220 has a quick write function which allows changing the brightness
on all LEDS simultaneously when the brightness is changed on led 1.
For this we made quick write a device property which user can set on the device tree.
We would like to know if this approach is alright?
Another way we might want to know is, is it alright to just make a virtual led for the quick write function.
Changing brightness on the virtual led will change the brightness for all.
Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
---
Edelweise Escala (2):
dt-bindings: leds: Document LTC3220 18 channel LED Driver
leds: ltc3220: add driver
.../devicetree/bindings/leds/leds-ltc3220.yaml | 131 ++++++
MAINTAINERS | 8 +
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-ltc3220.c | 465 +++++++++++++++++++++
5 files changed, 615 insertions(+)
---
base-commit: 8856d7fe1758937ac528770f552ec58c388c255b
change-id: 20260106-ltc3220-driver-f9ab6cc9d1e4
Best regards,
--
Edelweise Escala <edelweise.escala@analog.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel LED Driver
2026-01-06 5:27 [PATCH 0/2] Add Support for LTC3220 18 Channel LED Driver Edelweise Escala
@ 2026-01-06 5:27 ` Edelweise Escala
2026-01-06 8:12 ` Krzysztof Kozlowski
2026-01-06 5:27 ` [PATCH 2/2] leds: ltc3220: add driver Edelweise Escala
1 sibling, 1 reply; 11+ messages in thread
From: Edelweise Escala @ 2026-01-06 5:27 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-leds, devicetree, linux-kernel, Edelweise Escala
Add dt-binding for ltc3220. LTC3220 18 Channel LED driver
Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
---
.../devicetree/bindings/leds/leds-ltc3220.yaml | 131 +++++++++++++++++++++
MAINTAINERS | 7 ++
2 files changed, 138 insertions(+)
diff --git a/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml b/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
new file mode 100644
index 000000000000..33285a7ac7a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/leds-ltc3220.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices LTC3220 LED Drivers
+
+maintainers:
+ - Edelweise Escala <edelweise.escala@analog.com>
+
+description: Bindings for the Analog Devices LTC3220 18 channel LED Drivers.
+
+ For more product information please see the link below
+ https://www.analog.com/en/products/ltc3220.html
+
+properties:
+ compatible:
+ enum:
+ - adi,ltc3220
+ - adi,ltc3220-1
+
+ reg:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO attached to the chip's reset pin
+
+ adi,force-cpo-level:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: Forces the Charge Pump Output to a specified multiplier.
+ enum:
+ - "0" # Auto(default) - Automatically selects optimal charge pump mode
+ - "1.5"
+ - "2"
+ - "1"
+ default: "0"
+
+ adi,quick-write:
+ type: boolean
+ description: If present, LED 1 output becomes a master control that
+ simultaneously updates all 18 LED outputs using the hardware's quick-write
+ mode. When enabled, led@1 must be defined in the device tree to provide
+ the control interface, even if no physical LED is connected to the D1
+ output pin. When disabled or not present, LED 1 operates as a normal
+ independent LED output.
+
+patternProperties:
+ "^led@([1-9]|1[0-8])$":
+ type: object
+ $ref: /schemas/leds/common.yaml#
+ unevaluatedProperties: false
+ properties:
+ reg:
+ description: Output channel for the LED (1-18 maps to LED outputs D1-D18).
+ minimum: 1
+ maximum: 18
+
+ required:
+ - reg
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/leds/common.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led-controller@1c {
+ compatible = "adi,ltc3220";
+ reg = <0x1c>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reset-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
+ adi,force-cpo-level = "0";
+ adi,quick-write;
+
+ led@1 {
+ reg = <1>;
+ function = LED_FUNCTION_INDICATOR;
+ function-enumerator = <1>;
+ };
+
+ led@2 {
+ reg = <2>;
+ function = LED_FUNCTION_INDICATOR;
+ function-enumerator = <2>;
+ };
+
+ led@3 {
+ reg = <3>;
+ function = LED_FUNCTION_INDICATOR;
+ function-enumerator = <3>;
+ };
+
+ led@4 {
+ reg = <4>;
+ function = LED_FUNCTION_INDICATOR;
+ function-enumerator = <4>;
+ };
+
+ led@5 {
+ reg = <5>;
+ function = LED_FUNCTION_INDICATOR;
+ function-enumerator = <5>;
+ };
+
+ led@6 {
+ reg = <6>;
+ function = LED_FUNCTION_INDICATOR;
+ function-enumerator = <6>;
+ };
+ };
+ };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 327d74ca7ecb..d640c35d1f93 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14955,6 +14955,13 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
F: drivers/iio/temperature/ltc2983.c
+LTC3220 LED DRIVER
+M: Edelweise Escala <edelweise.escala@analog.com>
+L: linux-leds@vger.kernel.org
+S: Maintained
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
+
LTC4282 HARDWARE MONITOR DRIVER
M: Nuno Sa <nuno.sa@analog.com>
L: linux-hwmon@vger.kernel.org
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/2] leds: ltc3220: add driver
2026-01-06 5:27 [PATCH 0/2] Add Support for LTC3220 18 Channel LED Driver Edelweise Escala
2026-01-06 5:27 ` [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel " Edelweise Escala
@ 2026-01-06 5:27 ` Edelweise Escala
2026-01-06 8:14 ` Krzysztof Kozlowski
2026-01-06 12:03 ` Jacek Anaszewski
1 sibling, 2 replies; 11+ messages in thread
From: Edelweise Escala @ 2026-01-06 5:27 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-leds, devicetree, linux-kernel, Edelweise Escala
Add driver for ltc3220. LTC3220 18 Channel LED Driver
Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
---
MAINTAINERS | 1 +
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-ltc3220.c | 465 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 477 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d640c35d1f93..fda0d2963c4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14961,6 +14961,7 @@ L: linux-leds@vger.kernel.org
S: Maintained
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
+F: drivers/leds/leds-ltc3220.c
LTC4282 HARDWARE MONITOR DRIVER
M: Nuno Sa <nuno.sa@analog.com>
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 597d7a79c988..a1c34b2deded 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -1001,6 +1001,16 @@ config LEDS_ST1202
Say Y to enable support for LEDs connected to LED1202
LED driver chips accessed via the I2C bus.
+config LEDS_LTC3220
+ tristate "LED Driver for LTC3220/LTC3220-1"
+ depends on I2C && LEDS_CLASS
+ help
+ If you have an 18-Channel LED Driver connected to LTC3220, or LTC3220-1
+ say Y here to enable this driver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ltc3220.
+
config LEDS_TPS6105X
tristate "LED support for TI TPS6105X"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8fdb45d5b439..5301568d9e00 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
obj-$(CONFIG_LEDS_LP8864) += leds-lp8864.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
+obj-$(CONFIG_LEDS_LTC3220) += leds-ltc3220.o
obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o
obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o
obj-$(CONFIG_LEDS_MAX77705) += leds-max77705.o
diff --git a/drivers/leds/leds-ltc3220.c b/drivers/leds/leds-ltc3220.c
new file mode 100644
index 000000000000..588074464165
--- /dev/null
+++ b/drivers/leds/leds-ltc3220.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LTC3220 18-Channel LED Driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ *
+ * Author: Edelweise Escala <edelweise.escala@analog.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+/* LTC3220 Registers */
+#define LTC3220_COMMAND 0x00
+#define LTC3220_ULED(x) (0x01 + (x))
+#define LTC3220_GRAD_BLINK 0x13
+
+#define LTC3220_GRAD_COUNT_UP BIT(0)
+#define LTC3220_COMMAND_QUICK_WRITE BIT(0)
+#define LTC3220_COMMAND_SHUTDOWN BIT(3)
+
+#define LTC3220_LED_CURRENT_MASK GENMASK(5, 0)
+#define LTC3220_LED_MODE_MASK GENMASK(7, 6)
+#define LTC3220_BLINK_MASK GENMASK(4, 3)
+#define LTC3220_GRADATION_MASK GENMASK(2, 1)
+#define LTC3220_CPO_COMMAND_MASK GENMASK(2, 1)
+
+#define LTC3220_NUM_LEDS 18
+
+static const char * const ltc3220_cpo_levels[] = { "0", "1.5", "2", "1" };
+
+struct ltc3220_command_cfg {
+ bool quick_write;
+ bool is_shutdown;
+ u8 force_cpo_level;
+};
+
+struct ltc3220_uled_cfg {
+ struct ltc3220_state *ltc3220_state;
+ struct led_classdev led_cdev;
+ u8 reg_value;
+ u8 led_index;
+};
+
+struct ltc3220_grad_cfg {
+ bool is_increasing;
+ u8 gradation_period_ms;
+};
+
+struct ltc3220_state {
+ struct ltc3220_command_cfg command_cfg;
+ struct ltc3220_uled_cfg uled_cfg[LTC3220_NUM_LEDS];
+ struct ltc3220_grad_cfg grad_cfg;
+ struct i2c_client *client;
+ u8 blink_mode;
+};
+
+static int ltc3220_set_command(struct ltc3220_state *ltc3220_state)
+{
+ struct i2c_client *client = ltc3220_state->client;
+ u8 reg_val;
+
+ reg_val = FIELD_PREP(LTC3220_COMMAND_SHUTDOWN, ltc3220_state->command_cfg.is_shutdown);
+ reg_val |= FIELD_PREP(LTC3220_CPO_COMMAND_MASK,
+ ltc3220_state->command_cfg.force_cpo_level);
+ reg_val |= FIELD_PREP(LTC3220_COMMAND_QUICK_WRITE,
+ ltc3220_state->command_cfg.quick_write);
+
+ return i2c_smbus_write_byte_data(client, LTC3220_COMMAND, reg_val);
+}
+
+static int ltc3220_shutdown(struct ltc3220_state *ltc3220_state)
+{
+ struct i2c_client *client = ltc3220_state->client;
+ u8 reg_val;
+ int ret;
+
+ reg_val = FIELD_PREP(LTC3220_COMMAND_SHUTDOWN, 1);
+ reg_val |= FIELD_PREP(LTC3220_CPO_COMMAND_MASK,
+ ltc3220_state->command_cfg.force_cpo_level);
+
+ ret = i2c_smbus_write_byte_data(client, LTC3220_COMMAND, reg_val);
+ if (ret == 0)
+ ltc3220_state->command_cfg.is_shutdown = true;
+
+ return ret;
+}
+
+static int ltc3220_resume_from_shutdown(struct ltc3220_state *ltc3220_state)
+{
+ int ret;
+
+ ltc3220_state->command_cfg.is_shutdown = false;
+ ret = ltc3220_set_command(ltc3220_state);
+ if (ret < 0)
+ ltc3220_state->command_cfg.is_shutdown = true;
+
+ return ret;
+}
+
+/*
+ * Set LED brightness and mode.
+ * The brightness value determines both the LED current and operating mode:
+ * 0-63: Normal mode - LED current from 0-63 (off to full brightness)
+ * 64-127: Blink mode - LED blinks with current level (brightness - 64)
+ * 128-191: Gradation mode - LED gradually changes brightness (brightness - 128)
+ * 192-255: GPO mode - LED operates as general purpose output (brightness - 192)
+ */
+static int ltc3220_set_led_data(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct ltc3220_state *ltc3220_state;
+ struct ltc3220_uled_cfg *uled_cfg;
+ int ret;
+ int i;
+
+ uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg, led_cdev);
+ ltc3220_state = uled_cfg->ltc3220_state;
+
+ ret = i2c_smbus_write_byte_data(ltc3220_state->client,
+ LTC3220_ULED(uled_cfg->led_index), brightness);
+ if (ret < 0)
+ return ret;
+
+ uled_cfg->reg_value = brightness;
+
+ /*
+ * When quick-write is enabled, writing to LED 1 updates all
+ * LEDs simultaneously via quick-write mode. Update cached values for
+ * all LEDs to reflect the synchronized state.
+ */
+ if (ltc3220_state->command_cfg.quick_write && uled_cfg->led_index == 0) {
+ for (i = 0; i < LTC3220_NUM_LEDS; i++)
+ ltc3220_state->uled_cfg[i].reg_value = brightness;
+ }
+
+ return 0;
+}
+
+static enum led_brightness ltc3220_get_led_data(struct led_classdev *led_cdev)
+{
+ struct ltc3220_uled_cfg *uled_cfg;
+
+ uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg, led_cdev);
+
+ return uled_cfg->reg_value;
+}
+
+static int ltc3220_set_blink_and_gradation(struct ltc3220_state *ltc3220_state,
+ u8 blink_cfg, u8 gradation_period_ms, bool is_increasing)
+{
+ struct i2c_client *client = ltc3220_state->client;
+ u8 reg_val;
+
+ reg_val = FIELD_PREP(LTC3220_BLINK_MASK, blink_cfg);
+ reg_val |= FIELD_PREP(LTC3220_GRADATION_MASK, gradation_period_ms);
+ reg_val |= FIELD_PREP(LTC3220_GRAD_COUNT_UP, is_increasing);
+
+ return i2c_smbus_write_byte_data(client, LTC3220_GRAD_BLINK, reg_val);
+}
+
+/*
+ * LTC3220 pattern support for hardware-assisted breathing/gradation.
+ * The hardware supports 3 gradation ramp time 240ms, 480ms, 960ms)
+ * and can ramp up or down.
+ *
+ * Pattern array interpretation:
+ * pattern[0].brightness = start brightness (0-63)
+ * pattern[0].delta_t = ramp time in milliseconds
+ * pattern[1].brightness = end brightness (0-63)
+ * pattern[1].delta_t = (optional, can be 0 or same as pattern[0].delta_t)
+ */
+static int ltc3220_pattern_set(struct led_classdev *led_cdev,
+ struct led_pattern *pattern,
+ u32 len, int repeat)
+{
+ struct ltc3220_state *ltc3220_state;
+ struct ltc3220_uled_cfg *uled_cfg;
+ u8 gradation_period;
+ u8 start_brightness;
+ u8 end_brightness;
+ bool is_increasing;
+ int ret;
+
+ if (len != 2)
+ return -EINVAL;
+
+ uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg, led_cdev);
+ ltc3220_state = uled_cfg->ltc3220_state;
+
+ start_brightness = pattern[0].brightness & LTC3220_LED_CURRENT_MASK;
+ end_brightness = pattern[1].brightness & LTC3220_LED_CURRENT_MASK;
+
+ is_increasing = end_brightness > start_brightness;
+
+ if (pattern[0].delta_t == 0)
+ gradation_period = 0;
+ else if (pattern[0].delta_t <= 240)
+ gradation_period = 1;
+ else if (pattern[0].delta_t <= 480)
+ gradation_period = 2;
+ else
+ gradation_period = 3;
+
+ ret = ltc3220_set_blink_and_gradation(ltc3220_state,
+ ltc3220_state->blink_mode,
+ gradation_period,
+ is_increasing);
+ if (ret < 0)
+ return ret;
+
+ ltc3220_state->grad_cfg.gradation_period_ms = gradation_period;
+ ltc3220_state->grad_cfg.is_increasing = is_increasing;
+
+ ret = ltc3220_set_led_data(led_cdev, start_brightness);
+ if (ret < 0)
+ return ret;
+
+ return ltc3220_set_led_data(led_cdev, 128 + end_brightness);
+}
+
+static int ltc3220_pattern_clear(struct led_classdev *led_cdev)
+{
+ struct ltc3220_state *ltc3220_state;
+ struct ltc3220_uled_cfg *uled_cfg;
+ int ret;
+
+ uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg, led_cdev);
+ ltc3220_state = uled_cfg->ltc3220_state;
+
+ ret = ltc3220_set_blink_and_gradation(ltc3220_state,
+ ltc3220_state->blink_mode,
+ 0, false);
+ if (ret < 0)
+ return ret;
+
+ ltc3220_state->grad_cfg.gradation_period_ms = 0;
+ ltc3220_state->grad_cfg.is_increasing = false;
+
+ return 0;
+}
+
+/*
+ * LTC3220 has a global blink configuration that affects all LEDs.
+ * This implementation allows per-LED blink requests, but the blink timing
+ * will be shared across all LEDs. The delay values are mapped to the
+ * hardware's discrete blink rates.
+ */
+static int ltc3220_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct ltc3220_state *ltc3220_state;
+ struct ltc3220_uled_cfg *uled_cfg;
+ unsigned long period;
+ u8 blink_mode;
+ int ret;
+
+ uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg, led_cdev);
+ ltc3220_state = uled_cfg->ltc3220_state;
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ blink_mode = 1;
+ *delay_on = 500;
+ *delay_off = 500;
+ } else {
+ period = *delay_on + *delay_off;
+
+ if (period <= 750) {
+ blink_mode = 0;
+ *delay_on = 250;
+ *delay_off = 250;
+ } else if (period <= 1500) {
+ blink_mode = 1;
+ *delay_on = 500;
+ *delay_off = 500;
+ } else if (period <= 3000) {
+ blink_mode = 2;
+ *delay_on = 1000;
+ *delay_off = 1000;
+ } else {
+ blink_mode = 3;
+ *delay_on = 2000;
+ *delay_off = 2000;
+ }
+ }
+
+ ret = ltc3220_set_blink_and_gradation(ltc3220_state, blink_mode,
+ ltc3220_state->grad_cfg.gradation_period_ms,
+ ltc3220_state->grad_cfg.is_increasing);
+ if (ret < 0)
+ return ret;
+
+ ltc3220_state->blink_mode = blink_mode;
+
+ return 0;
+}
+
+static void ltc3220_reset_gpio_action(void *data)
+{
+ struct gpio_desc *reset_gpio = data;
+
+ gpiod_set_value_cansleep(reset_gpio, 1);
+}
+
+static int ltc3220_reset(struct ltc3220_state *ltc3220_state, struct i2c_client *client)
+{
+ struct gpio_desc *reset_gpio;
+ int ret;
+ int i;
+
+ reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(reset_gpio),
+ "Failed to set reset GPIO\n");
+
+ if (reset_gpio) {
+ gpiod_set_value_cansleep(reset_gpio, 0);
+
+ ret = devm_add_action_or_reset(&client->dev, ltc3220_reset_gpio_action, reset_gpio);
+ if (ret)
+ return ret;
+
+ } else {
+ ret = ltc3220_set_command(ltc3220_state);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < LTC3220_NUM_LEDS; i++) {
+ ret = i2c_smbus_write_byte_data(client, LTC3220_ULED(i), 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ltc3220_set_blink_and_gradation(ltc3220_state, 0, 0, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ltc3220_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3220_state *ltc3220_state = i2c_get_clientdata(client);
+
+ return ltc3220_shutdown(ltc3220_state);
+}
+
+static int ltc3220_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ltc3220_state *ltc3220_state = i2c_get_clientdata(client);
+
+ return ltc3220_resume_from_shutdown(ltc3220_state);
+}
+
+static SIMPLE_DEV_PM_OPS(ltc3220_pm_ops, ltc3220_suspend, ltc3220_resume);
+
+static int ltc3220_probe(struct i2c_client *client)
+{
+ struct ltc3220_state *ltc3220_state;
+ u8 i = 0;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return dev_err_probe(&client->dev, -EIO,
+ "SMBUS Byte Data not Supported\n");
+
+ ltc3220_state = devm_kzalloc(&client->dev, sizeof(*ltc3220_state), GFP_KERNEL);
+ if (!ltc3220_state)
+ return -ENOMEM;
+
+ ltc3220_state->client = client;
+ i2c_set_clientdata(client, ltc3220_state);
+
+ if (device_property_read_bool(&client->dev, "adi,quick-write"))
+ ltc3220_state->command_cfg.quick_write = true;
+
+ ret = ltc3220_reset(ltc3220_state, client);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to reset device\n");
+
+ ret = device_property_match_property_string(&client->dev, "adi,force-cpo-level",
+ ltc3220_cpo_levels, ARRAY_SIZE(ltc3220_cpo_levels));
+ if (ret >= 0)
+ ltc3220_state->command_cfg.force_cpo_level = ret;
+
+ ret = ltc3220_set_command(ltc3220_state);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to set command\n");
+
+ device_for_each_child_node_scoped(&client->dev, child) {
+ struct led_init_data init_data = {};
+ struct ltc3220_uled_cfg *led;
+ u32 source;
+
+ ret = fwnode_property_read_u32(child, "reg", &source);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Couldn't read LED address\n");
+
+ if (!source || source > LTC3220_NUM_LEDS)
+ return dev_err_probe(&client->dev, -EINVAL,
+ "LED address out of range\n");
+
+ init_data.fwnode = child;
+ init_data.devicename = "ltc3220";
+ init_data.devname_mandatory = true;
+
+ /* LED node reg/index/address goes from 1 to 18 */
+ i = source - 1;
+ led = <c3220_state->uled_cfg[i];
+ led->led_index = i;
+ led->reg_value = 0;
+ led->ltc3220_state = ltc3220_state;
+ led->led_cdev.brightness_set_blocking = ltc3220_set_led_data;
+ led->led_cdev.brightness_get = ltc3220_get_led_data;
+ led->led_cdev.max_brightness = 255;
+ led->led_cdev.blink_set = ltc3220_blink_set;
+ led->led_cdev.pattern_set = ltc3220_pattern_set;
+ led->led_cdev.pattern_clear = ltc3220_pattern_clear;
+
+ ret = devm_led_classdev_register_ext(&client->dev,
+ &led->led_cdev,
+ &init_data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to register LED class device\n");
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ltc3220_of_match[] = {
+ { .compatible = "adi,ltc3220", },
+ { .compatible = "adi,ltc3220-1", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc3220_of_match);
+
+static struct i2c_driver ltc3220_led_driver = {
+ .driver = {
+ .name = "ltc3220",
+ .of_match_table = ltc3220_of_match,
+ .pm = pm_sleep_ptr(<c3220_pm_ops),
+ },
+ .probe = ltc3220_probe,
+};
+module_i2c_driver(ltc3220_led_driver);
+
+MODULE_AUTHOR("Edelweise Escala <edelweise.escala@analog.com>");
+MODULE_DESCRIPTION("LED driver for LTC3220 controllers");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel LED Driver
2026-01-06 5:27 ` [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel " Edelweise Escala
@ 2026-01-06 8:12 ` Krzysztof Kozlowski
2026-01-07 9:52 ` Escala, Edelweise
0 siblings, 1 reply; 11+ messages in thread
From: Krzysztof Kozlowski @ 2026-01-06 8:12 UTC (permalink / raw)
To: Edelweise Escala
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-leds, devicetree, linux-kernel
On Tue, Jan 06, 2026 at 01:27:07PM +0800, Edelweise Escala wrote:
> Add dt-binding for ltc3220. LTC3220 18 Channel LED driver
Subject, commit msg, binding: driver as Linux driver? If yes, then
rewrite everything to match hardware.
Otherwise what is exactly a LED driver? I know motor drivers.
>
> Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
> ---
> .../devicetree/bindings/leds/leds-ltc3220.yaml | 131 +++++++++++++++++++++
> MAINTAINERS | 7 ++
> 2 files changed, 138 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml b/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
> new file mode 100644
> index 000000000000..33285a7ac7a3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
Filename matching compatible.
> @@ -0,0 +1,131 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/leds-ltc3220.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices LTC3220 LED Drivers
> +
> +maintainers:
> + - Edelweise Escala <edelweise.escala@analog.com>
> +
> +description: Bindings for the Analog Devices LTC3220 18 channel LED Drivers.
Missing wrapping after description. Look at other bindings.
Also, drop "Bindings for". You should describe here hardware, not that
binding is a binding for.
> +
> + For more product information please see the link below
> + https://www.analog.com/en/products/ltc3220.html
> +
> +properties:
> + compatible:
> + enum:
> + - adi,ltc3220
> + - adi,ltc3220-1
What is -1 device? Why description does not help here at all?
> +
> + reg:
> + maxItems: 1
> +
> + '#address-cells':
> + const: 1
> +
> + '#size-cells':
> + const: 0
> +
> + reset-gpios:
> + maxItems: 1
> + description: GPIO attached to the chip's reset pin
Drop description, obvious, redundant.
> +
> + adi,force-cpo-level:
> + $ref: /schemas/types.yaml#/definitions/string
> + description: Forces the Charge Pump Output to a specified multiplier.
> + enum:
> + - "0" # Auto(default) - Automatically selects optimal charge pump mode
> + - "1.5"
> + - "2"
> + - "1"
Numbers are not a string, so choose appropriate number format. Also,
oddly sorted. I don't understand what this property is for so not sure
what to recommend.
> + default: "0"
> +
> + adi,quick-write:
> + type: boolean
> + description: If present, LED 1 output becomes a master control that
> + simultaneously updates all 18 LED outputs using the hardware's quick-write
> + mode. When enabled, led@1 must be defined in the device tree to provide
> + the control interface, even if no physical LED is connected to the D1
> + output pin. When disabled or not present, LED 1 operates as a normal
> + independent LED output.
If there is no led@1 physically, you cannot add it to the DT. It seems
you described some sort of driver behavior, instead of hardware.
> +
> +patternProperties:
> + "^led@([1-9]|1[0-8])$":
Use consistent quotes, so '.
> + type: object
> + $ref: /schemas/leds/common.yaml#
> + unevaluatedProperties: false
> + properties:
> + reg:
> + description: Output channel for the LED (1-18 maps to LED outputs D1-D18).
> + minimum: 1
> + maximum: 18
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] leds: ltc3220: add driver
2026-01-06 5:27 ` [PATCH 2/2] leds: ltc3220: add driver Edelweise Escala
@ 2026-01-06 8:14 ` Krzysztof Kozlowski
2026-01-07 9:52 ` Escala, Edelweise
2026-01-06 12:03 ` Jacek Anaszewski
1 sibling, 1 reply; 11+ messages in thread
From: Krzysztof Kozlowski @ 2026-01-06 8:14 UTC (permalink / raw)
To: Edelweise Escala
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-leds, devicetree, linux-kernel
On Tue, Jan 06, 2026 at 01:27:08PM +0800, Edelweise Escala wrote:
> +
> +static int ltc3220_probe(struct i2c_client *client)
> +{
> + struct ltc3220_state *ltc3220_state;
> + u8 i = 0;
> + int ret;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_BYTE_DATA))
Odd wrapping.
> + return dev_err_probe(&client->dev, -EIO,
> + "SMBUS Byte Data not Supported\n");
> +
> + ltc3220_state = devm_kzalloc(&client->dev, sizeof(*ltc3220_state), GFP_KERNEL);
> + if (!ltc3220_state)
> + return -ENOMEM;
> +
> + ltc3220_state->client = client;
> + i2c_set_clientdata(client, ltc3220_state);
> +
> + if (device_property_read_bool(&client->dev, "adi,quick-write"))
> + ltc3220_state->command_cfg.quick_write = true;
> +
> + ret = ltc3220_reset(ltc3220_state, client);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "Failed to reset device\n");
Your code is not really readable.
> +
> + ret = device_property_match_property_string(&client->dev, "adi,force-cpo-level",
> + ltc3220_cpo_levels, ARRAY_SIZE(ltc3220_cpo_levels));
> + if (ret >= 0)
> + ltc3220_state->command_cfg.force_cpo_level = ret;
> +
> + ret = ltc3220_set_command(ltc3220_state);
> + if (ret < 0)
> + return dev_err_probe(&client->dev, ret,
> + "Failed to set command\n");
> +
> + device_for_each_child_node_scoped(&client->dev, child) {
> + struct led_init_data init_data = {};
> + struct ltc3220_uled_cfg *led;
> + u32 source;
> +
> + ret = fwnode_property_read_u32(child, "reg", &source);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "Couldn't read LED address\n");
> +
> + if (!source || source > LTC3220_NUM_LEDS)
> + return dev_err_probe(&client->dev, -EINVAL,
> + "LED address out of range\n");
> +
> + init_data.fwnode = child;
> + init_data.devicename = "ltc3220";
> + init_data.devname_mandatory = true;
> +
> + /* LED node reg/index/address goes from 1 to 18 */
> + i = source - 1;
> + led = <c3220_state->uled_cfg[i];
> + led->led_index = i;
> + led->reg_value = 0;
> + led->ltc3220_state = ltc3220_state;
> + led->led_cdev.brightness_set_blocking = ltc3220_set_led_data;
> + led->led_cdev.brightness_get = ltc3220_get_led_data;
> + led->led_cdev.max_brightness = 255;
> + led->led_cdev.blink_set = ltc3220_blink_set;
> + led->led_cdev.pattern_set = ltc3220_pattern_set;
> + led->led_cdev.pattern_clear = ltc3220_pattern_clear;
> +
> + ret = devm_led_classdev_register_ext(&client->dev,
> + &led->led_cdev,
> + &init_data);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "Failed to register LED class device\n");
> + }
> +
> + return 0;
> +}
> +
> +static const struct of_device_id ltc3220_of_match[] = {
> + { .compatible = "adi,ltc3220", },
> + { .compatible = "adi,ltc3220-1", },
Why do you have this "-1"? Even if this was a real hardware, devices are
clearly compatible based on above.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] leds: ltc3220: add driver
2026-01-06 5:27 ` [PATCH 2/2] leds: ltc3220: add driver Edelweise Escala
2026-01-06 8:14 ` Krzysztof Kozlowski
@ 2026-01-06 12:03 ` Jacek Anaszewski
2026-01-07 9:52 ` Escala, Edelweise
1 sibling, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2026-01-06 12:03 UTC (permalink / raw)
To: Edelweise Escala, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-leds, devicetree, linux-kernel
On 1/6/26 06:27, Edelweise Escala wrote:
> Add driver for ltc3220. LTC3220 18 Channel LED Driver
>
> Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
> ---
> MAINTAINERS | 1 +
> drivers/leds/Kconfig | 10 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-ltc3220.c | 465 ++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 477 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d640c35d1f93..fda0d2963c4f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14961,6 +14961,7 @@ L: linux-leds@vger.kernel.org
> S: Maintained
> W: https://ez.analog.com/linux-software-drivers
> F: Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
> +F: drivers/leds/leds-ltc3220.c
>
[...]
> + "Failed to set command\n");
> +
> + device_for_each_child_node_scoped(&client->dev, child) {
> + struct led_init_data init_data = {};
> + struct ltc3220_uled_cfg *led;
> + u32 source;
> +
> + ret = fwnode_property_read_u32(child, "reg", &source);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "Couldn't read LED address\n");
> +
> + if (!source || source > LTC3220_NUM_LEDS)
> + return dev_err_probe(&client->dev, -EINVAL,
> + "LED address out of range\n");
> +
> + init_data.fwnode = child;
> + init_data.devicename = "ltc3220";
> + init_data.devname_mandatory = true;
Why? Please get acquainted with the section "LED Device Naming" in [0],
and the documentation of this property in [1].
> +
> + /* LED node reg/index/address goes from 1 to 18 */
> + i = source - 1;
> + led = <c3220_state->uled_cfg[i];
> + led->led_index = i;
> + led->reg_value = 0;
> + led->ltc3220_state = ltc3220_state;
> + led->led_cdev.brightness_set_blocking = ltc3220_set_led_data;
> + led->led_cdev.brightness_get = ltc3220_get_led_data;
> + led->led_cdev.max_brightness = 255;
> + led->led_cdev.blink_set = ltc3220_blink_set;
> + led->led_cdev.pattern_set = ltc3220_pattern_set;
> + led->led_cdev.pattern_clear = ltc3220_pattern_clear;
> +
> + ret = devm_led_classdev_register_ext(&client->dev,
> + &led->led_cdev,
> + &init_data);
> + if (ret)
> + return dev_err_probe(&client->dev, ret,
> + "Failed to register LED class device\n");
[0] Documentation/leds/leds-class.rst
[1] include/linux/leds.h
--
Best regards,
Jacek Anaszewski
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel LED Driver
2026-01-06 8:12 ` Krzysztof Kozlowski
@ 2026-01-07 9:52 ` Escala, Edelweise
2026-01-07 10:57 ` Krzysztof Kozlowski
0 siblings, 1 reply; 11+ messages in thread
From: Escala, Edelweise @ 2026-01-07 9:52 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-leds, devicetree, linux-kernel
> On Tue, Jan 06, 2026 at 01:27:07PM +0800, Edelweise Escala wrote:
> > Add dt-binding for ltc3220. LTC3220 18 Channel LED driver
>
> Subject, commit msg, binding: driver as Linux driver? If yes, then rewrite
> everything to match hardware.
>
> Otherwise what is exactly a LED driver? I know motor drivers.
>
I will rewrite to match hardware.
> >
> > Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
> > ---
> > .../devicetree/bindings/leds/leds-ltc3220.yaml | 131
> +++++++++++++++++++++
> > MAINTAINERS | 7 ++
> > 2 files changed, 138 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
> > b/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
> > new file mode 100644
> > index 000000000000..33285a7ac7a3
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
>
> Filename matching compatible.
>
>
> > @@ -0,0 +1,131 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2
> > +---
> > +$id:
> > +https://urldefense.com/v3/__http://devicetree.org/schemas/leds/leds-l
> > +tc3220.yaml*__;Iw!!A3Ni8CS0y2Y!-
> bTmoOEA7bjPt5CM_hEEqaRU4x0nvJNS2Y926U
> > +ULgO8UsbmEQwBUHq-AZjzofrOg5HnnWnBflY9LKFGVrA$
> > +$schema:
> > +https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.y
> > +aml*__;Iw!!A3Ni8CS0y2Y!-
> bTmoOEA7bjPt5CM_hEEqaRU4x0nvJNS2Y926UULgO8Usb
> > +mEQwBUHq-AZjzofrOg5HnnWnBflY9BRg_sig$
> > +
> > +title: Analog Devices LTC3220 LED Drivers
> > +
> > +maintainers:
> > + - Edelweise Escala <edelweise.escala@analog.com>
> > +
> > +description: Bindings for the Analog Devices LTC3220 18 channel LED
> Drivers.
>
> Missing wrapping after description. Look at other bindings.
>
> Also, drop "Bindings for". You should describe here hardware, not that
> binding is a binding for.
>
I will fix the missing wrapping and improve the description.
> > +
> > + For more product information please see the link below
> > + https://www.analog.com/en/products/ltc3220.html
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - adi,ltc3220
> > + - adi,ltc3220-1
>
> What is -1 device? Why description does not help here at all?
>
It is just a version of ltc3220 with different slave address, I will drop it.
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + '#address-cells':
> > + const: 1
> > +
> > + '#size-cells':
> > + const: 0
> > +
> > + reset-gpios:
> > + maxItems: 1
> > + description: GPIO attached to the chip's reset pin
>
> Drop description, obvious, redundant.
>
> > +
> > + adi,force-cpo-level:
> > + $ref: /schemas/types.yaml#/definitions/string
> > + description: Forces the Charge Pump Output to a specified multiplier.
> > + enum:
> > + - "0" # Auto(default) - Automatically selects optimal charge pump mode
> > + - "1.5"
> > + - "2"
> > + - "1"
>
> Numbers are not a string, so choose appropriate number format. Also, oddly
> sorted. I don't understand what this property is for so not sure what to
> recommend.
It is arranged this way to match the value for the register.
I plan to keep it as string and just do
enum:
- auto
- 1.5x
- 2x
- 1x
>
> > + default: "0"
> > +
> > + adi,quick-write:
> > + type: boolean
> > + description: If present, LED 1 output becomes a master control that
> > + simultaneously updates all 18 LED outputs using the hardware's quick-
> write
> > + mode. When enabled, led@1 must be defined in the device tree to
> provide
> > + the control interface, even if no physical LED is connected to the D1
> > + output pin. When disabled or not present, LED 1 operates as a normal
> > + independent LED output.
>
> If there is no led@1 physically, you cannot add it to the DT. It seems you
> described some sort of driver behavior, instead of hardware.
>
This is also a hardware feature, when enabled a write to the LED 1
output register simultaneously updates all 18 LED output registers
to the same value.
> > +
> > +patternProperties:
> > + "^led@([1-9]|1[0-8])$":
>
> Use consistent quotes, so '.
>
> > + type: object
> > + $ref: /schemas/leds/common.yaml#
> > + unevaluatedProperties: false
> > + properties:
> > + reg:
> > + description: Output channel for the LED (1-18 maps to LED outputs
> D1-D18).
> > + minimum: 1
> > + maximum: 18
>
Best Regards,
Edelweise
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH 2/2] leds: ltc3220: add driver
2026-01-06 8:14 ` Krzysztof Kozlowski
@ 2026-01-07 9:52 ` Escala, Edelweise
0 siblings, 0 replies; 11+ messages in thread
From: Escala, Edelweise @ 2026-01-07 9:52 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-leds, devicetree, linux-kernel
> On Tue, Jan 06, 2026 at 01:27:08PM +0800, Edelweise Escala wrote:
> > +
> > +static int ltc3220_probe(struct i2c_client *client) {
> > + struct ltc3220_state *ltc3220_state;
> > + u8 i = 0;
> > + int ret;
> > +
> > + if (!i2c_check_functionality(client->adapter,
> > + I2C_FUNC_SMBUS_BYTE_DATA))
>
> Odd wrapping.
I will fix the wrapping.
> > + return dev_err_probe(&client->dev, -EIO,
> > + "SMBUS Byte Data not Supported\n");
> > +
> > + ltc3220_state = devm_kzalloc(&client->dev, sizeof(*ltc3220_state),
> GFP_KERNEL);
> > + if (!ltc3220_state)
> > + return -ENOMEM;
> > +
> > + ltc3220_state->client = client;
> > + i2c_set_clientdata(client, ltc3220_state);
> > +
> > + if (device_property_read_bool(&client->dev, "adi,quick-write"))
> > + ltc3220_state->command_cfg.quick_write = true;
> > +
> > + ret = ltc3220_reset(ltc3220_state, client);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Failed to reset device\n");
>
> Your code is not really readable.
Do you mean the text wrapping? I will fix it.
> > +
> > + ret = device_property_match_property_string(&client->dev,
> "adi,force-cpo-level",
> > + ltc3220_cpo_levels,
> ARRAY_SIZE(ltc3220_cpo_levels));
> > + if (ret >= 0)
> > + ltc3220_state->command_cfg.force_cpo_level = ret;
> > +
> > + ret = ltc3220_set_command(ltc3220_state);
> > + if (ret < 0)
> > + return dev_err_probe(&client->dev, ret,
> > + "Failed to set command\n");
> > +
> > + device_for_each_child_node_scoped(&client->dev, child) {
> > + struct led_init_data init_data = {};
> > + struct ltc3220_uled_cfg *led;
> > + u32 source;
> > +
> > + ret = fwnode_property_read_u32(child, "reg", &source);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Couldn't read LED address\n");
> > +
> > + if (!source || source > LTC3220_NUM_LEDS)
> > + return dev_err_probe(&client->dev, -EINVAL,
> > + "LED address out of range\n");
> > +
> > + init_data.fwnode = child;
> > + init_data.devicename = "ltc3220";
> > + init_data.devname_mandatory = true;
> > +
> > + /* LED node reg/index/address goes from 1 to 18 */
> > + i = source - 1;
> > + led = <c3220_state->uled_cfg[i];
> > + led->led_index = i;
> > + led->reg_value = 0;
> > + led->ltc3220_state = ltc3220_state;
> > + led->led_cdev.brightness_set_blocking =
> ltc3220_set_led_data;
> > + led->led_cdev.brightness_get = ltc3220_get_led_data;
> > + led->led_cdev.max_brightness = 255;
> > + led->led_cdev.blink_set = ltc3220_blink_set;
> > + led->led_cdev.pattern_set = ltc3220_pattern_set;
> > + led->led_cdev.pattern_clear = ltc3220_pattern_clear;
> > +
> > + ret = devm_led_classdev_register_ext(&client->dev,
> > + &led->led_cdev,
> > + &init_data);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Failed to register LED class
> device\n");
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id ltc3220_of_match[] = {
> > + { .compatible = "adi,ltc3220", },
> > + { .compatible = "adi,ltc3220-1", },
>
> Why do you have this "-1"? Even if this was a real hardware, devices are
> clearly compatible based on above.
>
I wil drop the -1
Best Regards,
Edelweise Escala
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH 2/2] leds: ltc3220: add driver
2026-01-06 12:03 ` Jacek Anaszewski
@ 2026-01-07 9:52 ` Escala, Edelweise
0 siblings, 0 replies; 11+ messages in thread
From: Escala, Edelweise @ 2026-01-07 9:52 UTC (permalink / raw)
To: Jacek Anaszewski, Lee Jones, Pavel Machek, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-leds, devicetree, linux-kernel
> On 1/6/26 06:27, Edelweise Escala wrote:
> > Add driver for ltc3220. LTC3220 18 Channel LED Driver
> >
> > Signed-off-by: Edelweise Escala <edelweise.escala@analog.com>
> > ---
> > MAINTAINERS | 1 +
> > drivers/leds/Kconfig | 10 +
> > drivers/leds/Makefile | 1 +
> > drivers/leds/leds-ltc3220.c | 465
> ++++++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 477 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index
> > d640c35d1f93..fda0d2963c4f 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -14961,6 +14961,7 @@ L: linux-leds@vger.kernel.org
> > S: Maintained
> > W: https://ez.analog.com/linux-software-drivers
> > F: Documentation/devicetree/bindings/leds/leds-ltc3220.yaml
> > +F: drivers/leds/leds-ltc3220.c
> >
> [...]
> > + "Failed to set command\n");
> > +
> > + device_for_each_child_node_scoped(&client->dev, child) {
> > + struct led_init_data init_data = {};
> > + struct ltc3220_uled_cfg *led;
> > + u32 source;
> > +
> > + ret = fwnode_property_read_u32(child, "reg", &source);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Couldn't read LED address\n");
> > +
> > + if (!source || source > LTC3220_NUM_LEDS)
> > + return dev_err_probe(&client->dev, -EINVAL,
> > + "LED address out of range\n");
> > +
> > + init_data.fwnode = child;
> > + init_data.devicename = "ltc3220";
> > + init_data.devname_mandatory = true;
>
> Why? Please get acquainted with the section "LED Device Naming" in [0], and
> the documentation of this property in [1].
devname_mandatory will be dropped, Thank you
> > +
> > + /* LED node reg/index/address goes from 1 to 18 */
> > + i = source - 1;
> > + led = <c3220_state->uled_cfg[i];
> > + led->led_index = i;
> > + led->reg_value = 0;
> > + led->ltc3220_state = ltc3220_state;
> > + led->led_cdev.brightness_set_blocking =
> ltc3220_set_led_data;
> > + led->led_cdev.brightness_get = ltc3220_get_led_data;
> > + led->led_cdev.max_brightness = 255;
> > + led->led_cdev.blink_set = ltc3220_blink_set;
> > + led->led_cdev.pattern_set = ltc3220_pattern_set;
> > + led->led_cdev.pattern_clear = ltc3220_pattern_clear;
> > +
> > + ret = devm_led_classdev_register_ext(&client->dev,
> > + &led->led_cdev,
> > + &init_data);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Failed to register LED class
> device\n");
>
> [0] Documentation/leds/leds-class.rst
> [1] include/linux/leds.h
>
Best Regards,
Edelweise Escala
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel LED Driver
2026-01-07 9:52 ` Escala, Edelweise
@ 2026-01-07 10:57 ` Krzysztof Kozlowski
2026-01-08 6:02 ` Escala, Edelweise
0 siblings, 1 reply; 11+ messages in thread
From: Krzysztof Kozlowski @ 2026-01-07 10:57 UTC (permalink / raw)
To: Escala, Edelweise
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-leds, devicetree, linux-kernel
On 07/01/2026 10:52, Escala, Edelweise wrote:
>>
>>> +
>>> + adi,force-cpo-level:
>>> + $ref: /schemas/types.yaml#/definitions/string
>>> + description: Forces the Charge Pump Output to a specified multiplier.
>>> + enum:
>>> + - "0" # Auto(default) - Automatically selects optimal charge pump mode
>>> + - "1.5"
>>> + - "2"
>>> + - "1"
>>
>> Numbers are not a string, so choose appropriate number format. Also, oddly
>> sorted. I don't understand what this property is for so not sure what to
>> recommend.
>
> It is arranged this way to match the value for the register.
Makes no sense. The order here does not matter for driver and registers
at all.
> I plan to keep it as string and just do
> enum:
> - auto
> - 1.5x
> - 2x
> - 1x
Still wrongly ordered and still I do not understand the purpose of this
property.
Datasheet mentions some sort of impedance. Impedance has units (see
property units in dtschema), but you call it "level". Maybe you want to
achieve some specific current on output? But for current we already have
LED related properties.
Also, "auto" is redundant unless lack of the property has a meaning?
Why exactly this varies between boards?
>
>>
>>> + default: "0"
>>> +
>>> + adi,quick-write:
>>> + type: boolean
>>> + description: If present, LED 1 output becomes a master control that
>>> + simultaneously updates all 18 LED outputs using the hardware's quick-
>> write
>>> + mode. When enabled, led@1 must be defined in the device tree to
>> provide
>>> + the control interface, even if no physical LED is connected to the D1
>>> + output pin. When disabled or not present, LED 1 operates as a normal
>>> + independent LED output.
>>
>> If there is no led@1 physically, you cannot add it to the DT. It seems you
>> described some sort of driver behavior, instead of hardware.
>>
>
> This is also a hardware feature, when enabled a write to the LED 1
> output register simultaneously updates all 18 LED output registers
> to the same value.
You still cannot add fake nodes to DT. Fake means there is no actual LED.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel LED Driver
2026-01-07 10:57 ` Krzysztof Kozlowski
@ 2026-01-08 6:02 ` Escala, Edelweise
0 siblings, 0 replies; 11+ messages in thread
From: Escala, Edelweise @ 2026-01-08 6:02 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-leds, devicetree, linux-kernel
> >>> + adi,force-cpo-level:
> >>> + $ref: /schemas/types.yaml#/definitions/string
> >>> + description: Forces the Charge Pump Output to a specified multiplier.
> >>> + enum:
> >>> + - "0" # Auto(default) - Automatically selects optimal charge pump
> mode
> >>> + - "1.5"
> >>> + - "2"
> >>> + - "1"
> >>
> >> Numbers are not a string, so choose appropriate number format. Also,
> >> oddly sorted. I don't understand what this property is for so not
> >> sure what to recommend.
> >
> > It is arranged this way to match the value for the register.
>
> Makes no sense. The order here does not matter for driver and registers at
> all.
>
> > I plan to keep it as string and just do
> > enum:
> > - auto
> > - 1.5x
> > - 2x
> > - 1x
>
> Still wrongly ordered and still I do not understand the purpose of this
> property.
>
> Datasheet mentions some sort of impedance. Impedance has units (see
> property units in dtschema), but you call it "level". Maybe you want to
> achieve some specific current on output? But for current we already have LED
> related properties.
>
> Also, "auto" is redundant unless lack of the property has a meaning?
>
> Why exactly this varies between boards?
After rechecking the datasheet I think, it should be dropped.
It should be automatic so it can change the strength of the charge pump
to match the required current.
> >
> >>
> >>> + default: "0"
> >>> +
> >>> + adi,quick-write:
> >>> + type: boolean
> >>> + description: If present, LED 1 output becomes a master control that
> >>> + simultaneously updates all 18 LED outputs using the
> >>> + hardware's quick-
> >> write
> >>> + mode. When enabled, led@1 must be defined in the device tree
> >>> + to
> >> provide
> >>> + the control interface, even if no physical LED is connected to the D1
> >>> + output pin. When disabled or not present, LED 1 operates as a
> normal
> >>> + independent LED output.
> >>
> >> If there is no led@1 physically, you cannot add it to the DT. It
> >> seems you described some sort of driver behavior, instead of hardware.
> >>
> >
> > This is also a hardware feature, when enabled a write to the LED 1
> > output register simultaneously updates all 18 LED output registers to
> > the same value.
>
> You still cannot add fake nodes to DT. Fake means there is no actual LED.
>
I will add in the description that this should only be used when LED 1 is present
Thank You!
Best Regards,
Edelweise Escala
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-01-08 6:02 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-06 5:27 [PATCH 0/2] Add Support for LTC3220 18 Channel LED Driver Edelweise Escala
2026-01-06 5:27 ` [PATCH 1/2] dt-bindings: leds: Document LTC3220 18 channel " Edelweise Escala
2026-01-06 8:12 ` Krzysztof Kozlowski
2026-01-07 9:52 ` Escala, Edelweise
2026-01-07 10:57 ` Krzysztof Kozlowski
2026-01-08 6:02 ` Escala, Edelweise
2026-01-06 5:27 ` [PATCH 2/2] leds: ltc3220: add driver Edelweise Escala
2026-01-06 8:14 ` Krzysztof Kozlowski
2026-01-07 9:52 ` Escala, Edelweise
2026-01-06 12:03 ` Jacek Anaszewski
2026-01-07 9:52 ` Escala, Edelweise
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®