Skip to content

Commit 55d5d3b

Browse files
Dan Murphypavelmachek
authored andcommitted
leds: multicolor: Introduce a multicolor class definition
Introduce a multicolor class that groups colored LEDs within a LED node. The multicolor class groups monochrome LEDs and allows controlling two aspects of the final combined color: hue and lightness. The former is controlled via the intensity file and the latter is controlled via brightness file. Signed-off-by: Dan Murphy <dmurphy@ti.com> Acked-by: Jacek Anaszewski <jacek.anaszewski@gmail.com> Signed-off-by: Pavel Machek <pavel@ucw.cz> [squashed leds: multicolor: Fix camel case in documentation in]
1 parent 10d3e0d commit 55d5d3b

7 files changed

Lines changed: 457 additions & 0 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
What: /sys/class/leds/<led>/brightness
2+
Date: March 2020
3+
KernelVersion: 5.9
4+
Contact: Dan Murphy <dmurphy@ti.com>
5+
Description: read/write
6+
Writing to this file will update all LEDs within the group to a
7+
calculated percentage of what each color LED intensity is set
8+
to. The percentage is calculated for each grouped LED via the
9+
equation below:
10+
11+
led_brightness = brightness * multi_intensity/max_brightness
12+
13+
For additional details please refer to
14+
Documentation/leds/leds-class-multicolor.rst.
15+
16+
The value of the LED is from 0 to
17+
/sys/class/leds/<led>/max_brightness.
18+
19+
What: /sys/class/leds/<led>/multi_index
20+
Date: March 2020
21+
KernelVersion: 5.9
22+
Contact: Dan Murphy <dmurphy@ti.com>
23+
Description: read
24+
The multi_index array, when read, will output the LED colors
25+
as an array of strings as they are indexed in the
26+
multi_intensity file.
27+
28+
What: /sys/class/leds/<led>/multi_intensity
29+
Date: March 2020
30+
KernelVersion: 5.9
31+
Contact: Dan Murphy <dmurphy@ti.com>
32+
Description: read/write
33+
This file contains array of integers. Order of components is
34+
described by the multi_index array. The maximum intensity should
35+
not exceed /sys/class/leds/<led>/max_brightness.

Documentation/leds/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ LEDs
99

1010
leds-class
1111
leds-class-flash
12+
leds-class-multicolor
1213
ledtrig-oneshot
1314
ledtrig-transient
1415
ledtrig-usbport
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
====================================
4+
Multicolor LED handling under Linux
5+
====================================
6+
7+
Description
8+
===========
9+
The multicolor class groups monochrome LEDs and allows controlling two
10+
aspects of the final combined color: hue and lightness. The former is
11+
controlled via the multi_intensity array file and the latter is controlled
12+
via brightness file.
13+
14+
Multicolor Class Control
15+
========================
16+
The multicolor class presents files that groups the colors as indexes in an
17+
array. These files are children under the LED parent node created by the
18+
led_class framework. The led_class framework is documented in led-class.rst
19+
within this documentation directory.
20+
21+
Each colored LED will be indexed under the multi_* files. The order of the
22+
colors will be arbitrary. The multi_index file can be read to determine the
23+
color name to indexed value.
24+
25+
The multi_index file is an array that contains the string list of the colors as
26+
they are defined in each multi_* array file.
27+
28+
The multi_intensity is an array that can be read or written to for the
29+
individual color intensities. All elements within this array must be written in
30+
order for the color LED intensities to be updated.
31+
32+
Directory Layout Example
33+
========================
34+
root:/sys/class/leds/multicolor:status# ls -lR
35+
-rw-r--r-- 1 root root 4096 Oct 19 16:16 brightness
36+
-r--r--r-- 1 root root 4096 Oct 19 16:16 max_brightness
37+
-r--r--r-- 1 root root 4096 Oct 19 16:16 multi_index
38+
-rw-r--r-- 1 root root 4096 Oct 19 16:16 multi_intensity
39+
40+
Multicolor Class Brightness Control
41+
===================================
42+
The brightness level for each LED is calculated based on the color LED
43+
intensity setting divided by the global max_brightness setting multiplied by
44+
the requested brightness.
45+
46+
led_brightness = brightness * multi_intensity/max_brightness
47+
48+
Example:
49+
A user first writes the multi_intensity file with the brightness levels
50+
for each LED that are necessary to achieve a certain color output from a
51+
multicolor LED group.
52+
53+
cat /sys/class/leds/multicolor:status/multi_index
54+
green blue red
55+
56+
echo 43 226 138 > /sys/class/leds/multicolor:status/multi_intensity
57+
58+
red -
59+
intensity = 138
60+
max_brightness = 255
61+
green -
62+
intensity = 43
63+
max_brightness = 255
64+
blue -
65+
intensity = 226
66+
max_brightness = 255
67+
68+
The user can control the brightness of that multicolor LED group by writing the
69+
global 'brightness' control. Assuming a max_brightness of 255 the user
70+
may want to dim the LED color group to half. The user would write a value of
71+
128 to the global brightness file then the values written to each LED will be
72+
adjusted base on this value.
73+
74+
cat /sys/class/leds/multicolor:status/max_brightness
75+
255
76+
echo 128 > /sys/class/leds/multicolor:status/brightness
77+
78+
adjusted_red_value = 128 * 138/255 = 69
79+
adjusted_green_value = 128 * 43/255 = 21
80+
adjusted_blue_value = 128 * 226/255 = 113
81+
82+
Reading the global brightness file will return the current brightness value of
83+
the color LED group.
84+
85+
cat /sys/class/leds/multicolor:status/brightness
86+
128

drivers/leds/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ config LEDS_CLASS_FLASH
3030
for the flash related features of a LED device. It can be built
3131
as a module.
3232

33+
config LEDS_CLASS_MULTICOLOR
34+
tristate "LED Multicolor Class Support"
35+
depends on LEDS_CLASS
36+
help
37+
This option enables the multicolor LED sysfs class in /sys/class/leds.
38+
It wraps LED class and adds multicolor LED specific sysfs attributes
39+
and kernel internal API to it. You'll need this to provide support
40+
for multicolor LEDs that are grouped together. This class is not
41+
intended for single color LEDs. It can be built as a module.
42+
3343
config LEDS_BRIGHTNESS_HW_CHANGED
3444
bool "LED Class brightness_hw_changed attribute support"
3545
depends on LEDS_CLASS

drivers/leds/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
obj-$(CONFIG_NEW_LEDS) += led-core.o
55
obj-$(CONFIG_LEDS_CLASS) += led-class.o
66
obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
7+
obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += led-class-multicolor.o
78
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
89

910
# LED Platform Drivers (keep this sorted, M-| sort)
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// LED Multicolor class interface
3+
// Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
4+
// Author: Dan Murphy <dmurphy@ti.com>
5+
6+
#include <linux/device.h>
7+
#include <linux/init.h>
8+
#include <linux/led-class-multicolor.h>
9+
#include <linux/module.h>
10+
#include <linux/slab.h>
11+
#include <linux/uaccess.h>
12+
13+
#include "leds.h"
14+
15+
int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
16+
enum led_brightness brightness)
17+
{
18+
struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
19+
int i;
20+
21+
for (i = 0; i < mcled_cdev->num_colors; i++)
22+
mcled_cdev->subled_info[i].brightness = brightness *
23+
mcled_cdev->subled_info[i].intensity /
24+
led_cdev->max_brightness;
25+
26+
return 0;
27+
}
28+
EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
29+
30+
static ssize_t multi_intensity_store(struct device *dev,
31+
struct device_attribute *intensity_attr,
32+
const char *buf, size_t size)
33+
{
34+
struct led_classdev *led_cdev = dev_get_drvdata(dev);
35+
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
36+
int nrchars, offset = 0;
37+
int intensity_value[LED_COLOR_ID_MAX];
38+
int i;
39+
ssize_t ret;
40+
41+
mutex_lock(&led_cdev->led_access);
42+
43+
for (i = 0; i < mcled_cdev->num_colors; i++) {
44+
ret = sscanf(buf + offset, "%i%n",
45+
&intensity_value[i], &nrchars);
46+
if (ret != 1) {
47+
ret = -EINVAL;
48+
goto err_out;
49+
}
50+
offset += nrchars;
51+
}
52+
53+
offset++;
54+
if (offset < size) {
55+
ret = -EINVAL;
56+
goto err_out;
57+
}
58+
59+
for (i = 0; i < mcled_cdev->num_colors; i++)
60+
mcled_cdev->subled_info[i].intensity = intensity_value[i];
61+
62+
led_set_brightness(led_cdev, led_cdev->brightness);
63+
ret = size;
64+
err_out:
65+
mutex_unlock(&led_cdev->led_access);
66+
return ret;
67+
}
68+
69+
static ssize_t multi_intensity_show(struct device *dev,
70+
struct device_attribute *intensity_attr,
71+
char *buf)
72+
{
73+
struct led_classdev *led_cdev = dev_get_drvdata(dev);
74+
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
75+
int len = 0;
76+
int i;
77+
78+
for (i = 0; i < mcled_cdev->num_colors; i++) {
79+
len += sprintf(buf + len, "%d",
80+
mcled_cdev->subled_info[i].intensity);
81+
if (i < mcled_cdev->num_colors - 1)
82+
len += sprintf(buf + len, " ");
83+
}
84+
85+
buf[len++] = '\n';
86+
return len;
87+
}
88+
static DEVICE_ATTR_RW(multi_intensity);
89+
90+
static ssize_t multi_index_show(struct device *dev,
91+
struct device_attribute *multi_index_attr,
92+
char *buf)
93+
{
94+
struct led_classdev *led_cdev = dev_get_drvdata(dev);
95+
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
96+
int len = 0;
97+
int index;
98+
int i;
99+
100+
for (i = 0; i < mcled_cdev->num_colors; i++) {
101+
index = mcled_cdev->subled_info[i].color_index;
102+
len += sprintf(buf + len, "%s", led_colors[index]);
103+
if (i < mcled_cdev->num_colors - 1)
104+
len += sprintf(buf + len, " ");
105+
}
106+
107+
buf[len++] = '\n';
108+
return len;
109+
}
110+
static DEVICE_ATTR_RO(multi_index);
111+
112+
static struct attribute *led_multicolor_attrs[] = {
113+
&dev_attr_multi_intensity.attr,
114+
&dev_attr_multi_index.attr,
115+
NULL,
116+
};
117+
ATTRIBUTE_GROUPS(led_multicolor);
118+
119+
int led_classdev_multicolor_register_ext(struct device *parent,
120+
struct led_classdev_mc *mcled_cdev,
121+
struct led_init_data *init_data)
122+
{
123+
struct led_classdev *led_cdev;
124+
125+
if (!mcled_cdev)
126+
return -EINVAL;
127+
128+
if (mcled_cdev->num_colors <= 0)
129+
return -EINVAL;
130+
131+
if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
132+
return -EINVAL;
133+
134+
led_cdev = &mcled_cdev->led_cdev;
135+
mcled_cdev->led_cdev.groups = led_multicolor_groups;
136+
137+
return led_classdev_register_ext(parent, led_cdev, init_data);
138+
}
139+
EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
140+
141+
void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
142+
{
143+
if (!mcled_cdev)
144+
return;
145+
146+
led_classdev_unregister(&mcled_cdev->led_cdev);
147+
}
148+
EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
149+
150+
static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
151+
{
152+
led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
153+
}
154+
155+
int devm_led_classdev_multicolor_register_ext(struct device *parent,
156+
struct led_classdev_mc *mcled_cdev,
157+
struct led_init_data *init_data)
158+
{
159+
struct led_classdev_mc **dr;
160+
int ret;
161+
162+
dr = devres_alloc(devm_led_classdev_multicolor_release,
163+
sizeof(*dr), GFP_KERNEL);
164+
if (!dr)
165+
return -ENOMEM;
166+
167+
ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
168+
init_data);
169+
if (ret) {
170+
devres_free(dr);
171+
return ret;
172+
}
173+
174+
*dr = mcled_cdev;
175+
devres_add(parent, dr);
176+
177+
return 0;
178+
}
179+
EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
180+
181+
static int devm_led_classdev_multicolor_match(struct device *dev,
182+
void *res, void *data)
183+
{
184+
struct led_classdev_mc **p = res;
185+
186+
if (WARN_ON(!p || !*p))
187+
return 0;
188+
189+
return *p == data;
190+
}
191+
192+
void devm_led_classdev_multicolor_unregister(struct device *dev,
193+
struct led_classdev_mc *mcled_cdev)
194+
{
195+
WARN_ON(devres_release(dev,
196+
devm_led_classdev_multicolor_release,
197+
devm_led_classdev_multicolor_match, mcled_cdev));
198+
}
199+
EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
200+
201+
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
202+
MODULE_DESCRIPTION("Multicolor LED class interface");
203+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)