Skip to content

Commit

Permalink
Merge tag 'leds-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/j.anaszewski/linux-leds

Pull LED subsystem updates from Jacek Anaszewski:
 "Besides regular driver updates, we introduce a portion of LED core
  improvements, that allow to avoid the need for using work queues in
  the LED class drivers, that set brightness in a blocking way.

  Affected LED class drivers are being optimized accordingly.

   - LED core improvements:
        - use EXPORT_SYMBOL_GPL consistently,
        - add two new LED_BLINK_ flags,
        - rename brightness_set_sync op to brightness_set_blocking,
        - add led_set_brightness_nosleep{nopm} functions,
        - use set_brightness_work for the blocking op,
        - drivers shouldn't enforce SYNC/ASYNC brightness setting,
        - turn off the LED and wait for completion on unregistering LED
          class device,
        - add managed version of led_trigger_register,
        - add description of brightness setting API to the LED class doc.

   - Remove work queues from drivers: leds-tlc591xx, leds-88pm860x, leds-adp5520,
        leds-bd2802, leds-blinkm, leds-lm3533, leds-lm3642, leds-pca9532,
        leds-lp3944, leds-lp55xx, leds-lp8788, leds-lp8860, leds-pca955x,
        leds-pca963x, leds-wm831x, leds-da903x, leds-da9052, leds-dac124d085,
        leds-lt3593, leds-max8997, leds-mc13783, leds-regulator, leds-wm8350,
        leds-max77693, leds-aat1290, leds-ktd2692, leds-gpio, leds-pwm,
        leds-lm355x, leds-ns2.

   - Replace brightness_set op with a new brightness_set_blocking op to
     make the drivers compatible with led triggers: leds-ipaq-micro,
     leds-powernv.

   - Add missing of_node_put: leds-ktd2692, leds-aat1290, leds-max77693.

   - Make the driver explicitly non-modular: ledtrig-cpu,
     ledtrig-ide-disk, leds-syscon.

   - Improvements to leds-bcm6328:
        - reuse bcm6328_led_set() instead of copying its functionality,
        - swap LED ON and OFF definitions,
        - improve blink support,
        - simplify duplicated unlock in bcm6328_blink_set,
        - add little endian support,
        - remove unneded lock when checking initial LED status,
        - add HAS_IOMEM dependency,
        - code cleaning.

   - Improvements to leds-bcm6358:
        - use bcm6358_led_set() in order to get rid of the lock,
        - merge bcm6358_led_mode and bcm6358_led_set,
        - add little endian support,
        - remove unneded lock when checking initial LED status,
        - add HAS_IOMEM dependency,
        - remove unneeded busy status check.

   - Call led_pwm_set() in leds-pwm to enforce default LED_OFF.

   - Fix duration to be msec instead of jiffies: ledtrig-transient.

   - Removing NULL check: leds-powernv.

   - Use platform_register/unregister_drivers(): leds-sunfire.

   - Fix module license specification: ledtrig-oneshot.

   - Fix driver description and make license match the header: leds-pwm.

   - Remove checking for state < 1 in flash_strobe_store():
     led-class-flash.

   - Use led_set_brightness_sync for torch brightness:
     v4l2-flash-led-class"

* tag 'leds-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds: (68 commits)
  leds: add HAS_IOMEM dependency to LEDS_BCM6328/LEDS_BCM6358
  leds: core: add managed version of led_trigger_register
  leds: bcm6358: remove unneeded busy status check
  leds: bcm6328: improve blink support
  leds: bcm6358: merge bcm6358_led_mode and bcm6358_led_set
  leds: bcm6328: simplify duplicated unlock in bcm6328_blink_set
  leds: bcm6358: add little endian support
  leds: bcm6328: add little endian support
  leds: bcm6358: remove unneded lock when checking initial LED status
  leds: bcm6358: Use bcm6358_led_set() in order to get rid of the lock
  leds: bcm6328: remove unneded lock when checking initial LED
  leds: bcm6328: code cleaning
  leds: syscon: Make the driver explicitly non-modular
  leds: ledtrig-ide-disk: Make the driver explicitly non-modular
  leds: ledtrig-cpu: Make the driver explicitly non-modular
  leds: sunfire: Use platform_register/unregister_drivers()
  leds: max77693: Add missing of_node_put
  leds: aat1290: Add missing of_node_put
  leds: powernv: Implement brightness_set_blocking op
  leds: ipaq-micro: Implement brightness_set_blocking op
  ...
  • Loading branch information
