Jacek
Replying to code comments.
On 1/15/19 3:47 PM, Jacek Anaszewski wrote:
Hi Da,
Thank you for the v2.
I will probably submit v3 outside the realm of the multi color framework.
We can always convert as Pavel pointed out.
I have some remarks below.
On 1/14/19 10:17 PM, Dan Murphy wrote:
Introduce the LP5036/30/24/18 RGB LED driver.
The difference in these parts are the number of
LED outputs where the:
LP5036 can control 36 LEDs
LP5030 can control 30 LEDs
LP5024 can control 24 LEDs
LP5018 can control 18 LEDs
The device has the ability to group LED output into control banks
so that multiple LED banks can be controlled with the same mixing and
brightness. Inversely the LEDs can also be controlled independently.
Signed-off-by: Dan Murphy <dmurphy@xxxxxx>
---
v2 - Changed the mix and module files to a single "color" file, added the LP5030
and LP5036 register mapping, added ABI documentation, updated the parsing of
DT and led sources to match DT, renamed driver to leds-lp50xx.c - https://lore.kernel.org/patchwork/patch/1026515/
 Documentation/leds/leds-lp50xx.txt | 36 ++
 drivers/leds/Kconfig | 7 +
 drivers/leds/Makefile | 1 +
 drivers/leds/leds-lp50xx.c | 754 +++++++++++++++++++++++++++++
 4 files changed, 798 insertions(+)
 create mode 100644 Documentation/leds/leds-lp50xx.txt
 create mode 100644 drivers/leds/leds-lp50xx.c
diff --git a/Documentation/leds/leds-lp50xx.txt b/Documentation/leds/leds-lp50xx.txt
Please move it to
Documentation/ABI/testing/sysfs-class-led-driver-lp50xx
and use standard ABI documentation format.
Ack. I will add this file as well per the doc format.
new file mode 100644
index 000000000000..8b1b01dfdd22
--- /dev/null
+++ b/Documentation/leds/leds-lp50xx.txt
@@ -0,0 +1,36 @@
+LP5018/LP5024/LP5030/LP5036 Common Driver
+=================================================
+
+Authors: Dan Murphy <dmurphy@xxxxxx>
+
+Description
+-----------
+The LP50XX RGB LED drivers have the ability to group multiple RGB cluster
+LEDs into a single group for simultaneous control or expose single RGB cluster
+for control. This device exposes different register interfaces to control
+the cluster brightness as well as the individual RGB LEDs color intensity.
+
+RGB Cluster Color Control
+-------------------------
+The LP50xx driver will expose a file called "color" for each LED class instance
+defined. This file will accept a 24-bit RGB value in which the the color of the
+RGB LEDs will be set.
+
+The 24-bit RGB value passed in follows the pattern 0xXXRRGGBB
+XX - Do not care ignored by the driver
+RR - is the 8 bit Red LED value
+GG - is the 8 bit Green LED value
+BB - is the 8 bit Blue LED value
+
+Example:
+LED module output 4 of the LP5024 will be a yellow color:
+echo 0xe6de00 > /sys/class/leds/lp5024\:led4_mod/color
+
+LED module output 4 of the LP5024 will be dimmed 50%:
+echo 0x80 > /sys/class/leds/lp5024\:led4_mod/brightness
+
+LED banked RGBs of the LP5036 will be a white color:
+echo 0xffffff > /sys/class/leds/lp5036\:led_banked/color
This part with example cans remain in Documentation/leds if you like.
+
+LED banked RGBs of the LP50364 will be dimmed 50%:
+echo 0x80 > /sys/class/leds/lp5036\:led_banked/brightness
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index a72f97fca57b..5f413445a667 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -326,6 +326,13 @@ config LEDS_LP3952
ÂÂÂÂÂÂÂ To compile this driver as a module, choose M here: the
ÂÂÂÂÂÂÂ module will be called leds-lp3952.
 +config LEDS_LP50XX
+ÂÂÂ tristate "LED Support for TI LP5036/30/24/18 LED driver chip"
+ÂÂÂ depends on LEDS_CLASS && REGMAP_I2C
+ÂÂÂ help
+ÂÂÂÂÂ If you say yes here you get support for the Texas Instruments
+ÂÂÂÂÂ LP5036, LP5030, LP5024 and LP5018 LED driver.
+
 config LEDS_LP55XX_COMMON
ÂÂÂÂÂ tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
ÂÂÂÂÂ depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4c1b0054f379..852eff0b773f 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_LEDS_GPIO_REGISTER)ÂÂÂ += leds-gpio-register.o
 obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
 obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
 obj-$(CONFIG_LEDS_LP3952) += leds-lp3952.o
+obj-$(CONFIG_LEDS_LP50XX)ÂÂÂÂÂÂÂ += leds-lp50xx.o
 obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
 obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
 obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c
