mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property
@ 2026-09-15 13:43 Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property Nora Schiffer
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer,
	Krzysztof Kozlowski

The PCA9952/PCA9955B/PCA9956B have a reset pin. Allow describing it in
the Device Tree.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2:
- Use full hardware model names in commit description
- Add Reviewed-by tag

 Documentation/devicetree/bindings/leds/nxp,pca995x.yaml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml b/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
index ab8c90cbadb5c..201f32f1b8139 100644
--- a/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
+++ b/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
@@ -36,6 +36,9 @@ properties:
   "#size-cells":
     const: 0
 
+  reset-gpios:
+    maxItems: 1
+
 patternProperties:
   "^led@[0-9a-f]+$":
     type: object
@@ -54,6 +57,7 @@ additionalProperties: false
 
 examples:
   - |
+    #include <dt-bindings/gpio/gpio.h>
     #include <dt-bindings/leds/common.h>
 
     i2c {
@@ -65,6 +69,7 @@ examples:
             reg = <0x01>;
             #address-cells = <1>;
             #size-cells = <0>;
+            reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
 
             led@0 {
                 reg = <0x0>;
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  2026-09-17 10:16   ` Krzysztof Kozlowski
  2026-09-15 13:43 ` [PATCH v2 3/8] leds: pca995x: Fix maximum LED index for 16-channel variants Nora Schiffer
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

Allow configuring the output current to adjust for different hardware
designs.

The output gain relates the output current with an external resistor
connected to the LED driver. It does not directly correspond to a
meaningful physical unit of measurement, so we refer to the datasheet
for information on its calculation.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2:
- Add nxp, vendor prefix
- Fix default value to match existing driver code
- Describe retionale for using register value directly in commit message

 Documentation/devicetree/bindings/leds/nxp,pca995x.yaml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml b/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
index 201f32f1b8139..372ecd18043e0 100644
--- a/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
+++ b/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml
@@ -39,6 +39,14 @@ properties:
   reset-gpios:
     maxItems: 1
 
+  nxp,output-gain:
+    description:
+      Set the output gain controlling the LED current as described in the
+      PCA995x datasheets.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 127
+    maximum: 255
+
 patternProperties:
   "^led@[0-9a-f]+$":
     type: object
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 3/8] leds: pca995x: Fix maximum LED index for 16-channel variants
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 4/8] leds: pca995x: Add support for reset GPIO Nora Schiffer
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

The pca995x_chipdef for the 16-channel variants PCA9952 and PCA9955B
correctly sets num_leds to 16, but the value was not actually used when
checking and iterating over the LEDs during probe.

Fixes: 68d6520d2e76 ("leds: leds-pca995x: Add support for NXP PCA9956B")
Link: https://sashiko.dev/#/patchset/6d3600f99c77c31166f0b6a5ccfb7f518b53c4c9.1788350618.git.nora.schiffer@ew.tq-group.com
Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---
v2: new patch

 drivers/leds/leds-pca995x.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
index fee6216cd1bd0..8b05b3f12cf38 100644
--- a/drivers/leds/leds-pca995x.c
+++ b/drivers/leds/leds-pca995x.c
@@ -146,7 +146,7 @@ static int pca995x_probe(struct i2c_client *client)
 		if (ret)
 			return ret;
 
-		if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg])
+		if (reg < 0 || reg >= chipdef->num_leds || led_fwnodes[reg])
 			return -EINVAL;
 
 		led = &chip->leds[reg];
@@ -157,7 +157,7 @@ static int pca995x_probe(struct i2c_client *client)
 		led->ldev.max_brightness = 255;
 	}
 
