Commit cd2a3bf0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'leds-for-5.1-rc1' of...

Merge tag 'leds-for-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds

Pull LED updates from Jacek Anaszewski:

 - finalize previously announced support for initialization of pattern
   triggers from Device Tree

 - fix for null deref on firmware load failure in leds-lp55xx-common.c

* tag 'leds-for-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds:
  leds: lp55xx: fix null deref on firmware load failure
  leds: trigger: timer: Add initialization from Device Tree
  leds: trigger: oneshot: Add initialization from Device Tree
  leds: trigger: pattern: Add pattern initialization from Device Tree
  leds: Add helper for getting default pattern from Device Tree
  dt-bindings: leds: Add pattern initialization from Device Tree
parents 7629bac6 5ddb0869
...@@ -7,55 +7,10 @@ Description: ...@@ -7,55 +7,10 @@ Description:
timer. It can do gradual dimming and step change of brightness. timer. It can do gradual dimming and step change of brightness.
The pattern is given by a series of tuples, of brightness and The pattern is given by a series of tuples, of brightness and
duration (ms). The LED is expected to traverse the series and duration (ms).
each brightness value for the specified duration. Duration of
0 means brightness should immediately change to new value, and
writing malformed pattern deactivates any active one.
1. For gradual dimming, the dimming interval now is set as 50 The exact format is described in:
milliseconds. So the tuple with duration less than dimming Documentation/devicetree/bindings/leds/leds-trigger-pattern.txt
interval (50ms) is treated as a step change of brightness,
i.e. the subsequent brightness will be applied without adding
intervening dimming intervals.
The gradual dimming format of the software pattern values should be:
"brightness_1 duration_1 brightness_2 duration_2 brightness_3
duration_3 ...". For example:
echo 0 1000 255 2000 > pattern
It will make the LED go gradually from zero-intensity to max (255)
intensity in 1000 milliseconds, then back to zero intensity in 2000
milliseconds:
LED brightness
^
255-| / \ / \ /
| / \ / \ /
| / \ / \ /
| / \ / \ /
0-| / \/ \/
+---0----1----2----3----4----5----6------------> time (s)
2. To make the LED go instantly from one brightness value to another,
we should use zero-time lengths (the brightness must be same as
the previous tuple's). So the format should be:
"brightness_1 duration_1 brightness_1 0 brightness_2 duration_2
brightness_2 0 ...". For example:
echo 0 1000 0 0 255 2000 255 0 > pattern
It will make the LED stay off for one second, then stay at max brightness
for two seconds:
LED brightness
^
255-| +---------+ +---------+
| | | | |
| | | | |
| | | | |
0-| -----+ +----+ +----
+---0----1----2----3----4----5----6------------> time (s)
What: /sys/class/leds/<led>/hw_pattern What: /sys/class/leds/<led>/hw_pattern
Date: September 2018 Date: September 2018
......
...@@ -37,6 +37,18 @@ Optional properties for child nodes: ...@@ -37,6 +37,18 @@ Optional properties for child nodes:
"ide-disk" - LED indicates IDE disk activity (deprecated), "ide-disk" - LED indicates IDE disk activity (deprecated),
in new implementations use "disk-activity" in new implementations use "disk-activity"
"timer" - LED flashes at a fixed, configurable rate "timer" - LED flashes at a fixed, configurable rate
"pattern" - LED alters the brightness for the specified duration with one
software timer (requires "led-pattern" property)
- led-pattern : Array of integers with default pattern for certain triggers.
Each trigger may parse this property differently:
- one-shot : two numbers specifying delay on and delay off (in ms),
- timer : two numbers specifying delay on and delay off (in ms),
- pattern : the pattern is given by a series of tuples, of
brightness and duration (in ms). The exact format is
described in:
Documentation/devicetree/bindings/leds/leds-trigger-pattern.txt
- led-max-microamp : Maximum LED supply current in microamperes. This property - led-max-microamp : Maximum LED supply current in microamperes. This property
can be made mandatory for the board configurations can be made mandatory for the board configurations
......
* Pattern format for LED pattern trigger
The pattern is given by a series of tuples, of brightness and duration (ms).
The LED is expected to traverse the series and each brightness value for the
specified duration. Duration of 0 means brightness should immediately change to
new value, and writing malformed pattern deactivates any active one.
1. For gradual dimming, the dimming interval now is set as 50 milliseconds. So
the tuple with duration less than dimming interval (50ms) is treated as a step
change of brightness, i.e. the subsequent brightness will be applied without
adding intervening dimming intervals.
The gradual dimming format of the software pattern values should be:
"brightness_1 duration_1 brightness_2 duration_2 brightness_3 duration_3 ...".
For example (using sysfs interface):
echo 0 1000 255 2000 > pattern
It will make the LED go gradually from zero-intensity to max (255) intensity in
1000 milliseconds, then back to zero intensity in 2000 milliseconds:
LED brightness
^
255-| / \ / \ /
| / \ / \ /
| / \ / \ /
| / \ / \ /
0-| / \/ \/
+---0----1----2----3----4----5----6------------> time (s)
2. To make the LED go instantly from one brightness value to another, we should
use zero-time lengths (the brightness must be same as the previous tuple's). So
the format should be: "brightness_1 duration_1 brightness_1 0 brightness_2
duration_2 brightness_2 0 ...".
For example (using sysfs interface):
echo 0 1000 0 0 255 2000 255 0 > pattern
It will make the LED stay off for one second, then stay at max brightness for
two seconds:
LED brightness
^
255-| +---------+ +---------+
| | | | |
| | | | |
| | | | |
0-| -----+ +----+ +----
+---0----1----2----3----4----5----6------------> time (s)
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/slab.h>
#include "leds.h" #include "leds.h"
DECLARE_RWSEM(leds_list_lock); DECLARE_RWSEM(leds_list_lock);
...@@ -310,6 +312,34 @@ int led_update_brightness(struct led_classdev *led_cdev) ...@@ -310,6 +312,34 @@ int led_update_brightness(struct led_classdev *led_cdev)
} }
EXPORT_SYMBOL_GPL(led_update_brightness); EXPORT_SYMBOL_GPL(led_update_brightness);
u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
{
struct device_node *np = dev_of_node(led_cdev->dev);
u32 *pattern;
int count;
if (!np)
return NULL;
count = of_property_count_u32_elems(np, "led-pattern");
if (count < 0)
return NULL;
pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
if (!pattern)
return NULL;
if (of_property_read_u32_array(np, "led-pattern", pattern, count)) {
kfree(pattern);
return NULL;
}
*size = count;
return pattern;
}
EXPORT_SYMBOL_GPL(led_get_default_pattern);
/* Caller must ensure led_cdev->led_access held */ /* Caller must ensure led_cdev->led_access held */
void led_sysfs_disable(struct led_classdev *led_cdev) void led_sysfs_disable(struct led_classdev *led_cdev)
{ {
......
...@@ -201,7 +201,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) ...@@ -201,7 +201,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
if (!fw) { if (!fw) {
dev_err(dev, "firmware request failed\n"); dev_err(dev, "firmware request failed\n");
goto out; return;
} }
/* handling firmware data is chip dependent */ /* handling firmware data is chip dependent */
...@@ -214,9 +214,9 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) ...@@ -214,9 +214,9 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
out:
/* firmware should be released for other channel use */ /* firmware should be released for other channel use */
release_firmware(chip->fw); release_firmware(chip->fw);
chip->fw = NULL;
} }
static int lp55xx_request_firmware(struct lp55xx_chip *chip) static int lp55xx_request_firmware(struct lp55xx_chip *chip)
......
...@@ -130,6 +130,34 @@ static struct attribute *oneshot_trig_attrs[] = { ...@@ -130,6 +130,34 @@ static struct attribute *oneshot_trig_attrs[] = {
}; };
ATTRIBUTE_GROUPS(oneshot_trig); ATTRIBUTE_GROUPS(oneshot_trig);
static void pattern_init(struct led_classdev *led_cdev)
{
u32 *pattern;
unsigned int size = 0;
pattern = led_get_default_pattern(led_cdev, &size);
if (!pattern)
goto out_default;
if (size != 2) {
dev_warn(led_cdev->dev,
"Expected 2 but got %u values for delays pattern\n",
size);
goto out_default;
}
led_cdev->blink_delay_on = pattern[0];
led_cdev->blink_delay_off = pattern[1];
kfree(pattern);
return;
out_default:
kfree(pattern);
led_cdev->blink_delay_on = DEFAULT_DELAY;
led_cdev->blink_delay_off = DEFAULT_DELAY;
}
static int oneshot_trig_activate(struct led_classdev *led_cdev) static int oneshot_trig_activate(struct led_classdev *led_cdev)
{ {
struct oneshot_trig_data *oneshot_data; struct oneshot_trig_data *oneshot_data;
...@@ -140,8 +168,14 @@ static int oneshot_trig_activate(struct led_classdev *led_cdev) ...@@ -140,8 +168,14 @@ static int oneshot_trig_activate(struct led_classdev *led_cdev)
led_set_trigger_data(led_cdev, oneshot_data); led_set_trigger_data(led_cdev, oneshot_data);
led_cdev->blink_delay_on = DEFAULT_DELAY; if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
led_cdev->blink_delay_off = DEFAULT_DELAY; pattern_init(led_cdev);
/*
* Mark as initialized even on pattern_init() error because
* any consecutive call to it would produce the same error.
*/
led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
}
return 0; return 0;
} }
......
...@@ -220,12 +220,48 @@ static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data, ...@@ -220,12 +220,48 @@ static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
return count; return count;
} }
static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
const char *buf, size_t count)
{
int ccount, cr, offset = 0;
while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
cr = 0;
ccount = sscanf(buf + offset, "%d %u %n",
&data->patterns[data->npatterns].brightness,
&data->patterns[data->npatterns].delta_t, &cr);
if (ccount != 2) {
data->npatterns = 0;
return -EINVAL;
}
offset += cr;
data->npatterns++;
}
return 0;
}
static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
const u32 *buf, size_t count)
{
unsigned int i;
for (i = 0; i < count; i += 2) {
data->patterns[data->npatterns].brightness = buf[i];
data->patterns[data->npatterns].delta_t = buf[i + 1];
data->npatterns++;
}
return 0;
}
static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev, static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
const char *buf, size_t count, const char *buf, const u32 *buf_int,
bool hw_pattern) size_t count, bool hw_pattern)
{ {
struct pattern_trig_data *data = led_cdev->trigger_data; struct pattern_trig_data *data = led_cdev->trigger_data;
int ccount, cr, offset = 0, err = 0; int err = 0;
mutex_lock(&data->lock); mutex_lock(&data->lock);
...@@ -237,20 +273,12 @@ static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev, ...@@ -237,20 +273,12 @@ static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
data->is_hw_pattern = hw_pattern; data->is_hw_pattern = hw_pattern;
data->npatterns = 0; data->npatterns = 0;
while (offset < count - 1 && data->npatterns < MAX_PATTERNS) { if (buf)
cr = 0; err = pattern_trig_store_patterns_string(data, buf, count);
ccount = sscanf(buf + offset, "%d %u %n", else
&data->patterns[data->npatterns].brightness, err = pattern_trig_store_patterns_int(data, buf_int, count);
&data->patterns[data->npatterns].delta_t, &cr); if (err)
if (ccount != 2) {
data->npatterns = 0;
err = -EINVAL;
goto out; goto out;
}
offset += cr;
data->npatterns++;
}
err = pattern_trig_start_pattern(led_cdev); err = pattern_trig_start_pattern(led_cdev);
if (err) if (err)
...@@ -275,7 +303,7 @@ static ssize_t pattern_store(struct device *dev, struct device_attribute *attr, ...@@ -275,7 +303,7 @@ static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
{ {
struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev *led_cdev = dev_get_drvdata(dev);
return pattern_trig_store_patterns(led_cdev, buf, count, false); return pattern_trig_store_patterns(led_cdev, buf, NULL, count, false);
} }
static DEVICE_ATTR_RW(pattern); static DEVICE_ATTR_RW(pattern);
...@@ -295,7 +323,7 @@ static ssize_t hw_pattern_store(struct device *dev, ...@@ -295,7 +323,7 @@ static ssize_t hw_pattern_store(struct device *dev,
{ {
struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev *led_cdev = dev_get_drvdata(dev);
return pattern_trig_store_patterns(led_cdev, buf, count, true); return pattern_trig_store_patterns(led_cdev, buf, NULL, count, true);
} }
static DEVICE_ATTR_RW(hw_pattern); static DEVICE_ATTR_RW(hw_pattern);
...@@ -331,6 +359,30 @@ static const struct attribute_group *pattern_trig_groups[] = { ...@@ -331,6 +359,30 @@ static const struct attribute_group *pattern_trig_groups[] = {
NULL, NULL,
}; };
static void pattern_init(struct led_classdev *led_cdev)
{
unsigned int size = 0;
u32 *pattern;
int err;
pattern = led_get_default_pattern(led_cdev, &size);
if (!pattern)
return;
if (size % 2) {
dev_warn(led_cdev->dev, "Expected pattern of tuples\n");
goto out;
}
err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, false);
if (err < 0)
dev_warn(led_cdev->dev,
"Pattern initialization failed with error %d\n", err);
out:
kfree(pattern);
}
static int pattern_trig_activate(struct led_classdev *led_cdev) static int pattern_trig_activate(struct led_classdev *led_cdev)
{ {
struct pattern_trig_data *data; struct pattern_trig_data *data;
...@@ -354,6 +406,15 @@ static int pattern_trig_activate(struct led_classdev *led_cdev) ...@@ -354,6 +406,15 @@ static int pattern_trig_activate(struct led_classdev *led_cdev)
timer_setup(&data->timer, pattern_trig_timer_function, 0); timer_setup(&data->timer, pattern_trig_timer_function, 0);
led_cdev->activated = true; led_cdev->activated = true;
if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
pattern_init(led_cdev);
/*
* Mark as initialized even on pattern_init() error because
* any consecutive call to it would produce the same error.
*/
led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
}
return 0; return 0;
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/leds.h> #include <linux/leds.h>
static ssize_t led_delay_on_show(struct device *dev, static ssize_t led_delay_on_show(struct device *dev,
...@@ -77,8 +78,41 @@ static struct attribute *timer_trig_attrs[] = { ...@@ -77,8 +78,41 @@ static struct attribute *timer_trig_attrs[] = {
}; };
ATTRIBUTE_GROUPS(timer_trig); ATTRIBUTE_GROUPS(timer_trig);
static void pattern_init(struct led_classdev *led_cdev)
{
u32 *pattern;
unsigned int size = 0;
pattern = led_get_default_pattern(led_cdev, &size);
if (!pattern)
return;
if (size != 2) {
dev_warn(led_cdev->dev,
"Expected 2 but got %u values for delays pattern\n",
size);
goto out;
}
led_cdev->blink_delay_on = pattern[0];
led_cdev->blink_delay_off = pattern[1];
/* led_blink_set() called by caller */
out:
kfree(pattern);
}
static int timer_trig_activate(struct led_classdev *led_cdev) static int timer_trig_activate(struct led_classdev *led_cdev)
{ {
if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
pattern_init(led_cdev);
/*
* Mark as initialized even on pattern_init() error because
* any consecutive call to it would produce the same error.
*/
led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
}
led_blink_set(led_cdev, &led_cdev->blink_delay_on, led_blink_set(led_cdev, &led_cdev->blink_delay_on,
&led_cdev->blink_delay_off); &led_cdev->blink_delay_off);
......
...@@ -218,6 +218,19 @@ extern int led_set_brightness_sync(struct led_classdev *led_cdev, ...@@ -218,6 +218,19 @@ extern int led_set_brightness_sync(struct led_classdev *led_cdev,
*/ */
extern int led_update_brightness(struct led_classdev *led_cdev); extern int led_update_brightness(struct led_classdev *led_cdev);
/**
* led_get_default_pattern - return default pattern
*
* @led_cdev: the LED to get default pattern for
* @size: pointer for storing the number of elements in returned array,
* modified only if return != NULL
*
* Return: Allocated array of integers with default pattern from device tree
* or NULL. Caller is responsible for kfree().
*/
extern u32 *led_get_default_pattern(struct led_classdev *led_cdev,
unsigned int *size);
/** /**
* led_sysfs_disable - disable LED sysfs interface * led_sysfs_disable - disable LED sysfs interface
* @led_cdev: the LED to set * @led_cdev: the LED to set
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment