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

Commit 7aa7d608 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull LED updates from Jacek Anaszewski:
 "New features and improvements:

   - add new optional brightness_hw_changed attribute for the LEDs that
     may have their brightness level changed autonomously (outside of
     kernel control) by hardware / firmware. The attribute supports
     userspace notifications through POLLPRI events

   - add led_brightness_hw_mon tool that demonstrates how to use the
     aforementioned feature

   - add LED_ON enum for LEDs that can be only turned on/off, and don't
     allow setting other brightness levels

   - allow for adjusting heartbeat trigger blink brightness level

  Fixes and cleanups:

   - avoid harmless maybe-uninitialized warning in leds-ktd2692.c

   - add context to the existing example entries in common LED bindings
     to make the documentation more clear"

* tag 'leds_for_4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds:
  leds: ledtrig-heartbeat: Make top brightness adjustable
  tools/leds: Add led_hw_brightness_mon program
  leds: class: Add new optional brightness_hw_changed attribute
  leds: ktd2692: avoid harmless maybe-uninitialized warning
  leds: add LED_ON brightness as boolean value
  DT: leds: Improve examples by adding some context
parents 85adbcd5 fb3d7691
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -23,6 +23,23 @@ Description:
		If the LED does not support different brightness levels, this
		should be 1.

What:		/sys/class/leds/<led>/brightness_hw_changed
Date:		January 2017
KernelVersion:	4.11
Description:
		Last hardware set brightness level for this LED. Some LEDs
		may be changed autonomously by hardware/firmware. Only LEDs
		where this happens and the driver can detect this, will have
		this file.

		This file supports poll() to detect when the hardware changes
		the brightness.

		Reading this file will return the last brightness level set
		by the hardware, this may be different from the current
		brightness. Reading this file when no hw brightness change
		event has happened will return an ENODATA error.

What:		/sys/class/leds/<led>/trigger
Date:		March 2006
KernelVersion:	2.6.17
+18 −10
Original line number Diff line number Diff line
@@ -61,12 +61,19 @@ property can be omitted.

Examples:

gpio-leds {
	compatible = "gpio-leds";

	system-status {
		label = "Status";
		linux,default-trigger = "heartbeat";
	...
		gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
	};
};

max77693-led {
	compatible = "maxim,max77693-led";

	camera-flash {
		label = "Flash";
		led-sources = <0>, <1>;
@@ -74,3 +81,4 @@ camera-flash {
		flash-max-microamp = <320000>;
		flash-max-timeout-us = <500000>;
	};
};
+15 −0
Original line number Diff line number Diff line
@@ -65,6 +65,21 @@ LED subsystem core exposes following API for setting brightness:
		blinking, returns -EBUSY if software blink fallback is enabled.


LED registration API
====================

A driver wanting to register a LED classdev for use by other drivers /
userspace needs to allocate and fill a led_classdev struct and then call
[devm_]led_classdev_register. If the non devm version is used the driver
must call led_classdev_unregister from its remove function before
free-ing the led_classdev struct.

If the driver can detect hardware initiated brightness changes and thus
wants to have a brightness_hw_changed attribute then the LED_BRIGHT_HW_CHANGED
flag must be set in flags before registering. Calling
led_classdev_notify_brightness_hw_changed on a classdev not registered with
the LED_BRIGHT_HW_CHANGED flag is a bug and will trigger a WARN_ON.

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

+9 −0
Original line number Diff line number Diff line
@@ -29,6 +29,15 @@ config LEDS_CLASS_FLASH
	  for the flash related features of a LED device. It can be built
	  as a module.

config LEDS_BRIGHTNESS_HW_CHANGED
	bool "LED Class brightness_hw_changed attribute support"
	depends on LEDS_CLASS
	help
	  This option enables support for the brightness_hw_changed attribute
	  for led sysfs class devices under /sys/class/leds.

	  See Documentation/ABI/testing/sysfs-class-led for details.

comment "LED drivers"

config LEDS_88PM860X
+76 −0
Original line number Diff line number Diff line
@@ -103,6 +103,68 @@ static const struct attribute_group *led_groups[] = {
	NULL,
};

#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
static ssize_t brightness_hw_changed_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);

	if (led_cdev->brightness_hw_changed == -1)
		return -ENODATA;

	return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
}

static DEVICE_ATTR_RO(brightness_hw_changed);

static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
{
	struct device *dev = led_cdev->dev;
	int ret;

	ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
	if (ret) {
		dev_err(dev, "Error creating brightness_hw_changed\n");
		return ret;
	}

	led_cdev->brightness_hw_changed_kn =
		sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
	if (!led_cdev->brightness_hw_changed_kn) {
		dev_err(dev, "Error getting brightness_hw_changed kn\n");
		device_remove_file(dev, &dev_attr_brightness_hw_changed);
		return -ENXIO;
	}

	return 0;
}

static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
{
	sysfs_put(led_cdev->brightness_hw_changed_kn);
	device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
}

void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev,
					       enum led_brightness brightness)
{
	if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
		return;

	led_cdev->brightness_hw_changed = brightness;
	sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
}
EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
#else
static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
{
	return 0;
}
static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
{
}
#endif

/**
 * led_classdev_suspend - suspend an led_classdev.
 * @led_cdev: the led_classdev to suspend.
@@ -204,9 +266,20 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
		dev_warn(parent, "Led %s renamed to %s due to name collision",
				led_cdev->name, dev_name(led_cdev->dev));

	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
		ret = led_add_brightness_hw_changed(led_cdev);
		if (ret) {
			device_unregister(led_cdev->dev);
			return ret;
		}
	}

	led_cdev->work_flags = 0;
#ifdef CONFIG_LEDS_TRIGGERS
	init_rwsem(&led_cdev->trigger_lock);
#endif
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
	led_cdev->brightness_hw_changed = -1;
#endif
	mutex_init(&led_cdev->led_access);
	/* add to the list of leds */
@@ -256,6 +329,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)

	flush_work(&led_cdev->set_brightness_work);

	if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
		led_remove_brightness_hw_changed(led_cdev);

	device_unregister(led_cdev->dev);

	down_write(&leds_list_lock);
Loading