Hello
Changes from v12:
https://lore.kernel.org/patchwork/project/lkml/list/?series=413816
Add 2 patches per request.
- Updated LP55xx dt binding to LED node naming convention [18/18]
- Update LP55xx-common to use devm_* for LED class registration [12/18]
Removed the dt binding changes from the 4/18 back into 1/18
MC Framework
- Removed inline functions from the header due to not being able to build the
code as a module as ifdef was not set giving a redefinition issue when the module
was being created.
LP55xx/23/21
- Renamed intensity_fn to multicolor_brightness_fn
- Call the devm_multicolor class registration
LP50xx
- Call the devm_multicolor class registration
Dan
Dan Murphy (18):
dt: bindings: Add multicolor class dt bindings documention
dt-bindings: leds: Add multicolor ID to the color ID list
leds: Add multicolor ID to the color ID list
leds: multicolor: Introduce a multicolor class definition
dt: bindings: lp50xx: Introduce the lp50xx family of RGB drivers
leds: lp50xx: Add the LP50XX family of the RGB LED driver
dt: bindings: lp55xx: Be consistent in the document with LED acronym
dt: bindings: lp55xx: Update binding for Multicolor Framework
ARM: dts: n900: Add reg property to the LP5523 channel node
ARM: dts: imx6dl-yapp4: Add reg property to the lp5562 channel node
ARM: dts: ste-href: Add reg property to the LP5521 channel nodes
leds: lp55xx: Convert LED class registration to devm_*
leds: lp55xx: Add multicolor framework support to lp55xx
leds: lp5523: Update the lp5523 code to add multicolor brightness
function
leds: lp5521: Add multicolor framework multicolor brightness support
leds: lp55xx: Fix checkpatch file permissions issues
leds: lp5523: Fix checkpatch issues in the code
dt: bindings: Update lp55xx binding to recommended LED naming
.../ABI/testing/sysfs-class-led-multicolor | 36 +
.../bindings/leds/leds-class-multicolor.txt | 98 +++
.../devicetree/bindings/leds/leds-lp50xx.txt | 148 ++++
.../devicetree/bindings/leds/leds-lp55xx.txt | 163 +++-
Documentation/leds/index.rst | 1 +
Documentation/leds/leds-class-multicolor.rst | 96 +++
arch/arm/boot/dts/imx6dl-yapp4-common.dtsi | 14 +-
arch/arm/boot/dts/omap3-n900.dts | 29 +-
arch/arm/boot/dts/ste-href.dtsi | 22 +-
drivers/leds/Kconfig | 23 +
drivers/leds/Makefile | 2 +
drivers/leds/led-class-multicolor.c | 271 ++++++
drivers/leds/led-core.c | 1 +
drivers/leds/leds-lp50xx.c | 799 ++++++++++++++++++
drivers/leds/leds-lp5521.c | 20 +
drivers/leds/leds-lp5523.c | 39 +-
drivers/leds/leds-lp55xx-common.c | 199 ++++-
drivers/leds/leds-lp55xx-common.h | 9 +
include/dt-bindings/leds/common.h | 3 +-
include/linux/led-class-multicolor.h | 97 +++
include/linux/platform_data/leds-lp55xx.h | 7 +
21 files changed, 1980 insertions(+), 97 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-led-multicolor
create mode 100644 Documentation/devicetree/bindings/leds/leds-class-multicolor.txt
create mode 100644 Documentation/devicetree/bindings/leds/leds-lp50xx.txt
create mode 100644 Documentation/leds/leds-class-multicolor.rst
create mode 100644 drivers/leds/led-class-multicolor.c
create mode 100644 drivers/leds/leds-lp50xx.c
create mode 100644 include/linux/led-class-multicolor.h
--
2.22.0.214.g8dca754b1e
Add multicolor framework support for the lp55xx family.
Signed-off-by: Dan Murphy <[email protected]>
---
drivers/leds/Kconfig | 2 +
drivers/leds/leds-lp55xx-common.c | 175 +++++++++++++++++++---
drivers/leds/leds-lp55xx-common.h | 9 ++
include/linux/platform_data/leds-lp55xx.h | 7 +
4 files changed, 170 insertions(+), 23 deletions(-)
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index fb614a6b9afa..a121a2855c06 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -377,8 +377,10 @@ config LEDS_LP50XX
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
+ depends on OF
select FW_LOADER
select FW_LOADER_USER_HELPER
+ select LEDS_CLASS_MULTI_COLOR
help
This option supports common operations for LP5521/5523/55231/5562/8501
devices.
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index 824d1d73dde1..026ebc2f8e18 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -131,14 +131,54 @@ static struct attribute *lp55xx_led_attrs[] = {
};
ATTRIBUTE_GROUPS(lp55xx_led);
+static int lp55xx_map_channel(struct lp55xx_led *led, int color_id,
+ enum led_brightness brightness)
+{
+ int i;
+
+ for (i = 0; i < led->mc_cdev.num_leds; i++) {
+ if (led->color_component[i].color_id == color_id) {
+ led->color_component[i].brightness = brightness;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
static int lp55xx_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
+ struct led_mc_color_conversion color_component[LP55XX_MAX_GROUPED_CHAN];
struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
struct lp55xx_device_config *cfg = led->chip->cfg;
+ int ret;
+ int i;
- led->brightness = (u8)brightness;
- return cfg->brightness_fn(led);
+ if (led->mc_cdev.num_leds > 1) {
+ if (!cfg->multicolor_brightness_fn)
+ return -EINVAL;
+
+ led_mc_calc_color_components(&led->mc_cdev, brightness,
+ color_component);
+
+ for (i = 0; i < led->mc_cdev.num_leds; i++) {
+ ret = lp55xx_map_channel(led,
+ color_component[i].color_id,
+ color_component[i].brightness);
+ if (ret)
+ return ret;
+ }
+
+ ret = cfg->multicolor_brightness_fn(led);
+ if (ret)
+ return ret;
+ } else {
+ led->brightness = (u8)brightness;
+ ret = cfg->brightness_fn(led);
+ }
+
+ return ret;
}
static int lp55xx_init_led(struct lp55xx_led *led,
@@ -147,9 +187,9 @@ static int lp55xx_init_led(struct lp55xx_led *led,
struct lp55xx_platform_data *pdata = chip->pdata;
struct lp55xx_device_config *cfg = chip->cfg;
struct device *dev = &chip->cl->dev;
+ int max_channel = cfg->max_channel;
char name[32];
int ret;
- int max_channel = cfg->max_channel;
if (chan >= max_channel) {
dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
@@ -159,10 +199,34 @@ static int lp55xx_init_led(struct lp55xx_led *led,
if (pdata->led_config[chan].led_current == 0)
return 0;
+ if (pdata->led_config[chan].name) {
+ led->cdev.name = pdata->led_config[chan].name;
+ } else {
+ snprintf(name, sizeof(name), "%s:channel%d",
+ pdata->label ? : chip->cl->name, chan);
+ led->cdev.name = name;
+ }
+
+ if (pdata->led_config[chan].num_colors > 1) {
+ led->mc_cdev.led_cdev = &led->cdev;
+ led->cdev.brightness_set_blocking = lp55xx_set_brightness;
+ led->cdev.groups = lp55xx_led_groups;
+ led->mc_cdev.num_leds = pdata->led_config[chan].num_colors;
+ led->mc_cdev.available_colors =
+ pdata->led_config[chan].available_colors;
+ memcpy(led->color_component,
+ pdata->led_config[chan].color_component,
+ sizeof(led->color_component));
+ } else {
+
+ led->cdev.default_trigger =
+ pdata->led_config[chan].default_trigger;
+ led->cdev.brightness_set_blocking = lp55xx_set_brightness;
+ } led->cdev.groups = lp55xx_led_groups;
+
led->led_current = pdata->led_config[chan].led_current;
led->max_current = pdata->led_config[chan].max_current;
led->chan_nr = pdata->led_config[chan].chan_nr;
- led->cdev.default_trigger = pdata->led_config[chan].default_trigger;
if (led->chan_nr >= max_channel) {
dev_err(dev, "Use channel numbers between 0 and %d\n",
@@ -170,18 +234,11 @@ static int lp55xx_init_led(struct lp55xx_led *led,
return -EINVAL;
}
- led->cdev.brightness_set_blocking = lp55xx_set_brightness;
- led->cdev.groups = lp55xx_led_groups;
+ if (pdata->led_config[chan].num_colors > 1)
+ ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev);
+ else
+ ret = devm_led_classdev_register(dev, &led->cdev);
- if (pdata->led_config[chan].name) {
- led->cdev.name = pdata->led_config[chan].name;
- } else {
- snprintf(name, sizeof(name), "%s:channel%d",
- pdata->label ? : chip->cl->name, chan);
- led->cdev.name = name;
- }
-
- ret = devm_led_classdev_register(dev, &led->cdev);
if (ret) {
dev_err(dev, "led register err: %d\n", ret);
return ret;
@@ -538,6 +595,82 @@ void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
+static int lp5xx_parse_common_child(struct device_node *np,
+ struct lp55xx_led_config *cfg,
+ int chan_num, bool is_multicolor,
+ int color_num)
+{
+ u32 led_number;
+ int ret;
+
+ of_property_read_string(np, "chan-name",
+ &cfg[chan_num].name);
+ of_property_read_u8(np, "led-cur",
+ &cfg[chan_num].led_current);
+ of_property_read_u8(np, "max-cur",
+ &cfg[chan_num].max_current);
+
+ ret = of_property_read_u32(np, "reg", &led_number);
+ if (ret)
+ return ret;
+
+ if (led_number < 0 || led_number > 6)
+ return -EINVAL;
+
+ if (is_multicolor)
+ cfg[chan_num].color_component[color_num].output_num =
+ led_number;
+ else
+ cfg[chan_num].chan_nr = led_number;
+
+ return 0;
+}
+
+static int lp5xx_parse_channel_child(struct device_node *np,
+ struct lp55xx_led_config *cfg,
+ int child_number)
+{
+ struct device_node *child;
+ int channel_color;
+ int num_colors = 0;
+ u32 color_id;
+ int ret;
+
+ cfg[child_number].default_trigger =
+ of_get_property(np, "linux,default-trigger", NULL);
+
+ ret = of_property_read_u32(np, "color", &channel_color);
+ if (ret)
+ channel_color = ret;
+
+
+ if (channel_color == LED_COLOR_ID_MULTI) {
+ for_each_child_of_node(np, child) {
+ ret = lp5xx_parse_common_child(child, cfg,
+ child_number, true,
+ num_colors);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(child, "color", &color_id);
+ if (ret)
+ return ret;
+
+ cfg[child_number].color_component[num_colors].color_id =
+ color_id;
+ set_bit(color_id, &cfg[child_number].available_colors);
+ num_colors++;
+ }
+
+ cfg[child_number].num_colors = num_colors;
+ } else {
+ return lp5xx_parse_common_child(np, cfg, child_number, false,
+ num_colors);
+ }
+
+ return 0;
+}
+
struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
struct device_node *np)
{
@@ -546,6 +679,7 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
struct lp55xx_led_config *cfg;
int num_channels;
int i = 0;
+ int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -565,14 +699,9 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
pdata->num_channels = num_channels;
for_each_child_of_node(np, child) {
- cfg[i].chan_nr = i;
-
- of_property_read_string(child, "chan-name", &cfg[i].name);
- of_property_read_u8(child, "led-cur", &cfg[i].led_current);
- of_property_read_u8(child, "max-cur", &cfg[i].max_current);
- cfg[i].default_trigger =
- of_get_property(child, "linux,default-trigger", NULL);
-
+ ret = lp5xx_parse_channel_child(child, cfg, i);
+ if (ret)
+ return ERR_PTR(-EINVAL);
i++;
}
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h
index 783ed5103ce5..bf68b8a78930 100644
--- a/drivers/leds/leds-lp55xx-common.h
+++ b/drivers/leds/leds-lp55xx-common.h
@@ -12,6 +12,8 @@
#ifndef _LEDS_LP55XX_COMMON_H
#define _LEDS_LP55XX_COMMON_H
+#include <linux/led-class-multicolor.h>
+
enum lp55xx_engine_index {
LP55XX_ENGINE_INVALID,
LP55XX_ENGINE_1,
@@ -109,6 +111,9 @@ struct lp55xx_device_config {
/* access brightness register */
int (*brightness_fn)(struct lp55xx_led *led);
+ /* perform brightness value to multiple LEDs */
+ int (*multicolor_brightness_fn)(struct lp55xx_led *led);
+
/* current setting function */
void (*set_led_current) (struct lp55xx_led *led, u8 led_current);
@@ -159,6 +164,8 @@ struct lp55xx_chip {
* struct lp55xx_led
* @chan_nr : Channel number
* @cdev : LED class device
+ * @mc_cdev : Multi color class device
+ * @color_component : Multi color LED map information
* @led_current : Current setting at each led channel
* @max_current : Maximun current at each led channel
* @brightness : Brightness value
@@ -167,6 +174,8 @@ struct lp55xx_chip {
struct lp55xx_led {
int chan_nr;
struct led_classdev cdev;
+ struct led_classdev_mc mc_cdev;
+ struct led_mc_color_conversion color_component[LP55XX_MAX_GROUPED_CHAN];
u8 led_current;
u8 max_current;
u8 brightness;
diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h
index 96a787100fda..0f9b88019c18 100644
--- a/include/linux/platform_data/leds-lp55xx.h
+++ b/include/linux/platform_data/leds-lp55xx.h
@@ -12,17 +12,24 @@
#ifndef _LEDS_LP55XX_H
#define _LEDS_LP55XX_H
+#include <linux/led-class-multicolor.h>
+
/* Clock configuration */
#define LP55XX_CLOCK_AUTO 0
#define LP55XX_CLOCK_INT 1
#define LP55XX_CLOCK_EXT 2
+#define LP55XX_MAX_GROUPED_CHAN 4
+
struct lp55xx_led_config {
const char *name;
const char *default_trigger;
u8 chan_nr;
u8 led_current; /* mA x10, 0 if led is not connected */
u8 max_current;
+ int num_colors;
+ unsigned long available_colors;
+ struct led_mc_color_conversion color_component[LP55XX_MAX_GROUPED_CHAN];
};
struct lp55xx_predef_pattern {
--
2.22.0.214.g8dca754b1e
Dan,
Some variable naming related nitpicking below.
On 10/16/19 5:59 PM, Dan Murphy wrote:
> Add multicolor framework support for the lp55xx family.
>
> Signed-off-by: Dan Murphy <[email protected]>
> ---
> drivers/leds/Kconfig | 2 +
> drivers/leds/leds-lp55xx-common.c | 175 +++++++++++++++++++---
> drivers/leds/leds-lp55xx-common.h | 9 ++
> include/linux/platform_data/leds-lp55xx.h | 7 +
> 4 files changed, 170 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index fb614a6b9afa..a121a2855c06 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -377,8 +377,10 @@ config LEDS_LP50XX
> 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
> + depends on OF
> select FW_LOADER
> select FW_LOADER_USER_HELPER
> + select LEDS_CLASS_MULTI_COLOR
> help
> This option supports common operations for LP5521/5523/55231/5562/8501
> devices.
> diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
> index 824d1d73dde1..026ebc2f8e18 100644
> --- a/drivers/leds/leds-lp55xx-common.c
> +++ b/drivers/leds/leds-lp55xx-common.c
> @@ -131,14 +131,54 @@ static struct attribute *lp55xx_led_attrs[] = {
> };
> ATTRIBUTE_GROUPS(lp55xx_led);
>
> +static int lp55xx_map_channel(struct lp55xx_led *led, int color_id,
> + enum led_brightness brightness)
> +{
> + int i;
> +
> + for (i = 0; i < led->mc_cdev.num_leds; i++) {
> + if (led->color_component[i].color_id == color_id) {
I'd use plural "color_components" for the property name.
> + led->color_component[i].brightness = brightness;
> + return 0;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> static int lp55xx_set_brightness(struct led_classdev *cdev,
> enum led_brightness brightness)
> {
> + struct led_mc_color_conversion color_component[LP55XX_MAX_GROUPED_CHAN];
> struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
> struct lp55xx_device_config *cfg = led->chip->cfg;
> + int ret;
> + int i;
>
> - led->brightness = (u8)brightness;
> - return cfg->brightness_fn(led);
> + if (led->mc_cdev.num_leds > 1) {
> + if (!cfg->multicolor_brightness_fn)
> + return -EINVAL;
> +
> + led_mc_calc_color_components(&led->mc_cdev, brightness,
> + color_component);
Similarly here - you calculate all components so it is weird to pass
variable of singular color_component form.
> +
> + for (i = 0; i < led->mc_cdev.num_leds; i++) {
> + ret = lp55xx_map_channel(led,
> + color_component[i].color_id,
> + color_component[i].brightness);
> + if (ret)
> + return ret;
> + }
> +
> + ret = cfg->multicolor_brightness_fn(led);
> + if (ret)
> + return ret;
> + } else {
> + led->brightness = (u8)brightness;
> + ret = cfg->brightness_fn(led);
> + }
> +
> + return ret;
[...]
--
Best regards,
Jacek Anaszewski
And one more issue regarding LEDS_CLASS_MULTI_COLOR config.
On 10/16/19 10:44 PM, Jacek Anaszewski wrote:
> Dan,
>
> Some variable naming related nitpicking below.
>
> On 10/16/19 5:59 PM, Dan Murphy wrote:
>> Add multicolor framework support for the lp55xx family.
>>
>> Signed-off-by: Dan Murphy <[email protected]>
>> ---
>> drivers/leds/Kconfig | 2 +
>> drivers/leds/leds-lp55xx-common.c | 175 +++++++++++++++++++---
>> drivers/leds/leds-lp55xx-common.h | 9 ++
>> include/linux/platform_data/leds-lp55xx.h | 7 +
>> 4 files changed, 170 insertions(+), 23 deletions(-)
>>
>> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
>> index fb614a6b9afa..a121a2855c06 100644
>> --- a/drivers/leds/Kconfig
>> +++ b/drivers/leds/Kconfig
>> @@ -377,8 +377,10 @@ config LEDS_LP50XX
>> 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
>> + depends on OF
>> select FW_LOADER
>> select FW_LOADER_USER_HELPER
>> + select LEDS_CLASS_MULTI_COLOR
Why so? This is unnecessary.
>> help
>> This option supports common operations for LP5521/5523/55231/5562/8501
>> devices.
>> diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
>> index 824d1d73dde1..026ebc2f8e18 100644
>> --- a/drivers/leds/leds-lp55xx-common.c
>> +++ b/drivers/leds/leds-lp55xx-common.c
>> @@ -131,14 +131,54 @@ static struct attribute *lp55xx_led_attrs[] = {
>> };
>> ATTRIBUTE_GROUPS(lp55xx_led);
>>
>> +static int lp55xx_map_channel(struct lp55xx_led *led, int color_id,
>> + enum led_brightness brightness)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < led->mc_cdev.num_leds; i++) {
>> + if (led->color_component[i].color_id == color_id) {
>
> I'd use plural "color_components" for the property name.
>
>> + led->color_component[i].brightness = brightness;
>> + return 0;
>> + }
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +
>> static int lp55xx_set_brightness(struct led_classdev *cdev,
>> enum led_brightness brightness)
>> {
>> + struct led_mc_color_conversion color_component[LP55XX_MAX_GROUPED_CHAN];
>> struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
>> struct lp55xx_device_config *cfg = led->chip->cfg;
>> + int ret;
>> + int i;
>>
>> - led->brightness = (u8)brightness;
>> - return cfg->brightness_fn(led);
>> + if (led->mc_cdev.num_leds > 1) {
>> + if (!cfg->multicolor_brightness_fn)
>> + return -EINVAL;
>> +
>> + led_mc_calc_color_components(&led->mc_cdev, brightness,
>> + color_component);
>
> Similarly here - you calculate all components so it is weird to pass
> variable of singular color_component form.
>
>> +
>> + for (i = 0; i < led->mc_cdev.num_leds; i++) {
>> + ret = lp55xx_map_channel(led,
>> + color_component[i].color_id,
>> + color_component[i].brightness);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + ret = cfg->multicolor_brightness_fn(led);
>> + if (ret)
>> + return ret;
Please wrap what you have under "if: case into a function, e.g.:
static int lp55xx_set_mc_brightness()
{
int ret = -EINVAL;
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTI_COLOR)
if (!cfg->multicolor_brightness_fn)
....
#endif
return ret;
}
And then have here:
if (led->mc_cdev.num_leds > 1)
ret = lp55xx_set_mc_brightness();
You won't need inline empty led_mc_calc_color_components() then.
>> + } else {
>> + led->brightness = (u8)brightness;
>> + ret = cfg->brightness_fn(led);
>> + }
>> +
>> + return ret;
> [...]
>
--
Best regards,
Jacek Anaszewski