linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic
@ 2021-11-09 10:08 Florian Eckert
  2021-11-09 10:08 ` [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the " Florian Eckert
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Florian Eckert @ 2021-11-09 10:08 UTC (permalink / raw)
  To: pavel, robh+dt, Eckert.Florian
  Cc: linux-leds, devicetree, linux-kernel, Florian Eckert

Introduce the KTD2061/58/59/60 RGB LEDs driver. The difference in these
parts are the address number on the I2C bus the device is listen on.

All KT20xx device could control up to 12 LEDs. The chip can be operated
in two variants.

Florian Eckert (2):
  leds: ktd20xx: Add the KTD20xx family of the RGB LEDs driver from
    Kinetic
  dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers

 .../bindings/leds/leds-ktd20xx.yaml           | 123 +++
 MAINTAINERS                                   |   7 +
 drivers/leds/Kconfig                          |  13 +
 drivers/leds/Makefile                         |   1 +
 drivers/leds/leds-ktd20xx.c                   | 801 ++++++++++++++++++
 5 files changed, 945 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-ktd20xx.yaml
 create mode 100644 drivers/leds/leds-ktd20xx.c

-- 
2.20.1


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

* [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the RGB LEDs driver from Kinetic
  2021-11-09 10:08 [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Florian Eckert
@ 2021-11-09 10:08 ` Florian Eckert
  2021-11-09 23:29   ` Pavel Machek
  2021-11-22 10:32   ` Dan Carpenter
  2021-11-09 10:08 ` [PATCH 2/2] dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers Florian Eckert
  2021-11-09 23:29 ` [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Pavel Machek
  2 siblings, 2 replies; 8+ messages in thread
From: Florian Eckert @ 2021-11-09 10:08 UTC (permalink / raw)
  To: pavel, robh+dt, Eckert.Florian
  Cc: linux-leds, devicetree, linux-kernel, Florian Eckert

Introduce the KTD2061/58/59/60 RGB LEDs driver. The difference in these
parts are the address number on the I2C bus the device is listen on.

All KT20xx device could control up to 12 LEDs. The chip can be operated
in two variants.

Variant 1:
The device has the ability to group LED outputs into two banks so that
the two LED banks can be controlled with the same color. This could not
be done via the LEDs 'sysfs' entry because of the limitation on the color
register count. The color of the two banks can be configured via device
'sysfs' entry for all LEDs at once [current_color0|current_color1].
Which color the LED is to be used can be set via the 'sysfs' of the
individual LEDs via the 'multi_intensity' file. Valid values for the
colors (RGB) are 0 | 1. The value 0 selects the color register 0 and the
value 1 selects the color register 1.

Variant 2:
The device can also set the LED color independently. Since the chip only
has two color registers, but we want to control the 12 LEDs
independently via the 'led-class-multicolour' sysfs entry,
the full RGB color depth cannot be used. Due to this limitation, only 7
colors and the color black (off) can be set. To use this mode the color
registers must be preset via the device tree or the device 'sysfs'. The
color registers 0 must be preset with 0x00 (Red=0x00 Green=0x00 Blue=0x00).
The color register1 should be preset all with the same value. This value
depends on which light intensity is to be used in the setup.

Signed-off-by: Florian Eckert <fe@dev.tdt.de>
---
 MAINTAINERS                 |   6 +
 drivers/leds/Kconfig        |  13 +
 drivers/leds/Makefile       |   1 +
 drivers/leds/leds-ktd20xx.c | 801 ++++++++++++++++++++++++++++++++++++
 4 files changed, 821 insertions(+)
 create mode 100644 drivers/leds/leds-ktd20xx.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9096c64d8d09..736d564f7e93 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10603,6 +10603,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/leds/backlight/kinetic,ktd253.yaml
 F:	drivers/video/backlight/ktd253-backlight.c
 
+KTD20XX LED CONTROLLER DRIVER
+M:	Florian Eckert <fe@dev.tdt.de>
+L:	linux-leds@vger.kernel.org
+S:	Maintained
+F:	drivers/leds/leds-ktd20xx.c
+
 KTEST
 M:	Steven Rostedt <rostedt@goodmis.org>
 M:	John Hawley <warthog9@eaglescrag.net>
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ed800f5da7d8..b313b5c6a445 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -157,6 +157,19 @@ config LEDS_EL15203000
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-el15203000.
 
+config LEDS_KTD20XX
+	tristate "LED Support for KTD2061/58/59/60 LED driver chip"
+	depends on LEDS_CLASS && I2C
+	depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
+	depends on OF
+	select REGMAP_I2C
+	help
+	  If you say yes here you get support for the Kinetic
+	  KTD2061, KTD2058, KTD2059 and KTD2060 LED driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called leds-ktd20xx.
+
 config LEDS_TURRIS_OMNIA
 	tristate "LED support for CZ.NIC's Turris Omnia"
 	depends on LEDS_CLASS_MULTICOLOR
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index c636ec069612..7004ec953c87 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_LEDS_IP30)			+= leds-ip30.o
 obj-$(CONFIG_LEDS_IPAQ_MICRO)		+= leds-ipaq-micro.o
 obj-$(CONFIG_LEDS_IS31FL319X)		+= leds-is31fl319x.o
 obj-$(CONFIG_LEDS_IS31FL32XX)		+= leds-is31fl32xx.o
+obj-${CONFIG_LEDS_KTD20XX}		+= leds-ktd20xx.o
 obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
 obj-$(CONFIG_LEDS_LM3532)		+= leds-lm3532.o
 obj-$(CONFIG_LEDS_LM3533)		+= leds-lm3533.o