Linus Torvalds committed Jan 12, 2016
2 parents d870a9d + 522f17e commit 2c48712
Show file tree
Hide file tree
Showing 59 changed files with 677 additions and 1,146 deletions.
13 changes: 13 additions & 0 deletions Documentation/leds/leds-class.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ above leaves scope for further attributes should they be needed. If sections
of the name don't apply, just leave that section blank.


Brightness setting API
======================

LED subsystem core exposes following API for setting brightness:

- led_set_brightness : it is guaranteed not to sleep, passing LED_OFF stops
blinking,
- led_set_brightness_sync : for use cases when immediate effect is desired -
it can block the caller for the time required for accessing
device registers and can sleep, passing LED_OFF stops hardware
blinking, returns -EBUSY if software blink fallback is enabled.


Hardware accelerated blink of LEDs
==================================

Expand Down
2 changes: 2 additions & 0 deletions drivers/leds/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ config LEDS_AAT1290
config LEDS_BCM6328
tristate "LED Support for Broadcom BCM6328"
depends on LEDS_CLASS
depends on HAS_IOMEM
depends on OF
help
This option enables support for LEDs connected to the BCM6328
Expand All @@ -60,6 +61,7 @@ config LEDS_BCM6328
config LEDS_BCM6358
tristate "LED Support for Broadcom BCM6358"
depends on LEDS_CLASS
depends on HAS_IOMEM
depends on OF
help
This option enables support for LEDs connected to the BCM6358
Expand Down
8 changes: 2 additions & 6 deletions drivers/leds/led-class-flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ static ssize_t flash_strobe_store(struct device *dev,
if (ret)
goto unlock;

if (state < 0 || state > 1) {
if (state > 1) {
ret = -EINVAL;
goto unlock;
}
Expand Down Expand Up @@ -298,7 +298,7 @@ int led_classdev_flash_register(struct device *parent,
led_cdev = &fled_cdev->led_cdev;

if (led_cdev->flags & LED_DEV_CAP_FLASH) {
if (!led_cdev->brightness_set_sync)
if (!led_cdev->brightness_set_blocking)
return -EINVAL;

ops = fled_cdev->ops;
Expand All @@ -316,10 +316,6 @@ int led_classdev_flash_register(struct device *parent,
if (ret < 0)
return ret;

/* Setting a torch brightness needs to have immediate effect */
led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC;
led_cdev->flags |= SET_BRIGHTNESS_SYNC;

return 0;
}
EXPORT_SYMBOL_GPL(led_classdev_flash_register);
Expand Down
11 changes: 5 additions & 6 deletions drivers/leds/led-class.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ static const struct attribute_group *led_groups[] = {
void led_classdev_suspend(struct led_classdev *led_cdev)
{
led_cdev->flags |= LED_SUSPENDED;
led_cdev->brightness_set(led_cdev, 0);
led_set_brightness_nopm(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);

Expand All @@ -119,7 +119,7 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
*/
void led_classdev_resume(struct led_classdev *led_cdev)
{
led_cdev->brightness_set(led_cdev, led_cdev->brightness);
led_set_brightness_nopm(led_cdev, led_cdev->brightness);

if (led_cdev->flash_resume)
led_cdev->flash_resume(led_cdev);
Expand Down Expand Up @@ -215,8 +215,6 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
if (!led_cdev->max_brightness)
led_cdev->max_brightness = LED_FULL;

led_cdev->flags |= SET_BRIGHTNESS_ASYNC;

led_update_brightness(led_cdev);

led_init_core(led_cdev);
Expand Down Expand Up @@ -247,12 +245,13 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
up_write(&led_cdev->trigger_lock);
#endif

cancel_work_sync(&led_cdev->set_brightness_work);

/* Stop blinking */
led_stop_software_blink(led_cdev);

led_set_brightness(led_cdev, LED_OFF);

flush_work(&led_cdev->set_brightness_work);

device_unregister(led_cdev->dev);

down_write(&leds_list_lock);
Expand Down
120 changes: 90 additions & 30 deletions drivers/leds/led-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static void led_timer_function(unsigned long data)
unsigned long delay;

if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
led_set_brightness_async(led_cdev, LED_OFF);
led_set_brightness_nosleep(led_cdev, LED_OFF);
return;
}

Expand All @@ -44,23 +44,23 @@ static void led_timer_function(unsigned long data)
brightness = led_get_brightness(led_cdev);
if (!brightness) {
/* Time to switch the LED on. */
if (led_cdev->delayed_set_value) {
led_cdev->blink_brightness =
led_cdev->delayed_set_value;
led_cdev->delayed_set_value = 0;
}
brightness = led_cdev->blink_brightness;
delay = led_cdev->blink_delay_on;
} else {
/* Store the current brightness value to be able
* to restore it when the delay_off period is over.
* Do it only if there is no pending blink brightness
* change, to avoid overwriting the new value.
*/
led_cdev->blink_brightness = brightness;
if (!(led_cdev->flags & LED_BLINK_BRIGHTNESS_CHANGE))
led_cdev->blink_brightness = brightness;
else
led_cdev->flags &= ~LED_BLINK_BRIGHTNESS_CHANGE;
brightness = LED_OFF;
delay = led_cdev->blink_delay_off;
}

led_set_brightness_async(led_cdev, brightness);
led_set_brightness_nosleep(led_cdev, brightness);

/* Return in next iteration if led is in one-shot mode and we are in
* the final blink state so that the led is toggled each delay_on +
Expand All @@ -83,10 +83,24 @@ static void set_brightness_delayed(struct work_struct *ws)
{
struct led_classdev *led_cdev =
container_of(ws, struct led_classdev, set_brightness_work);
int ret = 0;

led_stop_software_blink(led_cdev);
if (led_cdev->flags & LED_BLINK_DISABLE) {
led_cdev->delayed_set_value = LED_OFF;
led_stop_software_blink(led_cdev);
led_cdev->flags &= ~LED_BLINK_DISABLE;
}

led_set_brightness_async(led_cdev, led_cdev->delayed_set_value);
if (led_cdev->brightness_set)
led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
else if (led_cdev->brightness_set_blocking)
ret = led_cdev->brightness_set_blocking(led_cdev,
led_cdev->delayed_set_value);
else
ret = -ENOTSUPP;
if (ret < 0)
dev_err(led_cdev->dev,
"Setting an LED's brightness failed (%d)\n", ret);
}

static void led_set_software_blink(struct led_classdev *led_cdev,
Expand All @@ -106,13 +120,14 @@ static void led_set_software_blink(struct led_classdev *led_cdev,

/* never on - just set to off */
if (!delay_on) {
led_set_brightness_async(led_cdev, LED_OFF);
led_set_brightness_nosleep(led_cdev, LED_OFF);
return;
}

/* never off - just set to brightness */
if (!delay_off) {
led_set_brightness_async(led_cdev, led_cdev->blink_brightness);
led_set_brightness_nosleep(led_cdev,
led_cdev->blink_brightness);
return;
}

Expand Down Expand Up @@ -156,7 +171,7 @@ void led_blink_set(struct led_classdev *led_cdev,

led_blink_setup(led_cdev, delay_on, delay_off);
}
EXPORT_SYMBOL(led_blink_set);
EXPORT_SYMBOL_GPL(led_blink_set);

void led_blink_set_oneshot(struct led_classdev *led_cdev,
unsigned long *delay_on,
Expand All @@ -177,7 +192,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,

led_blink_setup(led_cdev, delay_on, delay_off);
}
EXPORT_SYMBOL(led_blink_set_oneshot);
EXPORT_SYMBOL_GPL(led_blink_set_oneshot);

void led_stop_software_blink(struct led_classdev *led_cdev)
{
Expand All @@ -190,29 +205,74 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink);
void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
int ret = 0;

/* delay brightness if soft-blink is active */
/*
* In case blinking is on delay brightness setting
* until the next timer tick.
*/
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
led_cdev->delayed_set_value = brightness;
if (brightness == LED_OFF)
/*
* If we need to disable soft blinking delegate this to the
* work queue task to avoid problems in case we are called
* from hard irq context.
*/
if (brightness == LED_OFF) {
led_cdev->flags |= LED_BLINK_DISABLE;
schedule_work(&led_cdev->set_brightness_work);
} else {
led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE;
led_cdev->blink_brightness = brightness;
}
return;
}

if (led_cdev->flags & SET_BRIGHTNESS_ASYNC) {
led_set_brightness_async(led_cdev, brightness);
led_set_brightness_nosleep(led_cdev, brightness);
}
EXPORT_SYMBOL_GPL(led_set_brightness);

void led_set_brightness_nopm(struct led_classdev *led_cdev,
enum led_brightness value)
{
/* Use brightness_set op if available, it is guaranteed not to sleep */
if (led_cdev->brightness_set) {
led_cdev->brightness_set(led_cdev, value);
return;
} else if (led_cdev->flags & SET_BRIGHTNESS_SYNC)
ret = led_set_brightness_sync(led_cdev, brightness);
else
ret = -EINVAL;
}

if (ret < 0)
dev_dbg(led_cdev->dev, "Setting LED brightness failed (%d)\n",
ret);
/* If brightness setting can sleep, delegate it to a work queue task */
led_cdev->delayed_set_value = value;
schedule_work(&led_cdev->set_brightness_work);
}
EXPORT_SYMBOL_GPL(led_set_brightness_nopm);

void led_set_brightness_nosleep(struct led_classdev *led_cdev,
enum led_brightness value)
{
led_cdev->brightness = min(value, led_cdev->max_brightness);

if (led_cdev->flags & LED_SUSPENDED)
return;

led_set_brightness_nopm(led_cdev, led_cdev->brightness);
}
EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);

int led_set_brightness_sync(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
return -EBUSY;

led_cdev->brightness = min(value, led_cdev->max_brightness);

if (led_cdev->flags & LED_SUSPENDED)
return 0;

if (led_cdev->brightness_set_blocking)
return led_cdev->brightness_set_blocking(led_cdev,
led_cdev->brightness);
return -ENOTSUPP;
}
EXPORT_SYMBOL(led_set_brightness);
EXPORT_SYMBOL_GPL(led_set_brightness_sync);

int led_update_brightness(struct led_classdev *led_cdev)
{
Expand All @@ -228,7 +288,7 @@ int led_update_brightness(struct led_classdev *led_cdev)

return ret;
}
EXPORT_SYMBOL(led_update_brightness);
EXPORT_SYMBOL_GPL(led_update_brightness);

/* Caller must ensure led_cdev->led_access held */
void led_sysfs_disable(struct led_classdev *led_cdev)
Expand Down
28 changes: 28 additions & 0 deletions drivers/leds/led-triggers.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,34 @@ void led_trigger_unregister(struct led_trigger *trig)
}
EXPORT_SYMBOL_GPL(led_trigger_unregister);

static void devm_led_trigger_release(struct device *dev, void *res)
{
led_trigger_unregister(*(struct led_trigger **)res);
}

int devm_led_trigger_register(struct device *dev,
struct led_trigger *trig)
{
struct led_trigger **dr;
int rc;

dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
GFP_KERNEL);
if (!dr)
return -ENOMEM;

*dr = trig;

rc = led_trigger_register(trig);
if (rc)
devres_free(dr);
else
devres_add(dev, dr);

return rc;
}
EXPORT_SYMBOL_GPL(devm_led_trigger_register);

/* Simple LED Tigger Interface */

void led_trigger_event(struct led_trigger *trig,
Expand Down
Loading

0 comments on commit 2c48712

Please sign in to comment.