diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-04 19:33:04 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-04 19:33:04 -0800 |
commit | cd2a3bf02625ffad02a6b9f7df758ee36cf12769 (patch) | |
tree | 3eea69c80535c60bd421cf0c1273e05959cf9df5 /drivers | |
parent | 7629bac64204ff256d3b2415767a7acb1401047b (diff) | |
parent | 5ddb0869bfc1bca6cfc592c74c64a026f936638c (diff) | |
download | linux-cd2a3bf02625ffad02a6b9f7df758ee36cf12769.tar.bz2 |
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
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/leds/led-core.c | 30 | ||||
-rw-r--r-- | drivers/leds/leds-lp55xx-common.c | 4 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-oneshot.c | 38 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-pattern.c | 99 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-timer.c | 34 |
5 files changed, 182 insertions, 23 deletions
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index ede4fa0ac2cc..e3da7c03da1b 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -16,7 +16,9 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/of.h> #include <linux/rwsem.h> +#include <linux/slab.h> #include "leds.h" DECLARE_RWSEM(leds_list_lock); @@ -310,6 +312,34 @@ int led_update_brightness(struct led_classdev *led_cdev) } 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 */ void led_sysfs_disable(struct led_classdev *led_cdev) { diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 3d79a6380761..723f2f17497a 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -201,7 +201,7 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) if (!fw) { dev_err(dev, "firmware request failed\n"); - goto out; + return; } /* handling firmware data is chip dependent */ @@ -214,9 +214,9 @@ static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) mutex_unlock(&chip->lock); -out: /* firmware should be released for other channel use */ release_firmware(chip->fw); + chip->fw = NULL; } static int lp55xx_request_firmware(struct lp55xx_chip *chip) diff --git a/drivers/leds/trigger/ledtrig-oneshot.c b/drivers/leds/trigger/ledtrig-oneshot.c index 95c9be4b6e7e..8808f0ad7339 100644 --- a/drivers/leds/trigger/ledtrig-oneshot.c +++ b/drivers/leds/trigger/ledtrig-oneshot.c @@ -130,6 +130,34 @@ static struct attribute *oneshot_trig_attrs[] = { }; 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) { struct oneshot_trig_data *oneshot_data; @@ -140,8 +168,14 @@ static int oneshot_trig_activate(struct led_classdev *led_cdev) led_set_trigger_data(led_cdev, oneshot_data); - led_cdev->blink_delay_on = DEFAULT_DELAY; - led_cdev->blink_delay_off = DEFAULT_DELAY; + 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; } diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index 1870cf87afe1..718729c89440 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -220,22 +220,10 @@ out: return count; } -static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev, - const char *buf, size_t count, - bool hw_pattern) +static int pattern_trig_store_patterns_string(struct pattern_trig_data *data, + const char *buf, size_t count) { - struct pattern_trig_data *data = led_cdev->trigger_data; - int ccount, cr, offset = 0, err = 0; - - mutex_lock(&data->lock); - - del_timer_sync(&data->timer); - - if (data->is_hw_pattern) - led_cdev->pattern_clear(led_cdev); - - data->is_hw_pattern = hw_pattern; - data->npatterns = 0; + int ccount, cr, offset = 0; while (offset < count - 1 && data->npatterns < MAX_PATTERNS) { cr = 0; @@ -244,14 +232,54 @@ static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev, &data->patterns[data->npatterns].delta_t, &cr); if (ccount != 2) { data->npatterns = 0; - err = -EINVAL; - goto out; + 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, + const char *buf, const u32 *buf_int, + size_t count, bool hw_pattern) +{ + struct pattern_trig_data *data = led_cdev->trigger_data; + int err = 0; + + mutex_lock(&data->lock); + + del_timer_sync(&data->timer); + + if (data->is_hw_pattern) + led_cdev->pattern_clear(led_cdev); + + data->is_hw_pattern = hw_pattern; + data->npatterns = 0; + + if (buf) + err = pattern_trig_store_patterns_string(data, buf, count); + else + err = pattern_trig_store_patterns_int(data, buf_int, count); + if (err) + goto out; + err = pattern_trig_start_pattern(led_cdev); if (err) data->npatterns = 0; @@ -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); - 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); @@ -295,7 +323,7 @@ static ssize_t hw_pattern_store(struct device *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); @@ -331,6 +359,30 @@ static const struct attribute_group *pattern_trig_groups[] = { 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) { struct pattern_trig_data *data; @@ -354,6 +406,15 @@ static int pattern_trig_activate(struct led_classdev *led_cdev) timer_setup(&data->timer, pattern_trig_timer_function, 0); 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; } diff --git a/drivers/leds/trigger/ledtrig-timer.c b/drivers/leds/trigger/ledtrig-timer.c index 7c14983781ee..ca898c1383be 100644 --- a/drivers/leds/trigger/ledtrig-timer.c +++ b/drivers/leds/trigger/ledtrig-timer.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/ctype.h> +#include <linux/slab.h> #include <linux/leds.h> static ssize_t led_delay_on_show(struct device *dev, @@ -77,8 +78,41 @@ static struct attribute *timer_trig_attrs[] = { }; 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) { + 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_cdev->blink_delay_off); |