-	for (i = 0; i < PCA995X_MAX_OUTPUTS; i++) {
+	for (i = 0; i < chipdef->num_leds; i++) {
 		struct led_init_data init_data = {};
 
 		if (!led_fwnodes[i])
@@ -169,7 +169,7 @@ static int pca995x_probe(struct i2c_client *client)
 						     &chip->leds[i].ldev,
 						     &init_data);
 		if (ret < 0) {
-			for (j = i; j < PCA995X_MAX_OUTPUTS; j++)
+			for (j = i; j < chipdef->num_leds; j++)
 				fwnode_handle_put(led_fwnodes[j]);
 			return dev_err_probe(dev, ret,
 					     "Could not register LED %s\n",
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 4/8] leds: pca995x: Add support for reset GPIO
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 3/8] leds: pca995x: Fix maximum LED index for 16-channel variants Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 5/8] leds: pca995x: Make output gain configurable Nora Schiffer
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

The PCA9952/9955B/9956B have a reset pin. Use it to reset the LED driver
if provided in the Device Tree.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2:
- Use correct #includes
- Use udelay for small delay
- Use full hardware model names in commit description

 drivers/leds/leds-pca995x.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
index 8b05b3f12cf38..0c0ea57f3225a 100644
--- a/drivers/leds/leds-pca995x.c
+++ b/drivers/leds/leds-pca995x.c
@@ -8,6 +8,8 @@
  * Copyright 2023 Marek Vasut
  */
 
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/leds.h>
 #include <linux/module.h>
@@ -121,6 +123,7 @@ static int pca995x_probe(struct i2c_client *client)
 	struct fwnode_handle *led_fwnodes[PCA995X_MAX_OUTPUTS] = { 0 };
 	struct device *dev = &client->dev;
 	const struct pca995x_chipdef *chipdef;
+	struct gpio_desc *reset_gpio;
 	struct pca995x_chip *chip;
 	struct pca995x_led *led;
 	int i, j, reg, ret;
@@ -130,6 +133,16 @@ static int pca995x_probe(struct i2c_client *client)
 	if (!dev_fwnode(dev))
 		return -ENODEV;
 
+	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(reset_gpio),
+				     "failed to request reset GPIO\n");
+	if (reset_gpio) {
+		udelay(3);
+		gpiod_set_value_cansleep(reset_gpio, 0);
+		usleep_range(1500, 1600);
+	}
+
 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 5/8] leds: pca995x: Make output gain configurable
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
                   ` (2 preceding siblings ...)
  2026-09-15 13:43 ` [PATCH v2 4/8] leds: pca995x: Add support for reset GPIO Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  2026-09-22 16:12   ` Lee Jones
  2026-09-15 13:43 ` [PATCH v2 6/8] leds: pca995x: Add sysfs files for error reporting Nora Schiffer
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

The output gain linearly configures the LED current in 255 levels; the
absolute current depends on both the gain value and the R_ext resistor
connected to the LED driver. Make the gain configurable in the Device
Tree instead of hardcoding it to 128, allowing to adjust the current
for different hardware designs.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2: Add nxp, vendor prefix

 drivers/leds/leds-pca995x.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
index 0c0ea57f3225a..9cd5702573b0d 100644
--- a/drivers/leds/leds-pca995x.c
+++ b/drivers/leds/leds-pca995x.c
@@ -127,6 +127,7 @@ static int pca995x_probe(struct i2c_client *client)
 	struct pca995x_chip *chip;
 	struct pca995x_led *led;
 	int i, j, reg, ret;
+	u32 iref;
 
 	chipdef = device_get_match_data(&client->dev);
 
@@ -154,6 +155,11 @@ static int pca995x_probe(struct i2c_client *client)
 
 	i2c_set_clientdata(client, chip);
 
+	if (device_property_read_u32(dev, "nxp,output-gain", &iref))
+		iref = PCA995X_IREFALL_HALF_CFG;
+	else if (iref > PCA995X_IREFALL_FULL_CFG)
+		return dev_err_probe(dev, -EINVAL, "invalid nxp,output-gain\n");
+
 	device_for_each_child_node_scoped(dev, child) {
 		ret = fwnode_property_read_u32(child, "reg", &reg);
 		if (ret)
@@ -196,7 +202,7 @@ static int pca995x_probe(struct i2c_client *client)
 		return ret;
 
 	/* IREF Output current value for all LEDn outputs */
-	return regmap_write(chip->regmap, chipdef->irefall, PCA995X_IREFALL_HALF_CFG);
+	return regmap_write(chip->regmap, chipdef->irefall, iref);
 }
 
 static const struct i2c_device_id pca995x_id[] = {
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 6/8] leds: pca995x: Add sysfs files for error reporting
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
                   ` (3 preceding siblings ...)
  2026-09-15 13:43 ` [PATCH v2 5/8] leds: pca995x: Make output gain configurable Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 7/8] leds: pca995x: Do not use full on LED mode Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 8/8] leds: pca995x: Add support for group brightness control Nora Schiffer
  6 siblings, 0 replies; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

The PCA995x has builtin failure detection. Provide sysfs files for
individual LED status (reporting "okay", "short-circuit" or
"open-circuit") as well as a global "has_errors" flag. has_errors is
sticky and must be cleared by writing "clear" to the sysfs file.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2:
- Use sysfs_streq()
- Explicitly initialize MODE2 register, clear errors on probe

 drivers/leds/leds-pca995x.c | 111 +++++++++++++++++++++++++++++++++++-
 1 file changed, 110 insertions(+), 1 deletion(-)

diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
index 9cd5702573b0d..a9e312aaf0ad1 100644
--- a/drivers/leds/leds-pca995x.c
+++ b/drivers/leds/leds-pca995x.c
@@ -8,6 +8,7 @@
  * Copyright 2023 Marek Vasut
  */
 
+#include <linux/bits.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -24,6 +25,12 @@
 /* Auto-increment disabled. Normal mode */
 #define PCA995X_MODE1_CFG		0x00
 
+#define PCA995X_MODE2_CLRERR		BIT(4)
+#define PCA995X_MODE2_ERROR		BIT(6)
+
+/* Clear errors on probe, group brightness control, linear adjustment */
+#define PCA995X_MODE2_CFG		PCA995X_MODE2_CLRERR
+
 /* LED select registers determine the source that drives LED outputs */
 #define PCA995X_LED_OFF			0x0
 #define PCA995X_LED_ON			0x1
@@ -37,30 +44,37 @@
 #define PCA995X_IREFALL_FULL_CFG	0xFF
 #define PCA995X_IREFALL_HALF_CFG	(PCA995X_IREFALL_FULL_CFG / 2)
 
+#define PCA995X_EFLAG_BITS		2
+#define PCA995X_EFLAG_MASK		GENMASK(1, 0)
+
 #define ldev_to_led(c)	container_of(c, struct pca995x_led, ldev)
 
 struct pca995x_chipdef {
 	unsigned int num_leds;
 	u8 pwm_base;
 	u8 irefall;
+	u8 eflag_base;
 };
 
 static const struct pca995x_chipdef pca9952_chipdef = {
 	.num_leds	= 16,
 	.pwm_base	= 0x0a,
 	.irefall	= 0x43,
+	.eflag_base	= 0x44,
 };
 
 static const struct pca995x_chipdef pca9955b_chipdef = {
 	.num_leds	= 16,
 	.pwm_base	= 0x08,
 	.irefall	= 0x45,
+	.eflag_base	= 0x46,
 };
 
 static const struct pca995x_chipdef pca9956b_chipdef = {
 	.num_leds	= 24,
 	.pwm_base	= 0x0a,
 	.irefall	= 0x40,
+	.eflag_base	= 0x41,
 };
 
 struct pca995x_led {
@@ -112,6 +126,83 @@ static int pca995x_brightness_set(struct led_classdev *led_cdev,
 	}
 }
 
+static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct pca995x_led *led = ldev_to_led(led_cdev);
+	struct pca995x_chip *chip = led->chip;
+	const struct pca995x_chipdef *chipdef = chip->chipdef;
+	const char *status = "unknown";
+	unsigned int val;
+	int shift, ret;
+	u8 reg;
+
+	reg = chipdef->eflag_base + (led->led_no / PCA995X_OUTPUTS_PER_REG);
+	shift = PCA995X_EFLAG_BITS * (led->led_no % PCA995X_OUTPUTS_PER_REG);
+
+	ret = regmap_read(chip->regmap, reg, &val);
+	if (ret)
+		return ret;
+
+	switch ((val >> shift) & PCA995X_EFLAG_MASK) {
+	case 0:
+		status = "okay";
+		break;
+	case 1:
+		status = "short-circuit";
+		break;
+	case 2:
+		status = "open-circuit";
+	}
+
+	return sysfs_emit(buf, "%s\n", status);
+}
+
+static DEVICE_ATTR_RO(status);
+
+static struct attribute *pca995x_led_attrs[] = {
+	&dev_attr_status.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(pca995x_led);
+
+static ssize_t has_errors_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pca995x_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(chip->regmap, PCA995X_MODE2, &val);
+	if (ret)
+		return ret;
+
+
+	return sysfs_emit(buf, "%d\n", !!(val & PCA995X_MODE2_ERROR));
+}
+
+static ssize_t has_errors_store(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct pca995x_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+	int ret;
+
+	if (!sysfs_streq(buf, "clear"))
+		return -EINVAL;
+
+	ret = regmap_update_bits(chip->regmap, PCA995X_MODE2,
+				 PCA995X_MODE2_CLRERR, PCA995X_MODE2_CLRERR);
+
+	return ret ?: count;
+}
+
+static DEVICE_ATTR_RW(has_errors);
+
+static struct attribute *pca995x_attrs[] = {
+	&dev_attr_has_errors.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(pca995x);
+
 static const struct regmap_config pca995x_regmap = {
 	.reg_bits = 8,
 	.val_bits = 8,
@@ -174,6 +265,7 @@ static int pca995x_probe(struct i2c_client *client)
 		led->led_no = reg;
 		led->ldev.brightness_set_blocking = pca995x_brightness_set;
 		led->ldev.max_brightness = 255;
+		led->ldev.groups = pca995x_led_groups;
 	}
 
 	for (i = 0; i < chipdef->num_leds; i++) {
@@ -201,8 +293,24 @@ static int pca995x_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
+	/* Clear errors on probe */
+	ret = regmap_write(chip->regmap, PCA995X_MODE2, PCA995X_MODE2_CFG);
+	if (ret)
+		return ret;
+
 	/* IREF Output current value for all LEDn outputs */
-	return regmap_write(chip->regmap, chipdef->irefall, iref);
+	ret = regmap_write(chip->regmap, chipdef->irefall, iref);
+	if (ret)
+		return ret;
+
+	return sysfs_create_groups(&dev->kobj, pca995x_groups);
+}
+
+static void pca995x_remove(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+
+	return sysfs_remove_groups(&dev->kobj, pca995x_groups);
 }
 
 static const struct i2c_device_id pca995x_id[] = {
@@ -227,6 +335,7 @@ static struct i2c_driver pca995x_driver = {
 		.of_match_table = pca995x_of_match,
 	},
 	.probe = pca995x_probe,
+	.remove = pca995x_remove,
 	.id_table = pca995x_id,
 };
 module_i2c_driver(pca995x_driver);
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 7/8] leds: pca995x: Do not use full on LED mode
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
                   ` (4 preceding siblings ...)
  2026-09-15 13:43 ` [PATCH v2 6/8] leds: pca995x: Add sysfs files for error reporting Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  2026-09-15 13:43 ` [PATCH v2 8/8] leds: pca995x: Add support for group brightness control Nora Schiffer
  6 siblings, 0 replies; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

LEDs in full on mode are unaffected by group dimming. Avoid using it,
so group dimming is applied consistently.

This slightly changes the brightness at maximum (from 100% to 255/256
duty cycle), which should be imperceptible; arguably it is also more
consistent with the other brightness levels, as it will now scale
linearly from 0/256 to 255/256 over the whole range.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2: No changes

 drivers/leds/leds-pca995x.c | 33 ++++++++++++++-------------------
 1 file changed, 14 insertions(+), 19 deletions(-)

diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
index a9e312aaf0ad1..963e453ce117d 100644
--- a/drivers/leds/leds-pca995x.c
+++ b/drivers/leds/leds-pca995x.c
@@ -102,28 +102,23 @@ static int pca995x_brightness_set(struct led_classdev *led_cdev,
 	ledout_addr = PCA995X_LEDOUT0 + (led->led_no / PCA995X_OUTPUTS_PER_REG);
 	shift = PCA995X_LDRX_BITS * (led->led_no % PCA995X_OUTPUTS_PER_REG);
 
-	switch (brightness) {
-	case LED_FULL:
-		return regmap_update_bits(chip->regmap, ledout_addr,
-					  PCA995X_LDRX_MASK << shift,
-					  PCA995X_LED_ON << shift);
-	case LED_OFF:
+	if (brightness == LED_OFF) {
 		return regmap_update_bits(chip->regmap, ledout_addr,
 					  PCA995X_LDRX_MASK << shift, 0);
-	default:
-		/* Adjust brightness as per user input by changing individual PWM */
-		ret = regmap_write(chip->regmap, pwmout_addr, brightness);
-		if (ret)
-			return ret;
-
-		/*
-		 * Change LDRx configuration to individual brightness via PWM.
-		 * LED will stop blinking if it's doing so.
-		 */
-		return regmap_update_bits(chip->regmap, ledout_addr,
-					  PCA995X_LDRX_MASK << shift,
-					  PCA995X_LED_PWM_MODE << shift);
 	}
+
+	/* Adjust brightness as per user input by changing individual PWM */
+	ret = regmap_write(chip->regmap, pwmout_addr, brightness);
+	if (ret)
+		return ret;
+
+	/*
+	 * Change LDRx configuration to individual brightness via PWM.
+	 * LED will stop blinking if it's doing so.
+	 */
+	return regmap_update_bits(chip->regmap, ledout_addr,
+				  PCA995X_LDRX_MASK << shift,
+				  PCA995X_LED_PWM_MODE << shift);
 }
 
 static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf)
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2 8/8] leds: pca995x: Add support for group brightness control
  2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
                   ` (5 preceding siblings ...)
  2026-09-15 13:43 ` [PATCH v2 7/8] leds: pca995x: Do not use full on LED mode Nora Schiffer
@ 2026-09-15 13:43 ` Nora Schiffer
  6 siblings, 0 replies; 10+ messages in thread
From: Nora Schiffer @ 2026-09-15 13:43 UTC (permalink / raw)
  To: Lee Jones, Pavel Machek
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Isai Gaspar,
	linux, linux-leds, devicetree, linux-kernel, Nora Schiffer

When LEDs are set to PWM mode with group control enabled, their
brightness can control using a global "group PWM" setting, modulating
the individual LEDs' brightness with a second PWM running at a different
frequency. This lowers the minimum duty cycle from 1/256 to 1/65536
(averaged over the modulated signal). Group brightness control is
particularly useful to adjust for different levels of ambient light.

For simplicity, group PWM mode is always enabled, with the reset default
of 255 as group brightness. This reduces the effective duty cycle by
1/256 at all individual brightness levels (the individual PWM signals
are modulated with the 255/256 duty cycle group PWM), which should be
imperceptible.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
---

v2: Set GRPPWM register on probe

 drivers/leds/leds-pca995x.c | 46 ++++++++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
index 963e453ce117d..33da3cd9cbbbf 100644
--- a/drivers/leds/leds-pca995x.c
+++ b/drivers/leds/leds-pca995x.c
@@ -35,6 +35,7 @@
 #define PCA995X_LED_OFF			0x0
 #define PCA995X_LED_ON			0x1
 #define PCA995X_LED_PWM_MODE		0x2
+#define PCA995X_LED_PWM_MODE_GRP	0x3
 #define PCA995X_LDRX_MASK		0x3
 #define PCA995X_LDRX_BITS		2
 
@@ -52,6 +53,7 @@
 struct pca995x_chipdef {
 	unsigned int num_leds;
 	u8 pwm_base;
+	u8 grppwm;
 	u8 irefall;
 	u8 eflag_base;
 };
@@ -59,6 +61,7 @@ struct pca995x_chipdef {
 static const struct pca995x_chipdef pca9952_chipdef = {
 	.num_leds	= 16,
 	.pwm_base	= 0x0a,
+	.grppwm		= 0x08,
 	.irefall	= 0x43,
 	.eflag_base	= 0x44,
 };
@@ -66,6 +69,7 @@ static const struct pca995x_chipdef pca9952_chipdef = {
 static const struct pca995x_chipdef pca9955b_chipdef = {
 	.num_leds	= 16,
 	.pwm_base	= 0x08,
+	.grppwm		= 0x06,
 	.irefall	= 0x45,
 	.eflag_base	= 0x46,
 };
@@ -73,6 +77,7 @@ static const struct pca995x_chipdef pca9955b_chipdef = {
 static const struct pca995x_chipdef pca9956b_chipdef = {
 	.num_leds	= 24,
 	.pwm_base	= 0x0a,
+	.grppwm		= 0x08,
 	.irefall	= 0x40,
 	.eflag_base	= 0x41,
 };
@@ -114,11 +119,10 @@ static int pca995x_brightness_set(struct led_classdev *led_cdev,
 
 	/*
 	 * Change LDRx configuration to individual brightness via PWM.
-	 * LED will stop blinking if it's doing so.
 	 */
 	return regmap_update_bits(chip->regmap, ledout_addr,
 				  PCA995X_LDRX_MASK << shift,
-				  PCA995X_LED_PWM_MODE << shift);
+				  PCA995X_LED_PWM_MODE_GRP << shift);
 }
 
 static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -190,10 +194,41 @@ static ssize_t has_errors_store(struct device *dev, struct device_attribute *att
 	return ret ?: count;
 }
 
+static ssize_t group_brightness_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pca995x_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(chip->regmap, chip->chipdef->grppwm, &val);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t group_brightness_store(struct device *dev, struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct pca995x_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+	u8 val;
+	int ret;
+
+	ret = kstrtou8(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(chip->regmap, chip->chipdef->grppwm, val);
+
+	return ret ?: count;
+}
+
 static DEVICE_ATTR_RW(has_errors);
+static DEVICE_ATTR_RW(group_brightness);
 
 static struct attribute *pca995x_attrs[] = {
 	&dev_attr_has_errors.attr,
+	&dev_attr_group_brightness.attr,
 	NULL,
 };
 ATTRIBUTE_GROUPS(pca995x);
@@ -288,11 +323,16 @@ static int pca995x_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
-	/* Clear errors on probe */
+	/* Clear errors on probe, use GRPPWM register for group brightness control */
 	ret = regmap_write(chip->regmap, PCA995X_MODE2, PCA995X_MODE2_CFG);
 	if (ret)
 		return ret;
 
+	/* Full group brightness */
+	ret = regmap_write(chip->regmap, chipdef->grppwm, U8_MAX);
+	if (ret)
+		return ret;
+
 	/* IREF Output current value for all LEDn outputs */
 	ret = regmap_write(chip->regmap, chipdef->irefall, iref);
 	if (ret)
-- 
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property
  2026-09-15 13:43 ` [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property Nora Schiffer
@ 2026-09-17 10:16   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-17 10:16 UTC (permalink / raw)
  To: Nora Schiffer
  Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Isai Gaspar, linux, linux-leds, devicetree,
	linux-kernel

On Tue, Sep 15, 2026 at 03:43:50PM +0200, Nora Schiffer wrote:
> Allow configuring the output current to adjust for different hardware
> designs.
> 
> The output gain relates the output current with an external resistor
> connected to the LED driver. It does not directly correspond to a
> meaningful physical unit of measurement, so we refer to the datasheet
> for information on its calculation.
> 
> Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
> ---

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 5/8] leds: pca995x: Make output gain configurable
  2026-09-15 13:43 ` [PATCH v2 5/8] leds: pca995x: Make output gain configurable Nora Schiffer
@ 2026-09-22 16:12   ` Lee Jones
  0 siblings, 0 replies; 10+ messages in thread
From: Lee Jones @ 2026-09-22 16:12 UTC (permalink / raw)
  To: Nora Schiffer
  Cc: Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Isai Gaspar, linux, linux-leds, devicetree, linux-kernel

On Tue, 15 Sep 2026, Nora Schiffer wrote:

> The output gain linearly configures the LED current in 255 levels; the
> absolute current depends on both the gain value and the R_ext resistor
> connected to the LED driver. Make the gain configurable in the Device
> Tree instead of hardcoding it to 128, allowing to adjust the current
> for different hardware designs.
> 
> Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
> ---
> 
> v2: Add nxp, vendor prefix
> 
>  drivers/leds/leds-pca995x.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c
> index 0c0ea57f3225a..9cd5702573b0d 100644
> --- a/drivers/leds/leds-pca995x.c
> +++ b/drivers/leds/leds-pca995x.c
> @@ -127,6 +127,7 @@ static int pca995x_probe(struct i2c_client *client)
>  	struct pca995x_chip *chip;
>  	struct pca995x_led *led;
>  	int i, j, reg, ret;
> +	u32 iref;
>  
>  	chipdef = device_get_match_data(&client->dev);
>  
> @@ -154,6 +155,11 @@ static int pca995x_probe(struct i2c_client *client)
>  
>  	i2c_set_clientdata(client, chip);
>  
> +	if (device_property_read_u32(dev, "nxp,output-gain", &iref))
> +		iref = PCA995X_IREFALL_HALF_CFG;
> +	else if (iref > PCA995X_IREFALL_FULL_CFG)
> +		return dev_err_probe(dev, -EINVAL, "invalid nxp,output-gain\n");

As clever as is this, it's not that easy to follow.

I'd prefer:

	u32 iref = PCA995X_IREFALL_HALF_CFG;

	ret = device_property_read_u32(...);
	if (!ret) {
		if (val > PCA995X_IREFALL_FULL_CFG)
			 return dev_err_probe(dev, -EINVAL, "output-gain is out of range\n");
		iref = val;
	}

>  	device_for_each_child_node_scoped(dev, child) {
>  		ret = fwnode_property_read_u32(child, "reg", &reg);
>  		if (ret)
> @@ -196,7 +202,7 @@ static int pca995x_probe(struct i2c_client *client)
>  		return ret;
>  
>  	/* IREF Output current value for all LEDn outputs */
> -	return regmap_write(chip->regmap, chipdef->irefall, PCA995X_IREFALL_HALF_CFG);
> +	return regmap_write(chip->regmap, chipdef->irefall, iref);
>  }
>  
>  static const struct i2c_device_id pca995x_id[] = {
> -- 
> TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> Amtsgericht München, HRB 105018
> Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> https://www.tq-group.com/

-- 
Lee Jones

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-09-22 16:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 13:43 [PATCH v2 1/8] dt-bindings: leds: pca995x: Describe reset-gpios property Nora Schiffer
2026-09-15 13:43 ` [PATCH v2 2/8] dt-bindings: leds: pca995x: Describe nxp,output-gain property Nora Schiffer
2026-09-17 10:16   ` Krzysztof Kozlowski
2026-09-15 13:43 ` [PATCH v2 3/8] leds: pca995x: Fix maximum LED index for 16-channel variants Nora Schiffer
2026-09-15 13:43 ` [PATCH v2 4/8] leds: pca995x: Add support for reset GPIO Nora Schiffer
2026-09-15 13:43 ` [PATCH v2 5/8] leds: pca995x: Make output gain configurable Nora Schiffer
2026-09-22 16:12   ` Lee Jones
2026-09-15 13:43 ` [PATCH v2 6/8] leds: pca995x: Add sysfs files for error reporting Nora Schiffer
2026-09-15 13:43 ` [PATCH v2 7/8] leds: pca995x: Do not use full on LED mode Nora Schiffer
2026-09-15 13:43 ` [PATCH v2 8/8] leds: pca995x: Add support for group brightness control Nora Schiffer

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®