Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c487121 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
  ...
parents d870a9d5 522f17e1
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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
==================================

+2 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
+2 −6
Original line number Diff line number Diff line
@@ -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;
	}
@@ -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;
@@ -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);
+5 −6
Original line number Diff line number Diff line
@@ -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);

@@ -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);
@@ -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);
@@ -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);
+90 −30
Original line number Diff line number Diff line
@@ -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;
	}

@@ -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.
		 */
		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 +
@@ -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;

	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,
@@ -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;
	}

@@ -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,
@@ -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)
{
@@ -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)
{
@@ -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)
Loading