new file mode 100644
index 000000000000..41bb2e0129c8
--- /dev/null
+++ b/drivers/leds/leds-lp50xx.c
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: GPL-2.0
+/* TI LP50XX LED chip family driver
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
Let's use uniform "//" comment style here.
Ack
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#define LP50XX_DEV_CFG0ÂÂÂÂÂÂÂ 0x00
+#define LP50XX_DEV_CFG1ÂÂÂÂÂÂÂ 0x01
+#define LP50XX_LED_CFG0ÂÂÂÂÂÂÂ 0x02
+
+/* LP5018 and LP5024 registers */
+#define LP5024_BNK_BRTÂÂÂÂÂÂÂ 0x03
+#define LP5024_BNKA_CLRÂÂÂÂÂÂÂ 0x04
+#define LP5024_BNKB_CLRÂÂÂÂÂÂÂ 0x05
+#define LP5024_BNKC_CLRÂÂÂÂÂÂÂ 0x06
+#define LP5024_LED0_BRTÂÂÂÂÂÂÂ 0x07
+#define LP5024_LED1_BRTÂÂÂÂÂÂÂ 0x08
+#define LP5024_LED2_BRTÂÂÂÂÂÂÂ 0x09
+#define LP5024_LED3_BRTÂÂÂÂÂÂÂ 0x0a
+#define LP5024_LED4_BRTÂÂÂÂÂÂÂ 0x0b
+#define LP5024_LED5_BRTÂÂÂÂÂÂÂ 0x0c
+#define LP5024_LED6_BRTÂÂÂÂÂÂÂ 0x0d
+#define LP5024_LED7_BRTÂÂÂÂÂÂÂ 0x0e
+
+#define LP5024_OUT0_CLRÂÂÂÂÂÂÂ 0x0f
+#define LP5024_OUT1_CLRÂÂÂÂÂÂÂ 0x10
+#define LP5024_OUT2_CLRÂÂÂÂÂÂÂ 0x11
+#define LP5024_OUT3_CLRÂÂÂÂÂÂÂ 0x12
+#define LP5024_OUT4_CLRÂÂÂÂÂÂÂ 0x13
+#define LP5024_OUT5_CLRÂÂÂÂÂÂÂ 0x14
+#define LP5024_OUT6_CLRÂÂÂÂÂÂÂ 0x15
+#define LP5024_OUT7_CLRÂÂÂÂÂÂÂ 0x16
+#define LP5024_OUT8_CLRÂÂÂÂÂÂÂ 0x17
+#define LP5024_OUT9_CLRÂÂÂÂÂÂÂ 0x18
+#define LP5024_OUT10_CLRÂÂÂ 0x19
+#define LP5024_OUT11_CLRÂÂÂ 0x1a
+#define LP5024_OUT12_CLRÂÂÂ 0x1b
+#define LP5024_OUT13_CLRÂÂÂ 0x1c
+#define LP5024_OUT14_CLRÂÂÂ 0x1d
+#define LP5024_OUT15_CLRÂÂÂ 0x1e
+#define LP5024_OUT16_CLRÂÂÂ 0x1f
+#define LP5024_OUT17_CLRÂÂÂ 0x20
+#define LP5024_OUT18_CLRÂÂÂ 0x21
+#define LP5024_OUT19_CLRÂÂÂ 0x22
+#define LP5024_OUT20_CLRÂÂÂ 0x23
+#define LP5024_OUT21_CLRÂÂÂ 0x24
+#define LP5024_OUT22_CLRÂÂÂ 0x25
+#define LP5024_OUT23_CLRÂÂÂ 0x26
+#define LP5024_RESETÂÂÂÂÂÂÂ 0x27
+
+/* LP5030 and LP5036 registers */
+#define LP5036_LED_CFG1ÂÂÂÂÂÂÂ 0x03
+#define LP5036_BNK_BRTÂÂÂÂÂÂÂ 0x04
+#define LP5036_BNKA_CLRÂÂÂÂÂÂÂ 0x05
+#define LP5036_BNKB_CLRÂÂÂÂÂÂÂ 0x06
+#define LP5036_BNKC_CLRÂÂÂÂÂÂÂ 0x07
+#define LP5036_LED0_BRTÂÂÂÂÂÂÂ 0x08
+#define LP5036_LED1_BRTÂÂÂÂÂÂÂ 0x09
+#define LP5036_LED2_BRTÂÂÂÂÂÂÂ 0x0a
+#define LP5036_LED3_BRTÂÂÂÂÂÂÂ 0x0b
+#define LP5036_LED4_BRTÂÂÂÂÂÂÂ 0x0c
+#define LP5036_LED5_BRTÂÂÂÂÂÂÂ 0x0d
+#define LP5036_LED6_BRTÂÂÂÂÂÂÂ 0x0e
+#define LP5036_LED7_BRTÂÂÂÂÂÂÂ 0x0f
+#define LP5036_LED8_BRTÂÂÂÂÂÂÂ 0x10
+#define LP5036_LED9_BRTÂÂÂÂÂÂÂ 0x11
+#define LP5036_LED10_BRTÂÂÂ 0x12
+#define LP5036_LED11_BRTÂÂÂ 0x13
+
+#define LP5036_OUT0_CLRÂÂÂÂÂÂÂ 0x14
+#define LP5036_OUT1_CLRÂÂÂÂÂÂÂ 0x15
+#define LP5036_OUT2_CLRÂÂÂÂÂÂÂ 0x16
+#define LP5036_OUT3_CLRÂÂÂÂÂÂÂ 0x17
+#define LP5036_OUT4_CLRÂÂÂÂÂÂÂ 0x18
+#define LP5036_OUT5_CLRÂÂÂÂÂÂÂ 0x19
+#define LP5036_OUT6_CLRÂÂÂÂÂÂÂ 0x1a
+#define LP5036_OUT7_CLRÂÂÂÂÂÂÂ 0x1b
+#define LP5036_OUT8_CLRÂÂÂÂÂÂÂ 0x1c
+#define LP5036_OUT9_CLRÂÂÂÂÂÂÂ 0x1d
+#define LP5036_OUT10_CLRÂÂÂ 0x1e
+#define LP5036_OUT11_CLRÂÂÂ 0x1f
+#define LP5036_OUT12_CLRÂÂÂ 0x20
+#define LP5036_OUT13_CLRÂÂÂ 0x21
+#define LP5036_OUT14_CLRÂÂÂ 0x22
+#define LP5036_OUT15_CLRÂÂÂ 0x23
+#define LP5036_OUT16_CLRÂÂÂ 0x24
+#define LP5036_OUT17_CLRÂÂÂ 0x25
+#define LP5036_OUT18_CLRÂÂÂ 0x26
+#define LP5036_OUT19_CLRÂÂÂ 0x27
+#define LP5036_OUT20_CLRÂÂÂ 0x28
+#define LP5036_OUT21_CLRÂÂÂ 0x29
+#define LP5036_OUT22_CLRÂÂÂ 0x2a
+#define LP5036_OUT23_CLRÂÂÂ 0x2b
+#define LP5036_OUT24_CLRÂÂÂ 0x2c
+#define LP5036_OUT25_CLRÂÂÂ 0x2d
+#define LP5036_OUT26_CLRÂÂÂ 0x2e
+#define LP5036_OUT27_CLRÂÂÂ 0x2f
+#define LP5036_OUT28_CLRÂÂÂ 0x30
+#define LP5036_OUT29_CLRÂÂÂ 0x31
+#define LP5036_OUT30_CLRÂÂÂ 0x32
+#define LP5036_OUT31_CLRÂÂÂ 0x33
+#define LP5036_OUT32_CLRÂÂÂ 0x34
+#define LP5036_OUT33_CLRÂÂÂ 0x35
+#define LP5036_OUT34_CLRÂÂÂ 0x36
+#define LP5036_OUT35_CLRÂÂÂ 0x37
+#define LP5036_RESETÂÂÂÂÂÂÂ 0x38
+
+#define LP50XX_SW_RESETÂÂÂÂÂÂÂ 0xff
+
+#define LP50XX_CHIP_ENÂÂÂÂÂÂÂ BIT(6)
+
+#define LP5018_MAX_LED_STRINGSÂÂÂ 6
+#define LP5024_MAX_LED_STRINGSÂÂÂ 8
+#define LP5030_MAX_LED_STRINGSÂÂÂ 10
+#define LP5036_MAX_LED_STRINGSÂÂÂ 12
+
+enum lp50xx_model {
+ÂÂÂ LP5018,
+ÂÂÂ LP5024,
+ÂÂÂ LP5030,
+ÂÂÂ LP5036,
+};
+
+struct lp50xx_led {
+ÂÂÂ u32 led_strings[LP5036_MAX_LED_STRINGS];
It is possible to have only one bank, so this can be a property
of struct lp50xx. Moreover, it doesn't need to be an array,
but should be:
unsigned long bank_modules;
Then you will be able to use bitops on it, where bit position will
refer to the id of RGB LED module assigned to the bank.
ACK. Had to think a bit on this to make sure there was enough room
but aligning on LEDn_MODULES/BANKS should be fine
+ÂÂÂ char label[LED_MAX_NAME_SIZE];
+ÂÂÂ struct led_classdev led_dev;
+ÂÂÂ struct lp50xx *priv;
+ÂÂÂ int led_number;
+ÂÂÂ u8 ctrl_bank_enabled;
+};
+
+/**
+ * struct lp50xx -
+ * @enable_gpio: Hardware enable gpio
+ * @regulator: LED supply regulator pointer
+ * @client: Pointer to the I2C client
+ * @regmap: Devices register map
+ * @dev: Pointer to the devices device struct
+ * @lock: Lock for reading/writing the device
+ * @model_id: ID of the device
+ * @leds: Array of LED strings
Please don't use capital letters for property description.
Still, some of the properties below remain undocumented.
You are referring to ID and I2C or do you mean no capitals to start the
description?
Yes I added the properties and missed documenting them.
+ */
+struct lp50xx {
+ÂÂÂ struct gpio_desc *enable_gpio;
+ÂÂÂ struct regulator *regulator;
+ÂÂÂ struct i2c_client *client;
+ÂÂÂ struct regmap *regmap;
+ÂÂÂ struct device *dev;
+ÂÂÂ struct mutex lock;
+ÂÂÂ enum lp50xx_model model_id;
+ÂÂÂ int max_leds;
+ÂÂÂ int num_of_leds;
+
+ÂÂÂ u8 led_brightness0_reg;
+ÂÂÂ u8 mix_out0_reg;
+ÂÂÂ u8 bank_brt_reg;
+ÂÂÂ u8 bank_mix_reg;
+ÂÂÂ u8 reset_reg;
+
+ÂÂÂ /* This needs to be at the end of the struct */
+ÂÂÂ struct lp50xx_led leds[];
+};
+
+static const struct reg_default lp5024_reg_defs[] = {
+ÂÂÂ {LP50XX_DEV_CFG0, 0x0},
+ÂÂÂ {LP50XX_DEV_CFG1, 0x3c},
+ÂÂÂ {LP50XX_LED_CFG0, 0x0},
+ÂÂÂ {LP5024_BNK_BRT, 0xff},
+ÂÂÂ {LP5024_BNKA_CLR, 0x0f},
+ÂÂÂ {LP5024_BNKB_CLR, 0x0f},
+ÂÂÂ {LP5024_BNKC_CLR, 0x0f},
+ÂÂÂ {LP5024_LED0_BRT, 0x0f},
+ÂÂÂ {LP5024_LED1_BRT, 0xff},
+ÂÂÂ {LP5024_LED2_BRT, 0xff},
+ÂÂÂ {LP5024_LED3_BRT, 0xff},
+ÂÂÂ {LP5024_LED4_BRT, 0xff},
+ÂÂÂ {LP5024_LED5_BRT, 0xff},
+ÂÂÂ {LP5024_LED6_BRT, 0xff},
+ÂÂÂ {LP5024_LED7_BRT, 0xff},
+ÂÂÂ {LP5024_OUT0_CLR, 0x0f},
+ÂÂÂ {LP5024_OUT1_CLR, 0x00},
+ÂÂÂ {LP5024_OUT2_CLR, 0x00},
+ÂÂÂ {LP5024_OUT3_CLR, 0x00},
+ÂÂÂ {LP5024_OUT4_CLR, 0x00},
+ÂÂÂ {LP5024_OUT5_CLR, 0x00},
+ÂÂÂ {LP5024_OUT6_CLR, 0x00},
+ÂÂÂ {LP5024_OUT7_CLR, 0x00},
+ÂÂÂ {LP5024_OUT8_CLR, 0x00},
+ÂÂÂ {LP5024_OUT9_CLR, 0x00},
+ÂÂÂ {LP5024_OUT10_CLR, 0x00},
+ÂÂÂ {LP5024_OUT11_CLR, 0x00},
+ÂÂÂ {LP5024_OUT12_CLR, 0x00},
+ÂÂÂ {LP5024_OUT13_CLR, 0x00},
+ÂÂÂ {LP5024_OUT14_CLR, 0x00},
+ÂÂÂ {LP5024_OUT15_CLR, 0x00},
+ÂÂÂ {LP5024_OUT16_CLR, 0x00},
+ÂÂÂ {LP5024_OUT17_CLR, 0x00},
+ÂÂÂ {LP5024_OUT18_CLR, 0x00},
+ÂÂÂ {LP5024_OUT19_CLR, 0x00},
+ÂÂÂ {LP5024_OUT20_CLR, 0x00},
+ÂÂÂ {LP5024_OUT21_CLR, 0x00},
+ÂÂÂ {LP5024_OUT22_CLR, 0x00},
+ÂÂÂ {LP5024_OUT23_CLR, 0x00},
+ÂÂÂ {LP5024_RESET, 0x00}
+};
+
+static const struct reg_default lp5036_reg_defs[] = {
+ÂÂÂ {LP50XX_DEV_CFG0, 0x0},
+ÂÂÂ {LP50XX_DEV_CFG1, 0x3c},
+ÂÂÂ {LP50XX_LED_CFG0, 0x0},
+ÂÂÂ {LP5036_LED_CFG1, 0x0},
+ÂÂÂ {LP5036_BNK_BRT, 0xff},
+ÂÂÂ {LP5036_BNKA_CLR, 0x0f},
+ÂÂÂ {LP5036_BNKB_CLR, 0x0f},
+ÂÂÂ {LP5036_BNKC_CLR, 0x0f},
+ÂÂÂ {LP5036_LED0_BRT, 0x0f},
+ÂÂÂ {LP5036_LED1_BRT, 0xff},
+ÂÂÂ {LP5036_LED2_BRT, 0xff},
+ÂÂÂ {LP5036_LED3_BRT, 0xff},
+ÂÂÂ {LP5036_LED4_BRT, 0xff},
+ÂÂÂ {LP5036_LED5_BRT, 0xff},
+ÂÂÂ {LP5036_LED6_BRT, 0xff},
+ÂÂÂ {LP5036_LED7_BRT, 0xff},
+ÂÂÂ {LP5036_OUT0_CLR, 0x0f},
+ÂÂÂ {LP5036_OUT1_CLR, 0x00},
+ÂÂÂ {LP5036_OUT2_CLR, 0x00},
+ÂÂÂ {LP5036_OUT3_CLR, 0x00},
+ÂÂÂ {LP5036_OUT4_CLR, 0x00},
+ÂÂÂ {LP5036_OUT5_CLR, 0x00},
+ÂÂÂ {LP5036_OUT6_CLR, 0x00},
+ÂÂÂ {LP5036_OUT7_CLR, 0x00},
+ÂÂÂ {LP5036_OUT8_CLR, 0x00},
+ÂÂÂ {LP5036_OUT9_CLR, 0x00},
+ÂÂÂ {LP5036_OUT10_CLR, 0x00},
+ÂÂÂ {LP5036_OUT11_CLR, 0x00},
+ÂÂÂ {LP5036_OUT12_CLR, 0x00},
+ÂÂÂ {LP5036_OUT13_CLR, 0x00},
+ÂÂÂ {LP5036_OUT14_CLR, 0x00},
+ÂÂÂ {LP5036_OUT15_CLR, 0x00},
+ÂÂÂ {LP5036_OUT16_CLR, 0x00},
+ÂÂÂ {LP5036_OUT17_CLR, 0x00},
+ÂÂÂ {LP5036_OUT18_CLR, 0x00},
+ÂÂÂ {LP5036_OUT19_CLR, 0x00},
+ÂÂÂ {LP5036_OUT20_CLR, 0x00},
+ÂÂÂ {LP5036_OUT21_CLR, 0x00},
+ÂÂÂ {LP5036_OUT22_CLR, 0x00},
+ÂÂÂ {LP5036_OUT23_CLR, 0x00},
+ÂÂÂ {LP5036_OUT24_CLR, 0x00},
+ÂÂÂ {LP5036_OUT25_CLR, 0x00},
+ÂÂÂ {LP5036_OUT26_CLR, 0x00},
+ÂÂÂ {LP5036_OUT27_CLR, 0x00},
+ÂÂÂ {LP5036_OUT28_CLR, 0x00},
+ÂÂÂ {LP5036_OUT29_CLR, 0x00},
+ÂÂÂ {LP5036_OUT30_CLR, 0x00},
+ÂÂÂ {LP5036_OUT31_CLR, 0x00},
+ÂÂÂ {LP5036_OUT32_CLR, 0x00},
+ÂÂÂ {LP5036_OUT33_CLR, 0x00},
+ÂÂÂ {LP5036_OUT34_CLR, 0x00},
+ÂÂÂ {LP5036_OUT35_CLR, 0x00},
+ÂÂÂ {LP5036_RESET, 0x00}
+};
+
+static const struct regmap_config lp5024_regmap_config = {
+ÂÂÂ .reg_bits = 8,
+ÂÂÂ .val_bits = 8,
+
+ÂÂÂ .max_register = LP5024_RESET,
+ÂÂÂ .reg_defaults = lp5024_reg_defs,
+ÂÂÂ .num_reg_defaults = ARRAY_SIZE(lp5024_reg_defs),
+ÂÂÂ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config lp5036_regmap_config = {
+ÂÂÂ .reg_bits = 8,
+ÂÂÂ .val_bits = 8,
+
+ÂÂÂ .max_register = LP5036_RESET,
+ÂÂÂ .reg_defaults = lp5036_reg_defs,
+ÂÂÂ .num_reg_defaults = ARRAY_SIZE(lp5036_reg_defs),
+ÂÂÂ .cache_type = REGCACHE_RBTREE,
+};
+
+static ssize_t color_show(struct device *dev,
+ÂÂÂÂÂÂÂÂÂÂÂ struct device_attribute *attr,
+ÂÂÂÂÂÂÂÂÂÂÂ char *buf)
+{
+ÂÂÂ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ÂÂÂ struct lp50xx_led *led = container_of(led_cdev, struct lp50xx_led,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_dev);
+ÂÂÂ struct lp50xx *priv = led->priv;
+ÂÂÂ unsigned int red_val, green_val, blue_val;
+ÂÂÂ u8 red_reg, green_reg, blue_reg;
+ÂÂÂ u32 mix_value = 0;
+ÂÂÂ u8 led_offset;
+ÂÂÂ int ret;
+
+ÂÂÂ if (led->ctrl_bank_enabled) {
+ÂÂÂÂÂÂÂ red_reg = priv->bank_mix_reg;
+ÂÂÂÂÂÂÂ green_reg = priv->bank_mix_reg + 1;
+ÂÂÂÂÂÂÂ blue_reg = priv->bank_mix_reg + 2;
+ÂÂÂ } else {
+ÂÂÂÂÂÂÂ led_offset = (led->led_number * 3);
+ÂÂÂÂÂÂÂ red_reg = priv->mix_out0_reg + led_offset;
+ÂÂÂÂÂÂÂ green_reg = priv->mix_out0_reg + led_offset + 1;
+ÂÂÂÂÂÂÂ blue_reg = priv->mix_out0_reg + led_offset + 2;
+ÂÂÂ }
+
+ÂÂÂ ret = regmap_read(priv->regmap, red_reg, &red_val);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot read LED value\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ ret = regmap_read(priv->regmap, green_reg, &green_val);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot read LED value\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ ret = regmap_read(priv->regmap, blue_reg, &blue_val);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot read LED value\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ mix_value = (red_val << 16 | green_val << 8 | blue_val);
+
+out:
+ÂÂÂ return scnprintf(buf, PAGE_SIZE, "0x%X\n", mix_value);
+}
+
+static ssize_t color_store(struct device *dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct device_attribute *attr,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ const char *buf, size_t size)
+{
+ÂÂÂ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ÂÂÂ struct lp50xx_led *led = container_of(led_cdev, struct lp50xx_led,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_dev);
+ÂÂÂ struct lp50xx *priv = led->priv;
+ÂÂÂ u8 led_offset;
+ÂÂÂ unsigned long mix_value;
+ÂÂÂ u8 red_reg, green_reg, blue_reg;
+ÂÂÂ u8 red_val, green_val, blue_val;
+ÂÂÂ int ret;
+
+ÂÂÂ ret = kstrtoul(buf, 0, &mix_value);
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ if (led->ctrl_bank_enabled) {
+ÂÂÂÂÂÂÂ red_reg = priv->bank_mix_reg;
+ÂÂÂÂÂÂÂ green_reg = priv->bank_mix_reg + 1;
+ÂÂÂÂÂÂÂ blue_reg = priv->bank_mix_reg + 2;
+ÂÂÂ } else {
+ÂÂÂÂÂÂÂ led_offset = (led->led_number * 3);
+ÂÂÂÂÂÂÂ red_reg = priv->mix_out0_reg + led_offset;
+ÂÂÂÂÂÂÂ green_reg = priv->mix_out0_reg + led_offset + 1;
+ÂÂÂÂÂÂÂ blue_reg = priv->mix_out0_reg + led_offset + 2;
+ÂÂÂ }
+
+ÂÂÂ red_val = (mix_value & 0xff0000) >> 16;
+ÂÂÂ green_val = (mix_value & 0xff00) >> 8;
+ÂÂÂ blue_val = (mix_value & 0xff);
I've been rather thinking about space separated list of decimal
"red green blue" values, but maybe this way it will be less
controversial. Let's if there will be other opinions.
Has anything changed based on this?
Should I change the file name from "color" to something else?
+
+ÂÂÂ ret = regmap_write(priv->regmap, red_reg, red_val);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot write LED value\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ ret = regmap_write(priv->regmap, green_reg, green_val);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot write LED value\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ ret = regmap_write(priv->regmap, blue_reg, blue_val);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot write LED value\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+out:
+ÂÂÂ return size;
+}
+
+static DEVICE_ATTR_RW(color);
+
+static struct attribute *lp50xx_led_color_attrs[] = {
+ÂÂÂ &dev_attr_color.attr,
+ÂÂÂ NULL
+};
+ATTRIBUTE_GROUPS(lp50xx_led_color);
+
+static int lp50xx_brightness_set(struct led_classdev *led_cdev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ enum led_brightness brt_val)
+{
+ÂÂÂ struct lp50xx_led *led = container_of(led_cdev, struct lp50xx_led,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_dev);
+ÂÂÂ int ret = 0;
+ÂÂÂ u8 reg_val;
+
+ÂÂÂ mutex_lock(&led->priv->lock);
+
+ÂÂÂ if (led->ctrl_bank_enabled)
+ÂÂÂÂÂÂÂ reg_val = led->priv->bank_brt_reg;
+ÂÂÂ else
+ÂÂÂÂÂÂÂ reg_val = led->priv->led_brightness0_reg + led->led_number;
+
+ÂÂÂ ret = regmap_write(led->priv->regmap, reg_val, brt_val);
+
+ÂÂÂ mutex_unlock(&led->priv->lock);
+
+ÂÂÂ return ret;
+}
+
+static enum led_brightness lp50xx_brightness_get(struct led_classdev *led_cdev)
+{
+ÂÂÂ struct lp50xx_led *led = container_of(led_cdev, struct lp50xx_led,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_dev);
+ÂÂÂ unsigned int brt_val;
+ÂÂÂ u8 reg_val;
+ÂÂÂ int ret;
+
+ÂÂÂ mutex_lock(&led->priv->lock);
+
+ÂÂÂ if (led->ctrl_bank_enabled)
+ÂÂÂÂÂÂÂ reg_val = led->priv->bank_brt_reg;
+ÂÂÂ else
+ÂÂÂÂÂÂÂ reg_val = led->priv->led_brightness0_reg + led->led_number;
+
+ÂÂÂ ret = regmap_read(led->priv->regmap, reg_val, &brt_val);
+
+ÂÂÂ mutex_unlock(&led->priv->lock);
+
+ÂÂÂ return brt_val;
+}
+
+static void lp50xx_set_led_values(struct lp50xx *priv)
+{
+ÂÂÂ if (priv->model_id == LP5018 || priv->model_id == LP5024) {
+ÂÂÂÂÂÂÂ priv->led_brightness0_reg = LP5024_LED0_BRT;
+ÂÂÂÂÂÂÂ priv->mix_out0_reg = LP5024_OUT0_CLR;
+ÂÂÂÂÂÂÂ priv->bank_brt_reg = LP5024_BNK_BRT;
+ÂÂÂÂÂÂÂ priv->bank_mix_reg = LP5024_BNKA_CLR;
+ÂÂÂÂÂÂÂ priv->reset_reg = LP5024_RESET;
+ÂÂÂ } else {
+ÂÂÂÂÂÂÂ priv->led_brightness0_reg = LP5036_LED0_BRT;
+ÂÂÂÂÂÂÂ priv->mix_out0_reg = LP5036_OUT0_CLR;
+ÂÂÂÂÂÂÂ priv->bank_brt_reg = LP5036_BNK_BRT;
+ÂÂÂÂÂÂÂ priv->bank_mix_reg = LP5036_BNKA_CLR;
+ÂÂÂÂÂÂÂ priv->reset_reg = LP5036_RESET;
+ÂÂÂ }
+}
+
+static int lp50xx_set_banks(struct lp50xx *priv)
+{
+ÂÂÂ struct lp50xx_led *led;
+ÂÂÂ u8 led_ctrl_enable = 0;
+ÂÂÂ u8 led1_ctrl_enable = 0;
+ÂÂÂ u8 ctrl_ext = 0;
+ÂÂÂ int i, j;
+ÂÂÂ int ret;
+
+ÂÂÂ for (i = 0; i <= priv->num_of_leds; i++) {
+ÂÂÂÂÂÂÂ led = &priv->leds[i];
+ÂÂÂÂÂÂÂ if (!led->ctrl_bank_enabled)
+ÂÂÂÂÂÂÂÂÂÂÂ continue;
+
+ÂÂÂÂÂÂÂ for (j = 0; j <= priv->max_leds - 1; j++) {
+ÂÂÂÂÂÂÂÂÂÂÂ if (led->led_strings[j]ÂÂÂ > (LP5024_MAX_LED_STRINGS - 1)) {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ctrl_ext = led->led_strings[j] - LP5024_MAX_LED_STRINGS;
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led1_ctrl_enable |= (1 << ctrl_ext);
+ÂÂÂÂÂÂÂÂÂÂÂ } else {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led_ctrl_enable |= (1 << led->led_strings[j]);
+ÂÂÂÂÂÂÂÂÂÂÂ }
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
With centralized bank_modules flags it should look simpler.
I don't think so. The LP5030 and LP5036 have 2 registers to denote Module vs banked.
But once I convert it we will know.
+
+ÂÂÂ ret = regmap_write(priv->regmap, LP50XX_LED_CFG0, led_ctrl_enable);
+
+ÂÂÂ if (led1_ctrl_enable)
+ÂÂÂÂÂÂÂ ret = regmap_write(priv->regmap, LP5036_LED_CFG1,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led1_ctrl_enable);
+
+ÂÂÂ return ret;
+}
+
+static int lp50xx_init(struct lp50xx *priv)
+{
+ÂÂÂ int ret;
+
+ÂÂÂ lp50xx_set_led_values(priv);
+
+ÂÂÂ if (priv->enable_gpio) {
+ÂÂÂÂÂÂÂ gpiod_direction_output(priv->enable_gpio, 1);
+ÂÂÂ } else {
+ÂÂÂÂÂÂÂ ret = regmap_write(priv->regmap, priv->reset_reg,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ LP50XX_SW_RESET);
+ÂÂÂÂÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(&priv->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Cannot reset the device\n");
+ÂÂÂÂÂÂÂÂÂÂÂ goto out;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
+
+ÂÂÂ ret = lp50xx_set_banks(priv);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot set the banks\n");
+ÂÂÂÂÂÂÂ goto out;
+ÂÂÂ }
+
+ÂÂÂ ret = regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
+
+out:
+ÂÂÂ return ret;
+}
+
+static int lp50xx_probe_dt(struct lp50xx *priv)
+{
+ÂÂÂ struct fwnode_handle *child = NULL;
+ÂÂÂ struct lp50xx_led *led;
+ÂÂÂ int control_bank_defined = 0;
+ÂÂÂ const char *name;
+ÂÂÂ int led_number;
+ÂÂÂ size_t i = 0;
+ÂÂÂ int ret;
+
+ÂÂÂ priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "enable", GPIOD_OUT_LOW);
+ÂÂÂ if (IS_ERR(priv->enable_gpio)) {
+ÂÂÂÂÂÂÂ ret = PTR_ERR(priv->enable_gpio);
+ÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂ ret);
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
+ÂÂÂ if (IS_ERR(priv->regulator))
+ÂÂÂÂÂÂÂ priv->regulator = NULL;
+
+ÂÂÂ if (priv->model_id == LP5018)
+ÂÂÂÂÂÂÂ priv->max_leds = LP5018_MAX_LED_STRINGS;
+ÂÂÂ else if (priv->model_id == LP5024)
+ÂÂÂÂÂÂÂ priv->max_leds = LP5024_MAX_LED_STRINGS;
+ÂÂÂ else if (priv->model_id == LP5030)
+ÂÂÂÂÂÂÂ priv->max_leds = LP5030_MAX_LED_STRINGS;
+ÂÂÂ else
+ÂÂÂÂÂÂÂ priv->max_leds = LP5036_MAX_LED_STRINGS;
Let's change STRINGS to MODULEs.
ACK.
+
+ÂÂÂ device_for_each_child_node(&priv->client->dev, child) {
+ÂÂÂÂÂÂÂ led = &priv->leds[i];
+
+ÂÂÂÂÂÂÂ if (fwnode_property_present(child, "ti,led-bank")) {
+ÂÂÂÂÂÂÂÂÂÂÂ led->ctrl_bank_enabled = 1;
+ÂÂÂÂÂÂÂÂÂÂÂ if (!control_bank_defined)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ control_bank_defined = 1;
+ÂÂÂÂÂÂÂÂÂÂÂ else {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ dev_err(&priv->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "ti,led-bank defined twice\n");
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ fwnode_handle_put(child);
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ goto child_out;
+ÂÂÂÂÂÂÂÂÂÂÂ }
+ÂÂÂÂÂÂÂ } else {
+ÂÂÂÂÂÂÂÂÂÂÂ led->ctrl_bank_enabled = 0;
+ÂÂÂÂÂÂÂ }
Any bit set in bank_modules will signify that bank is defined
and enabled.
This is stored so that when the brightness or color is called the correct
register either bank or LEDn_MODULES is used.
This way I don't have to go out and read the devices bank enable register
and try to determine what was banked and what was not.
+ÂÂÂÂÂÂÂ if (led->ctrl_bank_enabled) {
+ÂÂÂÂÂÂÂÂÂÂÂ ret = fwnode_property_read_u32_array(child,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "ti,led-bank",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ NULL, 0);
+ÂÂÂÂÂÂÂÂÂÂÂ ret = fwnode_property_read_u32_array(child,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "ti,led-bank",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ led->led_strings,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret);
+
+ÂÂÂÂÂÂÂÂÂÂÂ led->led_number = led->led_strings[0];
+
+ÂÂÂÂÂÂÂ } else {
+ÂÂÂÂÂÂÂÂÂÂÂ ret = fwnode_property_read_u32(child, "ti,led-module",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &led_number);
+
+ÂÂÂÂÂÂÂÂÂÂÂ led->led_number = led_number;
+ÂÂÂÂÂÂÂ }
+ÂÂÂÂÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(&priv->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "led sourcing property missing\n");
+ÂÂÂÂÂÂÂÂÂÂÂ fwnode_handle_put(child);
+ÂÂÂÂÂÂÂÂÂÂÂ goto child_out;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ if (led_number > priv->max_leds) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(&priv->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "led-sources property is invalid\n");
+ÂÂÂÂÂÂÂÂÂÂÂ ret = -EINVAL;
+ÂÂÂÂÂÂÂÂÂÂÂ fwnode_handle_put(child);
+ÂÂÂÂÂÂÂÂÂÂÂ goto child_out;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ ret = fwnode_property_read_string(child, "label", &name);
+ÂÂÂÂÂÂÂ if (ret)
+ÂÂÂÂÂÂÂÂÂÂÂ snprintf(led->label, sizeof(led->label),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "%s::", priv->client->name);
+ÂÂÂÂÂÂÂ else
+ÂÂÂÂÂÂÂÂÂÂÂ snprintf(led->label, sizeof(led->label),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "%s:%s", priv->client->name, name);
+
+ÂÂÂÂÂÂÂ fwnode_property_read_string(child, "linux,default-trigger",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &led->led_dev.default_trigger);
+
+ÂÂÂÂÂÂÂ led->priv = priv;
+ÂÂÂÂÂÂÂ led->led_dev.name = led->label;
+ÂÂÂÂÂÂÂ led->led_dev.brightness_set_blocking = lp50xx_brightness_set;
+ÂÂÂÂÂÂÂ led->led_dev.brightness_get = lp50xx_brightness_get;
+ÂÂÂÂÂÂÂ led->led_dev.groups = lp50xx_led_color_groups;
+
+ÂÂÂÂÂÂÂ ret = devm_led_classdev_register(&priv->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &led->led_dev);
+ÂÂÂÂÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(&priv->client->dev, "led register err: %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret);
+ÂÂÂÂÂÂÂÂÂÂÂ fwnode_handle_put(child);
+ÂÂÂÂÂÂÂÂÂÂÂ goto child_out;
+ÂÂÂÂÂÂÂ }
+ÂÂÂÂÂÂÂ i++;
+ÂÂÂ }
+ÂÂÂ priv->num_of_leds = i;
+
+child_out:
+ÂÂÂ return ret;
+}
+
+static int lp50xx_probe(struct i2c_client *client,
+ÂÂÂÂÂÂÂÂÂÂÂ const struct i2c_device_id *id)
+{
+ÂÂÂ struct lp50xx *led;
+ÂÂÂ int count;
+ÂÂÂ int ret;
+
+ÂÂÂ count = device_get_child_node_count(&client->dev);
+ÂÂÂ if (!count) {
+ÂÂÂÂÂÂÂ dev_err(&client->dev, "LEDs are not defined in device tree!");
+ÂÂÂÂÂÂÂ return -ENODEV;
+ÂÂÂ }
+
+ÂÂÂ led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂ if (!led)
+ÂÂÂÂÂÂÂ return -ENOMEM;
+
+ÂÂÂ mutex_init(&led->lock);
+ÂÂÂ led->client = client;
+ÂÂÂ led->dev = &client->dev;
+ÂÂÂ led->model_id = id->driver_data;
+ÂÂÂ i2c_set_clientdata(client, led);
+
+ÂÂÂ if (led->model_id == LP5018 || led->model_id == LP5024)
+ÂÂÂÂÂÂÂ led->regmap = devm_regmap_init_i2c(client,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &lp5024_regmap_config);
+ÂÂÂ else
+ÂÂÂÂÂÂÂ led->regmap = devm_regmap_init_i2c(client,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &lp5036_regmap_config);
+
+ÂÂÂ if (IS_ERR(led->regmap)) {
+ÂÂÂÂÂÂÂ ret = PTR_ERR(led->regmap);
+ÂÂÂÂÂÂÂ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂ ret);
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ ret = lp50xx_probe_dt(led);
+ÂÂÂ if (ret)
+ÂÂÂÂÂïï return ret;
+
+ÂÂÂ ret = lp50xx_init(led);
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ return ret;
+
+ÂÂÂ return 0;
+}
+
+static int lp50xx_remove(struct i2c_client *client)
+{
+ÂÂÂ struct lp50xx *led = i2c_get_clientdata(client);
+ÂÂÂ int ret;
+
+ÂÂÂ ret = regmap_update_bits(led->regmap, LP50XX_DEV_CFG0,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ LP50XX_CHIP_EN, 0);
+ÂÂÂ if (ret) {
+ÂÂÂÂÂÂÂ dev_err(&led->client->dev, "Failed to disable regulator\n");
+ÂÂÂÂÂÂÂ return ret;
+ÂÂÂ }
+
+ÂÂÂ if (led->enable_gpio)
+ÂÂÂÂÂÂÂ gpiod_direction_output(led->enable_gpio, 0);
+
+ÂÂÂ if (led->regulator) {
+ÂÂÂÂÂÂÂ ret = regulator_disable(led->regulator);
+ÂÂÂÂÂÂÂ if (ret)
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(&led->client->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Failed to disable regulator\n");
+ÂÂÂ }
+
+ÂÂÂ mutex_destroy(&led->lock);
+
+ÂÂÂ return 0;
+}
+
+static const struct i2c_device_id lp50xx_id[] = {
+ÂÂÂ { "lp5018", LP5018 },
+ÂÂÂ { "lp5024", LP5024 },
+ÂÂÂ { "lp5030", LP5030 },
+ÂÂÂ { "lp5036", LP5036 },
+ÂÂÂ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp50xx_id);
+
+static const struct of_device_id of_lp50xx_leds_match[] = {
+ÂÂÂ { .compatible = "ti,lp5018", },
+ÂÂÂ { .compatible = "ti,lp5024", },
+ÂÂÂ { .compatible = "ti,lp5030", },
+ÂÂÂ { .compatible = "ti,lp5036", },
+ÂÂÂ {},
+};
+MODULE_DEVICE_TABLE(of, of_lp50xx_leds_match);
+
+static struct i2c_driver lp50xx_driver = {
+ÂÂÂ .driver = {
+ÂÂÂÂÂÂÂ .nameÂÂÂ = "lp50xx",
+ÂÂÂÂÂÂÂ .of_match_table = of_lp50xx_leds_match,
+ÂÂÂ },
+ÂÂÂ .probeÂÂÂÂÂÂÂ = lp50xx_probe,
+ÂÂÂ .removeÂÂÂÂÂÂÂ = lp50xx_remove,
+ÂÂÂ .id_tableÂÂÂ = lp50xx_id,
+};
+module_i2c_driver(lp50xx_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP5024 LED driver");
+MODULE_AUTHOR("Dan Murphy <dmurphy@xxxxxx>");
+MODULE_LICENSE("GPL v2");