diff --git a/drivers/leds/leds-ktd20xx.c b/drivers/leds/leds-ktd20xx.c
new file mode 100644
index 000000000000..ffe125c2f7d9
--- /dev/null
+++ b/drivers/leds/leds-ktd20xx.c
@@ -0,0 +1,801 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  LEDs driver for the Kinetic KDT20xx device
+ *
+ *  Copyright (C) 2021 TDT AG
+ *       Florian Eckert <fe@dev.tdt.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/led-class-multicolor.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+/* I2C Register Map */
+#define KTD20XX_ID		0x00
+#define KTD20XX_MONITOR		0x01
+#define KTD20XX_CONTROL		0x02
+
+/* Color0 Configuration Registers */
+#define KTD20XX_IRED0		0x03
+#define KTD20XX_IGRN0		0x04
+#define KTD20XX_IBLU0		0x05
+
+/* Color1 Configuration Registers */
+#define KTD20XX_IRED1		0x06
+#define KTD20XX_IGRN1		0x07
+#define KTD20XX_IBLU1		0x08
+
+/* Selection Configuration Register */
+#define KTD20XX_ISELA12		0x09
+#define KTD20XX_ISELA34		0x0A
+#define KTD20XX_ISELB12		0x0B
+#define KTD20XX_ISELB34		0x0C
+#define KTD20XX_ISELC12		0x0D
+#define KTD20XX_ISELC34		0x0E
+
+#define KTD20XX_MAX_LEDS	12
+#define KTD20XX_LED_CHANNELS	3
+
+enum ktd20xx_led_number {
+	/* ISELA12 */
+	RGB_A1,
+	RGB_A2,
+	/* ISELA34 */
+	RGB_A3,
+	RGB_A4,
+	/* ISELB12 */
+	RGB_B1,
+	RGB_B2,
+	/* ISELB34 */
+	RGB_B3,
+	RGB_B4,
+	/* ISELC12 */
+	RGB_C1,
+	RGB_C2,
+	/* ISELC34 */
+	RGB_C3,
+	RGB_C4,
+};
+
+enum ktd20xx_rgb {
+	RED,
+	GRN,
+	BLU,
+};
+
+enum ktd20xx_control_mode {
+	CONTROL_MODE_OFF,
+	CONTROL_MODE_NIGHT,
+	CONTROL_MODE_NORMAL,
+	CONTROL_MODE_RESET,
+};
+
+static const struct reg_default ktd20xx_reg_defs[] = {
+	/* Color0 Configuration Registers */
+	{KTD20XX_IRED0, 0x28},
+	{KTD20XX_IGRN0, 0x28},
+	{KTD20XX_IBLU0, 0x28},
+	/* Color1 Configuration Registers */
+	{KTD20XX_IRED1, 0x60},
+	{KTD20XX_IGRN1, 0x60},
+	{KTD20XX_IBLU1, 0x60},
+	/* Selection Configuration Register */
+	{KTD20XX_ISELA12, 0x00},
+	{KTD20XX_ISELA34, 0x00},
+	{KTD20XX_ISELB12, 0x00},
+	{KTD20XX_ISELB34, 0x00},
+	{KTD20XX_ISELC12, 0x00},
+	{KTD20XX_ISELC34, 0x00}
+};
+
+/* Chip values */
+static const struct reg_field kt20xx_control_mode = REG_FIELD(KTD20XX_CONTROL, 6, 7);
+static const struct reg_field kt20xx_faderate = REG_FIELD(KTD20XX_CONTROL, 0, 2);
+static const struct reg_field kt20xx_vendor = REG_FIELD(KTD20XX_ID, 5, 7);
+static const struct reg_field kt20xx_chip_id = REG_FIELD(KTD20XX_ID, 0, 4);
+static const struct reg_field kt20xx_chip_rev = REG_FIELD(KTD20XX_MONITOR, 4, 7);
+
+/* ISELA1 and ISELA2 */
+static const struct reg_field kt20xx_a1_select = REG_FIELD(KTD20XX_ISELA12, 4, 6);
+static const struct reg_field kt20xx_a1_enable = REG_FIELD(KTD20XX_ISELA12, 7, 7);
+static const struct reg_field kt20xx_a2_select = REG_FIELD(KTD20XX_ISELA12, 0, 2);
+static const struct reg_field kt20xx_a2_enable = REG_FIELD(KTD20XX_ISELA12, 3, 3);
+
+/* ISELA3 and ISELA4 */
+static const struct reg_field kt20xx_a3_select = REG_FIELD(KTD20XX_ISELA34, 4, 6);
+static const struct reg_field kt20xx_a3_enable = REG_FIELD(KTD20XX_ISELA34, 7, 7);
+static const struct reg_field kt20xx_a4_select = REG_FIELD(KTD20XX_ISELA34, 0, 2);
+static const struct reg_field kt20xx_a4_enable = REG_FIELD(KTD20XX_ISELA34, 3, 3);
+
+/* ISELB1 and ISELB2 */
+static const struct reg_field kt20xx_b1_select = REG_FIELD(KTD20XX_ISELB12, 4, 6);
+static const struct reg_field kt20xx_b1_enable = REG_FIELD(KTD20XX_ISELB12, 7, 7);
+static const struct reg_field kt20xx_b2_select = REG_FIELD(KTD20XX_ISELB12, 0, 2);
+static const struct reg_field kt20xx_b2_enable = REG_FIELD(KTD20XX_ISELB12, 3, 3);
+
+/* ISELB3 and ISELB4 */
+static const struct reg_field kt20xx_b3_select = REG_FIELD(KTD20XX_ISELB34, 4, 6);
+static const struct reg_field kt20xx_b3_enable = REG_FIELD(KTD20XX_ISELB34, 7, 7);
+static const struct reg_field kt20xx_b4_select = REG_FIELD(KTD20XX_ISELB34, 0, 2);
+static const struct reg_field kt20xx_b4_enable = REG_FIELD(KTD20XX_ISELB34, 3, 3);
+
+/* ISELC1 and ISELC2 */
+static const struct reg_field kt20xx_c1_select = REG_FIELD(KTD20XX_ISELC12, 4, 6);
+static const struct reg_field kt20xx_c1_enable = REG_FIELD(KTD20XX_ISELC12, 7, 7);
+static const struct reg_field kt20xx_c2_select = REG_FIELD(KTD20XX_ISELC12, 0, 2);
+static const struct reg_field kt20xx_c2_enable = REG_FIELD(KTD20XX_ISELC12, 3, 3);
+
+/* ISELC3 and ISELC4 */
+static const struct reg_field kt20xx_c3_select = REG_FIELD(KTD20XX_ISELC34, 4, 6);
+static const struct reg_field kt20xx_c3_enable = REG_FIELD(KTD20XX_ISELC34, 7, 7);
+static const struct reg_field kt20xx_c4_select = REG_FIELD(KTD20XX_ISELC34, 0, 2);
+static const struct reg_field kt20xx_c4_enable = REG_FIELD(KTD20XX_ISELC34, 3, 3);
+
+static const struct regmap_range ktd20xx_volatile_ranges = {
+	.range_min = KTD20XX_ID,
+	.range_max = KTD20XX_CONTROL,
+};
+
+static const struct regmap_access_table ktd20xx_volatile_table = {
+	.yes_ranges = &ktd20xx_volatile_ranges,
+	.n_yes_ranges = 1,
+};
+
+static const struct regmap_range ktd20xx_readable_ranges = {
+	.range_min = KTD20XX_ID,
+	.range_max = KTD20XX_MONITOR,
+};
+
+static const struct regmap_access_table ktd20xx_readable_table = {
+	.yes_ranges = &ktd20xx_readable_ranges,
+	.n_yes_ranges = 1,
+};
+
+static const struct regmap_config ktd20xx_regmap_config = {
+	.name = "ktd20xx_regmap",
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = KTD20XX_ISELC34,
+
+	.volatile_table = &ktd20xx_volatile_table,
+	.rd_table = &ktd20xx_readable_table,
+
+	.reg_defaults = ktd20xx_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(ktd20xx_reg_defs),
+	.cache_type = REGCACHE_FLAT,
+};
+
+struct ktd20xx_led {
+	struct led_classdev_mc mc_cdev;
+	struct mc_subled subled_info[KTD20XX_LED_CHANNELS];
+	int index;
+	struct regmap_field *enable;
+	struct regmap_field *select;
+	struct ktd20xx *chip;
+};
+
+struct ktd20xx {
+	struct mutex lock;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct regmap_field *control_mode;
+	struct regmap_field *faderate;
+	struct regmap_field *vendor;
+	struct regmap_field *chip_id;
+	struct regmap_field *chip_rev;
+	struct ktd20xx_led leds[KTD20XX_MAX_LEDS];
+};
+
+static int ktd20xx_hwinit(struct ktd20xx *chip)
+{
+	struct fwnode_handle *fwnode;
+	struct device *dev = &chip->client->dev;
+	u8 value[3];
+
+	fwnode = dev_fwnode(dev);
+
+	if (fwnode_property_read_u8_array(fwnode, "kinetic,color-current0", value, 3)) {
+		dev_warn(dev, "no kinetic,color-current0 property, use chip default value 0x28.\n");
+	} else {
+		regmap_write(chip->regmap, KTD20XX_IRED0, value[0]);
+		regmap_write(chip->regmap, KTD20XX_IGRN0, value[1]);
+		regmap_write(chip->regmap, KTD20XX_IBLU0, value[2]);
+	}
+
+	if (fwnode_property_read_u8_array(fwnode, "kinetic,color-current1", value, 3)) {
+		dev_warn(dev, "no kinetic,color-current1 property, use chip default value 0x60.\n");
+	} else {
+		regmap_write(chip->regmap, KTD20XX_IRED1, value[0]);
+		regmap_write(chip->regmap, KTD20XX_IGRN1, value[1]);
+		regmap_write(chip->regmap, KTD20XX_IBLU1, value[2]);
+	}
+
+	return 0;
+}
+
+static struct ktd20xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
+{
+	return container_of(mc_cdev, struct ktd20xx_led, mc_cdev);
+}
+
+static int ktd20xx_brightness_set(struct led_classdev *cdev,
+		enum led_brightness brightness)
+{
+	struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
+	struct ktd20xx_led *led = mcled_cdev_to_led(mc_dev);
+	struct device *dev = led->chip->dev;
+	int ret = 0;
+	int i = 0;
+	unsigned int rgb = 0;
+	unsigned int bit = 0;
+
+	mutex_lock(&led->chip->lock);
+	if (brightness > 0)
+		ret = regmap_field_write(led->enable, 1);
+	else
+		ret = regmap_field_write(led->enable, 0);
+
+	if (ret) {
+		dev_err(dev, "Cannot set enable flag of LED %d error: %d\n",
+				led->index, ret);
+		goto out;
+	}
+
+	for (i = 0; i < led->mc_cdev.num_colors; i++) {
+		unsigned int intensity = mc_dev->subled_info[i].intensity;
+
+		switch (i) {
+		case RED:
+			bit = 4;
+			break;
+		case GRN:
+			bit = 2;
+			break;
+		case BLU:
+			bit = 1;
+			break;
+		}
+
+		if (intensity > 0)
+			rgb += bit;
+	}
+
+	ret = regmap_field_write(led->select, rgb);
+	if (ret) {
+		dev_err(dev, "Can not set RGB for LED %d error: %d\n",
+				led->index, ret);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&led->chip->lock);
+	return ret;
+}
+
+static int ktd20xx_probe_dt(struct ktd20xx *chip)
+{
+	struct fwnode_handle *child = NULL;
+	struct led_init_data init_data = {};
+	struct led_classdev *led_cdev;
+	struct ktd20xx_led *led;
+	struct device *dev = &chip->client->dev;
+	u8 value[3] = { 0, 0, 0 };
+	int i = 0;
+	int ret = -EINVAL;
+	int color;
+
+	device_for_each_child_node(chip->dev, child) {
+		led = &chip->leds[i];
+
+		ret = fwnode_property_read_u32(child, "reg", &led->index);
+		if (ret) {
+			dev_err(dev, "missing property: reg\n");
+			goto child_out;
+		}
+		if (led->index >= KTD20XX_MAX_LEDS) {
+			dev_warn(dev, "property 'reg' must contain value between '0' and '%i'\n",
+					KTD20XX_MAX_LEDS);
+			goto child_out;
+		}
+
+		ret = fwnode_property_read_u32(child, "color", &color);
+		if (ret) {
+			dev_err(dev, "missing property: color\n");
+			goto child_out;
+		}
+		if (color != LED_COLOR_ID_RGB) {
+			dev_warn(dev, "property 'color' must contain value 'LED_COLOR_ID_RGB'\n");
+			goto child_out;
+		}
+
+		/* Get default color register selection */
+		if (fwnode_property_read_u8_array(child, "kinetic,color-selection", value, 3))
+			dev_warn(dev, "no kinetic,color-selection property found, use default rgbt color selection from register 0.\n");
+
+		led->subled_info[0].color_index = LED_COLOR_ID_RED;
+		led->subled_info[0].channel = 0;
+		led->subled_info[0].intensity = value[0];
+		led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
+		led->subled_info[1].channel = 1;
+		led->subled_info[1].intensity = value[1];
+		led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
+		led->subled_info[2].channel = 2;
+		led->subled_info[2].intensity = value[2];
+
+		led->mc_cdev.subled_info = led->subled_info;
+		led->mc_cdev.num_colors = KTD20XX_LED_CHANNELS;
+
+		init_data.fwnode = child;
+
+		led->chip = chip;
+		led_cdev = &led->mc_cdev.led_cdev;
+		led_cdev->brightness_set_blocking = ktd20xx_brightness_set;
+
+		switch (led->index) {
+		case RGB_A1:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a1_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a1_enable);
+			break;
+		case RGB_A2:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a2_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a2_enable);
+			break;
+		case RGB_A3:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a3_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a3_enable);
+			break;
+		case RGB_A4:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a4_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_a4_enable);
+			break;
+		case RGB_B1:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b1_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b1_enable);
+			break;
+		case RGB_B2:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b2_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b2_enable);
+			break;
+		case RGB_B3:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b3_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b3_enable);
+			break;
+		case RGB_B4:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b4_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_b4_enable);
+			break;
+		case RGB_C1:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c1_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c1_enable);
+			break;
+		case RGB_C2:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c2_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c2_enable);
+			break;
+		case RGB_C3:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c3_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c3_enable);
+			break;
+		case RGB_C4:
+			led->select = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c4_select);
+			led->enable = devm_regmap_field_alloc(chip->dev,
+					chip->regmap, kt20xx_c4_enable);
+			break;
+		}
+
+		ret = devm_led_classdev_multicolor_register_ext(&chip->client->dev,
+			&led->mc_cdev,
+			&init_data);
+
+		if (ret) {
+			dev_err(&chip->client->dev, "led register err: %d\n", ret);
+			goto child_out;
+		}
+
+		i++;
+		fwnode_handle_put(child);
+	}
+
+	return 0;
+
+child_out:
+	fwnode_handle_put(child);
+	return ret;
+}
+
+/* Device attribute for color0 register
+ *
+ * The device attribute colour1 is intended to adjust the colour space.
+ * The colour strength can be controlled via the current in 125uA steps.
+ * The maximum current for the individual channels is limited to 24mA.
+ * To set a new RGB value, 3 values must be passed. This value may not be
+ * less than 0 and also not greater than 194. The chip can only process the
+ * maximum current of 24mA. This means that any value greater than 194
+ * cannot be set.
+ */
+static ssize_t current_color0_show(struct device *dev,
+		struct device_attribute *a,
+		char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned int value;
+	int len = 0;
+
+	mutex_lock(&chip->lock);
+	regmap_read(chip->regmap, KTD20XX_IRED0, &value);
+	len += sprintf(buf + len, "%d", value);
+	len += sprintf(buf + len, " ");
+
+	regmap_read(chip->regmap, KTD20XX_IGRN0, &value);
+	len += sprintf(buf + len, "%d", value);
+	len += sprintf(buf + len, " ");
+
+	regmap_read(chip->regmap, KTD20XX_IBLU0, &value);
+	len += sprintf(buf + len, "%d", value);
+	mutex_unlock(&chip->lock);
+
+	buf[len++] = '\n';
+	return len;
+}
+
+static ssize_t current_color0_store(struct device *dev,
+		struct device_attribute *a,
+		const char *buf, size_t size)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned int value[3];
+	int i;
+	ssize_t ret;
+
+	ret = sscanf(buf, "%u %u %u", &value[0], &value[1], &value[2]);
+	if (ret < 3) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (value[i] > 194) {
+			ret = -EINVAL;
+			goto err_out;
+		}
+		if (value[i] < 0) {
+			ret = -EINVAL;
+			goto err_out;
+		}
+	}
+
+	mutex_lock(&chip->lock);
+	regmap_write(chip->regmap, KTD20XX_IRED0, value[0]);
+	regmap_write(chip->regmap, KTD20XX_IGRN0, value[1]);
+	regmap_write(chip->regmap, KTD20XX_IBLU0, value[2]);
+	mutex_unlock(&chip->lock);
+	return size;
+
+err_out:
+	return ret;
+}
+static DEVICE_ATTR_RW(current_color0);
+
+/* Device attribute for color1 register
+ *
+ * The device attribute colour1 is intended to adjust the colour space.
+ * The colour strength can be controlled via the current in 125uA steps.
+ * The maximum current for the individual channels is limited to 24mA.
+ * To set a new RGB value, 3 values must be passed. This value may not be
+ * less than 0 and also not greater than 194. The chip can only process the
+ * maximum current of 24mA. This means that any value greater than 194
+ * cannot be set.
+ */
+static ssize_t current_color1_show(struct device *dev,
+		struct device_attribute *a,
+		char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned int value;
+	int len = 0;
+
+	mutex_lock(&chip->lock);
+	regmap_read(chip->regmap, KTD20XX_IRED1, &value);
+	len += sprintf(buf + len, "%d", value);
+	len += sprintf(buf + len, " ");
+
+	regmap_read(chip->regmap, KTD20XX_IGRN1, &value);
+	len += sprintf(buf + len, "%d", value);
+	len += sprintf(buf + len, " ");
+
+	regmap_read(chip->regmap, KTD20XX_IBLU1, &value);
+	len += sprintf(buf + len, "%d", value);
+	mutex_unlock(&chip->lock);
+
+	buf[len++] = '\n';
+	return len;
+}
+static ssize_t current_color1_store(struct device *dev,
+		struct device_attribute *a,
+		const char *buf, size_t size)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned int value[3];
+	int i;
+	ssize_t ret;
+
+	ret = sscanf(buf, "%u %u %u", &value[0], &value[1], &value[2]);
+	if (ret < 3) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (value[i] > 194) {
+			ret = -EINVAL;
+			goto err_out;
+		}
+		if (value[i] < 0) {
+			ret = -EINVAL;
+			goto err_out;
+		}
+	}
+
+	mutex_lock(&chip->lock);
+	regmap_write(chip->regmap, KTD20XX_IRED1, value[0]);
+	regmap_write(chip->regmap, KTD20XX_IGRN1, value[1]);
+	regmap_write(chip->regmap, KTD20XX_IBLU1, value[2]);
+	mutex_unlock(&chip->lock);
+	return size;
+
+err_out:
+	return ret;
+}
+static DEVICE_ATTR_RW(current_color1);
+
+/*
+ * The chip also offers the option "Night Mode".
+ * All LED current settings are divided by 16 for a 0 to 1.5mA current
+ * setting range.
+ */
+static ssize_t nightmode_show(struct device *dev, struct device_attribute *a,
+		char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned int value;
+
+	mutex_lock(&chip->lock);
+	regmap_field_read(chip->control_mode, &value);
+	mutex_unlock(&chip->lock);
+
+	if (value == CONTROL_MODE_NIGHT)
+		return sprintf(buf, "%d\n", 1);
+	else
+		return sprintf(buf, "%d\n", 0);
+}
+
+static ssize_t nightmode_store(struct device *dev, struct device_attribute *a,
+		const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned long nightmode;
+	int ret;
+
+	if (kstrtoul(buf, 10, &nightmode))
+		return -EINVAL;
+
+	if (nightmode > 1)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	if (nightmode == 1)
+		ret = regmap_field_write(chip->control_mode, CONTROL_MODE_NIGHT);
+	else
+		ret = regmap_field_write(chip->control_mode, CONTROL_MODE_NORMAL);
+	mutex_unlock(&chip->lock);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+static DEVICE_ATTR_RW(nightmode);
+
+/*
+ * The chip also offers the option "Fade rate".
+ */
+static ssize_t faderate_show(struct device *dev, struct device_attribute *a,
+		char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned int value;
+
+	mutex_lock(&chip->lock);
+	regmap_field_read(chip->faderate, &value);
+	mutex_unlock(&chip->lock);
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t faderate_store(struct device *dev, struct device_attribute *a,
+		const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+	unsigned long faderate;
+
+	if (kstrtoul(buf, 10, &faderate))
+		return -EINVAL;
+
+	if (faderate < 0)
+		return -EINVAL;
+
+	if (faderate > 7)
+		return -EINVAL;
+
+	mutex_lock(&chip->lock);
+	regmap_field_write(chip->faderate, faderate);
+	mutex_unlock(&chip->lock);
+
+	return count;
+}
+static DEVICE_ATTR_RW(faderate);
+
+static struct attribute *ktd20xx_led_controller_attrs[] = {
+	&dev_attr_current_color0.attr,
+	&dev_attr_current_color1.attr,
+	&dev_attr_nightmode.attr,
+	&dev_attr_faderate.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(ktd20xx_led_controller);
+
+static int ktd20xx_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ktd20xx *chip;
+	unsigned int vendor;
+	unsigned int chip_id;
+	unsigned int chip_rev;
+	int ret;
+
+	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	mutex_init(&chip->lock);
+	chip->client = client;
+	chip->dev = &client->dev;
+	i2c_set_clientdata(client, chip);
+
+	chip->regmap = devm_regmap_init_i2c(client, &ktd20xx_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		ret = PTR_ERR(chip->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto error;
+	}
+
+	chip->control_mode = devm_regmap_field_alloc(chip->dev, chip->regmap,
+			kt20xx_control_mode);
+	chip->faderate = devm_regmap_field_alloc(chip->dev, chip->regmap,
+			kt20xx_faderate);
+	chip->vendor = devm_regmap_field_alloc(chip->dev, chip->regmap,
+			kt20xx_vendor);
+	chip->chip_id = devm_regmap_field_alloc(chip->dev, chip->regmap,
+			kt20xx_chip_id);
+	chip->chip_rev = devm_regmap_field_alloc(chip->dev, chip->regmap,
+			kt20xx_chip_rev);
+
+	regmap_field_write(chip->control_mode, CONTROL_MODE_RESET);
+	ret = regmap_field_read(chip->vendor, &vendor);
+	if (ret) {
+		dev_err(&client->dev, "Failed to read vendor: %d\n", ret);
+		goto error;
+	}
+
+	ret = regmap_field_read(chip->chip_id, &chip_id);
+	if (ret) {
+		dev_err(&client->dev, "Failed to read chip id: %d\n", ret);
+		goto error;
+	}
+
+	ret = regmap_field_read(chip->chip_rev, &chip_rev);
+	if (ret) {
+		dev_err(&client->dev, "Failed to read chip rev: %d\n", ret);
+		goto error;
+	}
+
+	dev_info(&client->dev, "vendor: 0x%02x chip-id: 0x%02x chip-rev: 0x%02x\n",
+			vendor, chip_id, chip_rev);
+
+	regmap_field_write(chip->control_mode, CONTROL_MODE_NORMAL);
+
+	if (devm_device_add_groups(chip->dev, ktd20xx_led_controller_groups))
+		dev_warn(&client->dev, "Could not add attribute group!\n");
+
+	ret = ktd20xx_hwinit(chip);
+	if (ret)
+		goto error;
+
+	ret = ktd20xx_probe_dt(chip);
+	if (ret)
+		goto error;
+
+	return 0;
+
+error:
+	mutex_destroy(&chip->lock);
+	return ret;
+}
+
+static int ktd20xx_remove(struct i2c_client *client)
+{
+	struct ktd20xx *chip = i2c_get_clientdata(client);
+
+	mutex_lock(&chip->lock);
+	regmap_field_write(chip->control_mode, CONTROL_MODE_OFF);
+	mutex_unlock(&chip->lock);
+
+	mutex_destroy(&chip->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id ktd20xx_id[] = {
+	{ "ktd20xx", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ktd20xx_id);
+
+static const struct of_device_id of_ktd20xx_leds_match[] = {
+	{ .compatible = "kinetic,ktd20xx", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, of_ktd20xx_leds_match);
+
+static struct i2c_driver ktd20xx_driver = {
+	.driver = {
+		.name	= "ktd20xx",
+		.of_match_table = of_ktd20xx_leds_match,
+	},
+	.probe		= ktd20xx_probe,
+	.remove		= ktd20xx_remove,
+	.id_table	= ktd20xx_id
+};
+module_i2c_driver(ktd20xx_driver);
+
+MODULE_DESCRIPTION("Kinetic KTD20xx LED driver");
+MODULE_AUTHOR("Florian Eckert <fe@dev.tdt.de>");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1


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

* [PATCH 2/2] dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers
  2021-11-09 10:08 [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Florian Eckert
  2021-11-09 10:08 ` [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the " Florian Eckert
@ 2021-11-09 10:08 ` Florian Eckert
  2021-11-09 23:29 ` [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Pavel Machek
  2 siblings, 0 replies; 8+ messages in thread
From: Florian Eckert @ 2021-11-09 10:08 UTC (permalink / raw)
  To: pavel, robh+dt, Eckert.Florian
  Cc: linux-leds, devicetree, linux-kernel, Florian Eckert

Introduce the bindings for the Kinetic KTD2061/58/59/60RGB LED device
driver. The KTD20xx can control RGB LEDs individually or as part of a
control bank group.

Signed-off-by: Florian Eckert <fe@dev.tdt.de>
---
 .../bindings/leds/leds-ktd20xx.yaml           | 123 ++++++++++++++++++
 MAINTAINERS                                   |   1 +
 2 files changed, 124 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-ktd20xx.yaml

diff --git a/Documentation/devicetree/bindings/leds/leds-ktd20xx.yaml b/Documentation/devicetree/bindings/leds/leds-ktd20xx.yaml
new file mode 100644
index 000000000000..b10b5fd507db
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-ktd20xx.yaml
@@ -0,0 +1,123 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/leds-ktd20xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LED driver for KTD20xx RGB LED from Kinetic.
+
+maintainers:
+  - Florian Eckert <fe@dev.tdt.de>
+
+description: |
+  The KTD20XX is multi-channel, I2C RGB LED Drivers that can group RGB LEDs into
+  a LED group or control them individually.
+
+  The difference in these RGB LED drivers is I2C address number the device is
+  listen on.
+
+properties:
+  compatible:
+    enum:
+      - kinetic,ktd20xx
+
+  reg:
+    maxItems: 1
+    description:
+      I2C slave address
+      ktd2061/58/59/60 0x68 0x69 0x6A 0x6B
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  'kinetic,color-current0':
+    description:
+      Specifies the current selection for the RGB color0.
+      Value 1 must be the current value for the color red.
+      Value 2 must be the current value for the color green.
+      Value 3 must be the current value for the color blue.
+      The current setting range is from 0mA to 24mA with 125uA steps.
+    $ref: /schemas/types.yaml#/definitions/uint8-array
+    items:
+      - minItems: 3
+      - maxItems: 3
+
+  'kinetic,color-current1':
+    description:
+      Specifies the current selection for the RGB color0.
+      Value 1 must be the current value for the color red.
+      Value 2 must be the current value for the color green.
+      Value 3 must be the current value for the color blue.
+      The current setting range is from 0mA to 24mA with 125uA steps.
+    $ref: /schemas/types.yaml#/definitions/uint8-array
+    items:
+      - minItems: 3
+      - maxItems: 3
+
+patternProperties:
+  '^multi-led@[0-9a-f]$':
+    type: object
+    allOf:
+      - $ref: leds-class-multicolor.yaml#
+    properties:
+      reg:
+        minItems: 1
+        maxItems: 12
+        description:
+          This property denotes the LED module number(s) that is used on the
+          for the child node.
+      'kinetic,color-selection':
+        description:
+          Specifies the color selection for this LED.
+          Value 1 selects the color register for color red.
+          Value 2 selects the color register for color green.
+          Value 3 selects the color register for color blue.
+          The value can be either 0 or 1. If 0, the current for the color
+          from color register 0 is used. If 1, the current for the color
+          from color register 1 is used.
+     $ref: /schemas/types.yaml#/definitions/uint8-array
+     items:
+       - minItems: 3
+       - maxItems: 3
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+   #include <dt-bindings/leds/common.h>
+
+   i2c {
+       #address-cells = <1>;
+       #size-cells = <0>;
+
+       led-controller@14 {
+           compatible = "ti,lp5009";
+           reg = <0x14>;
+           #address-cells = <1>;
+           #size-cells = <0>;
+           color-current0 = [ 00 00 00 ] // Current for RGB is 0mA
+           color-current1 = [ 28 28 28 ] // Current for RGB is 5mA
+
+           multi-led@0 {
+               reg = <0x0>;
+               color = <LED_COLOR_ID_RGB>;
+               function = LED_FUNCTION_CHARGING;
+                kinetic,color-selection = [ 00 01 00 ]; // Red=0mA Green=5mA Blue=0mA
+          };
+
+          multi-led@2 {
+            reg = <0x2>;
+            color = <LED_COLOR_ID_RGB>;
+            function = LED_FUNCTION_STANDBY;
+         };
+       };
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 736d564f7e93..125bae48c2d1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10607,6 +10607,7 @@ KTD20XX LED CONTROLLER DRIVER
 M:	Florian Eckert <fe@dev.tdt.de>
 L:	linux-leds@vger.kernel.org
 S:	Maintained
+F:	Documentation/devicetree/bindings/leds/leds-ktd20xx.yaml
 F:	drivers/leds/leds-ktd20xx.c
 
 KTEST
-- 
2.20.1


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

* Re: [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic
  2021-11-09 10:08 [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Florian Eckert
  2021-11-09 10:08 ` [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the " Florian Eckert
  2021-11-09 10:08 ` [PATCH 2/2] dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers Florian Eckert
@ 2021-11-09 23:29 ` Pavel Machek
  2021-11-10  9:35   ` Florian Eckert
  2021-11-22  7:17   ` Florian Eckert
  2 siblings, 2 replies; 8+ messages in thread
From: Pavel Machek @ 2021-11-09 23:29 UTC (permalink / raw)
  To: Florian Eckert
  Cc: robh+dt, Eckert.Florian, linux-leds, devicetree, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 786 bytes --]

Hi!

> Introduce the KTD2061/58/59/60 RGB LEDs driver. The difference in these
> parts are the address number on the I2C bus the device is listen on.
> 
> All KT20xx device could control up to 12 LEDs. The chip can be operated
> in two variants.
> 
> Florian Eckert (2):
>   leds: ktd20xx: Add the KTD20xx family of the RGB LEDs driver from
>     Kinetic
>   dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers

That's... not a nice piece of hardware.

We'll want dt changes first, driver next please.

If this uses non-standard interface, it will need to be
documented. But I would like to understand the limitations first.

Do you have actual device where this is used?

Best regards,
								Pavel
-- 
http://www.livejournal.com/~pavelmachek

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the RGB LEDs driver from Kinetic
  2021-11-09 10:08 ` [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the " Florian Eckert
@ 2021-11-09 23:29   ` Pavel Machek
  2021-11-22 10:32   ` Dan Carpenter
  1 sibling, 0 replies; 8+ messages in thread
From: Pavel Machek @ 2021-11-09 23:29 UTC (permalink / raw)
  To: Florian Eckert
  Cc: robh+dt, Eckert.Florian, linux-leds, devicetree, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2875 bytes --]

hi!

> Introduce the KTD2061/58/59/60 RGB LEDs driver. The difference in these
> parts are the address number on the I2C bus the device is listen on.
> 
> All KT20xx device could control up to 12 LEDs. The chip can be operated
> in two variants.

How are the variants selected?

> Variant 1:
> The device has the ability to group LED outputs into two banks so that
> the two LED banks can be controlled with the same color. This could not
> be done via the LEDs 'sysfs' entry because of the limitation on the color
> register count. The color of the two banks can be configured via device
> 'sysfs' entry for all LEDs at once [current_color0|current_color1].
> Which color the LED is to be used can be set via the 'sysfs' of the
> individual LEDs via the 'multi_intensity' file. Valid values for the
> colors (RGB) are 0 | 1. The value 0 selects the color register 0 and the
> value 1 selects the color register 1.

So... you can select two colors (current_color0, current_color1), and
then then each of the 12 LEDs get one of those colors. What about
intensities? Can brightness be set arbitrarily for each LED?

> Variant 2:
> The device can also set the LED color independently. Since the chip only
> has two color registers, but we want to control the 12 LEDs
> independently via the 'led-class-multicolour' sysfs entry,
> the full RGB color depth cannot be used. Due to this limitation, only 7
> colors and the color black (off) can be set. To use this mode the color
> registers must be preset via the device tree or the device 'sysfs'. The
> color registers 0 must be preset with 0x00 (Red=0x00 Green=0x00 Blue=0x00).
> The color register1 should be preset all with the same value. This value
> depends on which light intensity is to be used in the setup.

So now we have 7 colors we can select from. That sounds better than
two colors. Why would we ever want to use variant 1?

Can we simply pretend this is 7 LED RGB driver?

> +/* Device attribute for color0 register
> + *
> + * The device attribute colour1 is intended to adjust the colour space.

Use color, not colour. Plus run this through checkpatch.

> + * The colour strength can be controlled via the current in 125uA steps.

I don't know what "colour strength" is.

> +/*
> + * The chip also offers the option "Fade rate".
> + */
> +static ssize_t faderate_show(struct device *dev, struct device_attribute *a,
> +		char *buf)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct ktd20xx *chip = i2c_get_clientdata(client);
> +	unsigned int value;
> +
> +	mutex_lock(&chip->lock);
> +	regmap_field_read(chip->faderate, &value);
> +	mutex_unlock(&chip->lock);
> +
> +	return sprintf(buf, "%d\n", value);
> +}

That's way too hardware specific.

Best regards,
									Pavel
-- 
http://www.livejournal.com/~pavelmachek

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic
  2021-11-09 23:29 ` [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Pavel Machek
@ 2021-11-10  9:35   ` Florian Eckert
  2021-11-22  7:17   ` Florian Eckert
  1 sibling, 0 replies; 8+ messages in thread
From: Florian Eckert @ 2021-11-10  9:35 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, Eckert.Florian, linux-leds, devicetree, linux-kernel

> Hi!

Thanks for reviewing my patchset.

>> Florian Eckert (2):
>>   leds: ktd20xx: Add the KTD20xx family of the RGB LEDs driver from
>>     Kinetic
>>   dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers
> 
> That's... not a nice piece of hardware.

Yes, that may be, but I have tried to use what the chip can do.
So that it works for my use case. It would be great if we could 
integrate it
into the color LED framework of the kernel.


> If this uses non-standard interface, it will need to be
> documented. But I would like to understand the limitations first.

OK Then I will try it.

Register Layout:

| Address |  Name   |  Type  | Access | Default | B7 | B6 | B5 | B4 | B3 
| B2 | B1 | B0 |
|:-------:|:-------:|:------:|:------:|:-------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|  0x00   |   ID    |  Data  |   R    |  0xA4   | VENDOR[2:0]  |       
DIE_ID[4:0]      |
|  0x01   | MONITOR | Status |   R    |  0x30   |    DIE_REV[3:0]   | SC 
| BE | CE | UV |
|  0x02   | CONTROL | Config |  R/W   |  0x00   |MODE[1:0]| BE 
|TEMP[1:0]|FADE_RATE[2:0]|
|  0x03   |  IRED0  | Config |  R/W   |  0x28   |            
IRED_SET0[7:0]             |
|  0x04   |  IGRN0  | Config |  R/W   |  0x28   |            
IGRN_SET0[7:0]             |
|  0x05   |  IBLU0  | Config |  R/W   |  0x28   |            
IBLU_SET0[7:0]             |
|  0x06   |  IRED1  | Config |  R/W   |  0x60   |            
IRED_SET1[7:0]             |
|  0x07   |  IGRN1  | Config |  R/W   |  0x60   |            
IGRN_SET1[7:0]             |
|  0x08   |  IBLU1  | Config |  R/W   |  0x60   |            
IBLU_SET1[7:0]             |
|  0x09   | ISELA12 | Config |  R/W   |  0x00   
|ENA1|RGBA1_SEL[2:0]|ENA2|RGBA2_SEL[2:0]|
|  0x0A   | ISELA34 | Config |  R/W   |  0x00   
|ENA3|RGBA3_SEL[2:0]|ENA4|RGBA4_SEL[2:0]|
|  0x0B   | ISELB12 | Config |  R/W   |  0x00   
|ENB1|RGBB1_SEL[2:0]|ENB2|RGBB2_SEL[2:0]|
|  0x0C   | ISELB34 | Config |  R/W   |  0x00   
|ENB3|RGBB3_SEL[2:0]|ENB4|RGBB4_SEL[2:0]|
|  0x0D   | ISELC12 | Config |  R/W   |  0x00   
|ENC1|RGBC1_SEL[2:0]|ENC2|RGBC2_SEL[2:0]|
|  0x0E   | ISELc34 | Config |  R/W   |  0x00   
|ENC3|RGBC3_SEL[2:0]|ENC4|RGBC4_SEL[2:0]|

The registers 0x0A to 0x0E controls the LEDs. Each register controls two 
LEDs.
The top bit [3] of each byte nibble controls whether the LED is on or 
off.
The other bits [0:2] of each nibble, select which color register to use 
Ixxx_SET0
and Ixxx_SET1 (0x03 to 0x08).

Bit 0 -> Blue (RGBxx_SEL):
If this bit is set to 1 then use IBLU_SET1 value otherwise bit is 0 use 
IBLU_SET0 value.

Bit 1 -> Green RGBxx_SEL):
If this bit is set to 1 then use IGRN_SET1 value otherwise bit is 0 use 
IGRN_SET0 value.

Bit 2 -> Red RGBxx_SEL):
If this bit is set to 1 then use IRED_SET1 value otherwise bit is 0 use 
IRED_SET0 value.

This means that we can define two colors from the full RGB range in the 
Ixxx_SET0
and Ixxx_SET1 respectively. And the LEDs can select which RGB color, 
value they want
to use for the individual basic color via the RGBxx_SEL. In reality, 
this is the electrical
current that the LED gets. The different electric currents produce a 
color depending
on the current ratio.

There are now various possibilities for the whole chip to fit into the 
color LED framework
of the kernel.

Variant1:
Prefill Ixxx_SET0 with one value for example 0x28 and set Ixxx_SET1 to 
0x00.
Then we could select with the RGBxx_SEl[2:0] of an LED which color we 
want.
But we only could have 8 colors because of the limition

This are the colors we could produce with this setup.
| Red | Green | Blue |   Color |
|:---:|:-----:|:----:|:-------:|
|  0  |   0   |   1  |  Blue   |
|  0  |   1   |   0  |  Green  |
|  0  |   1   |   1  |Turquoise|
|  1  |   0   |   0  |  Red    |
|  1  |   0   |   1  |  Purple |
|  1  |   1   |   0  |  Yellow |
|  1  |   1   |   1  |  White  |
|  0  |   0   |   0  |  Black  |

The color brightness for the eight RGB colors can only be changed 
together.
To do this, the value of the entire Ixxx_SET0 must be changed at once, 
for
example 0x28 -> 0x14 to halve the brightness. However, this applies to 
all LEDS!
This variant would fit into the color LED framework of the kernel.

Variant2:
Prefill Ixxx_SET0 and Ixxx_SET1 via device sysfs with an RGB color, and
the select for every LEDS with RGBxx[2:0] which color ratio we want to 
use.
The problem with this is that we have to decide beforehand which color
we want to use. We should not change the Ixxx_SET0 and Ixx_SET1 register
value because that would affect all the LEDs! This variant would not fit 
so well
into the color LED framework of the kernel, as we could not selectively 
change the
color for each LED.

I hope I could make it understandable, if not please ask :-)

> Do you have actual device where this is used?

I don't know where the chip is installed, but we just have
new hardware in the pipeline that has this chip for LED control.
For our setup we only need 7 colors. Therefore, this chip is sufficient.


- Best regards

Florian

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

* Re: [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic
  2021-11-09 23:29 ` [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Pavel Machek
  2021-11-10  9:35   ` Florian Eckert
@ 2021-11-22  7:17   ` Florian Eckert
  1 sibling, 0 replies; 8+ messages in thread
From: Florian Eckert @ 2021-11-22  7:17 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, Eckert.Florian, linux-leds, devicetree, linux-kernel

Hello Pavel,

> If this uses non-standard interface, it will need to be
> documented. But I would like to understand the limitations first.

I have already sent you an explanation and the register description.
Did you get it? Do you need anything else?

Best regards,
Florian

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

* Re: [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the RGB LEDs driver from Kinetic
  2021-11-09 10:08 ` [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the " Florian Eckert
  2021-11-09 23:29   ` Pavel Machek
@ 2021-11-22 10:32   ` Dan Carpenter
  1 sibling, 0 replies; 8+ messages in thread
From: Dan Carpenter @ 2021-11-22 10:32 UTC (permalink / raw)
  To: kbuild, Florian Eckert, pavel, robh+dt, Eckert.Florian
  Cc: lkp, kbuild-all, linux-leds, devicetree, linux-kernel, Florian Eckert

Hi Florian,

url:    https://github.com/0day-ci/linux/commits/Florian-Eckert/leds-Add-KTD20xx-RGB-LEDs-driver-from-Kinetic/20211109-181728
base:   git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next
config: i386-randconfig-m031-20211115 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
drivers/leds/leds-ktd20xx.c:304 ktd20xx_probe_dt() warn: missing error code 'ret'
drivers/leds/leds-ktd20xx.c:492 current_color0_store() warn: unsigned 'value[i]' is never less than zero.
drivers/leds/leds-ktd20xx.c:566 current_color1_store() warn: unsigned 'value[i]' is never less than zero.
drivers/leds/leds-ktd20xx.c:661 faderate_store() warn: unsigned 'faderate' is never less than zero.

vim +/ret +304 drivers/leds/leds-ktd20xx.c

004e74105bb360 Florian Eckert 2021-11-09  281  static int ktd20xx_probe_dt(struct ktd20xx *chip)
004e74105bb360 Florian Eckert 2021-11-09  282  {
004e74105bb360 Florian Eckert 2021-11-09  283  	struct fwnode_handle *child = NULL;
004e74105bb360 Florian Eckert 2021-11-09  284  	struct led_init_data init_data = {};
004e74105bb360 Florian Eckert 2021-11-09  285  	struct led_classdev *led_cdev;
004e74105bb360 Florian Eckert 2021-11-09  286  	struct ktd20xx_led *led;
004e74105bb360 Florian Eckert 2021-11-09  287  	struct device *dev = &chip->client->dev;
004e74105bb360 Florian Eckert 2021-11-09  288  	u8 value[3] = { 0, 0, 0 };
004e74105bb360 Florian Eckert 2021-11-09  289  	int i = 0;
004e74105bb360 Florian Eckert 2021-11-09  290  	int ret = -EINVAL;
004e74105bb360 Florian Eckert 2021-11-09  291  	int color;
004e74105bb360 Florian Eckert 2021-11-09  292  
004e74105bb360 Florian Eckert 2021-11-09  293  	device_for_each_child_node(chip->dev, child) {
004e74105bb360 Florian Eckert 2021-11-09  294  		led = &chip->leds[i];
004e74105bb360 Florian Eckert 2021-11-09  295  
004e74105bb360 Florian Eckert 2021-11-09  296  		ret = fwnode_property_read_u32(child, "reg", &led->index);
004e74105bb360 Florian Eckert 2021-11-09  297  		if (ret) {
004e74105bb360 Florian Eckert 2021-11-09  298  			dev_err(dev, "missing property: reg\n");
004e74105bb360 Florian Eckert 2021-11-09  299  			goto child_out;
004e74105bb360 Florian Eckert 2021-11-09  300  		}
004e74105bb360 Florian Eckert 2021-11-09  301  		if (led->index >= KTD20XX_MAX_LEDS) {
004e74105bb360 Florian Eckert 2021-11-09  302  			dev_warn(dev, "property 'reg' must contain value between '0' and '%i'\n",
004e74105bb360 Florian Eckert 2021-11-09  303  					KTD20XX_MAX_LEDS);

ret = -EINVAL;

004e74105bb360 Florian Eckert 2021-11-09 @304  			goto child_out;
004e74105bb360 Florian Eckert 2021-11-09  305  		}
004e74105bb360 Florian Eckert 2021-11-09  306  
004e74105bb360 Florian Eckert 2021-11-09  307  		ret = fwnode_property_read_u32(child, "color", &color);
004e74105bb360 Florian Eckert 2021-11-09  308  		if (ret) {
004e74105bb360 Florian Eckert 2021-11-09  309  			dev_err(dev, "missing property: color\n");
004e74105bb360 Florian Eckert 2021-11-09  310  			goto child_out;
004e74105bb360 Florian Eckert 2021-11-09  311  		}
004e74105bb360 Florian Eckert 2021-11-09  312  		if (color != LED_COLOR_ID_RGB) {
004e74105bb360 Florian Eckert 2021-11-09  313  			dev_warn(dev, "property 'color' must contain value 'LED_COLOR_ID_RGB'\n");

ret = -EINVAL;

004e74105bb360 Florian Eckert 2021-11-09  314  			goto child_out;
004e74105bb360 Florian Eckert 2021-11-09  315  		}
004e74105bb360 Florian Eckert 2021-11-09  316  
004e74105bb360 Florian Eckert 2021-11-09  317  		/* Get default color register selection */
004e74105bb360 Florian Eckert 2021-11-09  318  		if (fwnode_property_read_u8_array(child, "kinetic,color-selection", value, 3))
004e74105bb360 Florian Eckert 2021-11-09  319  			dev_warn(dev, "no kinetic,color-selection property found, use default rgbt color selection from register 0.\n");
004e74105bb360 Florian Eckert 2021-11-09  320  
004e74105bb360 Florian Eckert 2021-11-09  321  		led->subled_info[0].color_index = LED_COLOR_ID_RED;
004e74105bb360 Florian Eckert 2021-11-09  322  		led->subled_info[0].channel = 0;
004e74105bb360 Florian Eckert 2021-11-09  323  		led->subled_info[0].intensity = value[0];
004e74105bb360 Florian Eckert 2021-11-09  324  		led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
004e74105bb360 Florian Eckert 2021-11-09  325  		led->subled_info[1].channel = 1;
004e74105bb360 Florian Eckert 2021-11-09  326  		led->subled_info[1].intensity = value[1];
004e74105bb360 Florian Eckert 2021-11-09  327  		led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
004e74105bb360 Florian Eckert 2021-11-09  328  		led->subled_info[2].channel = 2;
004e74105bb360 Florian Eckert 2021-11-09  329  		led->subled_info[2].intensity = value[2];
004e74105bb360 Florian Eckert 2021-11-09  330  
004e74105bb360 Florian Eckert 2021-11-09  331  		led->mc_cdev.subled_info = led->subled_info;
004e74105bb360 Florian Eckert 2021-11-09  332  		led->mc_cdev.num_colors = KTD20XX_LED_CHANNELS;
004e74105bb360 Florian Eckert 2021-11-09  333  
004e74105bb360 Florian Eckert 2021-11-09  334  		init_data.fwnode = child;
004e74105bb360 Florian Eckert 2021-11-09  335  
004e74105bb360 Florian Eckert 2021-11-09  336  		led->chip = chip;
004e74105bb360 Florian Eckert 2021-11-09  337  		led_cdev = &led->mc_cdev.led_cdev;
004e74105bb360 Florian Eckert 2021-11-09  338  		led_cdev->brightness_set_blocking = ktd20xx_brightness_set;
004e74105bb360 Florian Eckert 2021-11-09  339  
004e74105bb360 Florian Eckert 2021-11-09  340  		switch (led->index) {
004e74105bb360 Florian Eckert 2021-11-09  341  		case RGB_A1:
004e74105bb360 Florian Eckert 2021-11-09  342  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  343  					chip->regmap, kt20xx_a1_select);
004e74105bb360 Florian Eckert 2021-11-09  344  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  345  					chip->regmap, kt20xx_a1_enable);
004e74105bb360 Florian Eckert 2021-11-09  346  			break;
004e74105bb360 Florian Eckert 2021-11-09  347  		case RGB_A2:
004e74105bb360 Florian Eckert 2021-11-09  348  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  349  					chip->regmap, kt20xx_a2_select);
004e74105bb360 Florian Eckert 2021-11-09  350  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  351  					chip->regmap, kt20xx_a2_enable);
004e74105bb360 Florian Eckert 2021-11-09  352  			break;
004e74105bb360 Florian Eckert 2021-11-09  353  		case RGB_A3:
004e74105bb360 Florian Eckert 2021-11-09  354  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  355  					chip->regmap, kt20xx_a3_select);
004e74105bb360 Florian Eckert 2021-11-09  356  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  357  					chip->regmap, kt20xx_a3_enable);
004e74105bb360 Florian Eckert 2021-11-09  358  			break;
004e74105bb360 Florian Eckert 2021-11-09  359  		case RGB_A4:
004e74105bb360 Florian Eckert 2021-11-09  360  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  361  					chip->regmap, kt20xx_a4_select);
004e74105bb360 Florian Eckert 2021-11-09  362  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  363  					chip->regmap, kt20xx_a4_enable);
004e74105bb360 Florian Eckert 2021-11-09  364  			break;
004e74105bb360 Florian Eckert 2021-11-09  365  		case RGB_B1:
004e74105bb360 Florian Eckert 2021-11-09  366  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  367  					chip->regmap, kt20xx_b1_select);
004e74105bb360 Florian Eckert 2021-11-09  368  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  369  					chip->regmap, kt20xx_b1_enable);
004e74105bb360 Florian Eckert 2021-11-09  370  			break;
004e74105bb360 Florian Eckert 2021-11-09  371  		case RGB_B2:
004e74105bb360 Florian Eckert 2021-11-09  372  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  373  					chip->regmap, kt20xx_b2_select);
004e74105bb360 Florian Eckert 2021-11-09  374  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  375  					chip->regmap, kt20xx_b2_enable);
004e74105bb360 Florian Eckert 2021-11-09  376  			break;
004e74105bb360 Florian Eckert 2021-11-09  377  		case RGB_B3:
004e74105bb360 Florian Eckert 2021-11-09  378  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  379  					chip->regmap, kt20xx_b3_select);
004e74105bb360 Florian Eckert 2021-11-09  380  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  381  					chip->regmap, kt20xx_b3_enable);
004e74105bb360 Florian Eckert 2021-11-09  382  			break;
004e74105bb360 Florian Eckert 2021-11-09  383  		case RGB_B4:
004e74105bb360 Florian Eckert 2021-11-09  384  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  385  					chip->regmap, kt20xx_b4_select);
004e74105bb360 Florian Eckert 2021-11-09  386  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  387  					chip->regmap, kt20xx_b4_enable);
004e74105bb360 Florian Eckert 2021-11-09  388  			break;
004e74105bb360 Florian Eckert 2021-11-09  389  		case RGB_C1:
004e74105bb360 Florian Eckert 2021-11-09  390  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  391  					chip->regmap, kt20xx_c1_select);
004e74105bb360 Florian Eckert 2021-11-09  392  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  393  					chip->regmap, kt20xx_c1_enable);
004e74105bb360 Florian Eckert 2021-11-09  394  			break;
004e74105bb360 Florian Eckert 2021-11-09  395  		case RGB_C2:
004e74105bb360 Florian Eckert 2021-11-09  396  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  397  					chip->regmap, kt20xx_c2_select);
004e74105bb360 Florian Eckert 2021-11-09  398  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  399  					chip->regmap, kt20xx_c2_enable);
004e74105bb360 Florian Eckert 2021-11-09  400  			break;
004e74105bb360 Florian Eckert 2021-11-09  401  		case RGB_C3:
004e74105bb360 Florian Eckert 2021-11-09  402  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  403  					chip->regmap, kt20xx_c3_select);
004e74105bb360 Florian Eckert 2021-11-09  404  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  405  					chip->regmap, kt20xx_c3_enable);
004e74105bb360 Florian Eckert 2021-11-09  406  			break;
004e74105bb360 Florian Eckert 2021-11-09  407  		case RGB_C4:
004e74105bb360 Florian Eckert 2021-11-09  408  			led->select = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  409  					chip->regmap, kt20xx_c4_select);
004e74105bb360 Florian Eckert 2021-11-09  410  			led->enable = devm_regmap_field_alloc(chip->dev,
004e74105bb360 Florian Eckert 2021-11-09  411  					chip->regmap, kt20xx_c4_enable);
004e74105bb360 Florian Eckert 2021-11-09  412  			break;
004e74105bb360 Florian Eckert 2021-11-09  413  		}
004e74105bb360 Florian Eckert 2021-11-09  414  
004e74105bb360 Florian Eckert 2021-11-09  415  		ret = devm_led_classdev_multicolor_register_ext(&chip->client->dev,
004e74105bb360 Florian Eckert 2021-11-09  416  			&led->mc_cdev,
004e74105bb360 Florian Eckert 2021-11-09  417  			&init_data);
004e74105bb360 Florian Eckert 2021-11-09  418  
004e74105bb360 Florian Eckert 2021-11-09  419  		if (ret) {
004e74105bb360 Florian Eckert 2021-11-09  420  			dev_err(&chip->client->dev, "led register err: %d\n", ret);
004e74105bb360 Florian Eckert 2021-11-09  421  			goto child_out;
004e74105bb360 Florian Eckert 2021-11-09  422  		}
004e74105bb360 Florian Eckert 2021-11-09  423  
004e74105bb360 Florian Eckert 2021-11-09  424  		i++;
004e74105bb360 Florian Eckert 2021-11-09  425  		fwnode_handle_put(child);
004e74105bb360 Florian Eckert 2021-11-09  426  	}
004e74105bb360 Florian Eckert 2021-11-09  427  
004e74105bb360 Florian Eckert 2021-11-09  428  	return 0;
004e74105bb360 Florian Eckert 2021-11-09  429  
004e74105bb360 Florian Eckert 2021-11-09  430  child_out:
004e74105bb360 Florian Eckert 2021-11-09  431  	fwnode_handle_put(child);
004e74105bb360 Florian Eckert 2021-11-09  432  	return ret;
004e74105bb360 Florian Eckert 2021-11-09  433  }
004e74105bb360 Florian Eckert 2021-11-09  434  
004e74105bb360 Florian Eckert 2021-11-09  435  /* Device attribute for color0 register
004e74105bb360 Florian Eckert 2021-11-09  436   *
004e74105bb360 Florian Eckert 2021-11-09  437   * The device attribute colour1 is intended to adjust the colour space.
004e74105bb360 Florian Eckert 2021-11-09  438   * The colour strength can be controlled via the current in 125uA steps.
004e74105bb360 Florian Eckert 2021-11-09  439   * The maximum current for the individual channels is limited to 24mA.
004e74105bb360 Florian Eckert 2021-11-09  440   * To set a new RGB value, 3 values must be passed. This value may not be
004e74105bb360 Florian Eckert 2021-11-09  441   * less than 0 and also not greater than 194. The chip can only process the
004e74105bb360 Florian Eckert 2021-11-09  442   * maximum current of 24mA. This means that any value greater than 194
004e74105bb360 Florian Eckert 2021-11-09  443   * cannot be set.
004e74105bb360 Florian Eckert 2021-11-09  444   */
004e74105bb360 Florian Eckert 2021-11-09  445  static ssize_t current_color0_show(struct device *dev,
004e74105bb360 Florian Eckert 2021-11-09  446  		struct device_attribute *a,
004e74105bb360 Florian Eckert 2021-11-09  447  		char *buf)
004e74105bb360 Florian Eckert 2021-11-09  448  {
004e74105bb360 Florian Eckert 2021-11-09  449  	struct i2c_client *client = to_i2c_client(dev);
004e74105bb360 Florian Eckert 2021-11-09  450  	struct ktd20xx *chip = i2c_get_clientdata(client);
004e74105bb360 Florian Eckert 2021-11-09  451  	unsigned int value;
004e74105bb360 Florian Eckert 2021-11-09  452  	int len = 0;
004e74105bb360 Florian Eckert 2021-11-09  453  
004e74105bb360 Florian Eckert 2021-11-09  454  	mutex_lock(&chip->lock);
004e74105bb360 Florian Eckert 2021-11-09  455  	regmap_read(chip->regmap, KTD20XX_IRED0, &value);
004e74105bb360 Florian Eckert 2021-11-09  456  	len += sprintf(buf + len, "%d", value);
004e74105bb360 Florian Eckert 2021-11-09  457  	len += sprintf(buf + len, " ");
004e74105bb360 Florian Eckert 2021-11-09  458  
004e74105bb360 Florian Eckert 2021-11-09  459  	regmap_read(chip->regmap, KTD20XX_IGRN0, &value);
004e74105bb360 Florian Eckert 2021-11-09  460  	len += sprintf(buf + len, "%d", value);
004e74105bb360 Florian Eckert 2021-11-09  461  	len += sprintf(buf + len, " ");
004e74105bb360 Florian Eckert 2021-11-09  462  
004e74105bb360 Florian Eckert 2021-11-09  463  	regmap_read(chip->regmap, KTD20XX_IBLU0, &value);
004e74105bb360 Florian Eckert 2021-11-09  464  	len += sprintf(buf + len, "%d", value);
004e74105bb360 Florian Eckert 2021-11-09  465  	mutex_unlock(&chip->lock);
004e74105bb360 Florian Eckert 2021-11-09  466  
004e74105bb360 Florian Eckert 2021-11-09  467  	buf[len++] = '\n';
004e74105bb360 Florian Eckert 2021-11-09  468  	return len;
004e74105bb360 Florian Eckert 2021-11-09  469  }
004e74105bb360 Florian Eckert 2021-11-09  470  
004e74105bb360 Florian Eckert 2021-11-09  471  static ssize_t current_color0_store(struct device *dev,
004e74105bb360 Florian Eckert 2021-11-09  472  		struct device_attribute *a,
004e74105bb360 Florian Eckert 2021-11-09  473  		const char *buf, size_t size)
004e74105bb360 Florian Eckert 2021-11-09  474  {
004e74105bb360 Florian Eckert 2021-11-09  475  	struct i2c_client *client = to_i2c_client(dev);
004e74105bb360 Florian Eckert 2021-11-09  476  	struct ktd20xx *chip = i2c_get_clientdata(client);
004e74105bb360 Florian Eckert 2021-11-09  477  	unsigned int value[3];
004e74105bb360 Florian Eckert 2021-11-09  478  	int i;
004e74105bb360 Florian Eckert 2021-11-09  479  	ssize_t ret;
004e74105bb360 Florian Eckert 2021-11-09  480  
004e74105bb360 Florian Eckert 2021-11-09  481  	ret = sscanf(buf, "%u %u %u", &value[0], &value[1], &value[2]);
004e74105bb360 Florian Eckert 2021-11-09  482  	if (ret < 3) {
004e74105bb360 Florian Eckert 2021-11-09  483  		ret = -EINVAL;
004e74105bb360 Florian Eckert 2021-11-09  484  		goto err_out;
004e74105bb360 Florian Eckert 2021-11-09  485  	}
004e74105bb360 Florian Eckert 2021-11-09  486  
004e74105bb360 Florian Eckert 2021-11-09  487  	for (i = 0; i < 3; i++) {
004e74105bb360 Florian Eckert 2021-11-09  488  		if (value[i] > 194) {
004e74105bb360 Florian Eckert 2021-11-09  489  			ret = -EINVAL;
004e74105bb360 Florian Eckert 2021-11-09  490  			goto err_out;
004e74105bb360 Florian Eckert 2021-11-09  491  		}
004e74105bb360 Florian Eckert 2021-11-09 @492  		if (value[i] < 0) {
                                                            ^^^^^^^^^^^^
Impossible

004e74105bb360 Florian Eckert 2021-11-09  493  			ret = -EINVAL;
004e74105bb360 Florian Eckert 2021-11-09  494  			goto err_out;
004e74105bb360 Florian Eckert 2021-11-09  495  		}
004e74105bb360 Florian Eckert 2021-11-09  496  	}
004e74105bb360 Florian Eckert 2021-11-09  497  
004e74105bb360 Florian Eckert 2021-11-09  498  	mutex_lock(&chip->lock);
004e74105bb360 Florian Eckert 2021-11-09  499  	regmap_write(chip->regmap, KTD20XX_IRED0, value[0]);
004e74105bb360 Florian Eckert 2021-11-09  500  	regmap_write(chip->regmap, KTD20XX_IGRN0, value[1]);
004e74105bb360 Florian Eckert 2021-11-09  501  	regmap_write(chip->regmap, KTD20XX_IBLU0, value[2]);
004e74105bb360 Florian Eckert 2021-11-09  502  	mutex_unlock(&chip->lock);
004e74105bb360 Florian Eckert 2021-11-09  503  	return size;
004e74105bb360 Florian Eckert 2021-11-09  504  
004e74105bb360 Florian Eckert 2021-11-09  505  err_out:
004e74105bb360 Florian Eckert 2021-11-09  506  	return ret;
004e74105bb360 Florian Eckert 2021-11-09  507  }

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org


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

end of thread, other threads:[~2021-11-22 10:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-09 10:08 [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Florian Eckert
2021-11-09 10:08 ` [PATCH 1/2] leds: ktd20xx: Add the KTD20xx family of the " Florian Eckert
2021-11-09 23:29   ` Pavel Machek
2021-11-22 10:32   ` Dan Carpenter
2021-11-09 10:08 ` [PATCH 2/2] dt: bindings: KTD20xx: Introduce the ktd20xx family of RGB drivers Florian Eckert
2021-11-09 23:29 ` [PATCH 0/2] leds: Add KTD20xx RGB LEDs driver from Kinetic Pavel Machek
2021-11-10  9:35   ` Florian Eckert
2021-11-22  7:17   ` Florian Eckert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).