Re: [PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-20 Thread Dan Murphy

Jacek

On 9/19/19 4:32 PM, Jacek Anaszewski wrote:

Dan,

On 9/19/19 3:07 AM, Dan Murphy wrote:

Jacek

On 9/18/19 4:27 PM, Jacek Anaszewski wrote:

Hi Dan,

I think Greg's guidance clarified everything nicely -
we will avoid  sub-dirs in favour of prefixes
to *intensity and *max_intensity.

Yes I will make the change accordingly.  It will simplify the code.

Before you will send an update I have some improvement
ideas regarding the remnants after the previous approach,
where single color intensity update resulted in updating
hardware state. Now the update will happen only on write to
brightness file, so we will not need color_set/color_get ops
anymore.

I left those call backs in specifically for the LP50xx. Otherwise the
LEDs are only updated when the brightness file is written.
The LP50xx has an engine that performs the intensity computation for the
specific LED.  So there is no call back to the MC FW for calculating the
intensity.

The brightness and intensity are written directly to the device and the
MCU in the device does all the computations so you have real time update.

You can still handle that in brightness_set op. You need to compare
which color channels have changed and update them in hardware in
addition to setting LEDn_BRIGHTNESS register.

And yes - even updating a single color will need two operations:


If we kept the ops then the LP50xx device would only need one operation 
to the led intensity file to update the color.



echo 231 >  colors/red_intensity // only cache the color in MC core
echo 100 > brightness // do the actual hw update

And this is the way the LP55xx device works now.


Note that brightness value doesn't have to be necessarily different
from the previous one here, but writing brightness file will be needed
to trigger the hw update.


For the LP55xx device the LEDs are only updated when the brightness file
is written.

I think we can leave those call backs in if device driver or product
development teams would like to use them.

I'd not do that - it will be confusing. We can accomplish everything
in brightness_set{_blocking} op. It will have also the advantage of
same ABI semantics across all devices. Otherwise we would need separate
documentation for devices like LP50xx.


OK I am not going to argue this I will just remove the ops even though I 
don't agree.


Removing the ops will just make the LP50xx driver more complex then what 
it needs to be.


I will post v8 later today.



I have also another question - what with linear vs logarithmic
LP50xx brightness scale? I think we should make both options available
to the userspace.


I have no requirements from customers to provide this scaling.

It can be an enhancement to the driver later if we get the request.

Dan



Re: [PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-19 Thread Jacek Anaszewski
Dan,

On 9/19/19 3:07 AM, Dan Murphy wrote:
> Jacek
> 
> On 9/18/19 4:27 PM, Jacek Anaszewski wrote:
>> Hi Dan,
>>
>> I think Greg's guidance clarified everything nicely -
>> we will avoid  sub-dirs in favour of prefixes
>> to *intensity and *max_intensity.
> Yes I will make the change accordingly.  It will simplify the code.
>>
>> Before you will send an update I have some improvement
>> ideas regarding the remnants after the previous approach,
>> where single color intensity update resulted in updating
>> hardware state. Now the update will happen only on write to
>> brightness file, so we will not need color_set/color_get ops
>> anymore.
> 
> I left those call backs in specifically for the LP50xx. Otherwise the
> LEDs are only updated when the brightness file is written.

> The LP50xx has an engine that performs the intensity computation for the
> specific LED.  So there is no call back to the MC FW for calculating the
> intensity.
> 
> The brightness and intensity are written directly to the device and the
> MCU in the device does all the computations so you have real time update.

You can still handle that in brightness_set op. You need to compare
which color channels have changed and update them in hardware in
addition to setting LEDn_BRIGHTNESS register.

And yes - even updating a single color will need two operations:

echo 231 >  colors/red_intensity // only cache the color in MC core
echo 100 > brightness // do the actual hw update

Note that brightness value doesn't have to be necessarily different
from the previous one here, but writing brightness file will be needed
to trigger the hw update.

> For the LP55xx device the LEDs are only updated when the brightness file
> is written.
> 
> I think we can leave those call backs in if device driver or product
> development teams would like to use them.

I'd not do that - it will be confusing. We can accomplish everything
in brightness_set{_blocking} op. It will have also the advantage of
same ABI semantics across all devices. Otherwise we would need separate
documentation for devices like LP50xx.

I have also another question - what with linear vs logarithmic
LP50xx brightness scale? I think we should make both options available
to the userspace.

-- 
Best regards,
Jacek Anaszewski


Re: [PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-18 Thread Dan Murphy

Jacek

On 9/18/19 4:27 PM, Jacek Anaszewski wrote:

Hi Dan,

I think Greg's guidance clarified everything nicely -
we will avoid  sub-dirs in favour of prefixes
to *intensity and *max_intensity.

Yes I will make the change accordingly.  It will simplify the code.


Before you will send an update I have some improvement
ideas regarding the remnants after the previous approach,
where single color intensity update resulted in updating
hardware state. Now the update will happen only on write to
brightness file, so we will not need color_set/color_get ops
anymore.


I left those call backs in specifically for the LP50xx. Otherwise the 
LEDs are only updated when the brightness file is written.


The LP50xx has an engine that performs the intensity computation for the 
specific LED.  So there is no call back to the MC FW for calculating the 
intensity.


The brightness and intensity are written directly to the device and the 
MCU in the device does all the computations so you have real time update.


For the LP55xx device the LEDs are only updated when the brightness file 
is written.


I think we can leave those call backs in if device driver or product 
development teams would like to use them.


Dan



On 9/17/19 7:59 PM, Dan Murphy wrote:

Introduce a multicolor class that groups colored LEDs
within a LED node.

The framework allows for dynamically setting individual LEDs
or setting brightness levels of LEDs and updating them virtually
simultaneously.

Signed-off-by: Dan Murphy 
---

v6 removed color_id and color_mix files, used sysfs_create_groups instead of
kobject call for LED color directory, kept kobject_create for the "colors" 
directory,
removed the calculate function, updated the export for the intensity 
calculations.


  drivers/leds/Kconfig |  10 +
  drivers/leds/Makefile|   1 +
  drivers/leds/led-class-multicolor.c  | 306 +++
  include/linux/led-class-multicolor.h |  90 
  4 files changed, 407 insertions(+)
  create mode 100644 drivers/leds/led-class-multicolor.c
  create mode 100644 include/linux/led-class-multicolor.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1988de1d64c0..71e7fd4f6f15 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -30,6 +30,16 @@ config LEDS_CLASS_FLASH
  for the flash related features of a LED device. It can be built
  as a module.
  
+config LEDS_CLASS_MULTI_COLOR

+   tristate "LED Mulit Color LED Class Support"
+   depends on LEDS_CLASS
+   help
+ This option enables the multicolor LED sysfs class in /sys/class/leds.
+ It wraps LED class and adds multicolor LED specific sysfs attributes
+ and kernel internal API to it. You'll need this to provide support
+ for multicolor LEDs that are grouped together. This class is not
+ intended for single color LEDs. It can be built as a module.
+
  config LEDS_BRIGHTNESS_HW_CHANGED
bool "LED Class brightness_hw_changed attribute support"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 41fb073a39c1..897b810257dd 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -4,6 +4,7 @@
  obj-$(CONFIG_NEW_LEDS)+= led-core.o
  obj-$(CONFIG_LEDS_CLASS)  += led-class.o
  obj-$(CONFIG_LEDS_CLASS_FLASH)+= led-class-flash.o
+obj-$(CONFIG_LEDS_CLASS_MULTI_COLOR)   += led-class-multicolor.o
  obj-$(CONFIG_LEDS_TRIGGERS)   += led-triggers.o
  
  # LED Platform Drivers

diff --git a/drivers/leds/led-class-multicolor.c 
b/drivers/leds/led-class-multicolor.c
new file mode 100644
index ..d43bd344ed4c
--- /dev/null
+++ b/drivers/leds/led-class-multicolor.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+// LED Multi Color class interface
+// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "leds.h"
+
+struct led_mc_color_entry {
+   struct led_classdev_mc *mcled_cdev;
+
+   struct device_attribute max_intensity_attr;
+   struct device_attribute intensity_attr;
+
+   enum led_brightness max_intensity;
+   enum led_brightness intensity;
+
+   struct list_head list;
+
+   int led_color_id;
+};
+
+void led_mc_calc_brightness(struct led_classdev_mc *mcled_cdev,
+   enum led_brightness brightness,
+   int brightness_val[])
+{
+   struct led_classdev_mc_data *data = mcled_cdev->data;
+   struct led_mc_color_entry *priv;
+   int i = 0;
+
+   list_for_each_entry(priv, >color_list, list) {
+   brightness_val[i] = brightness *
+   priv->intensity / priv->max_intensity;
+   i++;
+   }
+}
+EXPORT_SYMBOL_GPL(led_mc_calc_brightness);
+
+static ssize_t intensity_store(struct device *dev,
+

Re: [PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-18 Thread Jacek Anaszewski
Hi Dan,

I think Greg's guidance clarified everything nicely -
we will avoid  sub-dirs in favour of prefixes
to *intensity and *max_intensity.

Before you will send an update I have some improvement
ideas regarding the remnants after the previous approach,
where single color intensity update resulted in updating
hardware state. Now the update will happen only on write to
brightness file, so we will not need color_set/color_get ops
anymore.

On 9/17/19 7:59 PM, Dan Murphy wrote:
> Introduce a multicolor class that groups colored LEDs
> within a LED node.
> 
> The framework allows for dynamically setting individual LEDs
> or setting brightness levels of LEDs and updating them virtually
> simultaneously.
> 
> Signed-off-by: Dan Murphy 
> ---
> 
> v6 removed color_id and color_mix files, used sysfs_create_groups instead of
> kobject call for LED color directory, kept kobject_create for the "colors" 
> directory,
> removed the calculate function, updated the export for the intensity 
> calculations.
> 
> 
>  drivers/leds/Kconfig |  10 +
>  drivers/leds/Makefile|   1 +
>  drivers/leds/led-class-multicolor.c  | 306 +++
>  include/linux/led-class-multicolor.h |  90 
>  4 files changed, 407 insertions(+)
>  create mode 100644 drivers/leds/led-class-multicolor.c
>  create mode 100644 include/linux/led-class-multicolor.h
> 
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 1988de1d64c0..71e7fd4f6f15 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -30,6 +30,16 @@ config LEDS_CLASS_FLASH
> for the flash related features of a LED device. It can be built
> as a module.
>  
> +config LEDS_CLASS_MULTI_COLOR
> + tristate "LED Mulit Color LED Class Support"
> + depends on LEDS_CLASS
> + help
> +   This option enables the multicolor LED sysfs class in /sys/class/leds.
> +   It wraps LED class and adds multicolor LED specific sysfs attributes
> +   and kernel internal API to it. You'll need this to provide support
> +   for multicolor LEDs that are grouped together. This class is not
> +   intended for single color LEDs. It can be built as a module.
> +
>  config LEDS_BRIGHTNESS_HW_CHANGED
>   bool "LED Class brightness_hw_changed attribute support"
>   depends on LEDS_CLASS
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 41fb073a39c1..897b810257dd 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -4,6 +4,7 @@
>  obj-$(CONFIG_NEW_LEDS)   += led-core.o
>  obj-$(CONFIG_LEDS_CLASS) += led-class.o
>  obj-$(CONFIG_LEDS_CLASS_FLASH)   += led-class-flash.o
> +obj-$(CONFIG_LEDS_CLASS_MULTI_COLOR) += led-class-multicolor.o
>  obj-$(CONFIG_LEDS_TRIGGERS)  += led-triggers.o
>  
>  # LED Platform Drivers
> diff --git a/drivers/leds/led-class-multicolor.c 
> b/drivers/leds/led-class-multicolor.c
> new file mode 100644
> index ..d43bd344ed4c
> --- /dev/null
> +++ b/drivers/leds/led-class-multicolor.c
> @@ -0,0 +1,306 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// LED Multi Color class interface
> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "leds.h"
> +
> +struct led_mc_color_entry {
> + struct led_classdev_mc *mcled_cdev;
> +
> + struct device_attribute max_intensity_attr;
> + struct device_attribute intensity_attr;
> +
> + enum led_brightness max_intensity;
> + enum led_brightness intensity;
> +
> + struct list_head list;
> +
> + int led_color_id;
> +};
> +
> +void led_mc_calc_brightness(struct led_classdev_mc *mcled_cdev,
> + enum led_brightness brightness,
> + int brightness_val[])
> +{
> + struct led_classdev_mc_data *data = mcled_cdev->data;
> + struct led_mc_color_entry *priv;
> + int i = 0;
> +
> + list_for_each_entry(priv, >color_list, list) {
> + brightness_val[i] = brightness *
> + priv->intensity / priv->max_intensity;
> + i++;
> + }
> +}
> +EXPORT_SYMBOL_GPL(led_mc_calc_brightness);
> +
> +static ssize_t intensity_store(struct device *dev,
> + struct device_attribute *intensity_attr,
> + const char *buf, size_t size)
> +{
> + struct led_mc_color_entry *priv = container_of(intensity_attr,
> + struct led_mc_color_entry,
> +   intensity_attr);
> + struct led_classdev_mc_data *data = priv->mcled_cdev->data;
> + struct led_classdev_mc *mcled_cdev = data->mcled_cdev;
> + struct led_classdev *led_cdev = priv->mcled_cdev->led_cdev;
> + unsigned long value;
> + ssize_t ret;
> +
> + mutex_lock(_cdev->led_access);
> +
> +

Re: [PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-18 Thread Greg KH
On Wed, Sep 18, 2019 at 12:09:12PM -0500, Dan Murphy wrote:
> Greg
> 
> 
> 
> > +static int led_multicolor_init_color(struct led_classdev_mc_data *data,
> > +struct led_classdev_mc *mcled_cdev,
> > +int color_id, int color_index)
> > +{
> > +   struct led_classdev *led_cdev = mcled_cdev->led_cdev;
> > +   struct led_mc_color_entry *mc_priv;
> > +   int ret;
> > +
> > +   mc_priv = devm_kzalloc(led_cdev->dev, sizeof(*mc_priv), GFP_KERNEL);
> > +   if (!mc_priv)
> > +   return -ENOMEM;
> > +
> > +   mc_priv->led_color_id = color_id;
> > +   mc_priv->mcled_cdev = mcled_cdev;
> > +
> > +   led_color_group.name = led_colors[color_id];
> > +   ret = sysfs_create_group(data->color_kobj, _color_group);
> > +   if (ret)
> > +   return ret;
> > +
> > +   sysfs_attr_init(_priv->intensity_attr.attr);
> > +   mc_priv->intensity_attr.attr.name = "intensity";
> > +   mc_priv->intensity_attr.attr.mode = 666;
> > +   mc_priv->intensity_attr.store = intensity_store;
> > +   mc_priv->intensity_attr.show = intensity_show;
> > +   ret = sysfs_add_file_to_group(data->color_kobj,
> > + _priv->intensity_attr.attr,
> > + led_color_group.name);
> > +   if (ret)
> > +   return ret;
> > +
> > +   sysfs_attr_init(_priv->max_intensity_attr.attr);
> > +   mc_priv->max_intensity_attr.attr.name = "max_intensity";
> > +   mc_priv->max_intensity_attr.attr.mode = 444;
> > +   mc_priv->max_intensity_attr.show = max_intensity_show;
> > +   ret = sysfs_add_file_to_group(data->color_kobj,
> > + _priv->max_intensity_attr.attr,
> > + led_color_group.name);
> > +   if (ret)
> > +   goto err_out;
> > +
> > +   mc_priv->max_intensity = LED_FULL;
> > +   list_add_tail(_priv->list, >color_list);
> > +
> > +err_out:
> > +   return ret;
> > +}
> > +
> > +static int led_multicolor_init_color_dir(struct led_classdev_mc_data *data,
> > +struct led_classdev_mc *mcled_cdev)
> > +{
> > +   struct led_classdev *led_cdev = mcled_cdev->led_cdev;
> > +   u32 color_id;
> > +   int ret;
> > +   int i, j = 0;
> > +
> > +   data->color_kobj = kobject_create_and_add("colors",
> > + _cdev->dev->kobj);
> 
> We need some guidance here on how to properly create sub directories more
> then 1 level deep.

Short answer, don't.

Long answer, use a 'struct device' but ONLY IF YOU KNOW WHAT YOU ARE
DOING!

Follow the short answer please.

> In short under the LED class device parent directory we want to create a
> directory called "colors".

Ok, that's simple.

> Under that directory we want to create a directory corresponding to the
> monochrome LED color.

Why?

> Under that directory we have the files to for intensity and max_intensity
> for the monochrome LED.

Why not just have colors/monochrome_intensity and
colors/monochrome_max_intensity as your files in the colors/ directory?

> We can use the LED class kobject to create the colors directory using the
> sysfs calls but the issue comes when creating the LED color directory.

Yes.

> We don't have a kobj for colors to associate those directories to. 

And you shouldn't :)

> The only API we see to use the kobject_create_and_add which then gives
> us the colors directory kobj.

Don't do that, you will break userspace code hard if you do that.

NEVER put a raw kobject after a 'struct device' in the sysfs tree if you
expect normal userspace libraries to be able to understand what is going
on.  That's why this is "hard", you are not supposed to be doing it.

> So the directory structure would look like this which is explained in this
> patch https://lore.kernel.org/patchwork/patch/1128444/
> 
> Directory Layout Example
> 
> root:/sys/class/leds/rgb:grouped_leds# ls -lR colors/
> colors/:
> drwxr-xr-x    2 root root 0 Jun 28 20:21 blue
> rwxr-xr-x    2 root root 0 Jun 28 20:21 green
> drwxr-xr-x    2 root root 0 Jun 28 20:21 red
> 
> colors/blue:
> -rw---    1 root root  4096 Jun 28 20:21 intensity
> -r    1 root root  4096 Jun 28 20:27 max_intensity
> +colors/green:
> -rw---    1 root root  4096 Jun 28 20:22 intensity
> -r    1 root root  4096 Jun 28 20:27 max_intensity
> 
> colors/red:
> -rw---    1 root root  4096 Jun 28 20:21 intensity
> -r    1 root root  4096 Jun 28 20:27 max_intensity


No, just add blue, green, red to the prefix of those files and all
should be fine.  Don't try to get fancy and use subdirs, that way lies
madness.

> I have reviewed your example code and read your blogs and papers on the
> subject but nothing really talks about sub-sub directories.

Because you shouldn't do it, I didn't think I had to describe everything
you 

Re: [PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-18 Thread Dan Murphy

Greg




+static int led_multicolor_init_color(struct led_classdev_mc_data *data,
+struct led_classdev_mc *mcled_cdev,
+int color_id, int color_index)
+{
+   struct led_classdev *led_cdev = mcled_cdev->led_cdev;
+   struct led_mc_color_entry *mc_priv;
+   int ret;
+
+   mc_priv = devm_kzalloc(led_cdev->dev, sizeof(*mc_priv), GFP_KERNEL);
+   if (!mc_priv)
+   return -ENOMEM;
+
+   mc_priv->led_color_id = color_id;
+   mc_priv->mcled_cdev = mcled_cdev;
+
+   led_color_group.name = led_colors[color_id];
+   ret = sysfs_create_group(data->color_kobj, _color_group);
+   if (ret)
+   return ret;
+
+   sysfs_attr_init(_priv->intensity_attr.attr);
+   mc_priv->intensity_attr.attr.name = "intensity";
+   mc_priv->intensity_attr.attr.mode = 666;
+   mc_priv->intensity_attr.store = intensity_store;
+   mc_priv->intensity_attr.show = intensity_show;
+   ret = sysfs_add_file_to_group(data->color_kobj,
+ _priv->intensity_attr.attr,
+ led_color_group.name);
+   if (ret)
+   return ret;
+
+   sysfs_attr_init(_priv->max_intensity_attr.attr);
+   mc_priv->max_intensity_attr.attr.name = "max_intensity";
+   mc_priv->max_intensity_attr.attr.mode = 444;
+   mc_priv->max_intensity_attr.show = max_intensity_show;
+   ret = sysfs_add_file_to_group(data->color_kobj,
+ _priv->max_intensity_attr.attr,
+ led_color_group.name);
+   if (ret)
+   goto err_out;
+
+   mc_priv->max_intensity = LED_FULL;
+   list_add_tail(_priv->list, >color_list);
+
+err_out:
+   return ret;
+}
+
+static int led_multicolor_init_color_dir(struct led_classdev_mc_data *data,
+struct led_classdev_mc *mcled_cdev)
+{
+   struct led_classdev *led_cdev = mcled_cdev->led_cdev;
+   u32 color_id;
+   int ret;
+   int i, j = 0;
+
+   data->color_kobj = kobject_create_and_add("colors",
+ _cdev->dev->kobj);


We need some guidance here on how to properly create sub directories 
more then 1 level deep.


In short under the LED class device parent directory we want to create a 
directory called "colors".


Under that directory we want to create a directory corresponding to the 
monochrome LED color.
Under that directory we have the files to for intensity and 
max_intensity for the monochrome LED.


We can use the LED class kobject to create the colors directory using 
the sysfs calls but the issue comes when creating the LED color 
directory.  We don't have a kobj for colors to associate those 
directories to.  The only API we see to use the kobject_create_and_add 
which then gives us the colors directory kobj.


So the directory structure would look like this which is explained in 
this patch https://lore.kernel.org/patchwork/patch/1128444/


Directory Layout Example

root:/sys/class/leds/rgb:grouped_leds# ls -lR colors/
colors/:
drwxr-xr-x    2 root root 0 Jun 28 20:21 blue
rwxr-xr-x    2 root root 0 Jun 28 20:21 green
drwxr-xr-x    2 root root 0 Jun 28 20:21 red

colors/blue:
-rw---    1 root root  4096 Jun 28 20:21 intensity
-r    1 root root  4096 Jun 28 20:27 max_intensity
+colors/green:
-rw---    1 root root  4096 Jun 28 20:22 intensity
-r    1 root root  4096 Jun 28 20:27 max_intensity

colors/red:
-rw---    1 root root  4096 Jun 28 20:21 intensity
-r    1 root root  4096 Jun 28 20:27 max_intensity

I have reviewed your example code and read your blogs and papers on the 
subject but nothing really talks about sub-sub directories.


Now if this is a no-no in the kernel that is fine we can adjust but 
Jacek wanted to get your opinon/guidance on this topic.


Dan




[PATCH v6 6/9] leds: multicolor: Introduce a multicolor class definition

2019-09-17 Thread Dan Murphy
Introduce a multicolor class that groups colored LEDs
within a LED node.

The framework allows for dynamically setting individual LEDs
or setting brightness levels of LEDs and updating them virtually
simultaneously.

Signed-off-by: Dan Murphy 
---

v6 removed color_id and color_mix files, used sysfs_create_groups instead of
kobject call for LED color directory, kept kobject_create for the "colors" 
directory,
removed the calculate function, updated the export for the intensity 
calculations.


 drivers/leds/Kconfig |  10 +
 drivers/leds/Makefile|   1 +
 drivers/leds/led-class-multicolor.c  | 306 +++
 include/linux/led-class-multicolor.h |  90 
 4 files changed, 407 insertions(+)
 create mode 100644 drivers/leds/led-class-multicolor.c
 create mode 100644 include/linux/led-class-multicolor.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1988de1d64c0..71e7fd4f6f15 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -30,6 +30,16 @@ config LEDS_CLASS_FLASH
  for the flash related features of a LED device. It can be built
  as a module.
 
+config LEDS_CLASS_MULTI_COLOR
+   tristate "LED Mulit Color LED Class Support"
+   depends on LEDS_CLASS
+   help
+ This option enables the multicolor LED sysfs class in /sys/class/leds.
+ It wraps LED class and adds multicolor LED specific sysfs attributes
+ and kernel internal API to it. You'll need this to provide support
+ for multicolor LEDs that are grouped together. This class is not
+ intended for single color LEDs. It can be built as a module.
+
 config LEDS_BRIGHTNESS_HW_CHANGED
bool "LED Class brightness_hw_changed attribute support"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 41fb073a39c1..897b810257dd 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -4,6 +4,7 @@
 obj-$(CONFIG_NEW_LEDS) += led-core.o
 obj-$(CONFIG_LEDS_CLASS)   += led-class.o
 obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
+obj-$(CONFIG_LEDS_CLASS_MULTI_COLOR)   += led-class-multicolor.o
 obj-$(CONFIG_LEDS_TRIGGERS)+= led-triggers.o
 
 # LED Platform Drivers
diff --git a/drivers/leds/led-class-multicolor.c 
b/drivers/leds/led-class-multicolor.c
new file mode 100644
index ..d43bd344ed4c
--- /dev/null
+++ b/drivers/leds/led-class-multicolor.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+// LED Multi Color class interface
+// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "leds.h"
+
+struct led_mc_color_entry {
+   struct led_classdev_mc *mcled_cdev;
+
+   struct device_attribute max_intensity_attr;
+   struct device_attribute intensity_attr;
+
+   enum led_brightness max_intensity;
+   enum led_brightness intensity;
+
+   struct list_head list;
+
+   int led_color_id;
+};
+
+void led_mc_calc_brightness(struct led_classdev_mc *mcled_cdev,
+   enum led_brightness brightness,
+   int brightness_val[])
+{
+   struct led_classdev_mc_data *data = mcled_cdev->data;
+   struct led_mc_color_entry *priv;
+   int i = 0;
+
+   list_for_each_entry(priv, >color_list, list) {
+   brightness_val[i] = brightness *
+   priv->intensity / priv->max_intensity;
+   i++;
+   }
+}
+EXPORT_SYMBOL_GPL(led_mc_calc_brightness);
+
+static ssize_t intensity_store(struct device *dev,
+   struct device_attribute *intensity_attr,
+   const char *buf, size_t size)
+{
+   struct led_mc_color_entry *priv = container_of(intensity_attr,
+   struct led_mc_color_entry,
+ intensity_attr);
+   struct led_classdev_mc_data *data = priv->mcled_cdev->data;
+   struct led_classdev_mc *mcled_cdev = data->mcled_cdev;
+   struct led_classdev *led_cdev = priv->mcled_cdev->led_cdev;
+   unsigned long value;
+   ssize_t ret;
+
+   mutex_lock(_cdev->led_access);
+
+   ret = kstrtoul(buf, 10, );
+   if (ret)
+   goto unlock;
+
+   if (value > priv->max_intensity) {
+   ret = -EINVAL;
+   goto unlock;
+   }
+
+   priv->intensity = value;
+
+   if (mcled_cdev->ops) {
+   ret = mcled_cdev->ops->set_color_brightness(mcled_cdev,
+   priv->led_color_id,
+   priv->intensity);
+   if (ret)
+   goto unlock;
+   }
+
+   ret = size;
+
+unlock:
+   mutex_unlock(_cdev->led_access);
+   return ret;