All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] led: core: RfC - add RGB LED handling to the core
@ 2016-01-10 20:27 Heiner Kallweit
  2016-01-13  9:42 ` Jacek Anaszewski
  2016-01-14 12:08 ` Jacek Anaszewski
  0 siblings, 2 replies; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-10 20:27 UTC (permalink / raw)
  To: linux-leds, Jacek Anaszewski

When playing with a ThingM Blink(1) USB RGB LED device I found that there
are few drivers for RGB LED's spread across the kernel and each one
implements the RGB functionality in its own way.
Some examples:
- drivers/hid/hid-thingm.c
- drivers/usb/misc/usbled.c
- drivers/leds/leds-bd2802.c
- drivers/leds/leds-blinkm.c
...
IMHO it would make sense to add generic RGB functionality to the LED core.

Things I didn't like too much in other driver implementations:
- one led_classdev per color or at least
- one sysfs attribute per color
Colors are not really independent therefore I'd prefer one led_classdev
per RGB LED. Also userspace should be able to change the color with one
call -> therefore only one sysfs attribute for the RGB values.

Also the current brightness-related functionality should not be effected
by the RGB extension.

This patch is intended to demonstrate the idea of the extension. Most likely
it's not ready yet for submission.

Main idea is to base the effective RGB value on a combination of brightness
and a scaled-to-max RGB value.
The RGB-related callbacks are basically the same as for brightness.
RGB functionally can be switched on with a new flag in the led_classdev.flags
bitmap.

Experimentally I changed the thingm driver to use this extension and it works
quite well. As one result the driver could be very much simplified.

Any feedback is appreciated.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
 drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/leds/leds.h      |  12 ++++++
 include/linux/leds.h     |  13 ++++++
 4 files changed, 193 insertions(+)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 14139c3..5e4c2f2 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -64,6 +64,47 @@ unlock:
 }
 static DEVICE_ATTR_RW(brightness);
 
+static ssize_t rgb_show(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	u32 rgb_val;
+
+	mutex_lock(&led_cdev->led_access);
+	led_update_rgb_val(led_cdev);
+	rgb_val = led_get_rgb_val(led_cdev);
+	mutex_unlock(&led_cdev->led_access);
+
+	return sprintf(buf, "0x%06x\n", rgb_val);
+}
+
+static ssize_t rgb_store(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	unsigned long state;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_access);
+
+	if (led_sysfs_is_disabled(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 0, &state);
+	if (ret)
+		goto unlock;
+
+	led_set_rgb_val(led_cdev, state);
+
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_access);
+	return ret;
+}
+static DEVICE_ATTR_RW(rgb);
+
 static ssize_t max_brightness_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -190,6 +231,16 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 	char name[64];
 	int ret;
 
+	/* FLASH is not supported for RGB LEDs so far
+	 * and RGB enforces max_brightness = LED_FULL.
+	 * Initialize the color as white.
+	 */
+	if (led_isRGB(led_cdev)) {
+		led_cdev->flags &= ~LED_DEV_CAP_FLASH;
+		led_cdev->max_brightness = LED_FULL;
+		led_cdev->rgb_val = 0xffffff;
+	}
+
 	ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
 	if (ret < 0)
 		return ret;
@@ -203,6 +254,14 @@ 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_isRGB(led_cdev)) {
+		ret = device_create_file(led_cdev->dev, &dev_attr_rgb);
+		if (ret) {
+			device_unregister(led_cdev->dev);
+			return ret;
+		}
+	}
+
 #ifdef CONFIG_LEDS_TRIGGERS
 	init_rwsem(&led_cdev->trigger_lock);
 #endif
@@ -252,6 +311,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
 
 	flush_work(&led_cdev->set_brightness_work);
 
+	if (led_isRGB(led_cdev))
+		device_remove_file(led_cdev->dev, &dev_attr_rgb);
+
 	device_unregister(led_cdev->dev);
 
 	down_write(&leds_list_lock);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 19e1e60..6563bd3 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -25,6 +25,48 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
 LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
 
+static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
+{
+	u32 red_raw = (rgb >> 16) & 0xff;
+	u32 green_raw = (rgb >> 8) & 0xff;
+	u32 blue_raw = rgb & 0xff;
+	u32 max_raw, red, green, blue;
+
+	max_raw = max(red_raw, green_raw);
+	if (blue_raw > max_raw)
+		max_raw = blue_raw;
+
+	if (!max_raw) {
+		cdev->brightness = 0;
+		cdev->rgb_val = 0;
+		return;
+	}
+
+	red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
+	green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
+	blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
+
+	cdev->brightness = max_raw;
+	cdev->rgb_val = (red << 16) + (green << 8) + blue;
+}
+
+static u32 led_get_rgb_raw(struct led_classdev *cdev, bool delayed)
+{
+	u32 rgb = delayed ? cdev->delayed_rgb_value : cdev->rgb_val;
+	u32 brightness = delayed ? cdev->delayed_set_value :
+			 cdev->brightness;
+	u32 red = (rgb >> 16) & 0xff;
+	u32 green = (rgb >> 8) & 0xff;
+	u32 blue = rgb & 0xff;
+	u32 red_raw, green_raw, blue_raw;
+
+	red_raw = DIV_ROUND_CLOSEST(red * brightness, LED_FULL);
+	green_raw = DIV_ROUND_CLOSEST(green * brightness, LED_FULL);
+	blue_raw = DIV_ROUND_CLOSEST(blue * brightness, LED_FULL);
+
+	return (red_raw << 16) + (green_raw << 8) + blue_raw;
+}
+
 static void led_timer_function(unsigned long data)
 {
 	struct led_classdev *led_cdev = (void *)data;
@@ -91,6 +133,21 @@ static void set_brightness_delayed(struct work_struct *ws)
 		led_cdev->flags &= ~LED_BLINK_DISABLE;
 	}
 
+	if (led_isRGB(led_cdev)) {
+		u32 rgb = led_get_rgb_raw(led_cdev, true);
+
+		if (led_cdev->rgb_set)
+			led_cdev->rgb_set(led_cdev, rgb);
+		else if (led_cdev->rgb_set_blocking)
+			ret = led_cdev->rgb_set_blocking(led_cdev, rgb);
+		else
+			ret = -EOPNOTSUPP;
+		if (ret < 0)
+			dev_err(led_cdev->dev,
+				"Setting LED RGB value failed (%d)\n", ret);
+		return;
+	}
+
 	if (led_cdev->brightness_set)
 		led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
 	else if (led_cdev->brightness_set_blocking)
@@ -229,9 +286,35 @@ void led_set_brightness(struct led_classdev *led_cdev,
 }
 EXPORT_SYMBOL_GPL(led_set_brightness);
 
+void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val)
+{
+	if (!led_isRGB(led_cdev) || (led_cdev->flags & LED_SUSPENDED))
+		return;
+
+	led_set_rgb_raw(led_cdev, rgb_val);
+
+	if (led_cdev->rgb_set) {
+		led_cdev->rgb_set(led_cdev, rgb_val);
+		return;
+	}
+
+	/* If RGB setting can sleep, delegate it to a work queue task */
+	led_cdev->delayed_set_value = led_cdev->brightness;
+	led_cdev->delayed_rgb_value = led_cdev->rgb_val;
+	schedule_work(&led_cdev->set_brightness_work);
+}
+EXPORT_SYMBOL_GPL(led_set_rgb_val);
+
 void led_set_brightness_nopm(struct led_classdev *led_cdev,
 			      enum led_brightness value)
 {
+	if (led_isRGB(led_cdev) && led_cdev->rgb_set) {
+		u32 rgb = led_get_rgb_raw(led_cdev, false);
+
+		led_cdev->rgb_set(led_cdev, rgb);
+		return;
+	}
+
 	/* Use brightness_set op if available, it is guaranteed not to sleep */
 	if (led_cdev->brightness_set) {
 		led_cdev->brightness_set(led_cdev, value);
@@ -240,6 +323,7 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev,
 
 	/* If brightness setting can sleep, delegate it to a work queue task */
 	led_cdev->delayed_set_value = value;
+	led_cdev->delayed_rgb_value = led_cdev->rgb_val;
 	schedule_work(&led_cdev->set_brightness_work);
 }
 EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
@@ -267,6 +351,12 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
 	if (led_cdev->flags & LED_SUSPENDED)
 		return 0;
 
+	if (led_isRGB(led_cdev) && led_cdev->rgb_set_blocking) {
+		u32 rgb = led_get_rgb_raw(led_cdev, false);
+
+		return led_cdev->rgb_set_blocking(led_cdev, rgb);
+	}
+
 	if (led_cdev->brightness_set_blocking)
 		return led_cdev->brightness_set_blocking(led_cdev,
 							 led_cdev->brightness);
@@ -290,6 +380,22 @@ int led_update_brightness(struct led_classdev *led_cdev)
 }
 EXPORT_SYMBOL_GPL(led_update_brightness);
 
+int led_update_rgb_val(struct led_classdev *led_cdev)
+{
+	s32 ret = 0;
+
+	if (led_isRGB(led_cdev) && led_cdev->rgb_get) {
+		ret = led_cdev->rgb_get(led_cdev);
+		if (ret >= 0) {
+			led_set_rgb_raw(led_cdev, ret);
+			return 0;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_update_rgb_val);
+
 /* Caller must ensure led_cdev->led_access held */
 void led_sysfs_disable(struct led_classdev *led_cdev)
 {
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index db3f20d..c733777 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -16,17 +16,29 @@
 #include <linux/rwsem.h>
 #include <linux/leds.h>
 
+static inline bool led_isRGB(struct led_classdev *led_cdev)
+{
+	return (led_cdev->flags & LED_RGB) != 0;
+}
+
 static inline int led_get_brightness(struct led_classdev *led_cdev)
 {
 	return led_cdev->brightness;
 }
 
+static inline u32 led_get_rgb_val(struct led_classdev *led_cdev)
+{
+	return led_cdev->rgb_val;
+}
+
 void led_init_core(struct led_classdev *led_cdev);
 void led_stop_software_blink(struct led_classdev *led_cdev);
 void led_set_brightness_nopm(struct led_classdev *led_cdev,
 				enum led_brightness value);
 void led_set_brightness_nosleep(struct led_classdev *led_cdev,
 				enum led_brightness value);
+void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val);
+int led_update_rgb_val(struct led_classdev *led_cdev);
 
 extern struct rw_semaphore leds_list_lock;
 extern struct list_head leds_list;
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 4429887..83d2912 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -35,6 +35,7 @@ struct led_classdev {
 	const char		*name;
 	enum led_brightness	 brightness;
 	enum led_brightness	 max_brightness;
+	u32			 rgb_val;
 	int			 flags;
 
 	/* Lower 16 bits reflect status */
@@ -48,6 +49,7 @@ struct led_classdev {
 #define LED_BLINK_DISABLE	(1 << 21)
 #define LED_SYSFS_DISABLE	(1 << 22)
 #define LED_DEV_CAP_FLASH	(1 << 23)
+#define LED_RGB			(1 << 24)
 
 	/* Set LED brightness level */
 	/* Must not sleep. If no non-blocking version can be provided
@@ -56,15 +58,25 @@ struct led_classdev {
 	 */
 	void		(*brightness_set)(struct led_classdev *led_cdev,
 					  enum led_brightness brightness);
+
+	void		(*rgb_set)(struct led_classdev *led_cdev,
+				   u32 rgb_val);
 	/*
 	 * Set LED brightness level immediately - it can block the caller for
 	 * the time required for accessing a LED device register.
 	 */
 	int (*brightness_set_blocking)(struct led_classdev *led_cdev,
 				       enum led_brightness brightness);
+
+	int (*rgb_set_blocking)(struct led_classdev *led_cdev,
+				u32 rgb_val);
+
 	/* Get LED brightness level */
 	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
 
+	/* Get LED RGB val */
+	s32 (*rgb_get)(struct led_classdev *led_cdev);
+
 	/*
 	 * Activate hardware accelerated blink, delays are in milliseconds
 	 * and if both are zero then a sensible default should be chosen.
@@ -90,6 +102,7 @@ struct led_classdev {
 
 	struct work_struct	set_brightness_work;
 	int			delayed_set_value;
+	u32			delayed_rgb_value;
 
 #ifdef CONFIG_LEDS_TRIGGERS
 	/* Protects the trigger data below */
-- 
2.7.0

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-10 20:27 [PATCH] led: core: RfC - add RGB LED handling to the core Heiner Kallweit
@ 2016-01-13  9:42 ` Jacek Anaszewski
  2016-01-13 19:54   ` Heiner Kallweit
  2016-01-14 12:08 ` Jacek Anaszewski
  1 sibling, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-13  9:42 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: linux-leds

Hi Heiner,

Thank you for the patch. We have to ask a question here - what benefits
these modifications bring about. Isn't all of this achievable in the
user space? And I think that RGB LED problem can be boiled down to even
more generic one, namely: how to make all the sub-LEDs controllable with
a single LED class device. This subject pops out from time to time and
it'd be good to have it tackled finally. Please refer to [1] and follow
the links.

[1] http://www.spinics.net/lists/linux-leds/msg04508.html

Best Regards,
Jacek Anaszewski

On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
> When playing with a ThingM Blink(1) USB RGB LED device I found that there
> are few drivers for RGB LED's spread across the kernel and each one
> implements the RGB functionality in its own way.
> Some examples:
> - drivers/hid/hid-thingm.c
> - drivers/usb/misc/usbled.c
> - drivers/leds/leds-bd2802.c
> - drivers/leds/leds-blinkm.c
> ...
> IMHO it would make sense to add generic RGB functionality to the LED core.
>
> Things I didn't like too much in other driver implementations:
> - one led_classdev per color or at least
> - one sysfs attribute per color
> Colors are not really independent therefore I'd prefer one led_classdev
> per RGB LED. Also userspace should be able to change the color with one
> call -> therefore only one sysfs attribute for the RGB values.
>
> Also the current brightness-related functionality should not be effected
> by the RGB extension.
>
> This patch is intended to demonstrate the idea of the extension. Most likely
> it's not ready yet for submission.
>
> Main idea is to base the effective RGB value on a combination of brightness
> and a scaled-to-max RGB value.
> The RGB-related callbacks are basically the same as for brightness.
> RGB functionally can be switched on with a new flag in the led_classdev.flags
> bitmap.
>
> Experimentally I changed the thingm driver to use this extension and it works
> quite well. As one result the driver could be very much simplified.
>
> Any feedback is appreciated.
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
>   drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>   drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
>   drivers/leds/leds.h      |  12 ++++++
>   include/linux/leds.h     |  13 ++++++
>   4 files changed, 193 insertions(+)
>
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 14139c3..5e4c2f2 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -64,6 +64,47 @@ unlock:
>   }
>   static DEVICE_ATTR_RW(brightness);
>
> +static ssize_t rgb_show(struct device *dev, struct device_attribute *attr,
> +			char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	u32 rgb_val;
> +
> +	mutex_lock(&led_cdev->led_access);
> +	led_update_rgb_val(led_cdev);
> +	rgb_val = led_get_rgb_val(led_cdev);
> +	mutex_unlock(&led_cdev->led_access);
> +
> +	return sprintf(buf, "0x%06x\n", rgb_val);
> +}
> +
> +static ssize_t rgb_store(struct device *dev, struct device_attribute *attr,
> +			 const char *buf, size_t size)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	unsigned long state;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_access);
> +
> +	if (led_sysfs_is_disabled(led_cdev)) {
> +		ret = -EBUSY;
> +		goto unlock;
> +	}
> +
> +	ret = kstrtoul(buf, 0, &state);
> +	if (ret)
> +		goto unlock;
> +
> +	led_set_rgb_val(led_cdev, state);
> +
> +	ret = size;
> +unlock:
> +	mutex_unlock(&led_cdev->led_access);
> +	return ret;
> +}
> +static DEVICE_ATTR_RW(rgb);
> +
>   static ssize_t max_brightness_show(struct device *dev,
>   		struct device_attribute *attr, char *buf)
>   {
> @@ -190,6 +231,16 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>   	char name[64];
>   	int ret;
>
> +	/* FLASH is not supported for RGB LEDs so far
> +	 * and RGB enforces max_brightness = LED_FULL.
> +	 * Initialize the color as white.
> +	 */
> +	if (led_isRGB(led_cdev)) {
> +		led_cdev->flags &= ~LED_DEV_CAP_FLASH;
> +		led_cdev->max_brightness = LED_FULL;
> +		led_cdev->rgb_val = 0xffffff;
> +	}
> +
>   	ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
>   	if (ret < 0)
>   		return ret;
> @@ -203,6 +254,14 @@ 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_isRGB(led_cdev)) {
> +		ret = device_create_file(led_cdev->dev, &dev_attr_rgb);
> +		if (ret) {
> +			device_unregister(led_cdev->dev);
> +			return ret;
> +		}
> +	}
> +
>   #ifdef CONFIG_LEDS_TRIGGERS
>   	init_rwsem(&led_cdev->trigger_lock);
>   #endif
> @@ -252,6 +311,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
>
>   	flush_work(&led_cdev->set_brightness_work);
>
> +	if (led_isRGB(led_cdev))
> +		device_remove_file(led_cdev->dev, &dev_attr_rgb);
> +
>   	device_unregister(led_cdev->dev);
>
>   	down_write(&leds_list_lock);
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 19e1e60..6563bd3 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -25,6 +25,48 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
>   LIST_HEAD(leds_list);
>   EXPORT_SYMBOL_GPL(leds_list);
>
> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
> +{
> +	u32 red_raw = (rgb >> 16) & 0xff;
> +	u32 green_raw = (rgb >> 8) & 0xff;
> +	u32 blue_raw = rgb & 0xff;
> +	u32 max_raw, red, green, blue;
> +
> +	max_raw = max(red_raw, green_raw);
> +	if (blue_raw > max_raw)
> +		max_raw = blue_raw;
> +
> +	if (!max_raw) {
> +		cdev->brightness = 0;
> +		cdev->rgb_val = 0;
> +		return;
> +	}
> +
> +	red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
> +	green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
> +	blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
> +
> +	cdev->brightness = max_raw;
> +	cdev->rgb_val = (red << 16) + (green << 8) + blue;
> +}
> +
> +static u32 led_get_rgb_raw(struct led_classdev *cdev, bool delayed)
> +{
> +	u32 rgb = delayed ? cdev->delayed_rgb_value : cdev->rgb_val;
> +	u32 brightness = delayed ? cdev->delayed_set_value :
> +			 cdev->brightness;
> +	u32 red = (rgb >> 16) & 0xff;
> +	u32 green = (rgb >> 8) & 0xff;
> +	u32 blue = rgb & 0xff;
> +	u32 red_raw, green_raw, blue_raw;
> +
> +	red_raw = DIV_ROUND_CLOSEST(red * brightness, LED_FULL);
> +	green_raw = DIV_ROUND_CLOSEST(green * brightness, LED_FULL);
> +	blue_raw = DIV_ROUND_CLOSEST(blue * brightness, LED_FULL);
> +
> +	return (red_raw << 16) + (green_raw << 8) + blue_raw;
> +}
> +
>   static void led_timer_function(unsigned long data)
>   {
>   	struct led_classdev *led_cdev = (void *)data;
> @@ -91,6 +133,21 @@ static void set_brightness_delayed(struct work_struct *ws)
>   		led_cdev->flags &= ~LED_BLINK_DISABLE;
>   	}
>
> +	if (led_isRGB(led_cdev)) {
> +		u32 rgb = led_get_rgb_raw(led_cdev, true);
> +
> +		if (led_cdev->rgb_set)
> +			led_cdev->rgb_set(led_cdev, rgb);
> +		else if (led_cdev->rgb_set_blocking)
> +			ret = led_cdev->rgb_set_blocking(led_cdev, rgb);
> +		else
> +			ret = -EOPNOTSUPP;
> +		if (ret < 0)
> +			dev_err(led_cdev->dev,
> +				"Setting LED RGB value failed (%d)\n", ret);
> +		return;
> +	}
> +
>   	if (led_cdev->brightness_set)
>   		led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
>   	else if (led_cdev->brightness_set_blocking)
> @@ -229,9 +286,35 @@ void led_set_brightness(struct led_classdev *led_cdev,
>   }
>   EXPORT_SYMBOL_GPL(led_set_brightness);
>
> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val)
> +{
> +	if (!led_isRGB(led_cdev) || (led_cdev->flags & LED_SUSPENDED))
> +		return;
> +
> +	led_set_rgb_raw(led_cdev, rgb_val);
> +
> +	if (led_cdev->rgb_set) {
> +		led_cdev->rgb_set(led_cdev, rgb_val);
> +		return;
> +	}
> +
> +	/* If RGB setting can sleep, delegate it to a work queue task */
> +	led_cdev->delayed_set_value = led_cdev->brightness;
> +	led_cdev->delayed_rgb_value = led_cdev->rgb_val;
> +	schedule_work(&led_cdev->set_brightness_work);
> +}
> +EXPORT_SYMBOL_GPL(led_set_rgb_val);
> +
>   void led_set_brightness_nopm(struct led_classdev *led_cdev,
>   			      enum led_brightness value)
>   {
> +	if (led_isRGB(led_cdev) && led_cdev->rgb_set) {
> +		u32 rgb = led_get_rgb_raw(led_cdev, false);
> +
> +		led_cdev->rgb_set(led_cdev, rgb);
> +		return;
> +	}
> +
>   	/* Use brightness_set op if available, it is guaranteed not to sleep */
>   	if (led_cdev->brightness_set) {
>   		led_cdev->brightness_set(led_cdev, value);
> @@ -240,6 +323,7 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev,
>
>   	/* If brightness setting can sleep, delegate it to a work queue task */
>   	led_cdev->delayed_set_value = value;
> +	led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>   	schedule_work(&led_cdev->set_brightness_work);
>   }
>   EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
> @@ -267,6 +351,12 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
>   	if (led_cdev->flags & LED_SUSPENDED)
>   		return 0;
>
> +	if (led_isRGB(led_cdev) && led_cdev->rgb_set_blocking) {
> +		u32 rgb = led_get_rgb_raw(led_cdev, false);
> +
> +		return led_cdev->rgb_set_blocking(led_cdev, rgb);
> +	}
> +
>   	if (led_cdev->brightness_set_blocking)
>   		return led_cdev->brightness_set_blocking(led_cdev,
>   							 led_cdev->brightness);
> @@ -290,6 +380,22 @@ int led_update_brightness(struct led_classdev *led_cdev)
>   }
>   EXPORT_SYMBOL_GPL(led_update_brightness);
>
> +int led_update_rgb_val(struct led_classdev *led_cdev)
> +{
> +	s32 ret = 0;
> +
> +	if (led_isRGB(led_cdev) && led_cdev->rgb_get) {
> +		ret = led_cdev->rgb_get(led_cdev);
> +		if (ret >= 0) {
> +			led_set_rgb_raw(led_cdev, ret);
> +			return 0;
> +		}
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(led_update_rgb_val);
> +
>   /* Caller must ensure led_cdev->led_access held */
>   void led_sysfs_disable(struct led_classdev *led_cdev)
>   {
> diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
> index db3f20d..c733777 100644
> --- a/drivers/leds/leds.h
> +++ b/drivers/leds/leds.h
> @@ -16,17 +16,29 @@
>   #include <linux/rwsem.h>
>   #include <linux/leds.h>
>
> +static inline bool led_isRGB(struct led_classdev *led_cdev)
> +{
> +	return (led_cdev->flags & LED_RGB) != 0;
> +}
> +
>   static inline int led_get_brightness(struct led_classdev *led_cdev)
>   {
>   	return led_cdev->brightness;
>   }
>
> +static inline u32 led_get_rgb_val(struct led_classdev *led_cdev)
> +{
> +	return led_cdev->rgb_val;
> +}
> +
>   void led_init_core(struct led_classdev *led_cdev);
>   void led_stop_software_blink(struct led_classdev *led_cdev);
>   void led_set_brightness_nopm(struct led_classdev *led_cdev,
>   				enum led_brightness value);
>   void led_set_brightness_nosleep(struct led_classdev *led_cdev,
>   				enum led_brightness value);
> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val);
> +int led_update_rgb_val(struct led_classdev *led_cdev);
>
>   extern struct rw_semaphore leds_list_lock;
>   extern struct list_head leds_list;
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 4429887..83d2912 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -35,6 +35,7 @@ struct led_classdev {
>   	const char		*name;
>   	enum led_brightness	 brightness;
>   	enum led_brightness	 max_brightness;
> +	u32			 rgb_val;
>   	int			 flags;
>
>   	/* Lower 16 bits reflect status */
> @@ -48,6 +49,7 @@ struct led_classdev {
>   #define LED_BLINK_DISABLE	(1 << 21)
>   #define LED_SYSFS_DISABLE	(1 << 22)
>   #define LED_DEV_CAP_FLASH	(1 << 23)
> +#define LED_RGB			(1 << 24)
>
>   	/* Set LED brightness level */
>   	/* Must not sleep. If no non-blocking version can be provided
> @@ -56,15 +58,25 @@ struct led_classdev {
>   	 */
>   	void		(*brightness_set)(struct led_classdev *led_cdev,
>   					  enum led_brightness brightness);
> +
> +	void		(*rgb_set)(struct led_classdev *led_cdev,
> +				   u32 rgb_val);
>   	/*
>   	 * Set LED brightness level immediately - it can block the caller for
>   	 * the time required for accessing a LED device register.
>   	 */
>   	int (*brightness_set_blocking)(struct led_classdev *led_cdev,
>   				       enum led_brightness brightness);
> +
> +	int (*rgb_set_blocking)(struct led_classdev *led_cdev,
> +				u32 rgb_val);
> +
>   	/* Get LED brightness level */
>   	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
>
> +	/* Get LED RGB val */
> +	s32 (*rgb_get)(struct led_classdev *led_cdev);
> +
>   	/*
>   	 * Activate hardware accelerated blink, delays are in milliseconds
>   	 * and if both are zero then a sensible default should be chosen.
> @@ -90,6 +102,7 @@ struct led_classdev {
>
>   	struct work_struct	set_brightness_work;
>   	int			delayed_set_value;
> +	u32			delayed_rgb_value;
>
>   #ifdef CONFIG_LEDS_TRIGGERS
>   	/* Protects the trigger data below */
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-13  9:42 ` Jacek Anaszewski
@ 2016-01-13 19:54   ` Heiner Kallweit
  2016-01-14 11:14     ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-13 19:54 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: linux-leds

Am 13.01.2016 um 10:42 schrieb Jacek Anaszewski:
> Hi Heiner,
> 
> Thank you for the patch. We have to ask a question here - what benefits
> these modifications bring about. Isn't all of this achievable in the
> user space? And I think that RGB LED problem can be boiled down to even
> more generic one, namely: how to make all the sub-LEDs controllable with
> a single LED class device. This subject pops out from time to time and
> it'd be good to have it tackled finally. Please refer to [1] and follow
> the links.
> 
> [1] http://www.spinics.net/lists/linux-leds/msg04508.html
> 
> Best Regards,
> Jacek Anaszewski
> 

Thanks for the prompt feedback, Jacek.
Regarding RGB support I also think of eventually making setting the color
available via triggers. This would allow kernel events to use colors.
I think of use cases like:
- add RGB support to heartbeat trigger: Then, with increasing load,
  the color can change from green to red.
- combine temperature monitoring with a RGB LED trigger.
- in general visualizing parameters in the kernel with >2 states or a range
  of possible values

Therefore I think having RGB LED support in the kernel makes sense.
And we have it anyway also as of today, just in different forms in different
corners of the kernel code.
Prerequiste for RGB-based triggers is one central API for controlling RGB LEDs.

When we come to sub-LEDs:
I'd define sub-LEDs as LEDs which are somehow grouped but basically independent.
I don't think this definition applies to a RGB LED as the three LEDs are not
independent.
I had a look at the referenced mail thread, however the usecase wasn't clear
to me. I understand that it's about making LEDs blink synchronously but
there was no example what it could be good for. Can you list some usecases?
Maybe once I see some usecases for sub-LEDs it becomes clearer what they have
in common with RGB LEDs.

The primary question with sub-LEDs most likely is:
Which properties can be controlled on a per-LED basis and which ones only
on a LED group basis?
(Or should it be supported that I can fiddle with properties on a per-LED
and on a group basis in parallel?)
I'd assume that the following idea is not brand-new: I could imagine to
have sysfs nodes for LED groups under /sys/class/leds including:
- attributes for the LED properties delegated to the group
- links to the led_classdev nodes of the respective LEDs in the group

Kind Regards, Heiner

> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>> When playing with a ThingM Blink(1) USB RGB LED device I found that there
>> are few drivers for RGB LED's spread across the kernel and each one
>> implements the RGB functionality in its own way.
>> Some examples:
>> - drivers/hid/hid-thingm.c
>> - drivers/usb/misc/usbled.c
>> - drivers/leds/leds-bd2802.c
>> - drivers/leds/leds-blinkm.c
>> ...
>> IMHO it would make sense to add generic RGB functionality to the LED core.
>>
>> Things I didn't like too much in other driver implementations:
>> - one led_classdev per color or at least
>> - one sysfs attribute per color
>> Colors are not really independent therefore I'd prefer one led_classdev
>> per RGB LED. Also userspace should be able to change the color with one
>> call -> therefore only one sysfs attribute for the RGB values.
>>
>> Also the current brightness-related functionality should not be effected
>> by the RGB extension.
>>
>> This patch is intended to demonstrate the idea of the extension. Most likely
>> it's not ready yet for submission.
>>
>> Main idea is to base the effective RGB value on a combination of brightness
>> and a scaled-to-max RGB value.
>> The RGB-related callbacks are basically the same as for brightness.
>> RGB functionally can be switched on with a new flag in the led_classdev.flags
>> bitmap.
>>
>> Experimentally I changed the thingm driver to use this extension and it works
>> quite well. As one result the driver could be very much simplified.
>>
>> Any feedback is appreciated.
>> [...]
> 
> 
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-13 19:54   ` Heiner Kallweit
@ 2016-01-14 11:14     ` Jacek Anaszewski
  2016-01-14 12:05       ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-14 11:14 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: linux-leds

On 01/13/2016 08:54 PM, Heiner Kallweit wrote:
> Am 13.01.2016 um 10:42 schrieb Jacek Anaszewski:
>> Hi Heiner,
>>
>> Thank you for the patch. We have to ask a question here - what benefits
>> these modifications bring about. Isn't all of this achievable in the
>> user space? And I think that RGB LED problem can be boiled down to even
>> more generic one, namely: how to make all the sub-LEDs controllable with
>> a single LED class device. This subject pops out from time to time and
>> it'd be good to have it tackled finally. Please refer to [1] and follow
>> the links.
>>
>> [1] http://www.spinics.net/lists/linux-leds/msg04508.html
>>
>> Best Regards,
>> Jacek Anaszewski
>>
>
> Thanks for the prompt feedback, Jacek.
> Regarding RGB support I also think of eventually making setting the color
> available via triggers. This would allow kernel events to use colors.
> I think of use cases like:
> - add RGB support to heartbeat trigger: Then, with increasing load,
>    the color can change from green to red.

What do you mean by increasing load? Heartbeat is just blinking with
specific time intervals and fixed brightness.

> - combine temperature monitoring with a RGB LED trigger.
> - in general visualizing parameters in the kernel with >2 states or a range
>    of possible values
>
> Therefore I think having RGB LED support in the kernel makes sense.
> And we have it anyway also as of today, just in different forms in different
> corners of the kernel code.
> Prerequiste for RGB-based triggers is one central API for controlling RGB LEDs.
>
> When we come to sub-LEDs:
> I'd define sub-LEDs as LEDs which are somehow grouped but basically independent.
> I don't think this definition applies to a RGB LED as the three LEDs are not
> independent.
> I had a look at the referenced mail thread, however the usecase wasn't clear
> to me. I understand that it's about making LEDs blink synchronously but
> there was no example what it could be good for. Can you list some usecases?
> Maybe once I see some usecases for sub-LEDs it becomes clearer what they have
> in common with RGB LEDs.

I got influenced by the fact that led-blinkm driver exposes separate
LED class devices per each color component. It automatically brought
sync subLEDs problem to my mind. The main benefit of synchronizing
subLEDs would be possibility of setting a trigger per group of subLEDs,
but current API doesn't allow for setting different brightness per
triggered subLED, so it wouldn't work properly for RGB LEDs.

It was not an issue in case of flash LEDs, from which the subject
arose originally. Only strobe operation was to be synchronized then
and brightness could had been set in one of the previous steps.

max77693-flash device driver was being developed then
(drivers/leds/leds-max77693.c), which exposes two current outputs, that 
are controlled separately, but can be joint to double the maximum
current for the flash strobe of a single LED.
Synchronization would be beneficial for the case when two separate
LEDs are connected to both outputs and we'd like to synchronize the
flash strobe. Strobing both LEDs required accessing a single register.

All the above seem to be irrelevant for RGB LEDs case.

Nonetheless, one straightforward idea came to my mind - we could expose
RGB LED as a single LED class device and control the intensity of each
color as usual with brightness attribute.

It is of enum type (i.e. at least 4 bytes), so each color component
could be stored in one byte. With this approach no changes would be
needed to the LED core to meet requirements from your RFC. Please let
me know if I missed something?

> The primary question with sub-LEDs most likely is:
> Which properties can be controlled on a per-LED basis and which ones only
> on a LED group basis?



> (Or should it be supported that I can fiddle with properties on a per-LED
> and on a group basis in parallel?)
> I'd assume that the following idea is not brand-new: I could imagine to
> have sysfs nodes for LED groups under /sys/class/leds including:
> - attributes for the LED properties delegated to the group
> - links to the led_classdev nodes of the respective LEDs in the group
>
> Kind Regards, Heiner
>
>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>> When playing with a ThingM Blink(1) USB RGB LED device I found that there
>>> are few drivers for RGB LED's spread across the kernel and each one
>>> implements the RGB functionality in its own way.
>>> Some examples:
>>> - drivers/hid/hid-thingm.c
>>> - drivers/usb/misc/usbled.c
>>> - drivers/leds/leds-bd2802.c
>>> - drivers/leds/leds-blinkm.c
>>> ...
>>> IMHO it would make sense to add generic RGB functionality to the LED core.
>>>
>>> Things I didn't like too much in other driver implementations:
>>> - one led_classdev per color or at least
>>> - one sysfs attribute per color
>>> Colors are not really independent therefore I'd prefer one led_classdev
>>> per RGB LED. Also userspace should be able to change the color with one
>>> call -> therefore only one sysfs attribute for the RGB values.
>>>
>>> Also the current brightness-related functionality should not be effected
>>> by the RGB extension.
>>>
>>> This patch is intended to demonstrate the idea of the extension. Most likely
>>> it's not ready yet for submission.
>>>
>>> Main idea is to base the effective RGB value on a combination of brightness
>>> and a scaled-to-max RGB value.
>>> The RGB-related callbacks are basically the same as for brightness.
>>> RGB functionally can be switched on with a new flag in the led_classdev.flags
>>> bitmap.
>>>
>>> Experimentally I changed the thingm driver to use this extension and it works
>>> quite well. As one result the driver could be very much simplified.
>>>
>>> Any feedback is appreciated.
>>> [...]
>>
>>
>>
>
>
>


-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-14 11:14     ` Jacek Anaszewski
@ 2016-01-14 12:05       ` Jacek Anaszewski
  0 siblings, 0 replies; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-14 12:05 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: linux-leds

On 01/14/2016 12:14 PM, Jacek Anaszewski wrote:
> On 01/13/2016 08:54 PM, Heiner Kallweit wrote:
>> Am 13.01.2016 um 10:42 schrieb Jacek Anaszewski:
>>> Hi Heiner,
>>>
>>> Thank you for the patch. We have to ask a question here - what benefits
>>> these modifications bring about. Isn't all of this achievable in the
>>> user space? And I think that RGB LED problem can be boiled down to even
>>> more generic one, namely: how to make all the sub-LEDs controllable with
>>> a single LED class device. This subject pops out from time to time and
>>> it'd be good to have it tackled finally. Please refer to [1] and follow
>>> the links.
>>>
>>> [1] http://www.spinics.net/lists/linux-leds/msg04508.html
>>>
>>> Best Regards,
>>> Jacek Anaszewski
>>>
>>
>> Thanks for the prompt feedback, Jacek.
>> Regarding RGB support I also think of eventually making setting the color
>> available via triggers. This would allow kernel events to use colors.
>> I think of use cases like:
>> - add RGB support to heartbeat trigger: Then, with increasing load,
>>    the color can change from green to red.
>
> What do you mean by increasing load? Heartbeat is just blinking with
> specific time intervals and fixed brightness.
>
>> - combine temperature monitoring with a RGB LED trigger.
>> - in general visualizing parameters in the kernel with >2 states or a
>> range
>>    of possible values
>>
>> Therefore I think having RGB LED support in the kernel makes sense.
>> And we have it anyway also as of today, just in different forms in
>> different
>> corners of the kernel code.
>> Prerequiste for RGB-based triggers is one central API for controlling
>> RGB LEDs.
>>
>> When we come to sub-LEDs:
>> I'd define sub-LEDs as LEDs which are somehow grouped but basically
>> independent.
>> I don't think this definition applies to a RGB LED as the three LEDs
>> are not
>> independent.
>> I had a look at the referenced mail thread, however the usecase wasn't
>> clear
>> to me. I understand that it's about making LEDs blink synchronously but
>> there was no example what it could be good for. Can you list some
>> usecases?
>> Maybe once I see some usecases for sub-LEDs it becomes clearer what
>> they have
>> in common with RGB LEDs.
>
> I got influenced by the fact that led-blinkm driver exposes separate
> LED class devices per each color component. It automatically brought
> sync subLEDs problem to my mind. The main benefit of synchronizing
> subLEDs would be possibility of setting a trigger per group of subLEDs,
> but current API doesn't allow for setting different brightness per
> triggered subLED, so it wouldn't work properly for RGB LEDs.
>
> It was not an issue in case of flash LEDs, from which the subject
> arose originally. Only strobe operation was to be synchronized then
> and brightness could had been set in one of the previous steps.
>
> max77693-flash device driver was being developed then
> (drivers/leds/leds-max77693.c), which exposes two current outputs, that
> are controlled separately, but can be joint to double the maximum
> current for the flash strobe of a single LED.
> Synchronization would be beneficial for the case when two separate
> LEDs are connected to both outputs and we'd like to synchronize the
> flash strobe. Strobing both LEDs required accessing a single register.
>
> All the above seem to be irrelevant for RGB LEDs case.
>
> Nonetheless, one straightforward idea came to my mind - we could expose
> RGB LED as a single LED class device and control the intensity of each
> color as usual with brightness attribute.
>
> It is of enum type (i.e. at least 4 bytes), so each color component
> could be stored in one byte. With this approach no changes would be
> needed to the LED core to meet requirements from your RFC. Please let
> me know if I missed something?

Of course I missed that we need both brightness and rgb value.
In this case, some of changes you proposed by are indeed required.
I'll add more comments in the response to your RFC.

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-10 20:27 [PATCH] led: core: RfC - add RGB LED handling to the core Heiner Kallweit
  2016-01-13  9:42 ` Jacek Anaszewski
@ 2016-01-14 12:08 ` Jacek Anaszewski
  2016-01-15 20:16   ` Heiner Kallweit
  1 sibling, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-14 12:08 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: linux-leds

On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
> When playing with a ThingM Blink(1) USB RGB LED device I found that there
> are few drivers for RGB LED's spread across the kernel and each one
> implements the RGB functionality in its own way.
> Some examples:
> - drivers/hid/hid-thingm.c
> - drivers/usb/misc/usbled.c
> - drivers/leds/leds-bd2802.c
> - drivers/leds/leds-blinkm.c
> ...
> IMHO it would make sense to add generic RGB functionality to the LED core.
>
> Things I didn't like too much in other driver implementations:
> - one led_classdev per color or at least
> - one sysfs attribute per color
> Colors are not really independent therefore I'd prefer one led_classdev
> per RGB LED. Also userspace should be able to change the color with one
> call -> therefore only one sysfs attribute for the RGB values.
>
> Also the current brightness-related functionality should not be effected
> by the RGB extension.
>
> This patch is intended to demonstrate the idea of the extension. Most likely
> it's not ready yet for submission.
>
> Main idea is to base the effective RGB value on a combination of brightness
> and a scaled-to-max RGB value.
> The RGB-related callbacks are basically the same as for brightness.
> RGB functionally can be switched on with a new flag in the led_classdev.flags
> bitmap.
>
> Experimentally I changed the thingm driver to use this extension and it works
> quite well. As one result the driver could be very much simplified.
>
> Any feedback is appreciated.
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
>   drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>   drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
>   drivers/leds/leds.h      |  12 ++++++
>   include/linux/leds.h     |  13 ++++++
>   4 files changed, 193 insertions(+)
>
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index 14139c3..5e4c2f2 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -64,6 +64,47 @@ unlock:
>   }
>   static DEVICE_ATTR_RW(brightness);
>
> +static ssize_t rgb_show(struct device *dev, struct device_attribute *attr,
> +			char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	u32 rgb_val;
> +
> +	mutex_lock(&led_cdev->led_access);
> +	led_update_rgb_val(led_cdev);
> +	rgb_val = led_get_rgb_val(led_cdev);
> +	mutex_unlock(&led_cdev->led_access);
> +
> +	return sprintf(buf, "0x%06x\n", rgb_val);
> +}
> +
> +static ssize_t rgb_store(struct device *dev, struct device_attribute *attr,
> +			 const char *buf, size_t size)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	unsigned long state;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_access);
> +
> +	if (led_sysfs_is_disabled(led_cdev)) {
> +		ret = -EBUSY;
> +		goto unlock;
> +	}
> +
> +	ret = kstrtoul(buf, 0, &state);
> +	if (ret)
> +		goto unlock;
> +
> +	led_set_rgb_val(led_cdev, state);

Please adhere to the current LED API naming style: led_rgb_set.

> +
> +	ret = size;
> +unlock:
> +	mutex_unlock(&led_cdev->led_access);
> +	return ret;
> +}
> +static DEVICE_ATTR_RW(rgb);
> +
>   static ssize_t max_brightness_show(struct device *dev,
>   		struct device_attribute *attr, char *buf)
>   {
> @@ -190,6 +231,16 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>   	char name[64];
>   	int ret;
>
> +	/* FLASH is not supported for RGB LEDs so far
> +	 * and RGB enforces max_brightness = LED_FULL.
> +	 * Initialize the color as white.
> +	 */
> +	if (led_isRGB(led_cdev)) {

Don't use camel case. Change it to led_is_rgb or check the flag
directly.

> +		led_cdev->flags &= ~LED_DEV_CAP_FLASH;

I'd rather check whether both FLASH and RGB flag are set and return
error in this case.

> +		led_cdev->max_brightness = LED_FULL;
> +		led_cdev->rgb_val = 0xffffff;
> +	}
> +
>   	ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
>   	if (ret < 0)
>   		return ret;
> @@ -203,6 +254,14 @@ 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_isRGB(led_cdev)) {
> +		ret = device_create_file(led_cdev->dev, &dev_attr_rgb);

Please use led_cdev->groups for this. You can refer to 
drivers/leds/led-class-flash.c.

> +		if (ret) {
> +			device_unregister(led_cdev->dev);
> +			return ret;
> +		}
> +	}
> +
>   #ifdef CONFIG_LEDS_TRIGGERS
>   	init_rwsem(&led_cdev->trigger_lock);
>   #endif
> @@ -252,6 +311,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
>
>   	flush_work(&led_cdev->set_brightness_work);
>
> +	if (led_isRGB(led_cdev))
> +		device_remove_file(led_cdev->dev, &dev_attr_rgb);
> +
>   	device_unregister(led_cdev->dev);
>
>   	down_write(&leds_list_lock);
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 19e1e60..6563bd3 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -25,6 +25,48 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
>   LIST_HEAD(leds_list);
>   EXPORT_SYMBOL_GPL(leds_list);
>
> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
> +{
> +	u32 red_raw = (rgb >> 16) & 0xff;
> +	u32 green_raw = (rgb >> 8) & 0xff;
> +	u32 blue_raw = rgb & 0xff;
> +	u32 max_raw, red, green, blue;
> +
> +	max_raw = max(red_raw, green_raw);
> +	if (blue_raw > max_raw)
> +		max_raw = blue_raw;
> +
> +	if (!max_raw) {
> +		cdev->brightness = 0;
> +		cdev->rgb_val = 0;
> +		return;
> +	}
> +
> +	red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
> +	green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
> +	blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
> +
> +	cdev->brightness = max_raw;
> +	cdev->rgb_val = (red << 16) + (green << 8) + blue;
> +}

I think that we shouldn't impose specific way of calculating brightness
depending on the rgb value set. We should just pass value from userspace
to the driver.

> +static u32 led_get_rgb_raw(struct led_classdev *cdev, bool delayed)
> +{
> +	u32 rgb = delayed ? cdev->delayed_rgb_value : cdev->rgb_val;
> +	u32 brightness = delayed ? cdev->delayed_set_value :
> +			 cdev->brightness;
> +	u32 red = (rgb >> 16) & 0xff;
> +	u32 green = (rgb >> 8) & 0xff;
> +	u32 blue = rgb & 0xff;
> +	u32 red_raw, green_raw, blue_raw;
> +
> +	red_raw = DIV_ROUND_CLOSEST(red * brightness, LED_FULL);
> +	green_raw = DIV_ROUND_CLOSEST(green * brightness, LED_FULL);
> +	blue_raw = DIV_ROUND_CLOSEST(blue * brightness, LED_FULL);
> +
> +	return (red_raw << 16) + (green_raw << 8) + blue_raw;
> +}
> +
>   static void led_timer_function(unsigned long data)
>   {
>   	struct led_classdev *led_cdev = (void *)data;
> @@ -91,6 +133,21 @@ static void set_brightness_delayed(struct work_struct *ws)
>   		led_cdev->flags &= ~LED_BLINK_DISABLE;
>   	}
>
> +	if (led_isRGB(led_cdev)) {
> +		u32 rgb = led_get_rgb_raw(led_cdev, true);
> +
> +		if (led_cdev->rgb_set)
> +			led_cdev->rgb_set(led_cdev, rgb);
> +		else if (led_cdev->rgb_set_blocking)
> +			ret = led_cdev->rgb_set_blocking(led_cdev, rgb);
> +		else
> +			ret = -EOPNOTSUPP;
> +		if (ret < 0)
> +			dev_err(led_cdev->dev,
> +				"Setting LED RGB value failed (%d)\n", ret);
> +		return;
> +	}
> +

Why this is needed? Could you share a use case?

>   	if (led_cdev->brightness_set)
>   		led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
>   	else if (led_cdev->brightness_set_blocking)
> @@ -229,9 +286,35 @@ void led_set_brightness(struct led_classdev *led_cdev,
>   }
>   EXPORT_SYMBOL_GPL(led_set_brightness);
>
> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val)
> +{
> +	if (!led_isRGB(led_cdev) || (led_cdev->flags & LED_SUSPENDED))
> +		return;
> +
> +	led_set_rgb_raw(led_cdev, rgb_val);
> +
> +	if (led_cdev->rgb_set) {
> +		led_cdev->rgb_set(led_cdev, rgb_val);
> +		return;
> +	}
> +
> +	/* If RGB setting can sleep, delegate it to a work queue task */
> +	led_cdev->delayed_set_value = led_cdev->brightness;
> +	led_cdev->delayed_rgb_value = led_cdev->rgb_val;
> +	schedule_work(&led_cdev->set_brightness_work);

I think that settings should be written to the device only in
the brightness_set op. We wouldn't need additional rgb op then.

> +}
> +EXPORT_SYMBOL_GPL(led_set_rgb_val);
> +
>   void led_set_brightness_nopm(struct led_classdev *led_cdev,
>   			      enum led_brightness value)
>   {
> +	if (led_isRGB(led_cdev) && led_cdev->rgb_set) {
> +		u32 rgb = led_get_rgb_raw(led_cdev, false);
> +
> +		led_cdev->rgb_set(led_cdev, rgb);
> +		return;
> +	}
> +
>   	/* Use brightness_set op if available, it is guaranteed not to sleep */
>   	if (led_cdev->brightness_set) {
>   		led_cdev->brightness_set(led_cdev, value);
> @@ -240,6 +323,7 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev,
>
>   	/* If brightness setting can sleep, delegate it to a work queue task */
>   	led_cdev->delayed_set_value = value;
> +	led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>   	schedule_work(&led_cdev->set_brightness_work);
>   }
>   EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
> @@ -267,6 +351,12 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
>   	if (led_cdev->flags & LED_SUSPENDED)
>   		return 0;
>
> +	if (led_isRGB(led_cdev) && led_cdev->rgb_set_blocking) {
> +		u32 rgb = led_get_rgb_raw(led_cdev, false);
> +
> +		return led_cdev->rgb_set_blocking(led_cdev, rgb);
> +	}
> +
>   	if (led_cdev->brightness_set_blocking)
>   		return led_cdev->brightness_set_blocking(led_cdev,
>   							 led_cdev->brightness);
> @@ -290,6 +380,22 @@ int led_update_brightness(struct led_classdev *led_cdev)
>   }
>   EXPORT_SYMBOL_GPL(led_update_brightness);
>
> +int led_update_rgb_val(struct led_classdev *led_cdev)
> +{
> +	s32 ret = 0;
> +
> +	if (led_isRGB(led_cdev) && led_cdev->rgb_get) {
> +		ret = led_cdev->rgb_get(led_cdev);
> +		if (ret >= 0) {
> +			led_set_rgb_raw(led_cdev, ret);
> +			return 0;
> +		}
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(led_update_rgb_val);
> +
>   /* Caller must ensure led_cdev->led_access held */
>   void led_sysfs_disable(struct led_classdev *led_cdev)
>   {
> diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
> index db3f20d..c733777 100644
> --- a/drivers/leds/leds.h
> +++ b/drivers/leds/leds.h
> @@ -16,17 +16,29 @@
>   #include <linux/rwsem.h>
>   #include <linux/leds.h>
>
> +static inline bool led_isRGB(struct led_classdev *led_cdev)
> +{
> +	return (led_cdev->flags & LED_RGB) != 0;
> +}
> +
>   static inline int led_get_brightness(struct led_classdev *led_cdev)
>   {
>   	return led_cdev->brightness;
>   }
>
> +static inline u32 led_get_rgb_val(struct led_classdev *led_cdev)
> +{
> +	return led_cdev->rgb_val;
> +}
> +
>   void led_init_core(struct led_classdev *led_cdev);
>   void led_stop_software_blink(struct led_classdev *led_cdev);
>   void led_set_brightness_nopm(struct led_classdev *led_cdev,
>   				enum led_brightness value);
>   void led_set_brightness_nosleep(struct led_classdev *led_cdev,
>   				enum led_brightness value);
> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val);
> +int led_update_rgb_val(struct led_classdev *led_cdev);
>
>   extern struct rw_semaphore leds_list_lock;
>   extern struct list_head leds_list;
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 4429887..83d2912 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -35,6 +35,7 @@ struct led_classdev {
>   	const char		*name;
>   	enum led_brightness	 brightness;
>   	enum led_brightness	 max_brightness;
> +	u32			 rgb_val;
>   	int			 flags;
>
>   	/* Lower 16 bits reflect status */
> @@ -48,6 +49,7 @@ struct led_classdev {
>   #define LED_BLINK_DISABLE	(1 << 21)
>   #define LED_SYSFS_DISABLE	(1 << 22)
>   #define LED_DEV_CAP_FLASH	(1 << 23)
> +#define LED_RGB			(1 << 24)

Please rename it to LED_DEV_CAP_RGB.

>
>   	/* Set LED brightness level */
>   	/* Must not sleep. If no non-blocking version can be provided
> @@ -56,15 +58,25 @@ struct led_classdev {
>   	 */
>   	void		(*brightness_set)(struct led_classdev *led_cdev,
>   					  enum led_brightness brightness);
> +
> +	void		(*rgb_set)(struct led_classdev *led_cdev,
> +				   u32 rgb_val);

Without this everything would be much simpler.

If we wanted to change the color and intensity we would need to:
1. Set rgb (value only cached in the LED core)
2. Set brightness (write both brightness and rgb settings to the hw)

Current brightness can always be obtained with led_get_brightness in
case only rgb is to be set.

>   	/*
>   	 * Set LED brightness level immediately - it can block the caller for
>   	 * the time required for accessing a LED device register.
>   	 */
>   	int (*brightness_set_blocking)(struct led_classdev *led_cdev,
>   				       enum led_brightness brightness);
> +
> +	int (*rgb_set_blocking)(struct led_classdev *led_cdev,
> +				u32 rgb_val);
> +

This is also not needed.

>   	/* Get LED brightness level */
>   	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
>
> +	/* Get LED RGB val */
> +	s32 (*rgb_get)(struct led_classdev *led_cdev);
> +

Ditto.

>   	/*
>   	 * Activate hardware accelerated blink, delays are in milliseconds
>   	 * and if both are zero then a sensible default should be chosen.
> @@ -90,6 +102,7 @@ struct led_classdev {
>
>   	struct work_struct	set_brightness_work;
>   	int			delayed_set_value;
> +	u32			delayed_rgb_value;

Ditto.

>
>   #ifdef CONFIG_LEDS_TRIGGERS
>   	/* Protects the trigger data below */
>


-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-14 12:08 ` Jacek Anaszewski
@ 2016-01-15 20:16   ` Heiner Kallweit
  2016-01-17 22:31     ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-15 20:16 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: linux-leds

Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>> When playing with a ThingM Blink(1) USB RGB LED device I found that there
>> are few drivers for RGB LED's spread across the kernel and each one
>> implements the RGB functionality in its own way.
>> Some examples:
>> - drivers/hid/hid-thingm.c
>> - drivers/usb/misc/usbled.c
>> - drivers/leds/leds-bd2802.c
>> - drivers/leds/leds-blinkm.c
>> ...
>> IMHO it would make sense to add generic RGB functionality to the LED core.
>>
>> Things I didn't like too much in other driver implementations:
>> - one led_classdev per color or at least
>> - one sysfs attribute per color
>> Colors are not really independent therefore I'd prefer one led_classdev
>> per RGB LED. Also userspace should be able to change the color with one
>> call -> therefore only one sysfs attribute for the RGB values.
>>
>> Also the current brightness-related functionality should not be effected
>> by the RGB extension.
>>
>> This patch is intended to demonstrate the idea of the extension. Most likely
>> it's not ready yet for submission.
>>
>> Main idea is to base the effective RGB value on a combination of brightness
>> and a scaled-to-max RGB value.
>> The RGB-related callbacks are basically the same as for brightness.
>> RGB functionally can be switched on with a new flag in the led_classdev.flags
>> bitmap.
>>
>> Experimentally I changed the thingm driver to use this extension and it works
>> quite well. As one result the driver could be very much simplified.
>>
>> Any feedback is appreciated.
>>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>>   drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>   drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
>>   drivers/leds/leds.h      |  12 ++++++
>>   include/linux/leds.h     |  13 ++++++
>>   4 files changed, 193 insertions(+)
>>
>> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
>> index 14139c3..5e4c2f2 100644
>> --- a/drivers/leds/led-class.c
>> +++ b/drivers/leds/led-class.c
>> @@ -64,6 +64,47 @@ unlock:
>>   }
>>   static DEVICE_ATTR_RW(brightness);
>>
>> +static ssize_t rgb_show(struct device *dev, struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> +    u32 rgb_val;
>> +
>> +    mutex_lock(&led_cdev->led_access);
>> +    led_update_rgb_val(led_cdev);
>> +    rgb_val = led_get_rgb_val(led_cdev);
>> +    mutex_unlock(&led_cdev->led_access);
>> +
>> +    return sprintf(buf, "0x%06x\n", rgb_val);
>> +}
>> +
>> +static ssize_t rgb_store(struct device *dev, struct device_attribute *attr,
>> +             const char *buf, size_t size)
>> +{
>> +    struct led_classdev *led_cdev = dev_get_drvdata(dev);
>> +    unsigned long state;
>> +    ssize_t ret;
>> +
>> +    mutex_lock(&led_cdev->led_access);
>> +
>> +    if (led_sysfs_is_disabled(led_cdev)) {
>> +        ret = -EBUSY;
>> +        goto unlock;
>> +    }
>> +
>> +    ret = kstrtoul(buf, 0, &state);
>> +    if (ret)
>> +        goto unlock;
>> +
>> +    led_set_rgb_val(led_cdev, state);
> 
> Please adhere to the current LED API naming style: led_rgb_set.
> 
Seems like currently two naming styles are use in the LED core.
We have e.g. led_blink_set and led_set_brightness.
However I'm fine with the suggested led_rgb_set.

>> +
>> +    ret = size;
>> +unlock:
>> +    mutex_unlock(&led_cdev->led_access);
>> +    return ret;
>> +}
>> +static DEVICE_ATTR_RW(rgb);
>> +
>>   static ssize_t max_brightness_show(struct device *dev,
>>           struct device_attribute *attr, char *buf)
>>   {
>> @@ -190,6 +231,16 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>>       char name[64];
>>       int ret;
>>
>> +    /* FLASH is not supported for RGB LEDs so far
>> +     * and RGB enforces max_brightness = LED_FULL.
>> +     * Initialize the color as white.
>> +     */
>> +    if (led_isRGB(led_cdev)) {
> 
> Don't use camel case. Change it to led_is_rgb or check the flag
> directly.
> 
OK, will change it to led_is_rgb.

>> +        led_cdev->flags &= ~LED_DEV_CAP_FLASH;
> 
> I'd rather check whether both FLASH and RGB flag are set and return
> error in this case.
> 
OK

>> +        led_cdev->max_brightness = LED_FULL;
>> +        led_cdev->rgb_val = 0xffffff;
>> +    }
>> +
>>       ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
>>       if (ret < 0)
>>           return ret;
>> @@ -203,6 +254,14 @@ 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_isRGB(led_cdev)) {
>> +        ret = device_create_file(led_cdev->dev, &dev_attr_rgb);
> 
> Please use led_cdev->groups for this. You can refer to drivers/leds/led-class-flash.c.
> 
OK

>> +        if (ret) {
>> +            device_unregister(led_cdev->dev);
>> +            return ret;
>> +        }
>> +    }
>> +
>>   #ifdef CONFIG_LEDS_TRIGGERS
>>       init_rwsem(&led_cdev->trigger_lock);
>>   #endif
>> @@ -252,6 +311,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
>>
>>       flush_work(&led_cdev->set_brightness_work);
>>
>> +    if (led_isRGB(led_cdev))
>> +        device_remove_file(led_cdev->dev, &dev_attr_rgb);
>> +
>>       device_unregister(led_cdev->dev);
>>
>>       down_write(&leds_list_lock);
>> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
>> index 19e1e60..6563bd3 100644
>> --- a/drivers/leds/led-core.c
>> +++ b/drivers/leds/led-core.c
>> @@ -25,6 +25,48 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
>>   LIST_HEAD(leds_list);
>>   EXPORT_SYMBOL_GPL(leds_list);
>>
>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>> +{
>> +    u32 red_raw = (rgb >> 16) & 0xff;
>> +    u32 green_raw = (rgb >> 8) & 0xff;
>> +    u32 blue_raw = rgb & 0xff;
>> +    u32 max_raw, red, green, blue;
>> +
>> +    max_raw = max(red_raw, green_raw);
>> +    if (blue_raw > max_raw)
>> +        max_raw = blue_raw;
>> +
>> +    if (!max_raw) {
>> +        cdev->brightness = 0;
>> +        cdev->rgb_val = 0;
>> +        return;
>> +    }
>> +
>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>> +
>> +    cdev->brightness = max_raw;
>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>> +}
> 
> I think that we shouldn't impose specific way of calculating brightness
> depending on the rgb value set. We should just pass value from userspace
> to the driver.
> 
Right, setting brightness from userspace is no problem.
But how about led_update_brightness? It's supposed to update the brightness
value in led_cdev with the actual value. And for a RGB LED we have only
the RGB values, so we need to calculate the brightness based on RGB values.
Or do you see a better way?

This leads me to another question: What do we need led_update_brightness for
in general? The cached and the actual value should always be in sync, except
the actual brightness can be changed from outside the driver.
Do we have such cases?

Last but not least I saw that led_update_brightness is called in
led_classdev_register.
Instead of updating our cached value with the actual value shouldn't we
initialize cached and actual value (most likely to 0)?
The actual value theoretically could be uninitialized.
Seems like we rely on BIOS / boot loader to initialize LEDs.

>> +static u32 led_get_rgb_raw(struct led_classdev *cdev, bool delayed)
>> +{
>> +    u32 rgb = delayed ? cdev->delayed_rgb_value : cdev->rgb_val;
>> +    u32 brightness = delayed ? cdev->delayed_set_value :
>> +             cdev->brightness;
>> +    u32 red = (rgb >> 16) & 0xff;
>> +    u32 green = (rgb >> 8) & 0xff;
>> +    u32 blue = rgb & 0xff;
>> +    u32 red_raw, green_raw, blue_raw;
>> +
>> +    red_raw = DIV_ROUND_CLOSEST(red * brightness, LED_FULL);
>> +    green_raw = DIV_ROUND_CLOSEST(green * brightness, LED_FULL);
>> +    blue_raw = DIV_ROUND_CLOSEST(blue * brightness, LED_FULL);
>> +
>> +    return (red_raw << 16) + (green_raw << 8) + blue_raw;
>> +}
>> +
>>   static void led_timer_function(unsigned long data)
>>   {
>>       struct led_classdev *led_cdev = (void *)data;
>> @@ -91,6 +133,21 @@ static void set_brightness_delayed(struct work_struct *ws)
>>           led_cdev->flags &= ~LED_BLINK_DISABLE;
>>       }
>>
>> +    if (led_isRGB(led_cdev)) {
>> +        u32 rgb = led_get_rgb_raw(led_cdev, true);
>> +
>> +        if (led_cdev->rgb_set)
>> +            led_cdev->rgb_set(led_cdev, rgb);
>> +        else if (led_cdev->rgb_set_blocking)
>> +            ret = led_cdev->rgb_set_blocking(led_cdev, rgb);
>> +        else
>> +            ret = -EOPNOTSUPP;
>> +        if (ret < 0)
>> +            dev_err(led_cdev->dev,
>> +                "Setting LED RGB value failed (%d)\n", ret);
>> +        return;
>> +    }
>> +
> 
> Why this is needed? Could you share a use case?
> 
If we re-use the existing brightness ops as suggested by you
then most of this code isn't needed.

>>       if (led_cdev->brightness_set)
>>           led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
>>       else if (led_cdev->brightness_set_blocking)
>> @@ -229,9 +286,35 @@ void led_set_brightness(struct led_classdev *led_cdev,
>>   }
>>   EXPORT_SYMBOL_GPL(led_set_brightness);
>>
>> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val)
>> +{
>> +    if (!led_isRGB(led_cdev) || (led_cdev->flags & LED_SUSPENDED))
>> +        return;
>> +
>> +    led_set_rgb_raw(led_cdev, rgb_val);
>> +
>> +    if (led_cdev->rgb_set) {
>> +        led_cdev->rgb_set(led_cdev, rgb_val);
>> +        return;
>> +    }
>> +
>> +    /* If RGB setting can sleep, delegate it to a work queue task */
>> +    led_cdev->delayed_set_value = led_cdev->brightness;
>> +    led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>> +    schedule_work(&led_cdev->set_brightness_work);
> 
> I think that settings should be written to the device only in
> the brightness_set op. We wouldn't need additional rgb op then.
> 
>> +}
>> +EXPORT_SYMBOL_GPL(led_set_rgb_val);
>> +
>>   void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>                     enum led_brightness value)
>>   {
>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_set) {
>> +        u32 rgb = led_get_rgb_raw(led_cdev, false);
>> +
>> +        led_cdev->rgb_set(led_cdev, rgb);
>> +        return;
>> +    }
>> +
>>       /* Use brightness_set op if available, it is guaranteed not to sleep */
>>       if (led_cdev->brightness_set) {
>>           led_cdev->brightness_set(led_cdev, value);
>> @@ -240,6 +323,7 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>
>>       /* If brightness setting can sleep, delegate it to a work queue task */
>>       led_cdev->delayed_set_value = value;
>> +    led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>>       schedule_work(&led_cdev->set_brightness_work);
>>   }
>>   EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
>> @@ -267,6 +351,12 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
>>       if (led_cdev->flags & LED_SUSPENDED)
>>           return 0;
>>
>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_set_blocking) {
>> +        u32 rgb = led_get_rgb_raw(led_cdev, false);
>> +
>> +        return led_cdev->rgb_set_blocking(led_cdev, rgb);
>> +    }
>> +
>>       if (led_cdev->brightness_set_blocking)
>>           return led_cdev->brightness_set_blocking(led_cdev,
>>                                led_cdev->brightness);
>> @@ -290,6 +380,22 @@ int led_update_brightness(struct led_classdev *led_cdev)
>>   }
>>   EXPORT_SYMBOL_GPL(led_update_brightness);
>>
>> +int led_update_rgb_val(struct led_classdev *led_cdev)
>> +{
>> +    s32 ret = 0;
>> +
>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_get) {
>> +        ret = led_cdev->rgb_get(led_cdev);
>> +        if (ret >= 0) {
>> +            led_set_rgb_raw(led_cdev, ret);
>> +            return 0;
>> +        }
>> +    }
>> +
>> +    return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(led_update_rgb_val);
>> +
>>   /* Caller must ensure led_cdev->led_access held */
>>   void led_sysfs_disable(struct led_classdev *led_cdev)
>>   {
>> diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
>> index db3f20d..c733777 100644
>> --- a/drivers/leds/leds.h
>> +++ b/drivers/leds/leds.h
>> @@ -16,17 +16,29 @@
>>   #include <linux/rwsem.h>
>>   #include <linux/leds.h>
>>
>> +static inline bool led_isRGB(struct led_classdev *led_cdev)
>> +{
>> +    return (led_cdev->flags & LED_RGB) != 0;
>> +}
>> +
>>   static inline int led_get_brightness(struct led_classdev *led_cdev)
>>   {
>>       return led_cdev->brightness;
>>   }
>>
>> +static inline u32 led_get_rgb_val(struct led_classdev *led_cdev)
>> +{
>> +    return led_cdev->rgb_val;
>> +}
>> +
>>   void led_init_core(struct led_classdev *led_cdev);
>>   void led_stop_software_blink(struct led_classdev *led_cdev);
>>   void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>                   enum led_brightness value);
>>   void led_set_brightness_nosleep(struct led_classdev *led_cdev,
>>                   enum led_brightness value);
>> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val);
>> +int led_update_rgb_val(struct led_classdev *led_cdev);
>>
>>   extern struct rw_semaphore leds_list_lock;
>>   extern struct list_head leds_list;
>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>> index 4429887..83d2912 100644
>> --- a/include/linux/leds.h
>> +++ b/include/linux/leds.h
>> @@ -35,6 +35,7 @@ struct led_classdev {
>>       const char        *name;
>>       enum led_brightness     brightness;
>>       enum led_brightness     max_brightness;
>> +    u32             rgb_val;
>>       int             flags;
>>
>>       /* Lower 16 bits reflect status */
>> @@ -48,6 +49,7 @@ struct led_classdev {
>>   #define LED_BLINK_DISABLE    (1 << 21)
>>   #define LED_SYSFS_DISABLE    (1 << 22)
>>   #define LED_DEV_CAP_FLASH    (1 << 23)
>> +#define LED_RGB            (1 << 24)
> 
> Please rename it to LED_DEV_CAP_RGB.
> 
OK

>>
>>       /* Set LED brightness level */
>>       /* Must not sleep. If no non-blocking version can be provided
>> @@ -56,15 +58,25 @@ struct led_classdev {
>>        */
>>       void        (*brightness_set)(struct led_classdev *led_cdev,
>>                         enum led_brightness brightness);
>> +
>> +    void        (*rgb_set)(struct led_classdev *led_cdev,
>> +                   u32 rgb_val);
> 
> Without this everything would be much simpler.
> 
Sure, we can re-use the existing ops. I just thought it would be
a little ugly to misuse enum led_brightness for RGB values.
But as it internally is an int we can also do it this way.

> If we wanted to change the color and intensity we would need to:
> 1. Set rgb (value only cached in the LED core)
> 2. Set brightness (write both brightness and rgb settings to the hw)
> 
> Current brightness can always be obtained with led_get_brightness in
> case only rgb is to be set.
> 
>>       /*
>>        * Set LED brightness level immediately - it can block the caller for
>>        * the time required for accessing a LED device register.
>>        */
>>       int (*brightness_set_blocking)(struct led_classdev *led_cdev,
>>                          enum led_brightness brightness);
>> +
>> +    int (*rgb_set_blocking)(struct led_classdev *led_cdev,
>> +                u32 rgb_val);
>> +
> 
> This is also not needed.
> 
>>       /* Get LED brightness level */
>>       enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
>>
>> +    /* Get LED RGB val */
>> +    s32 (*rgb_get)(struct led_classdev *led_cdev);
>> +
> 
> Ditto.
> 
>>       /*
>>        * Activate hardware accelerated blink, delays are in milliseconds
>>        * and if both are zero then a sensible default should be chosen.
>> @@ -90,6 +102,7 @@ struct led_classdev {
>>
>>       struct work_struct    set_brightness_work;
>>       int            delayed_set_value;
>> +    u32            delayed_rgb_value;
> 
> Ditto.
> 
If we have a blocking set callback then we have to set a RGB value from the
workqueue. And we need brightness + RGB to calculate the effective RGB values
in the worker func. Therefore I think delayed_rgb_val is needed.

>>
>>   #ifdef CONFIG_LEDS_TRIGGERS
>>       /* Protects the trigger data below */
>>
> 
> 
Thanks for the review, Heiner

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-15 20:16   ` Heiner Kallweit
@ 2016-01-17 22:31     ` Jacek Anaszewski
  2016-01-24 13:38       ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-17 22:31 UTC (permalink / raw)
  To: Heiner Kallweit, Jacek Anaszewski; +Cc: linux-leds

On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>> When playing with a ThingM Blink(1) USB RGB LED device I found that there
>>> are few drivers for RGB LED's spread across the kernel and each one
>>> implements the RGB functionality in its own way.
>>> Some examples:
>>> - drivers/hid/hid-thingm.c
>>> - drivers/usb/misc/usbled.c
>>> - drivers/leds/leds-bd2802.c
>>> - drivers/leds/leds-blinkm.c
>>> ...
>>> IMHO it would make sense to add generic RGB functionality to the LED core.
>>>
>>> Things I didn't like too much in other driver implementations:
>>> - one led_classdev per color or at least
>>> - one sysfs attribute per color
>>> Colors are not really independent therefore I'd prefer one led_classdev
>>> per RGB LED. Also userspace should be able to change the color with one
>>> call -> therefore only one sysfs attribute for the RGB values.
>>>
>>> Also the current brightness-related functionality should not be effected
>>> by the RGB extension.
>>>
>>> This patch is intended to demonstrate the idea of the extension. Most likely
>>> it's not ready yet for submission.
>>>
>>> Main idea is to base the effective RGB value on a combination of brightness
>>> and a scaled-to-max RGB value.
>>> The RGB-related callbacks are basically the same as for brightness.
>>> RGB functionally can be switched on with a new flag in the led_classdev.flags
>>> bitmap.
>>>
>>> Experimentally I changed the thingm driver to use this extension and it works
>>> quite well. As one result the driver could be very much simplified.
>>>
>>> Any feedback is appreciated.
>>>
>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>> ---
>>>    drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>    drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
>>>    drivers/leds/leds.h      |  12 ++++++
>>>    include/linux/leds.h     |  13 ++++++
>>>    4 files changed, 193 insertions(+)
>>>
>>> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
>>> index 14139c3..5e4c2f2 100644
>>> --- a/drivers/leds/led-class.c
>>> +++ b/drivers/leds/led-class.c
>>> @@ -64,6 +64,47 @@ unlock:
>>>    }
>>>    static DEVICE_ATTR_RW(brightness);
>>>
>>> +static ssize_t rgb_show(struct device *dev, struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct led_classdev *led_cdev = dev_get_drvdata(dev);
>>> +    u32 rgb_val;
>>> +
>>> +    mutex_lock(&led_cdev->led_access);
>>> +    led_update_rgb_val(led_cdev);
>>> +    rgb_val = led_get_rgb_val(led_cdev);
>>> +    mutex_unlock(&led_cdev->led_access);
>>> +
>>> +    return sprintf(buf, "0x%06x\n", rgb_val);
>>> +}
>>> +
>>> +static ssize_t rgb_store(struct device *dev, struct device_attribute *attr,
>>> +             const char *buf, size_t size)
>>> +{
>>> +    struct led_classdev *led_cdev = dev_get_drvdata(dev);
>>> +    unsigned long state;
>>> +    ssize_t ret;
>>> +
>>> +    mutex_lock(&led_cdev->led_access);
>>> +
>>> +    if (led_sysfs_is_disabled(led_cdev)) {
>>> +        ret = -EBUSY;
>>> +        goto unlock;
>>> +    }
>>> +
>>> +    ret = kstrtoul(buf, 0, &state);
>>> +    if (ret)
>>> +        goto unlock;
>>> +
>>> +    led_set_rgb_val(led_cdev, state);
>>
>> Please adhere to the current LED API naming style: led_rgb_set.
>>
> Seems like currently two naming styles are use in the LED core.
> We have e.g. led_blink_set and led_set_brightness.
> However I'm fine with the suggested led_rgb_set.

Indeed. Your version would be preferable in this case.
I wanted this to match led_set_brightness style, but had been
too lazy to double check that :-/

>>> +
>>> +    ret = size;
>>> +unlock:
>>> +    mutex_unlock(&led_cdev->led_access);
>>> +    return ret;
>>> +}
>>> +static DEVICE_ATTR_RW(rgb);
>>> +
>>>    static ssize_t max_brightness_show(struct device *dev,
>>>            struct device_attribute *attr, char *buf)
>>>    {
>>> @@ -190,6 +231,16 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>>>        char name[64];
>>>        int ret;
>>>
>>> +    /* FLASH is not supported for RGB LEDs so far
>>> +     * and RGB enforces max_brightness = LED_FULL.
>>> +     * Initialize the color as white.
>>> +     */
>>> +    if (led_isRGB(led_cdev)) {
>>
>> Don't use camel case. Change it to led_is_rgb or check the flag
>> directly.
>>
> OK, will change it to led_is_rgb.
>
>>> +        led_cdev->flags &= ~LED_DEV_CAP_FLASH;
>>
>> I'd rather check whether both FLASH and RGB flag are set and return
>> error in this case.
>>
> OK
>
>>> +        led_cdev->max_brightness = LED_FULL;
>>> +        led_cdev->rgb_val = 0xffffff;
>>> +    }
>>> +
>>>        ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
>>>        if (ret < 0)
>>>            return ret;
>>> @@ -203,6 +254,14 @@ 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_isRGB(led_cdev)) {
>>> +        ret = device_create_file(led_cdev->dev, &dev_attr_rgb);
>>
>> Please use led_cdev->groups for this. You can refer to drivers/leds/led-class-flash.c.
>>
> OK
>
>>> +        if (ret) {
>>> +            device_unregister(led_cdev->dev);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>    #ifdef CONFIG_LEDS_TRIGGERS
>>>        init_rwsem(&led_cdev->trigger_lock);
>>>    #endif
>>> @@ -252,6 +311,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
>>>
>>>        flush_work(&led_cdev->set_brightness_work);
>>>
>>> +    if (led_isRGB(led_cdev))
>>> +        device_remove_file(led_cdev->dev, &dev_attr_rgb);
>>> +
>>>        device_unregister(led_cdev->dev);
>>>
>>>        down_write(&leds_list_lock);
>>> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
>>> index 19e1e60..6563bd3 100644
>>> --- a/drivers/leds/led-core.c
>>> +++ b/drivers/leds/led-core.c
>>> @@ -25,6 +25,48 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
>>>    LIST_HEAD(leds_list);
>>>    EXPORT_SYMBOL_GPL(leds_list);
>>>
>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>> +{
>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>> +    u32 blue_raw = rgb & 0xff;
>>> +    u32 max_raw, red, green, blue;
>>> +
>>> +    max_raw = max(red_raw, green_raw);
>>> +    if (blue_raw > max_raw)
>>> +        max_raw = blue_raw;
>>> +
>>> +    if (!max_raw) {
>>> +        cdev->brightness = 0;
>>> +        cdev->rgb_val = 0;
>>> +        return;
>>> +    }
>>> +
>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>> +
>>> +    cdev->brightness = max_raw;
>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>> +}
>>
>> I think that we shouldn't impose specific way of calculating brightness
>> depending on the rgb value set. We should just pass value from userspace
>> to the driver.
>>
> Right, setting brightness from userspace is no problem.
> But how about led_update_brightness? It's supposed to update the brightness
> value in led_cdev with the actual value. And for a RGB LED we have only
> the RGB values, so we need to calculate the brightness based on RGB values.
> Or do you see a better way?

I thought that you had some device using both brightness and rgb
components settings (no idea if this could be sane in any way).
If there are only three color components, then why not just redefine
brightness to store three components in case of the LEDs with
LED_DEV_CAP_RGB flags set?

>
> This leads me to another question: What do we need led_update_brightness for
> in general? The cached and the actual value should always be in sync, except
> the actual brightness can be changed from outside the driver.
> Do we have such cases?

It is possible that hardware will limit the brightness, e.g. due to the
low battery voltage level.

>
> Last but not least I saw that led_update_brightness is called in
> led_classdev_register.
> Instead of updating our cached value with the actual value shouldn't we
> initialize cached and actual value (most likely to 0)?
> The actual value theoretically could be uninitialized.
> Seems like we rely on BIOS / boot loader to initialize LEDs.

Drivers are generally expected to initialize brightness to what
they deem valid initial value. If a driver implements brightness_get
op then brightness will be synchronized with device settings upon
registering automatically thanks to this call.

>>> +static u32 led_get_rgb_raw(struct led_classdev *cdev, bool delayed)
>>> +{
>>> +    u32 rgb = delayed ? cdev->delayed_rgb_value : cdev->rgb_val;
>>> +    u32 brightness = delayed ? cdev->delayed_set_value :
>>> +             cdev->brightness;
>>> +    u32 red = (rgb >> 16) & 0xff;
>>> +    u32 green = (rgb >> 8) & 0xff;
>>> +    u32 blue = rgb & 0xff;
>>> +    u32 red_raw, green_raw, blue_raw;
>>> +
>>> +    red_raw = DIV_ROUND_CLOSEST(red * brightness, LED_FULL);
>>> +    green_raw = DIV_ROUND_CLOSEST(green * brightness, LED_FULL);
>>> +    blue_raw = DIV_ROUND_CLOSEST(blue * brightness, LED_FULL);
>>> +
>>> +    return (red_raw << 16) + (green_raw << 8) + blue_raw;
>>> +}
>>> +
>>>    static void led_timer_function(unsigned long data)
>>>    {
>>>        struct led_classdev *led_cdev = (void *)data;
>>> @@ -91,6 +133,21 @@ static void set_brightness_delayed(struct work_struct *ws)
>>>            led_cdev->flags &= ~LED_BLINK_DISABLE;
>>>        }
>>>
>>> +    if (led_isRGB(led_cdev)) {
>>> +        u32 rgb = led_get_rgb_raw(led_cdev, true);
>>> +
>>> +        if (led_cdev->rgb_set)
>>> +            led_cdev->rgb_set(led_cdev, rgb);
>>> +        else if (led_cdev->rgb_set_blocking)
>>> +            ret = led_cdev->rgb_set_blocking(led_cdev, rgb);
>>> +        else
>>> +            ret = -EOPNOTSUPP;
>>> +        if (ret < 0)
>>> +            dev_err(led_cdev->dev,
>>> +                "Setting LED RGB value failed (%d)\n", ret);
>>> +        return;
>>> +    }
>>> +
>>
>> Why this is needed? Could you share a use case?
>>
> If we re-use the existing brightness ops as suggested by you
> then most of this code isn't needed.
>
>>>        if (led_cdev->brightness_set)
>>>            led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
>>>        else if (led_cdev->brightness_set_blocking)
>>> @@ -229,9 +286,35 @@ void led_set_brightness(struct led_classdev *led_cdev,
>>>    }
>>>    EXPORT_SYMBOL_GPL(led_set_brightness);
>>>
>>> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val)
>>> +{
>>> +    if (!led_isRGB(led_cdev) || (led_cdev->flags & LED_SUSPENDED))
>>> +        return;
>>> +
>>> +    led_set_rgb_raw(led_cdev, rgb_val);
>>> +
>>> +    if (led_cdev->rgb_set) {
>>> +        led_cdev->rgb_set(led_cdev, rgb_val);
>>> +        return;
>>> +    }
>>> +
>>> +    /* If RGB setting can sleep, delegate it to a work queue task */
>>> +    led_cdev->delayed_set_value = led_cdev->brightness;
>>> +    led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>>> +    schedule_work(&led_cdev->set_brightness_work);
>>
>> I think that settings should be written to the device only in
>> the brightness_set op. We wouldn't need additional rgb op then.
>>
>>> +}
>>> +EXPORT_SYMBOL_GPL(led_set_rgb_val);
>>> +
>>>    void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>>                      enum led_brightness value)
>>>    {
>>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_set) {
>>> +        u32 rgb = led_get_rgb_raw(led_cdev, false);
>>> +
>>> +        led_cdev->rgb_set(led_cdev, rgb);
>>> +        return;
>>> +    }
>>> +
>>>        /* Use brightness_set op if available, it is guaranteed not to sleep */
>>>        if (led_cdev->brightness_set) {
>>>            led_cdev->brightness_set(led_cdev, value);
>>> @@ -240,6 +323,7 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>>
>>>        /* If brightness setting can sleep, delegate it to a work queue task */
>>>        led_cdev->delayed_set_value = value;
>>> +    led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>>>        schedule_work(&led_cdev->set_brightness_work);
>>>    }
>>>    EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
>>> @@ -267,6 +351,12 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
>>>        if (led_cdev->flags & LED_SUSPENDED)
>>>            return 0;
>>>
>>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_set_blocking) {
>>> +        u32 rgb = led_get_rgb_raw(led_cdev, false);
>>> +
>>> +        return led_cdev->rgb_set_blocking(led_cdev, rgb);
>>> +    }
>>> +
>>>        if (led_cdev->brightness_set_blocking)
>>>            return led_cdev->brightness_set_blocking(led_cdev,
>>>                                 led_cdev->brightness);
>>> @@ -290,6 +380,22 @@ int led_update_brightness(struct led_classdev *led_cdev)
>>>    }
>>>    EXPORT_SYMBOL_GPL(led_update_brightness);
>>>
>>> +int led_update_rgb_val(struct led_classdev *led_cdev)
>>> +{
>>> +    s32 ret = 0;
>>> +
>>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_get) {
>>> +        ret = led_cdev->rgb_get(led_cdev);
>>> +        if (ret >= 0) {
>>> +            led_set_rgb_raw(led_cdev, ret);
>>> +            return 0;
>>> +        }
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(led_update_rgb_val);
>>> +
>>>    /* Caller must ensure led_cdev->led_access held */
>>>    void led_sysfs_disable(struct led_classdev *led_cdev)
>>>    {
>>> diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
>>> index db3f20d..c733777 100644
>>> --- a/drivers/leds/leds.h
>>> +++ b/drivers/leds/leds.h
>>> @@ -16,17 +16,29 @@
>>>    #include <linux/rwsem.h>
>>>    #include <linux/leds.h>
>>>
>>> +static inline bool led_isRGB(struct led_classdev *led_cdev)
>>> +{
>>> +    return (led_cdev->flags & LED_RGB) != 0;
>>> +}
>>> +
>>>    static inline int led_get_brightness(struct led_classdev *led_cdev)
>>>    {
>>>        return led_cdev->brightness;
>>>    }
>>>
>>> +static inline u32 led_get_rgb_val(struct led_classdev *led_cdev)
>>> +{
>>> +    return led_cdev->rgb_val;
>>> +}
>>> +
>>>    void led_init_core(struct led_classdev *led_cdev);
>>>    void led_stop_software_blink(struct led_classdev *led_cdev);
>>>    void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>>                    enum led_brightness value);
>>>    void led_set_brightness_nosleep(struct led_classdev *led_cdev,
>>>                    enum led_brightness value);
>>> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val);
>>> +int led_update_rgb_val(struct led_classdev *led_cdev);
>>>
>>>    extern struct rw_semaphore leds_list_lock;
>>>    extern struct list_head leds_list;
>>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>>> index 4429887..83d2912 100644
>>> --- a/include/linux/leds.h
>>> +++ b/include/linux/leds.h
>>> @@ -35,6 +35,7 @@ struct led_classdev {
>>>        const char        *name;
>>>        enum led_brightness     brightness;
>>>        enum led_brightness     max_brightness;
>>> +    u32             rgb_val;
>>>        int             flags;
>>>
>>>        /* Lower 16 bits reflect status */
>>> @@ -48,6 +49,7 @@ struct led_classdev {
>>>    #define LED_BLINK_DISABLE    (1 << 21)
>>>    #define LED_SYSFS_DISABLE    (1 << 22)
>>>    #define LED_DEV_CAP_FLASH    (1 << 23)
>>> +#define LED_RGB            (1 << 24)
>>
>> Please rename it to LED_DEV_CAP_RGB.
>>
> OK
>
>>>
>>>        /* Set LED brightness level */
>>>        /* Must not sleep. If no non-blocking version can be provided
>>> @@ -56,15 +58,25 @@ struct led_classdev {
>>>         */
>>>        void        (*brightness_set)(struct led_classdev *led_cdev,
>>>                          enum led_brightness brightness);
>>> +
>>> +    void        (*rgb_set)(struct led_classdev *led_cdev,
>>> +                   u32 rgb_val);
>>
>> Without this everything would be much simpler.
>>
> Sure, we can re-use the existing ops. I just thought it would be
> a little ugly to misuse enum led_brightness for RGB values.
> But as it internally is an int we can also do it this way.

I think that this is a good idea. It would require only addition
of one flag and maybe we could think of some new sysfs attribute
which would allow to detect if a LED is of RGB type.

>
>> If we wanted to change the color and intensity we would need to:
>> 1. Set rgb (value only cached in the LED core)
>> 2. Set brightness (write both brightness and rgb settings to the hw)
>>
>> Current brightness can always be obtained with led_get_brightness in
>> case only rgb is to be set.
>>
>>>        /*
>>>         * Set LED brightness level immediately - it can block the caller for
>>>         * the time required for accessing a LED device register.
>>>         */
>>>        int (*brightness_set_blocking)(struct led_classdev *led_cdev,
>>>                           enum led_brightness brightness);
>>> +
>>> +    int (*rgb_set_blocking)(struct led_classdev *led_cdev,
>>> +                u32 rgb_val);
>>> +
>>
>> This is also not needed.
>>
>>>        /* Get LED brightness level */
>>>        enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
>>>
>>> +    /* Get LED RGB val */
>>> +    s32 (*rgb_get)(struct led_classdev *led_cdev);
>>> +
>>
>> Ditto.
>>
>>>        /*
>>>         * Activate hardware accelerated blink, delays are in milliseconds
>>>         * and if both are zero then a sensible default should be chosen.
>>> @@ -90,6 +102,7 @@ struct led_classdev {
>>>
>>>        struct work_struct    set_brightness_work;
>>>        int            delayed_set_value;
>>> +    u32            delayed_rgb_value;
>>
>> Ditto.
>>
> If we have a blocking set callback then we have to set a RGB value from the
> workqueue. And we need brightness + RGB to calculate the effective RGB values
> in the worker func. Therefore I think delayed_rgb_val is needed.
>
>>>
>>>    #ifdef CONFIG_LEDS_TRIGGERS
>>>        /* Protects the trigger data below */
>>>
>>
>>
> Thanks for the review, Heiner
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-leds" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-17 22:31     ` Jacek Anaszewski
@ 2016-01-24 13:38       ` Heiner Kallweit
  2016-01-25  8:41         ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-24 13:38 UTC (permalink / raw)
  To: Jacek Anaszewski, Jacek Anaszewski; +Cc: linux-leds

Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that there
>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>> implements the RGB functionality in its own way.
>>>> Some examples:
>>>> - drivers/hid/hid-thingm.c
>>>> - drivers/usb/misc/usbled.c
>>>> - drivers/leds/leds-bd2802.c
>>>> - drivers/leds/leds-blinkm.c
>>>> ...
>>>> IMHO it would make sense to add generic RGB functionality to the LED core.
>>>>
>>>> Things I didn't like too much in other driver implementations:
>>>> - one led_classdev per color or at least
>>>> - one sysfs attribute per color
>>>> Colors are not really independent therefore I'd prefer one led_classdev
>>>> per RGB LED. Also userspace should be able to change the color with one
>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>
>>>> Also the current brightness-related functionality should not be effected
>>>> by the RGB extension.
>>>>
>>>> This patch is intended to demonstrate the idea of the extension. Most likely
>>>> it's not ready yet for submission.
>>>>
>>>> Main idea is to base the effective RGB value on a combination of brightness
>>>> and a scaled-to-max RGB value.
>>>> The RGB-related callbacks are basically the same as for brightness.
>>>> RGB functionally can be switched on with a new flag in the led_classdev.flags
>>>> bitmap.
>>>>
>>>> Experimentally I changed the thingm driver to use this extension and it works
>>>> quite well. As one result the driver could be very much simplified.
>>>>
>>>> Any feedback is appreciated.
>>>>
>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>> ---
>>>>    drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>    drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
>>>>    drivers/leds/leds.h      |  12 ++++++
>>>>    include/linux/leds.h     |  13 ++++++
>>>>    4 files changed, 193 insertions(+)
>>>>
>>>> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
>>>> index 14139c3..5e4c2f2 100644
>>>> --- a/drivers/leds/led-class.c
>>>> +++ b/drivers/leds/led-class.c
>>>> @@ -64,6 +64,47 @@ unlock:
>>>>    }
>>>>    static DEVICE_ATTR_RW(brightness);
>>>>
>>>> +static ssize_t rgb_show(struct device *dev, struct device_attribute *attr,
>>>> +            char *buf)
>>>> +{
>>>> +    struct led_classdev *led_cdev = dev_get_drvdata(dev);
>>>> +    u32 rgb_val;
>>>> +
>>>> +    mutex_lock(&led_cdev->led_access);
>>>> +    led_update_rgb_val(led_cdev);
>>>> +    rgb_val = led_get_rgb_val(led_cdev);
>>>> +    mutex_unlock(&led_cdev->led_access);
>>>> +
>>>> +    return sprintf(buf, "0x%06x\n", rgb_val);
>>>> +}
>>>> +
>>>> +static ssize_t rgb_store(struct device *dev, struct device_attribute *attr,
>>>> +             const char *buf, size_t size)
>>>> +{
>>>> +    struct led_classdev *led_cdev = dev_get_drvdata(dev);
>>>> +    unsigned long state;
>>>> +    ssize_t ret;
>>>> +
>>>> +    mutex_lock(&led_cdev->led_access);
>>>> +
>>>> +    if (led_sysfs_is_disabled(led_cdev)) {
>>>> +        ret = -EBUSY;
>>>> +        goto unlock;
>>>> +    }
>>>> +
>>>> +    ret = kstrtoul(buf, 0, &state);
>>>> +    if (ret)
>>>> +        goto unlock;
>>>> +
>>>> +    led_set_rgb_val(led_cdev, state);
>>>
>>> Please adhere to the current LED API naming style: led_rgb_set.
>>>
>> Seems like currently two naming styles are use in the LED core.
>> We have e.g. led_blink_set and led_set_brightness.
>> However I'm fine with the suggested led_rgb_set.
> 
> Indeed. Your version would be preferable in this case.
> I wanted this to match led_set_brightness style, but had been
> too lazy to double check that :-/
> 
>>>> +
>>>> +    ret = size;
>>>> +unlock:
>>>> +    mutex_unlock(&led_cdev->led_access);
>>>> +    return ret;
>>>> +}
>>>> +static DEVICE_ATTR_RW(rgb);
>>>> +
>>>>    static ssize_t max_brightness_show(struct device *dev,
>>>>            struct device_attribute *attr, char *buf)
>>>>    {
>>>> @@ -190,6 +231,16 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>>>>        char name[64];
>>>>        int ret;
>>>>
>>>> +    /* FLASH is not supported for RGB LEDs so far
>>>> +     * and RGB enforces max_brightness = LED_FULL.
>>>> +     * Initialize the color as white.
>>>> +     */
>>>> +    if (led_isRGB(led_cdev)) {
>>>
>>> Don't use camel case. Change it to led_is_rgb or check the flag
>>> directly.
>>>
>> OK, will change it to led_is_rgb.
>>
>>>> +        led_cdev->flags &= ~LED_DEV_CAP_FLASH;
>>>
>>> I'd rather check whether both FLASH and RGB flag are set and return
>>> error in this case.
>>>
>> OK
>>
>>>> +        led_cdev->max_brightness = LED_FULL;
>>>> +        led_cdev->rgb_val = 0xffffff;
>>>> +    }
>>>> +
>>>>        ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
>>>>        if (ret < 0)
>>>>            return ret;
>>>> @@ -203,6 +254,14 @@ 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_isRGB(led_cdev)) {
>>>> +        ret = device_create_file(led_cdev->dev, &dev_attr_rgb);
>>>
>>> Please use led_cdev->groups for this. You can refer to drivers/leds/led-class-flash.c.
>>>
>> OK
>>
>>>> +        if (ret) {
>>>> +            device_unregister(led_cdev->dev);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>    #ifdef CONFIG_LEDS_TRIGGERS
>>>>        init_rwsem(&led_cdev->trigger_lock);
>>>>    #endif
>>>> @@ -252,6 +311,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
>>>>
>>>>        flush_work(&led_cdev->set_brightness_work);
>>>>
>>>> +    if (led_isRGB(led_cdev))
>>>> +        device_remove_file(led_cdev->dev, &dev_attr_rgb);
>>>> +
>>>>        device_unregister(led_cdev->dev);
>>>>
>>>>        down_write(&leds_list_lock);
>>>> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
>>>> index 19e1e60..6563bd3 100644
>>>> --- a/drivers/leds/led-core.c
>>>> +++ b/drivers/leds/led-core.c
>>>> @@ -25,6 +25,48 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
>>>>    LIST_HEAD(leds_list);
>>>>    EXPORT_SYMBOL_GPL(leds_list);
>>>>
>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>> +{
>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>> +    u32 blue_raw = rgb & 0xff;
>>>> +    u32 max_raw, red, green, blue;
>>>> +
>>>> +    max_raw = max(red_raw, green_raw);
>>>> +    if (blue_raw > max_raw)
>>>> +        max_raw = blue_raw;
>>>> +
>>>> +    if (!max_raw) {
>>>> +        cdev->brightness = 0;
>>>> +        cdev->rgb_val = 0;
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>> +
>>>> +    cdev->brightness = max_raw;
>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>> +}
>>>
>>> I think that we shouldn't impose specific way of calculating brightness
>>> depending on the rgb value set. We should just pass value from userspace
>>> to the driver.
>>>
>> Right, setting brightness from userspace is no problem.
>> But how about led_update_brightness? It's supposed to update the brightness
>> value in led_cdev with the actual value. And for a RGB LED we have only
>> the RGB values, so we need to calculate the brightness based on RGB values.
>> Or do you see a better way?
> 
> I thought that you had some device using both brightness and rgb
> components settings (no idea if this could be sane in any way).
> If there are only three color components, then why not just redefine
> brightness to store three components in case of the LEDs with
> LED_DEV_CAP_RGB flags set?
> 
My devices just have the three color components (normal 8 bits per color).
Even in the new RGB mode I don't want to break the current brightness-based API
but extend it with RGB functionality. Therefore I think it's best to store
brightness and color separately.
Just one argument: If we store the color components only then we'll
lose the color information if the brightness is set to 0.
And I would like to support e.g. software blink in whatever color.

I pepared a new verion of the patch covering your review comments and will
submit it shortly.

Regards, Heiner

>>
>> This leads me to another question: What do we need led_update_brightness for
>> in general? The cached and the actual value should always be in sync, except
>> the actual brightness can be changed from outside the driver.
>> Do we have such cases?
> 
> It is possible that hardware will limit the brightness, e.g. due to the
> low battery voltage level.
> 
>>
>> Last but not least I saw that led_update_brightness is called in
>> led_classdev_register.
>> Instead of updating our cached value with the actual value shouldn't we
>> initialize cached and actual value (most likely to 0)?
>> The actual value theoretically could be uninitialized.
>> Seems like we rely on BIOS / boot loader to initialize LEDs.
> 
> Drivers are generally expected to initialize brightness to what
> they deem valid initial value. If a driver implements brightness_get
> op then brightness will be synchronized with device settings upon
> registering automatically thanks to this call.
> 
>>>> +static u32 led_get_rgb_raw(struct led_classdev *cdev, bool delayed)
>>>> +{
>>>> +    u32 rgb = delayed ? cdev->delayed_rgb_value : cdev->rgb_val;
>>>> +    u32 brightness = delayed ? cdev->delayed_set_value :
>>>> +             cdev->brightness;
>>>> +    u32 red = (rgb >> 16) & 0xff;
>>>> +    u32 green = (rgb >> 8) & 0xff;
>>>> +    u32 blue = rgb & 0xff;
>>>> +    u32 red_raw, green_raw, blue_raw;
>>>> +
>>>> +    red_raw = DIV_ROUND_CLOSEST(red * brightness, LED_FULL);
>>>> +    green_raw = DIV_ROUND_CLOSEST(green * brightness, LED_FULL);
>>>> +    blue_raw = DIV_ROUND_CLOSEST(blue * brightness, LED_FULL);
>>>> +
>>>> +    return (red_raw << 16) + (green_raw << 8) + blue_raw;
>>>> +}
>>>> +
>>>>    static void led_timer_function(unsigned long data)
>>>>    {
>>>>        struct led_classdev *led_cdev = (void *)data;
>>>> @@ -91,6 +133,21 @@ static void set_brightness_delayed(struct work_struct *ws)
>>>>            led_cdev->flags &= ~LED_BLINK_DISABLE;
>>>>        }
>>>>
>>>> +    if (led_isRGB(led_cdev)) {
>>>> +        u32 rgb = led_get_rgb_raw(led_cdev, true);
>>>> +
>>>> +        if (led_cdev->rgb_set)
>>>> +            led_cdev->rgb_set(led_cdev, rgb);
>>>> +        else if (led_cdev->rgb_set_blocking)
>>>> +            ret = led_cdev->rgb_set_blocking(led_cdev, rgb);
>>>> +        else
>>>> +            ret = -EOPNOTSUPP;
>>>> +        if (ret < 0)
>>>> +            dev_err(led_cdev->dev,
>>>> +                "Setting LED RGB value failed (%d)\n", ret);
>>>> +        return;
>>>> +    }
>>>> +
>>>
>>> Why this is needed? Could you share a use case?
>>>
>> If we re-use the existing brightness ops as suggested by you
>> then most of this code isn't needed.
>>
>>>>        if (led_cdev->brightness_set)
>>>>            led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
>>>>        else if (led_cdev->brightness_set_blocking)
>>>> @@ -229,9 +286,35 @@ void led_set_brightness(struct led_classdev *led_cdev,
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(led_set_brightness);
>>>>
>>>> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val)
>>>> +{
>>>> +    if (!led_isRGB(led_cdev) || (led_cdev->flags & LED_SUSPENDED))
>>>> +        return;
>>>> +
>>>> +    led_set_rgb_raw(led_cdev, rgb_val);
>>>> +
>>>> +    if (led_cdev->rgb_set) {
>>>> +        led_cdev->rgb_set(led_cdev, rgb_val);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    /* If RGB setting can sleep, delegate it to a work queue task */
>>>> +    led_cdev->delayed_set_value = led_cdev->brightness;
>>>> +    led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>>>> +    schedule_work(&led_cdev->set_brightness_work);
>>>
>>> I think that settings should be written to the device only in
>>> the brightness_set op. We wouldn't need additional rgb op then.
>>>
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(led_set_rgb_val);
>>>> +
>>>>    void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>>>                      enum led_brightness value)
>>>>    {
>>>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_set) {
>>>> +        u32 rgb = led_get_rgb_raw(led_cdev, false);
>>>> +
>>>> +        led_cdev->rgb_set(led_cdev, rgb);
>>>> +        return;
>>>> +    }
>>>> +
>>>>        /* Use brightness_set op if available, it is guaranteed not to sleep */
>>>>        if (led_cdev->brightness_set) {
>>>>            led_cdev->brightness_set(led_cdev, value);
>>>> @@ -240,6 +323,7 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>>>
>>>>        /* If brightness setting can sleep, delegate it to a work queue task */
>>>>        led_cdev->delayed_set_value = value;
>>>> +    led_cdev->delayed_rgb_value = led_cdev->rgb_val;
>>>>        schedule_work(&led_cdev->set_brightness_work);
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
>>>> @@ -267,6 +351,12 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
>>>>        if (led_cdev->flags & LED_SUSPENDED)
>>>>            return 0;
>>>>
>>>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_set_blocking) {
>>>> +        u32 rgb = led_get_rgb_raw(led_cdev, false);
>>>> +
>>>> +        return led_cdev->rgb_set_blocking(led_cdev, rgb);
>>>> +    }
>>>> +
>>>>        if (led_cdev->brightness_set_blocking)
>>>>            return led_cdev->brightness_set_blocking(led_cdev,
>>>>                                 led_cdev->brightness);
>>>> @@ -290,6 +380,22 @@ int led_update_brightness(struct led_classdev *led_cdev)
>>>>    }
>>>>    EXPORT_SYMBOL_GPL(led_update_brightness);
>>>>
>>>> +int led_update_rgb_val(struct led_classdev *led_cdev)
>>>> +{
>>>> +    s32 ret = 0;
>>>> +
>>>> +    if (led_isRGB(led_cdev) && led_cdev->rgb_get) {
>>>> +        ret = led_cdev->rgb_get(led_cdev);
>>>> +        if (ret >= 0) {
>>>> +            led_set_rgb_raw(led_cdev, ret);
>>>> +            return 0;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(led_update_rgb_val);
>>>> +
>>>>    /* Caller must ensure led_cdev->led_access held */
>>>>    void led_sysfs_disable(struct led_classdev *led_cdev)
>>>>    {
>>>> diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
>>>> index db3f20d..c733777 100644
>>>> --- a/drivers/leds/leds.h
>>>> +++ b/drivers/leds/leds.h
>>>> @@ -16,17 +16,29 @@
>>>>    #include <linux/rwsem.h>
>>>>    #include <linux/leds.h>
>>>>
>>>> +static inline bool led_isRGB(struct led_classdev *led_cdev)
>>>> +{
>>>> +    return (led_cdev->flags & LED_RGB) != 0;
>>>> +}
>>>> +
>>>>    static inline int led_get_brightness(struct led_classdev *led_cdev)
>>>>    {
>>>>        return led_cdev->brightness;
>>>>    }
>>>>
>>>> +static inline u32 led_get_rgb_val(struct led_classdev *led_cdev)
>>>> +{
>>>> +    return led_cdev->rgb_val;
>>>> +}
>>>> +
>>>>    void led_init_core(struct led_classdev *led_cdev);
>>>>    void led_stop_software_blink(struct led_classdev *led_cdev);
>>>>    void led_set_brightness_nopm(struct led_classdev *led_cdev,
>>>>                    enum led_brightness value);
>>>>    void led_set_brightness_nosleep(struct led_classdev *led_cdev,
>>>>                    enum led_brightness value);
>>>> +void led_set_rgb_val(struct led_classdev *led_cdev, u32 rgb_val);
>>>> +int led_update_rgb_val(struct led_classdev *led_cdev);
>>>>
>>>>    extern struct rw_semaphore leds_list_lock;
>>>>    extern struct list_head leds_list;
>>>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>>>> index 4429887..83d2912 100644
>>>> --- a/include/linux/leds.h
>>>> +++ b/include/linux/leds.h
>>>> @@ -35,6 +35,7 @@ struct led_classdev {
>>>>        const char        *name;
>>>>        enum led_brightness     brightness;
>>>>        enum led_brightness     max_brightness;
>>>> +    u32             rgb_val;
>>>>        int             flags;
>>>>
>>>>        /* Lower 16 bits reflect status */
>>>> @@ -48,6 +49,7 @@ struct led_classdev {
>>>>    #define LED_BLINK_DISABLE    (1 << 21)
>>>>    #define LED_SYSFS_DISABLE    (1 << 22)
>>>>    #define LED_DEV_CAP_FLASH    (1 << 23)
>>>> +#define LED_RGB            (1 << 24)
>>>
>>> Please rename it to LED_DEV_CAP_RGB.
>>>
>> OK
>>
>>>>
>>>>        /* Set LED brightness level */
>>>>        /* Must not sleep. If no non-blocking version can be provided
>>>> @@ -56,15 +58,25 @@ struct led_classdev {
>>>>         */
>>>>        void        (*brightness_set)(struct led_classdev *led_cdev,
>>>>                          enum led_brightness brightness);
>>>> +
>>>> +    void        (*rgb_set)(struct led_classdev *led_cdev,
>>>> +                   u32 rgb_val);
>>>
>>> Without this everything would be much simpler.
>>>
>> Sure, we can re-use the existing ops. I just thought it would be
>> a little ugly to misuse enum led_brightness for RGB values.
>> But as it internally is an int we can also do it this way.
> 
> I think that this is a good idea. It would require only addition
> of one flag and maybe we could think of some new sysfs attribute
> which would allow to detect if a LED is of RGB type.
> 
>>
>>> If we wanted to change the color and intensity we would need to:
>>> 1. Set rgb (value only cached in the LED core)
>>> 2. Set brightness (write both brightness and rgb settings to the hw)
>>>
>>> Current brightness can always be obtained with led_get_brightness in
>>> case only rgb is to be set.
>>>
>>>>        /*
>>>>         * Set LED brightness level immediately - it can block the caller for
>>>>         * the time required for accessing a LED device register.
>>>>         */
>>>>        int (*brightness_set_blocking)(struct led_classdev *led_cdev,
>>>>                           enum led_brightness brightness);
>>>> +
>>>> +    int (*rgb_set_blocking)(struct led_classdev *led_cdev,
>>>> +                u32 rgb_val);
>>>> +
>>>
>>> This is also not needed.
>>>
>>>>        /* Get LED brightness level */
>>>>        enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
>>>>
>>>> +    /* Get LED RGB val */
>>>> +    s32 (*rgb_get)(struct led_classdev *led_cdev);
>>>> +
>>>
>>> Ditto.
>>>
>>>>        /*
>>>>         * Activate hardware accelerated blink, delays are in milliseconds
>>>>         * and if both are zero then a sensible default should be chosen.
>>>> @@ -90,6 +102,7 @@ struct led_classdev {
>>>>
>>>>        struct work_struct    set_brightness_work;
>>>>        int            delayed_set_value;
>>>> +    u32            delayed_rgb_value;
>>>
>>> Ditto.
>>>
>> If we have a blocking set callback then we have to set a RGB value from the
>> workqueue. And we need brightness + RGB to calculate the effective RGB values
>> in the worker func. Therefore I think delayed_rgb_val is needed.
>>
>>>>
>>>>    #ifdef CONFIG_LEDS_TRIGGERS
>>>>        /* Protects the trigger data below */
>>>>
>>>
>>>
>> Thanks for the review, Heiner
>>
>> -- 
>> To unsubscribe from this list: send the line "unsubscribe linux-leds" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-24 13:38       ` Heiner Kallweit
@ 2016-01-25  8:41         ` Jacek Anaszewski
  2016-01-25  9:51           ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-25  8:41 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

Hi Heiner,

On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that there
>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>> implements the RGB functionality in its own way.
>>>>> Some examples:
>>>>> - drivers/hid/hid-thingm.c
>>>>> - drivers/usb/misc/usbled.c
>>>>> - drivers/leds/leds-bd2802.c
>>>>> - drivers/leds/leds-blinkm.c
>>>>> ...
>>>>> IMHO it would make sense to add generic RGB functionality to the LED core.
>>>>>
>>>>> Things I didn't like too much in other driver implementations:
>>>>> - one led_classdev per color or at least
>>>>> - one sysfs attribute per color
>>>>> Colors are not really independent therefore I'd prefer one led_classdev
>>>>> per RGB LED. Also userspace should be able to change the color with one
>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>
>>>>> Also the current brightness-related functionality should not be effected
>>>>> by the RGB extension.
>>>>>
>>>>> This patch is intended to demonstrate the idea of the extension. Most likely
>>>>> it's not ready yet for submission.
>>>>>
>>>>> Main idea is to base the effective RGB value on a combination of brightness
>>>>> and a scaled-to-max RGB value.
>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>> RGB functionally can be switched on with a new flag in the led_classdev.flags
>>>>> bitmap.
>>>>>
>>>>> Experimentally I changed the thingm driver to use this extension and it works
>>>>> quite well. As one result the driver could be very much simplified.
>>>>>
>>>>> Any feedback is appreciated.
>>>>>
>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>> ---
>>>>>     drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>     drivers/leds/led-core.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>     drivers/leds/leds.h      |  12 ++++++
>>>>>     include/linux/leds.h     |  13 ++++++
>>>>>     4 files changed, 193 insertions(+)
>>>>>
[...]
>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>> +{
>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>> +    u32 max_raw, red, green, blue;
>>>>> +
>>>>> +    max_raw = max(red_raw, green_raw);
>>>>> +    if (blue_raw > max_raw)
>>>>> +        max_raw = blue_raw;
>>>>> +
>>>>> +    if (!max_raw) {
>>>>> +        cdev->brightness = 0;
>>>>> +        cdev->rgb_val = 0;
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>> +
>>>>> +    cdev->brightness = max_raw;
>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>> +}
>>>>
>>>> I think that we shouldn't impose specific way of calculating brightness
>>>> depending on the rgb value set. We should just pass value from userspace
>>>> to the driver.
>>>>
>>> Right, setting brightness from userspace is no problem.
>>> But how about led_update_brightness? It's supposed to update the brightness
>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>> the RGB values, so we need to calculate the brightness based on RGB values.
>>> Or do you see a better way?
>>
>> I thought that you had some device using both brightness and rgb
>> components settings (no idea if this could be sane in any way).
>> If there are only three color components, then why not just redefine
>> brightness to store three components in case of the LEDs with
>> LED_DEV_CAP_RGB flags set?
>>
> My devices just have the three color components (normal 8 bits per color).
> Even in the new RGB mode I don't want to break the current brightness-based API
> but extend it with RGB functionality. Therefore I think it's best to store
> brightness and color separately.
> Just one argument: If we store the color components only then we'll
> lose the color information if the brightness is set to 0.
> And I would like to support e.g. software blink in whatever color.

Currently blinking works properly and brightness value isn't being lost.
We store it in the led_cdev->blink_brightness property. Could you
present exact use case you'd like to support and which is not feasible
with current LED core design?

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-25  8:41         ` Jacek Anaszewski
@ 2016-01-25  9:51           ` Heiner Kallweit
  2016-01-25 10:52             ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-25  9:51 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> Hi Heiner,
>
>
> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>
>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>
>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>
>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>
>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>
>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>> there
>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>> implements the RGB functionality in its own way.
>>>>>> Some examples:
>>>>>> - drivers/hid/hid-thingm.c
>>>>>> - drivers/usb/misc/usbled.c
>>>>>> - drivers/leds/leds-bd2802.c
>>>>>> - drivers/leds/leds-blinkm.c
>>>>>> ...
>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>> core.
>>>>>>
>>>>>> Things I didn't like too much in other driver implementations:
>>>>>> - one led_classdev per color or at least
>>>>>> - one sysfs attribute per color
>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>> led_classdev
>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>> one
>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>
>>>>>> Also the current brightness-related functionality should not be
>>>>>> effected
>>>>>> by the RGB extension.
>>>>>>
>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>> likely
>>>>>> it's not ready yet for submission.
>>>>>>
>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>> brightness
>>>>>> and a scaled-to-max RGB value.
>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>> led_classdev.flags
>>>>>> bitmap.
>>>>>>
>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>> it works
>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>
>>>>>> Any feedback is appreciated.
>>>>>>
>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>> ---
>>>>>>     drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>     drivers/leds/led-core.c  | 106
>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>     drivers/leds/leds.h      |  12 ++++++
>>>>>>     include/linux/leds.h     |  13 ++++++
>>>>>>     4 files changed, 193 insertions(+)
>>>>>>
> [...]
>
>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>> +{
>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>> +    u32 max_raw, red, green, blue;
>>>>>> +
>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>> +    if (blue_raw > max_raw)
>>>>>> +        max_raw = blue_raw;
>>>>>> +
>>>>>> +    if (!max_raw) {
>>>>>> +        cdev->brightness = 0;
>>>>>> +        cdev->rgb_val = 0;
>>>>>> +        return;
>>>>>> +    }
>>>>>> +
>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>> +
>>>>>> +    cdev->brightness = max_raw;
>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>> +}
>>>>>
>>>>>
>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>> depending on the rgb value set. We should just pass value from
>>>>> userspace
>>>>> to the driver.
>>>>>
>>>> Right, setting brightness from userspace is no problem.
>>>> But how about led_update_brightness? It's supposed to update the
>>>> brightness
>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>> values.
>>>> Or do you see a better way?
>>>
>>>
>>> I thought that you had some device using both brightness and rgb
>>> components settings (no idea if this could be sane in any way).
>>> If there are only three color components, then why not just redefine
>>> brightness to store three components in case of the LEDs with
>>> LED_DEV_CAP_RGB flags set?
>>>
>> My devices just have the three color components (normal 8 bits per color).
>> Even in the new RGB mode I don't want to break the current
>> brightness-based API
>> but extend it with RGB functionality. Therefore I think it's best to store
>> brightness and color separately.
>> Just one argument: If we store the color components only then we'll
>> lose the color information if the brightness is set to 0.
>> And I would like to support e.g. software blink in whatever color.
>
>
> Currently blinking works properly and brightness value isn't being lost.
> We store it in the led_cdev->blink_brightness property. Could you
> present exact use case you'd like to support and which is not feasible
> with current LED core design?
>
I didn't have a closer look on how soft blinking works and you're right,
this was a bad example.
It's not about that something is wrong with the current LED core design
but that IMHO storing just the raw RGB values wouldn't be a good idea
and we should store brightness and color separately.

So let's take another example:
I set a color via sysfs and a trigger is controling the LED. Once the LED
brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
an update the stored RGB value is 0,0,0.
If the LED is switched on again, then which color to choose?
That's why I'm saying we should store color and brightness separately to
avoid losing the color information information even if the brightness
is set to zero.

Regards, Heiner

> --
> Best Regards,
> Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-25  9:51           ` Heiner Kallweit
@ 2016-01-25 10:52             ` Jacek Anaszewski
  2016-01-25 19:09               ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-25 10:52 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
> <j.anaszewski@samsung.com> wrote:
>> Hi Heiner,
>>
>>
>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>
>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>
>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>
>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>
>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>
>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>> there
>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>> implements the RGB functionality in its own way.
>>>>>>> Some examples:
>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>> ...
>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>> core.
>>>>>>>
>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>> - one led_classdev per color or at least
>>>>>>> - one sysfs attribute per color
>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>> led_classdev
>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>> one
>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>
>>>>>>> Also the current brightness-related functionality should not be
>>>>>>> effected
>>>>>>> by the RGB extension.
>>>>>>>
>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>> likely
>>>>>>> it's not ready yet for submission.
>>>>>>>
>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>> brightness
>>>>>>> and a scaled-to-max RGB value.
>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>> led_classdev.flags
>>>>>>> bitmap.
>>>>>>>
>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>> it works
>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>
>>>>>>> Any feedback is appreciated.
>>>>>>>
>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>> ---
>>>>>>>      drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>      drivers/leds/led-core.c  | 106
>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>      drivers/leds/leds.h      |  12 ++++++
>>>>>>>      include/linux/leds.h     |  13 ++++++
>>>>>>>      4 files changed, 193 insertions(+)
>>>>>>>
>> [...]
>>
>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>> +{
>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>> +
>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>> +    if (blue_raw > max_raw)
>>>>>>> +        max_raw = blue_raw;
>>>>>>> +
>>>>>>> +    if (!max_raw) {
>>>>>>> +        cdev->brightness = 0;
>>>>>>> +        cdev->rgb_val = 0;
>>>>>>> +        return;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>> +
>>>>>>> +    cdev->brightness = max_raw;
>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>> +}
>>>>>>
>>>>>>
>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>> depending on the rgb value set. We should just pass value from
>>>>>> userspace
>>>>>> to the driver.
>>>>>>
>>>>> Right, setting brightness from userspace is no problem.
>>>>> But how about led_update_brightness? It's supposed to update the
>>>>> brightness
>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>> values.
>>>>> Or do you see a better way?
>>>>
>>>>
>>>> I thought that you had some device using both brightness and rgb
>>>> components settings (no idea if this could be sane in any way).
>>>> If there are only three color components, then why not just redefine
>>>> brightness to store three components in case of the LEDs with
>>>> LED_DEV_CAP_RGB flags set?
>>>>
>>> My devices just have the three color components (normal 8 bits per color).
>>> Even in the new RGB mode I don't want to break the current
>>> brightness-based API
>>> but extend it with RGB functionality. Therefore I think it's best to store
>>> brightness and color separately.
>>> Just one argument: If we store the color components only then we'll
>>> lose the color information if the brightness is set to 0.
>>> And I would like to support e.g. software blink in whatever color.
>>
>>
>> Currently blinking works properly and brightness value isn't being lost.
>> We store it in the led_cdev->blink_brightness property. Could you
>> present exact use case you'd like to support and which is not feasible
>> with current LED core design?
>>
> I didn't have a closer look on how soft blinking works and you're right,
> this was a bad example.
> It's not about that something is wrong with the current LED core design
> but that IMHO storing just the raw RGB values wouldn't be a good idea
> and we should store brightness and color separately.
>
> So let's take another example:
> I set a color via sysfs and a trigger is controling the LED. Once the LED
> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
> an update the stored RGB value is 0,0,0.
> If the LED is switched on again, then which color to choose?

The one stored in blink_brightness. I don't get how changing the
interpretation of brightness value can affect anything here.

> That's why I'm saying we should store color and brightness separately to
> avoid losing the color information information even if the brightness
> is set to zero.
>
> Regards, Heiner



-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-25 10:52             ` Jacek Anaszewski
@ 2016-01-25 19:09               ` Heiner Kallweit
  2016-01-26  9:37                 ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-25 19:09 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>> <j.anaszewski@samsung.com> wrote:
>>> Hi Heiner,
>>>
>>>
>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>
>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>
>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>
>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>
>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>
>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>> there
>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>> Some examples:
>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>> ...
>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>> core.
>>>>>>>>
>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>> - one led_classdev per color or at least
>>>>>>>> - one sysfs attribute per color
>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>> led_classdev
>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>> one
>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>
>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>> effected
>>>>>>>> by the RGB extension.
>>>>>>>>
>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>> likely
>>>>>>>> it's not ready yet for submission.
>>>>>>>>
>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>> brightness
>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>> led_classdev.flags
>>>>>>>> bitmap.
>>>>>>>>
>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>> it works
>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>
>>>>>>>> Any feedback is appreciated.
>>>>>>>>
>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>> ---
>>>>>>>>      drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>      drivers/leds/led-core.c  | 106
>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>      drivers/leds/leds.h      |  12 ++++++
>>>>>>>>      include/linux/leds.h     |  13 ++++++
>>>>>>>>      4 files changed, 193 insertions(+)
>>>>>>>>
>>> [...]
>>>
>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>> +{
>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>> +
>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>> +        max_raw = blue_raw;
>>>>>>>> +
>>>>>>>> +    if (!max_raw) {
>>>>>>>> +        cdev->brightness = 0;
>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>> +        return;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>> +
>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>> +}
>>>>>>>
>>>>>>>
>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>> userspace
>>>>>>> to the driver.
>>>>>>>
>>>>>> Right, setting brightness from userspace is no problem.
>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>> brightness
>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>> values.
>>>>>> Or do you see a better way?
>>>>>
>>>>>
>>>>> I thought that you had some device using both brightness and rgb
>>>>> components settings (no idea if this could be sane in any way).
>>>>> If there are only three color components, then why not just redefine
>>>>> brightness to store three components in case of the LEDs with
>>>>> LED_DEV_CAP_RGB flags set?
>>>>>
>>>> My devices just have the three color components (normal 8 bits per color).
>>>> Even in the new RGB mode I don't want to break the current
>>>> brightness-based API
>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>> brightness and color separately.
>>>> Just one argument: If we store the color components only then we'll
>>>> lose the color information if the brightness is set to 0.
>>>> And I would like to support e.g. software blink in whatever color.
>>>
>>>
>>> Currently blinking works properly and brightness value isn't being lost.
>>> We store it in the led_cdev->blink_brightness property. Could you
>>> present exact use case you'd like to support and which is not feasible
>>> with current LED core design?
>>>
>> I didn't have a closer look on how soft blinking works and you're right,
>> this was a bad example.
>> It's not about that something is wrong with the current LED core design
>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>> and we should store brightness and color separately.
>>
>> So let's take another example:
>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>> an update the stored RGB value is 0,0,0.
>> If the LED is switched on again, then which color to choose?
> 
> The one stored in blink_brightness. I don't get how changing the
> interpretation of brightness value can affect anything here.
> 
But blink_brightness is used in case of soft blink only.
If a trigger or userspace sets the brightness to zero then blink_brightness
isn't used.
I agreed that soft blink was a bad example.

>> That's why I'm saying we should store color and brightness separately to
>> avoid losing the color information information even if the brightness
>> is set to zero.
>>
>> Regards, Heiner
> 
> 
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-25 19:09               ` Heiner Kallweit
@ 2016-01-26  9:37                 ` Jacek Anaszewski
  2016-01-26 20:08                   ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-26  9:37 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>> <j.anaszewski@samsung.com> wrote:
>>>> Hi Heiner,
>>>>
>>>>
>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>
>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>
>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>
>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>
>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>
>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>> there
>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>> Some examples:
>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>> ...
>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>> core.
>>>>>>>>>
>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>> - one sysfs attribute per color
>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>> led_classdev
>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>> one
>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>
>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>> effected
>>>>>>>>> by the RGB extension.
>>>>>>>>>
>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>> likely
>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>
>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>> brightness
>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>> led_classdev.flags
>>>>>>>>> bitmap.
>>>>>>>>>
>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>> it works
>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>
>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>> ---
>>>>>>>>>       drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>       drivers/leds/led-core.c  | 106
>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>       drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>       include/linux/leds.h     |  13 ++++++
>>>>>>>>>       4 files changed, 193 insertions(+)
>>>>>>>>>
>>>> [...]
>>>>
>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>> +{
>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>> +
>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>> +
>>>>>>>>> +    if (!max_raw) {
>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>> +        return;
>>>>>>>>> +    }
>>>>>>>>> +
>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>> +
>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>> +}
>>>>>>>>
>>>>>>>>
>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>> userspace
>>>>>>>> to the driver.
>>>>>>>>
>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>> brightness
>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>> values.
>>>>>>> Or do you see a better way?
>>>>>>
>>>>>>
>>>>>> I thought that you had some device using both brightness and rgb
>>>>>> components settings (no idea if this could be sane in any way).
>>>>>> If there are only three color components, then why not just redefine
>>>>>> brightness to store three components in case of the LEDs with
>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>
>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>> Even in the new RGB mode I don't want to break the current
>>>>> brightness-based API
>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>> brightness and color separately.
>>>>> Just one argument: If we store the color components only then we'll
>>>>> lose the color information if the brightness is set to 0.
>>>>> And I would like to support e.g. software blink in whatever color.
>>>>
>>>>
>>>> Currently blinking works properly and brightness value isn't being lost.
>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>> present exact use case you'd like to support and which is not feasible
>>>> with current LED core design?
>>>>
>>> I didn't have a closer look on how soft blinking works and you're right,
>>> this was a bad example.
>>> It's not about that something is wrong with the current LED core design
>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>> and we should store brightness and color separately.
>>>
>>> So let's take another example:
>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>> an update the stored RGB value is 0,0,0.
>>> If the LED is switched on again, then which color to choose?
>>
>> The one stored in blink_brightness. I don't get how changing the
>> interpretation of brightness value can affect anything here.
>>
> But blink_brightness is used in case of soft blink only.

In case of hardware blinking it is driver's responsibility to report
current brightness while blinking is on. It is hardware specific whether
it allows to read current LED state during blinking. Nevertheless
I don't see where is the problem here either.

> If a trigger or userspace sets the brightness to zero then blink_brightness
> isn't used.

Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:

"However, if you set the brightness value to LED_OFF it will
also disable the timer trigger."

Now I see that documentation is inexact here. The paragraph says
about triggers, using timer trigger as an example. This trigger
is specific because of the software fallback implemented in the core.

Setting brightness to LED_OFF disables any trigger only when set from
sysfs (in drivers/leds/led-class.c brightness_store calls
led_trigger_remove if passed brightness is LED_OFF).

However, when setting brightness to LED_OFF directly through LED API,
and not through sysfs, only blink_timer is being disabled, but trigger
isn't being removed. Probably we'd have to remove trigger
from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
to make implementation in line with the documentation.

> I agreed that soft blink was a bad example.
>>> That's why I'm saying we should store color and brightness separately to
>>> avoid losing the color information information even if the brightness
>>> is set to zero.
>>>
>>> Regards, Heiner
>>
>>
>>
>
>
>


-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-26  9:37                 ` Jacek Anaszewski
@ 2016-01-26 20:08                   ` Heiner Kallweit
  2016-01-27  8:27                     ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-26 20:08 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>> <j.anaszewski@samsung.com> wrote:
>>>>> Hi Heiner,
>>>>>
>>>>>
>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>
>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>
>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>
>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>
>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>
>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>> there
>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>> Some examples:
>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>> ...
>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>> core.
>>>>>>>>>>
>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>> led_classdev
>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>> one
>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>
>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>> effected
>>>>>>>>>> by the RGB extension.
>>>>>>>>>>
>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>> likely
>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>
>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>> brightness
>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>> led_classdev.flags
>>>>>>>>>> bitmap.
>>>>>>>>>>
>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>> it works
>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>
>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>> ---
>>>>>>>>>>       drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>       drivers/leds/led-core.c  | 106
>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>       drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>       include/linux/leds.h     |  13 ++++++
>>>>>>>>>>       4 files changed, 193 insertions(+)
>>>>>>>>>>
>>>>> [...]
>>>>>
>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>> +{
>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>> +
>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>> +
>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>> +        return;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>> +
>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>> +}
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>> userspace
>>>>>>>>> to the driver.
>>>>>>>>>
>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>> brightness
>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>> values.
>>>>>>>> Or do you see a better way?
>>>>>>>
>>>>>>>
>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>> If there are only three color components, then why not just redefine
>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>
>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>> brightness-based API
>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>> brightness and color separately.
>>>>>> Just one argument: If we store the color components only then we'll
>>>>>> lose the color information if the brightness is set to 0.
>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>
>>>>>
>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>> present exact use case you'd like to support and which is not feasible
>>>>> with current LED core design?
>>>>>
>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>> this was a bad example.
>>>> It's not about that something is wrong with the current LED core design
>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>> and we should store brightness and color separately.
>>>>
>>>> So let's take another example:
>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>> an update the stored RGB value is 0,0,0.
>>>> If the LED is switched on again, then which color to choose?
>>>
>>> The one stored in blink_brightness. I don't get how changing the
>>> interpretation of brightness value can affect anything here.
>>>
>> But blink_brightness is used in case of soft blink only.
> 
> In case of hardware blinking it is driver's responsibility to report
> current brightness while blinking is on. It is hardware specific whether
> it allows to read current LED state during blinking. Nevertheless
> I don't see where is the problem here either.
> 
>> If a trigger or userspace sets the brightness to zero then blink_brightness
>> isn't used.
> 
> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
> 
> "However, if you set the brightness value to LED_OFF it will
> also disable the timer trigger."
> 
> Now I see that documentation is inexact here. The paragraph says
> about triggers, using timer trigger as an example. This trigger
> is specific because of the software fallback implemented in the core.
> 
> Setting brightness to LED_OFF disables any trigger only when set from
> sysfs (in drivers/leds/led-class.c brightness_store calls
> led_trigger_remove if passed brightness is LED_OFF).
> 
> However, when setting brightness to LED_OFF directly through LED API,
> and not through sysfs, only blink_timer is being disabled, but trigger
> isn't being removed. Probably we'd have to remove trigger
> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
> to make implementation in line with the documentation.
> 
Sorry, I'm afraid we lost track of the original subject.
At least I'm a little lost ..
My understanding is that this discussion is about whether:
- use current brightness member of led_classdev to store brightness and color
  (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
- store brightness and color separately (introduce a new member for the color)

Is this also your understanding? Or are you focussing on a different aspect
and I missed something?

Regards, Heiner

>> I agreed that soft blink was a bad example.
>>>> That's why I'm saying we should store color and brightness separately to
>>>> avoid losing the color information information even if the brightness
>>>> is set to zero.
>>>>
>>>> Regards, Heiner
>>>
>>>
>>>
>>
>>
>>
> 
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-26 20:08                   ` Heiner Kallweit
@ 2016-01-27  8:27                     ` Jacek Anaszewski
  2016-01-29  7:00                       ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-27  8:27 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>> Hi Heiner,
>>>>>>
>>>>>>
>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>
>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>
>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>
>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>
>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>
>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>> there
>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>> Some examples:
>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>> ...
>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>> core.
>>>>>>>>>>>
>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>> led_classdev
>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>> one
>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>
>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>> effected
>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>
>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>> likely
>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>
>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>> brightness
>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>> bitmap.
>>>>>>>>>>>
>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>> it works
>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>
>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>> ---
>>>>>>>>>>>        drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>        drivers/leds/led-core.c  | 106
>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>        drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>        include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>        4 files changed, 193 insertions(+)
>>>>>>>>>>>
>>>>>> [...]
>>>>>>
>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>> +{
>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>> +
>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>> +
>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>> +        return;
>>>>>>>>>>> +    }
>>>>>>>>>>> +
>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>> +
>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>> +}
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>> userspace
>>>>>>>>>> to the driver.
>>>>>>>>>>
>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>> brightness
>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>> values.
>>>>>>>>> Or do you see a better way?
>>>>>>>>
>>>>>>>>
>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>
>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>> brightness-based API
>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>> brightness and color separately.
>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>
>>>>>>
>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>> with current LED core design?
>>>>>>
>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>> this was a bad example.
>>>>> It's not about that something is wrong with the current LED core design
>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>> and we should store brightness and color separately.
>>>>>
>>>>> So let's take another example:
>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>> an update the stored RGB value is 0,0,0.
>>>>> If the LED is switched on again, then which color to choose?
>>>>
>>>> The one stored in blink_brightness. I don't get how changing the
>>>> interpretation of brightness value can affect anything here.
>>>>
>>> But blink_brightness is used in case of soft blink only.
>>
>> In case of hardware blinking it is driver's responsibility to report
>> current brightness while blinking is on. It is hardware specific whether
>> it allows to read current LED state during blinking. Nevertheless
>> I don't see where is the problem here either.
>>
>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>> isn't used.
>>
>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>
>> "However, if you set the brightness value to LED_OFF it will
>> also disable the timer trigger."
>>
>> Now I see that documentation is inexact here. The paragraph says
>> about triggers, using timer trigger as an example. This trigger
>> is specific because of the software fallback implemented in the core.
>>
>> Setting brightness to LED_OFF disables any trigger only when set from
>> sysfs (in drivers/leds/led-class.c brightness_store calls
>> led_trigger_remove if passed brightness is LED_OFF).
>>
>> However, when setting brightness to LED_OFF directly through LED API,
>> and not through sysfs, only blink_timer is being disabled, but trigger
>> isn't being removed. Probably we'd have to remove trigger
>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>> to make implementation in line with the documentation.
>>
> Sorry, I'm afraid we lost track of the original subject.
> At least I'm a little lost ..
> My understanding is that this discussion is about whether:
> - use current brightness member of led_classdev to store brightness and color
>    (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
> - store brightness and color separately (introduce a new member for the color)
>
> Is this also your understanding? Or are you focussing on a different aspect
> and I missed something?

Yes it is. I intended to explain that setting brightness to 0 is
supposed to turn the trigger off. In effect the brightness information
is not required afterwards because trigger is disabled.

During the analysis I realized about little discrepancy between the 
documentation and the implementation and proposed potential fix.

I analyzed this basing on the current interpretation of
brightness. I think we need to focus on your following requirement:

"And I would like to support e.g. software blink in whatever color"

Could you provide more details?

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-27  8:27                     ` Jacek Anaszewski
@ 2016-01-29  7:00                       ` Heiner Kallweit
  2016-01-29  8:24                         ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-01-29  7:00 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>> Hi Heiner,
>>>>>>>
>>>>>>>
>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>
>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>
>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>
>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>
>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>
>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>> there
>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>> Some examples:
>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>> ...
>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>> core.
>>>>>>>>>>>>
>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>> led_classdev
>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>> one
>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>
>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>> effected
>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>
>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>> likely
>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>
>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>> brightness
>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>
>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>> it works
>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>
>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>
>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>> ---
>>>>>>>>>>>>        drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>        drivers/leds/led-core.c  | 106
>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>        drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>        include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>        4 files changed, 193 insertions(+)
>>>>>>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>> +        return;
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +
>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>> +}
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>> userspace
>>>>>>>>>>> to the driver.
>>>>>>>>>>>
>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>> brightness
>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>> values.
>>>>>>>>>> Or do you see a better way?
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>
>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>> brightness-based API
>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>> brightness and color separately.
>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>
>>>>>>>
>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>> with current LED core design?
>>>>>>>
>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>> this was a bad example.
>>>>>> It's not about that something is wrong with the current LED core design
>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>> and we should store brightness and color separately.
>>>>>>
>>>>>> So let's take another example:
>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>> an update the stored RGB value is 0,0,0.
>>>>>> If the LED is switched on again, then which color to choose?
>>>>>
>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>> interpretation of brightness value can affect anything here.
>>>>>
>>>> But blink_brightness is used in case of soft blink only.
>>>
>>> In case of hardware blinking it is driver's responsibility to report
>>> current brightness while blinking is on. It is hardware specific whether
>>> it allows to read current LED state during blinking. Nevertheless
>>> I don't see where is the problem here either.
>>>
>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>> isn't used.
>>>
>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>
>>> "However, if you set the brightness value to LED_OFF it will
>>> also disable the timer trigger."
>>>
>>> Now I see that documentation is inexact here. The paragraph says
>>> about triggers, using timer trigger as an example. This trigger
>>> is specific because of the software fallback implemented in the core.
>>>
>>> Setting brightness to LED_OFF disables any trigger only when set from
>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>> led_trigger_remove if passed brightness is LED_OFF).
>>>
>>> However, when setting brightness to LED_OFF directly through LED API,
>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>> isn't being removed. Probably we'd have to remove trigger
>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>> to make implementation in line with the documentation.
>>>
>> Sorry, I'm afraid we lost track of the original subject.
>> At least I'm a little lost ..
>> My understanding is that this discussion is about whether:
>> - use current brightness member of led_classdev to store brightness and color
>>    (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>> - store brightness and color separately (introduce a new member for the color)
>>
>> Is this also your understanding? Or are you focussing on a different aspect
>> and I missed something?
> 
> Yes it is. I intended to explain that setting brightness to 0 is
> supposed to turn the trigger off. In effect the brightness information
> is not required afterwards because trigger is disabled.
> 
> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
> 
> I analyzed this basing on the current interpretation of
> brightness. I think we need to focus on your following requirement:
> 
> "And I would like to support e.g. software blink in whatever color"
> 
> Could you provide more details?
> 
Basically I want color and brightness to be independent.

One example: If a trigger is switching a LED on and off, then I want to be able to change the color
from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.

Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
the color information if color and brightness are stored together.
Partially because we'd not lose it if e.g. pure blue is set.
But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.

Regards, Heiner

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-29  7:00                       ` Heiner Kallweit
@ 2016-01-29  8:24                         ` Jacek Anaszewski
  2016-02-01  6:43                           ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-01-29  8:24 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>> Hi Heiner,
>>>>>>>>
>>>>>>>>
>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>
>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>
>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>
>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>
>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>> there
>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>> ...
>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>> core.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>> one
>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>> effected
>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>
>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>> likely
>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>> brightness
>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>> it works
>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>> ---
>>>>>>>>>>>>>         drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>         drivers/leds/led-core.c  | 106
>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>         drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>         include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>         4 files changed, 193 insertions(+)
>>>>>>>>>>>>>
>>>>>>>> [...]
>>>>>>>>
>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>> +    }
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>> +}
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>> userspace
>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>
>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>> brightness
>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>> values.
>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>
>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>> brightness-based API
>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>> brightness and color separately.
>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>
>>>>>>>>
>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>> with current LED core design?
>>>>>>>>
>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>> this was a bad example.
>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>> and we should store brightness and color separately.
>>>>>>>
>>>>>>> So let's take another example:
>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>
>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>> interpretation of brightness value can affect anything here.
>>>>>>
>>>>> But blink_brightness is used in case of soft blink only.
>>>>
>>>> In case of hardware blinking it is driver's responsibility to report
>>>> current brightness while blinking is on. It is hardware specific whether
>>>> it allows to read current LED state during blinking. Nevertheless
>>>> I don't see where is the problem here either.
>>>>
>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>> isn't used.
>>>>
>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>
>>>> "However, if you set the brightness value to LED_OFF it will
>>>> also disable the timer trigger."
>>>>
>>>> Now I see that documentation is inexact here. The paragraph says
>>>> about triggers, using timer trigger as an example. This trigger
>>>> is specific because of the software fallback implemented in the core.
>>>>
>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>
>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>> isn't being removed. Probably we'd have to remove trigger
>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>> to make implementation in line with the documentation.
>>>>
>>> Sorry, I'm afraid we lost track of the original subject.
>>> At least I'm a little lost ..
>>> My understanding is that this discussion is about whether:
>>> - use current brightness member of led_classdev to store brightness and color
>>>     (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>> - store brightness and color separately (introduce a new member for the color)
>>>
>>> Is this also your understanding? Or are you focussing on a different aspect
>>> and I missed something?
>>
>> Yes it is. I intended to explain that setting brightness to 0 is
>> supposed to turn the trigger off. In effect the brightness information
>> is not required afterwards because trigger is disabled.
>>
>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>
>> I analyzed this basing on the current interpretation of
>> brightness. I think we need to focus on your following requirement:
>>
>> "And I would like to support e.g. software blink in whatever color"
>>
>> Could you provide more details?
>>
> Basically I want color and brightness to be independent.
>
> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.

It is possible even now - brightness can be changed during blinking, but
it is applied from the next transition to "on" state.

> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
> the color information if color and brightness are stored together.
> Partially because we'd not lose it if e.g. pure blue is set.
> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.

It would suffice to implement dedicated trigger for this. IMO it would
be an over engineering, though. We can support what you want by
exposing each color as a separate LED class device and adding a means
for LED class device synchronization, I was proposing in the beginning.

It would be convenient because it would allow to avoid code duplication
in the LED core. I think that gathering all the colors in the single
functionality could be a task for the user space.

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-01-29  8:24                         ` Jacek Anaszewski
@ 2016-02-01  6:43                           ` Heiner Kallweit
  2016-02-01  8:26                             ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-02-01  6:43 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>> Hi Heiner,
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>
>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>
>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>
>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>> there
>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>> one
>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>         drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>         drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>         drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>         include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>         4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>
>>>>>>>>> [...]
>>>>>>>>>
>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>> userspace
>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>
>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>> brightness
>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>> values.
>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>
>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>> brightness-based API
>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>> brightness and color separately.
>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>> with current LED core design?
>>>>>>>>>
>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>> this was a bad example.
>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>> and we should store brightness and color separately.
>>>>>>>>
>>>>>>>> So let's take another example:
>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>
>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>
>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>
>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>> I don't see where is the problem here either.
>>>>>
>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>> isn't used.
>>>>>
>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>
>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>> also disable the timer trigger."
>>>>>
>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>> about triggers, using timer trigger as an example. This trigger
>>>>> is specific because of the software fallback implemented in the core.
>>>>>
>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>
>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>> to make implementation in line with the documentation.
>>>>>
>>>> Sorry, I'm afraid we lost track of the original subject.
>>>> At least I'm a little lost ..
>>>> My understanding is that this discussion is about whether:
>>>> - use current brightness member of led_classdev to store brightness and color
>>>>     (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>> - store brightness and color separately (introduce a new member for the color)
>>>>
>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>> and I missed something?
>>>
>>> Yes it is. I intended to explain that setting brightness to 0 is
>>> supposed to turn the trigger off. In effect the brightness information
>>> is not required afterwards because trigger is disabled.
>>>
>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>
>>> I analyzed this basing on the current interpretation of
>>> brightness. I think we need to focus on your following requirement:
>>>
>>> "And I would like to support e.g. software blink in whatever color"
>>>
>>> Could you provide more details?
>>>
>> Basically I want color and brightness to be independent.
>>
>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
> 
> It is possible even now - brightness can be changed during blinking, but
> it is applied from the next transition to "on" state.
> 
>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>> the color information if color and brightness are stored together.
>> Partially because we'd not lose it if e.g. pure blue is set.
>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
> 
> It would suffice to implement dedicated trigger for this. IMO it would
> be an over engineering, though. We can support what you want by
> exposing each color as a separate LED class device and adding a means
> for LED class device synchronization, I was proposing in the beginning.
> 
> It would be convenient because it would allow to avoid code duplication
> in the LED core. I think that gathering all the colors in the single
> functionality could be a task for the user space.
> 
By chance I had to deal with HSV color scheme in another project and
using this color scheme core-internally for color LED's might help us to
facilitate backwards compatibility and reduce the needed changes to the core.
It would also allow to easily solve the question we're discussing here.

The brightness is part of the HSV model anyway so we would just have to
add hue / saturation. Conversion to / from RGB would be done on demand.
I have a small prototype that needs much less changes to the core logic.
I'll come up with a re-worked proposal for handling color LED's.

Regards, Heiner

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-01  6:43                           ` Heiner Kallweit
@ 2016-02-01  8:26                             ` Jacek Anaszewski
  2016-02-01 21:41                               ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-02-01  8:26 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>> Hi Heiner,
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>
>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>
>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>          drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>          drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>          drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>          include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>          4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>
>>>>>>>>>> [...]
>>>>>>>>>>
>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>
>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>> brightness
>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>> values.
>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>
>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>> brightness-based API
>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>> with current LED core design?
>>>>>>>>>>
>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>> this was a bad example.
>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>
>>>>>>>>> So let's take another example:
>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>
>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>
>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>
>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>> I don't see where is the problem here either.
>>>>>>
>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>> isn't used.
>>>>>>
>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>
>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>> also disable the timer trigger."
>>>>>>
>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>
>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>
>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>> to make implementation in line with the documentation.
>>>>>>
>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>> At least I'm a little lost ..
>>>>> My understanding is that this discussion is about whether:
>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>      (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>
>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>> and I missed something?
>>>>
>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>> supposed to turn the trigger off. In effect the brightness information
>>>> is not required afterwards because trigger is disabled.
>>>>
>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>
>>>> I analyzed this basing on the current interpretation of
>>>> brightness. I think we need to focus on your following requirement:
>>>>
>>>> "And I would like to support e.g. software blink in whatever color"
>>>>
>>>> Could you provide more details?
>>>>
>>> Basically I want color and brightness to be independent.
>>>
>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>
>> It is possible even now - brightness can be changed during blinking, but
>> it is applied from the next transition to "on" state.
>>
>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>> the color information if color and brightness are stored together.
>>> Partially because we'd not lose it if e.g. pure blue is set.
>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>
>> It would suffice to implement dedicated trigger for this. IMO it would
>> be an over engineering, though. We can support what you want by
>> exposing each color as a separate LED class device and adding a means
>> for LED class device synchronization, I was proposing in the beginning.
>>
>> It would be convenient because it would allow to avoid code duplication
>> in the LED core. I think that gathering all the colors in the single
>> functionality could be a task for the user space.
>>
> By chance I had to deal with HSV color scheme in another project and
> using this color scheme core-internally for color LED's might help us to
> facilitate backwards compatibility and reduce the needed changes to the core.
> It would also allow to easily solve the question we're discussing here.
>
> The brightness is part of the HSV model anyway so we would just have to
> add hue / saturation. Conversion to / from RGB would be done on demand.
> I have a small prototype that needs much less changes to the core logic.
> I'll come up with a re-worked proposal for handling color LED's.

At first let's consider if LED synchronization doesn't fit your needs.
I am very much in favour of this solution since it would address also
other use cases. On the other hand I am opposed to adding any mechanisms
for color scheme conversion to the LED core since it can be covered by
the user space.

Drivers should expose device capabilities and not impose the policy on
how those capabilities can be used. If with slight modifications we
could stick to this rule, we should go in this direction.

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-01  8:26                             ` Jacek Anaszewski
@ 2016-02-01 21:41                               ` Heiner Kallweit
  2016-02-02  8:58                                 ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-02-01 21:41 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>
>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>          drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>          drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>          drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>          include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>          4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>
>>>>>>>>>>> [...]
>>>>>>>>>>>
>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>
>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>
>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>> this was a bad example.
>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>
>>>>>>>>>> So let's take another example:
>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>
>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>
>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>
>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>> I don't see where is the problem here either.
>>>>>>>
>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>> isn't used.
>>>>>>>
>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>
>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>> also disable the timer trigger."
>>>>>>>
>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>
>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>
>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>> to make implementation in line with the documentation.
>>>>>>>
>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>> At least I'm a little lost ..
>>>>>> My understanding is that this discussion is about whether:
>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>      (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>
>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>> and I missed something?
>>>>>
>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>> is not required afterwards because trigger is disabled.
>>>>>
>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>
>>>>> I analyzed this basing on the current interpretation of
>>>>> brightness. I think we need to focus on your following requirement:
>>>>>
>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>
>>>>> Could you provide more details?
>>>>>
>>>> Basically I want color and brightness to be independent.
>>>>
>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>
>>> It is possible even now - brightness can be changed during blinking, but
>>> it is applied from the next transition to "on" state.
>>>
>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>> the color information if color and brightness are stored together.
>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>
>>> It would suffice to implement dedicated trigger for this. IMO it would
>>> be an over engineering, though. We can support what you want by
>>> exposing each color as a separate LED class device and adding a means
>>> for LED class device synchronization, I was proposing in the beginning.
>>>
>>> It would be convenient because it would allow to avoid code duplication
>>> in the LED core. I think that gathering all the colors in the single
>>> functionality could be a task for the user space.
>>>
>> By chance I had to deal with HSV color scheme in another project and
>> using this color scheme core-internally for color LED's might help us to
>> facilitate backwards compatibility and reduce the needed changes to the core.
>> It would also allow to easily solve the question we're discussing here.
>>
>> The brightness is part of the HSV model anyway so we would just have to
>> add hue / saturation. Conversion to / from RGB would be done on demand.
>> I have a small prototype that needs much less changes to the core logic.
>> I'll come up with a re-worked proposal for handling color LED's.
> 
> At first let's consider if LED synchronization doesn't fit your needs.
> I am very much in favour of this solution since it would address also
> other use cases. On the other hand I am opposed to adding any mechanisms
> for color scheme conversion to the LED core since it can be covered by
> the user space.
> 
LED synchronization would be an option. So far I've seen it being implemented
directly in drivers like hid-thingm.
Where I see potential issues with LED synchronization:
- It changes the semantics of the entries /sys/class/leds from physical LED's
  to logical LED's. The grouping of LED's might be recognizable by the name.
  However a sub-structure like /sys/class/leds/<group>/<member> might be
  desirable.
- Users of the LED core (userspace + kernel triggers) may need more than one
  call to set a LED to a requested state.

What would need to be synchronized at a first glance:
- assigning triggers to a LED
- brightness_set/_get
- blinking
- ..

What "other use cases" are you thinking of?

> Drivers should expose device capabilities and not impose the policy on
> how those capabilities can be used. If with slight modifications we
> could stick to this rule, we should go in this direction.
> 
With regard to my HSV proposal I agree that having HSV/RGB conversion in the
kernel is not necessarily desirable. HSV just fits very well into the current
implementation of the LED core.
Color LED's might have different native color schemes (although so far I know
no color LED using another than RGB). If the LED core wants to provide a
unified interface to its users some transformation between core color scheme
and native color scheme (as exposed by the driver) might be needed eventually?

Regards, Heiner

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-01 21:41                               ` Heiner Kallweit
@ 2016-02-02  8:58                                 ` Jacek Anaszewski
  2016-02-03  6:42                                   ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-02-02  8:58 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 02/01/2016 10:41 PM, Heiner Kallweit wrote:
> Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
>> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>           drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>           drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>           drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>>           include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>>           4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>> [...]
>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>>
>>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>>
>>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>>> this was a bad example.
>>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>>
>>>>>>>>>>> So let's take another example:
>>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>>
>>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>>
>>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>>
>>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>>> I don't see where is the problem here either.
>>>>>>>>
>>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>>> isn't used.
>>>>>>>>
>>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>>
>>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>>> also disable the timer trigger."
>>>>>>>>
>>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>>
>>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>>
>>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>>> to make implementation in line with the documentation.
>>>>>>>>
>>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>>> At least I'm a little lost ..
>>>>>>> My understanding is that this discussion is about whether:
>>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>>       (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>>
>>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>>> and I missed something?
>>>>>>
>>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>>> is not required afterwards because trigger is disabled.
>>>>>>
>>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>>
>>>>>> I analyzed this basing on the current interpretation of
>>>>>> brightness. I think we need to focus on your following requirement:
>>>>>>
>>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>>
>>>>>> Could you provide more details?
>>>>>>
>>>>> Basically I want color and brightness to be independent.
>>>>>
>>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>>
>>>> It is possible even now - brightness can be changed during blinking, but
>>>> it is applied from the next transition to "on" state.
>>>>
>>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>>> the color information if color and brightness are stored together.
>>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>>
>>>> It would suffice to implement dedicated trigger for this. IMO it would
>>>> be an over engineering, though. We can support what you want by
>>>> exposing each color as a separate LED class device and adding a means
>>>> for LED class device synchronization, I was proposing in the beginning.
>>>>
>>>> It would be convenient because it would allow to avoid code duplication
>>>> in the LED core. I think that gathering all the colors in the single
>>>> functionality could be a task for the user space.
>>>>
>>> By chance I had to deal with HSV color scheme in another project and
>>> using this color scheme core-internally for color LED's might help us to
>>> facilitate backwards compatibility and reduce the needed changes to the core.
>>> It would also allow to easily solve the question we're discussing here.
>>>
>>> The brightness is part of the HSV model anyway so we would just have to
>>> add hue / saturation. Conversion to / from RGB would be done on demand.
>>> I have a small prototype that needs much less changes to the core logic.
>>> I'll come up with a re-worked proposal for handling color LED's.
>>
>> At first let's consider if LED synchronization doesn't fit your needs.
>> I am very much in favour of this solution since it would address also
>> other use cases. On the other hand I am opposed to adding any mechanisms
>> for color scheme conversion to the LED core since it can be covered by
>> the user space.
>>
> LED synchronization would be an option. So far I've seen it being implemented
> directly in drivers like hid-thingm.
> Where I see potential issues with LED synchronization:
> - It changes the semantics of the entries /sys/class/leds from physical LED's
>    to logical LED's. The grouping of LED's might be recognizable by the name.
>    However a sub-structure like /sys/class/leds/<group>/<member> might be
>    desirable.

Not necessarily. Actually similar functionality was present in the
mainline for a short time, but was reverted [1], due to the objections
raised in the discussion [2].

The idea to follow is to have a sysfs attribute which would indicate
the other LED class device to synchronize with. This way it is possible
to define a chain of LEDs to synchronize and whole chain could be driven
with any of chain's elements. On the led-class side we would need only
two additional sysfs attributes - one for defining the LED class device
to synchronize with and the other one for listing LED class devices
available for synchronization.

The rest of synchronization job will be done by the LED class driver,
basing on the sysfs settings of the LED class devices it exposes per
sub-LED.

> - Users of the LED core (userspace + kernel triggers) may need more than one
>    call to set a LED to a requested state.

Not necessarily as stated above.

> What would need to be synchronized at a first glance:
> - assigning triggers to a LED
> - brightness_set/_get
> - blinking

Any change of a single LED chain element state would propagate to all
other elements (LED class devices).

>
> What "other use cases" are you thinking of?

E.g. synchronous blinking of a number of sub-LEDs.

>> Drivers should expose device capabilities and not impose the policy on
>> how those capabilities can be used. If with slight modifications we
>> could stick to this rule, we should go in this direction.
>>
> With regard to my HSV proposal I agree that having HSV/RGB conversion in the
> kernel is not necessarily desirable. HSV just fits very well into the current
> implementation of the LED core.
> Color LED's might have different native color schemes (although so far I know
> no color LED using another than RGB). If the LED core wants to provide a
> unified interface to its users some transformation between core color scheme
> and native color scheme (as exposed by the driver) might be needed eventually?

IMO the synchronization mechanism design I described above is much
neater. The design of sysfs synchronization API seems to be the only
vital problem here. To be more precise the problem is: how to represent,
in an unambiguous and easy to parse way, the LED class devices available
for synchronization. The list of space separated LED class device
names is not an option since we cannot guarantee names without spaces.
The design requiring more complicated parsing was refused [2].

As a last resort we'd probably need to consult linux-api guys.


[1] https://lkml.org/lkml/2015/3/4/952
[2] http://www.spinics.net/lists/linux-leds/msg02995.html

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-02  8:58                                 ` Jacek Anaszewski
@ 2016-02-03  6:42                                   ` Heiner Kallweit
  2016-02-03  8:28                                     ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-02-03  6:42 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 02.02.2016 um 09:58 schrieb Jacek Anaszewski:
> On 02/01/2016 10:41 PM, Heiner Kallweit wrote:
>> Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
>>> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>>>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>           drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>           drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>           drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>>>           include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>>>           4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>> [...]
>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>>>
>>>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>>>> this was a bad example.
>>>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>>>
>>>>>>>>>>>> So let's take another example:
>>>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>>>
>>>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>>>
>>>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>>>
>>>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>>>> I don't see where is the problem here either.
>>>>>>>>>
>>>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>>>> isn't used.
>>>>>>>>>
>>>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>>>
>>>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>>>> also disable the timer trigger."
>>>>>>>>>
>>>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>>>
>>>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>>>
>>>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>>>> to make implementation in line with the documentation.
>>>>>>>>>
>>>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>>>> At least I'm a little lost ..
>>>>>>>> My understanding is that this discussion is about whether:
>>>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>>>       (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>>>
>>>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>>>> and I missed something?
>>>>>>>
>>>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>>>> is not required afterwards because trigger is disabled.
>>>>>>>
>>>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>>>
>>>>>>> I analyzed this basing on the current interpretation of
>>>>>>> brightness. I think we need to focus on your following requirement:
>>>>>>>
>>>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>>>
>>>>>>> Could you provide more details?
>>>>>>>
>>>>>> Basically I want color and brightness to be independent.
>>>>>>
>>>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>>>
>>>>> It is possible even now - brightness can be changed during blinking, but
>>>>> it is applied from the next transition to "on" state.
>>>>>
>>>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>>>> the color information if color and brightness are stored together.
>>>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>>>
>>>>> It would suffice to implement dedicated trigger for this. IMO it would
>>>>> be an over engineering, though. We can support what you want by
>>>>> exposing each color as a separate LED class device and adding a means
>>>>> for LED class device synchronization, I was proposing in the beginning.
>>>>>
>>>>> It would be convenient because it would allow to avoid code duplication
>>>>> in the LED core. I think that gathering all the colors in the single
>>>>> functionality could be a task for the user space.
>>>>>
>>>> By chance I had to deal with HSV color scheme in another project and
>>>> using this color scheme core-internally for color LED's might help us to
>>>> facilitate backwards compatibility and reduce the needed changes to the core.
>>>> It would also allow to easily solve the question we're discussing here.
>>>>
>>>> The brightness is part of the HSV model anyway so we would just have to
>>>> add hue / saturation. Conversion to / from RGB would be done on demand.
>>>> I have a small prototype that needs much less changes to the core logic.
>>>> I'll come up with a re-worked proposal for handling color LED's.
>>>
>>> At first let's consider if LED synchronization doesn't fit your needs.
>>> I am very much in favour of this solution since it would address also
>>> other use cases. On the other hand I am opposed to adding any mechanisms
>>> for color scheme conversion to the LED core since it can be covered by
>>> the user space.
>>>
>> LED synchronization would be an option. So far I've seen it being implemented
>> directly in drivers like hid-thingm.
>> Where I see potential issues with LED synchronization:
>> - It changes the semantics of the entries /sys/class/leds from physical LED's
>>    to logical LED's. The grouping of LED's might be recognizable by the name.
>>    However a sub-structure like /sys/class/leds/<group>/<member> might be
>>    desirable.
> 
> Not necessarily. Actually similar functionality was present in the
> mainline for a short time, but was reverted [1], due to the objections
> raised in the discussion [2].
> 
> The idea to follow is to have a sysfs attribute which would indicate
> the other LED class device to synchronize with. This way it is possible
> to define a chain of LEDs to synchronize and whole chain could be driven
> with any of chain's elements. On the led-class side we would need only
> two additional sysfs attributes - one for defining the LED class device
> to synchronize with and the other one for listing LED class devices
> available for synchronization.
> 
> The rest of synchronization job will be done by the LED class driver,
> basing on the sysfs settings of the LED class devices it exposes per
> sub-LED.
> 
>> - Users of the LED core (userspace + kernel triggers) may need more than one
>>    call to set a LED to a requested state.
> 
> Not necessarily as stated above.
> 
In the case of RGB LED's the brightness can't be synchronized and the
caller needs three calls to set the R/G/B values.

>> What would need to be synchronized at a first glance:
>> - assigning triggers to a LED
>> - brightness_set/_get
>> - blinking
> 
> Any change of a single LED chain element state would propagate to all
> other elements (LED class devices).
> 
>>
>> What "other use cases" are you thinking of?
> 
> E.g. synchronous blinking of a number of sub-LEDs.
> 
Do you mean that just the blinking frequency is synchronized in case the respective
LED is switched on and that it still should be possible to switch on / off LEDs
individually?
If not than what's the benefit of x LED's doing something synchronously?
The information I get from it is the same as from one LED.

As the original synchronization idea was intended for the flash functionality,
I assume it could primarily make sense there.
(But I have to admit that I have no real idea of the flash functionality.)

>>> Drivers should expose device capabilities and not impose the policy on
>>> how those capabilities can be used. If with slight modifications we
>>> could stick to this rule, we should go in this direction.
>>>
>> With regard to my HSV proposal I agree that having HSV/RGB conversion in the
>> kernel is not necessarily desirable. HSV just fits very well into the current
>> implementation of the LED core.
>> Color LED's might have different native color schemes (although so far I know
>> no color LED using another than RGB). If the LED core wants to provide a
>> unified interface to its users some transformation between core color scheme
>> and native color scheme (as exposed by the driver) might be needed eventually?
> 
> IMO the synchronization mechanism design I described above is much
> neater. The design of sysfs synchronization API seems to be the only
> vital problem here. To be more precise the problem is: how to represent,
> in an unambiguous and easy to parse way, the LED class devices available
> for synchronization. The list of space separated LED class device
> names is not an option since we cannot guarantee names without spaces.
> The design requiring more complicated parsing was refused [2].
> 
> As a last resort we'd probably need to consult linux-api guys.
> 
> 
> [1] https://lkml.org/lkml/2015/3/4/952
> [2] http://www.spinics.net/lists/linux-leds/msg02995.html
> 
Thanks for the links. Let me try to summarize some requirements + some inquiries:
- By synchronizing LEDs all main properties are synchronized
  - Should it be configurable which properties should be synchronized?
    E.g. in case of RGB LED's the brightness should not be synchronized, however
    there might be use cases where synchronizing brightness makes sense.

- Should defining and changing groups of synchronized LEDs be possible dynamically?
  Based on the history of this synchronization proposal I guess so.
  Alternatively it could be possible only when registering the led_classdev's,
  e.g. by passing a reference to a "master LED" which acts as group id.
  (But this might restrict the mechanism to LED's created by the same driver instance.)
  Or both methods ..

Regards, Heiner

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-03  6:42                                   ` Heiner Kallweit
@ 2016-02-03  8:28                                     ` Jacek Anaszewski
  2016-02-03 22:08                                       ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-02-03  8:28 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 02/03/2016 07:42 AM, Heiner Kallweit wrote:
> Am 02.02.2016 um 09:58 schrieb Jacek Anaszewski:
>> On 02/01/2016 10:41 PM, Heiner Kallweit wrote:
>>> Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
>>>> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>>>>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>>>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>            drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>            drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>            drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>>>>            include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>>>>            4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>> [...]
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>>>>
>>>>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>>>>> this was a bad example.
>>>>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>>>>
>>>>>>>>>>>>> So let's take another example:
>>>>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>>>>
>>>>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>>>>
>>>>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>>>>
>>>>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>>>>> I don't see where is the problem here either.
>>>>>>>>>>
>>>>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>>>>> isn't used.
>>>>>>>>>>
>>>>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>>>>
>>>>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>>>>> also disable the timer trigger."
>>>>>>>>>>
>>>>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>>>>
>>>>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>>>>
>>>>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>>>>> to make implementation in line with the documentation.
>>>>>>>>>>
>>>>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>>>>> At least I'm a little lost ..
>>>>>>>>> My understanding is that this discussion is about whether:
>>>>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>>>>        (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>>>>
>>>>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>>>>> and I missed something?
>>>>>>>>
>>>>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>>>>> is not required afterwards because trigger is disabled.
>>>>>>>>
>>>>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>>>>
>>>>>>>> I analyzed this basing on the current interpretation of
>>>>>>>> brightness. I think we need to focus on your following requirement:
>>>>>>>>
>>>>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>>>>
>>>>>>>> Could you provide more details?
>>>>>>>>
>>>>>>> Basically I want color and brightness to be independent.
>>>>>>>
>>>>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>>>>
>>>>>> It is possible even now - brightness can be changed during blinking, but
>>>>>> it is applied from the next transition to "on" state.
>>>>>>
>>>>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>>>>> the color information if color and brightness are stored together.
>>>>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>>>>
>>>>>> It would suffice to implement dedicated trigger for this. IMO it would
>>>>>> be an over engineering, though. We can support what you want by
>>>>>> exposing each color as a separate LED class device and adding a means
>>>>>> for LED class device synchronization, I was proposing in the beginning.
>>>>>>
>>>>>> It would be convenient because it would allow to avoid code duplication
>>>>>> in the LED core. I think that gathering all the colors in the single
>>>>>> functionality could be a task for the user space.
>>>>>>
>>>>> By chance I had to deal with HSV color scheme in another project and
>>>>> using this color scheme core-internally for color LED's might help us to
>>>>> facilitate backwards compatibility and reduce the needed changes to the core.
>>>>> It would also allow to easily solve the question we're discussing here.
>>>>>
>>>>> The brightness is part of the HSV model anyway so we would just have to
>>>>> add hue / saturation. Conversion to / from RGB would be done on demand.
>>>>> I have a small prototype that needs much less changes to the core logic.
>>>>> I'll come up with a re-worked proposal for handling color LED's.
>>>>
>>>> At first let's consider if LED synchronization doesn't fit your needs.
>>>> I am very much in favour of this solution since it would address also
>>>> other use cases. On the other hand I am opposed to adding any mechanisms
>>>> for color scheme conversion to the LED core since it can be covered by
>>>> the user space.
>>>>
>>> LED synchronization would be an option. So far I've seen it being implemented
>>> directly in drivers like hid-thingm.
>>> Where I see potential issues with LED synchronization:
>>> - It changes the semantics of the entries /sys/class/leds from physical LED's
>>>     to logical LED's. The grouping of LED's might be recognizable by the name.
>>>     However a sub-structure like /sys/class/leds/<group>/<member> might be
>>>     desirable.
>>
>> Not necessarily. Actually similar functionality was present in the
>> mainline for a short time, but was reverted [1], due to the objections
>> raised in the discussion [2].
>>
>> The idea to follow is to have a sysfs attribute which would indicate
>> the other LED class device to synchronize with. This way it is possible
>> to define a chain of LEDs to synchronize and whole chain could be driven
>> with any of chain's elements. On the led-class side we would need only
>> two additional sysfs attributes - one for defining the LED class device
>> to synchronize with and the other one for listing LED class devices
>> available for synchronization.
>>
>> The rest of synchronization job will be done by the LED class driver,
>> basing on the sysfs settings of the LED class devices it exposes per
>> sub-LED.
>>
>>> - Users of the LED core (userspace + kernel triggers) may need more than one
>>>     call to set a LED to a requested state.
>>
>> Not necessarily as stated above.
>>
> In the case of RGB LED's the brightness can't be synchronized and the
> caller needs three calls to set the R/G/B values.

Right. But this is not a big problem I think. It is just a part of
user space work that needs to be done to configure the device.
In case of V4L2 media controllers we also have to configure connections
between platform sub-devices.

One thing could be improved - we'd have to add a reservation to the
brightness attribute semantics. saying that any transition of
brightness attribute value from 0 to non-zero turns the LED on only
if it isn't synchronized with any other LED class device, i.e. it is
a master LED or a single LED. It would allow to avoid the risk of
noticeable delays between activation of a number of synchronized LEDs.

Effectively, the whole group would have to be controlled by a single
master sub-LED and all group members would have to synchronize with
the master.

>>> What would need to be synchronized at a first glance:
>>> - assigning triggers to a LED
>>> - brightness_set/_get
>>> - blinking
>>
>> Any change of a single LED chain element state would propagate to all
>> other elements (LED class devices).

In view of the above let's forget about this "chain" approach.

>>>
>>> What "other use cases" are you thinking of?
>>
>> E.g. synchronous blinking of a number of sub-LEDs.
>>
> Do you mean that just the blinking frequency is synchronized in case the respective
> LED is switched on and that it still should be possible to switch on / off LEDs
> individually?

This is how I see it:
1. LED1 selects LED4 as a master LED
2. LED1 sets brightness to 10
3. LED2 selects LED4 as a master LED
4. LED2 sets brightness to 150
5. LED3 selects LED4 as a master LED
6. LED3 sets brightness to 255
7. LED4 sets brightness to 200
    //which turns LED1, LED2, LED3 and LED4 on,
    //each of them with their own brightness
8. LED4 enables timer triger
    //LED1, LED2, LED3 and LED4 blink simultaneously
    //of course some devices may introduce little delays
    //due to the I2C transmission delays, it depends on
    //the particular device register design
9. LED2 sets brightness to 100
    //LED2 brightness is changed upon next transition
    //to "LED on" state
10. LED2 sets brightness to 0
     //LED2 is turned off and removed from the group
11. LED4 sets brightness to 0
     //LED1, LED3 and LED4 are turned off


> If not than what's the benefit of x LED's doing something synchronously?
> The information I get from it is the same as from one LED.
>
> As the original synchronization idea was intended for the flash functionality,
> I assume it could primarily make sense there.
> (But I have to admit that I have no real idea of the flash functionality.)
>
>>>> Drivers should expose device capabilities and not impose the policy on
>>>> how those capabilities can be used. If with slight modifications we
>>>> could stick to this rule, we should go in this direction.
>>>>
>>> With regard to my HSV proposal I agree that having HSV/RGB conversion in the
>>> kernel is not necessarily desirable. HSV just fits very well into the current
>>> implementation of the LED core.
>>> Color LED's might have different native color schemes (although so far I know
>>> no color LED using another than RGB). If the LED core wants to provide a
>>> unified interface to its users some transformation between core color scheme
>>> and native color scheme (as exposed by the driver) might be needed eventually?
>>
>> IMO the synchronization mechanism design I described above is much
>> neater. The design of sysfs synchronization API seems to be the only
>> vital problem here. To be more precise the problem is: how to represent,
>> in an unambiguous and easy to parse way, the LED class devices available
>> for synchronization. The list of space separated LED class device
>> names is not an option since we cannot guarantee names without spaces.
>> The design requiring more complicated parsing was refused [2].
>>
>> As a last resort we'd probably need to consult linux-api guys.
>>
>>
>> [1] https://lkml.org/lkml/2015/3/4/952
>> [2] http://www.spinics.net/lists/linux-leds/msg02995.html
>>
> Thanks for the links. Let me try to summarize some requirements + some inquiries:
> - By synchronizing LEDs all main properties are synchronized
>    - Should it be configurable which properties should be synchronized?
>      E.g. in case of RGB LED's the brightness should not be synchronized, however
>      there might be use cases where synchronizing brightness makes sense.

I think that synchronization should apply to simultaneous
activation/deactivation and triggers.

> - Should defining and changing groups of synchronized LEDs be possible dynamically?
>    Based on the history of this synchronization proposal I guess so.
>    Alternatively it could be possible only when registering the led_classdev's,
>    e.g. by passing a reference to a "master LED" which acts as group id.
>    (But this might restrict the mechanism to LED's created by the same driver instance.)
>    Or both methods ..

Reliable synchronization is possible only for the sub-LEDs of the same
device. All LED class devices exposed by an instance of a driver should
register with the predefined scope of the LED class devices available
for synchronization, the devices exposed by this driver.

-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-03  8:28                                     ` Jacek Anaszewski
@ 2016-02-03 22:08                                       ` Heiner Kallweit
  2016-02-04  8:30                                         ` Jacek Anaszewski
  0 siblings, 1 reply; 27+ messages in thread
From: Heiner Kallweit @ 2016-02-03 22:08 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 03.02.2016 um 09:28 schrieb Jacek Anaszewski:
> On 02/03/2016 07:42 AM, Heiner Kallweit wrote:
>> Am 02.02.2016 um 09:58 schrieb Jacek Anaszewski:
>>> On 02/01/2016 10:41 PM, Heiner Kallweit wrote:
>>>> Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
>>>>> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>>>>>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>>>>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>>>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>            drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>            drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>            drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>>>>>            include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>>>>>            4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> [...]
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>>>>>> this was a bad example.
>>>>>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> So let's take another example:
>>>>>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>>>>>
>>>>>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>>>>>
>>>>>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>>>>>
>>>>>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>>>>>> I don't see where is the problem here either.
>>>>>>>>>>>
>>>>>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>>>>>> isn't used.
>>>>>>>>>>>
>>>>>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>>>>>
>>>>>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>>>>>> also disable the timer trigger."
>>>>>>>>>>>
>>>>>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>>>>>
>>>>>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>>>>>
>>>>>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>>>>>> to make implementation in line with the documentation.
>>>>>>>>>>>
>>>>>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>>>>>> At least I'm a little lost ..
>>>>>>>>>> My understanding is that this discussion is about whether:
>>>>>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>>>>>        (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>>>>>
>>>>>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>>>>>> and I missed something?
>>>>>>>>>
>>>>>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>>>>>> is not required afterwards because trigger is disabled.
>>>>>>>>>
>>>>>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>>>>>
>>>>>>>>> I analyzed this basing on the current interpretation of
>>>>>>>>> brightness. I think we need to focus on your following requirement:
>>>>>>>>>
>>>>>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>>>>>
>>>>>>>>> Could you provide more details?
>>>>>>>>>
>>>>>>>> Basically I want color and brightness to be independent.
>>>>>>>>
>>>>>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>>>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>>>>>
>>>>>>> It is possible even now - brightness can be changed during blinking, but
>>>>>>> it is applied from the next transition to "on" state.
>>>>>>>
>>>>>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>>>>>> the color information if color and brightness are stored together.
>>>>>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>>>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>>>>>
>>>>>>> It would suffice to implement dedicated trigger for this. IMO it would
>>>>>>> be an over engineering, though. We can support what you want by
>>>>>>> exposing each color as a separate LED class device and adding a means
>>>>>>> for LED class device synchronization, I was proposing in the beginning.
>>>>>>>
>>>>>>> It would be convenient because it would allow to avoid code duplication
>>>>>>> in the LED core. I think that gathering all the colors in the single
>>>>>>> functionality could be a task for the user space.
>>>>>>>
>>>>>> By chance I had to deal with HSV color scheme in another project and
>>>>>> using this color scheme core-internally for color LED's might help us to
>>>>>> facilitate backwards compatibility and reduce the needed changes to the core.
>>>>>> It would also allow to easily solve the question we're discussing here.
>>>>>>
>>>>>> The brightness is part of the HSV model anyway so we would just have to
>>>>>> add hue / saturation. Conversion to / from RGB would be done on demand.
>>>>>> I have a small prototype that needs much less changes to the core logic.
>>>>>> I'll come up with a re-worked proposal for handling color LED's.
>>>>>
>>>>> At first let's consider if LED synchronization doesn't fit your needs.
>>>>> I am very much in favour of this solution since it would address also
>>>>> other use cases. On the other hand I am opposed to adding any mechanisms
>>>>> for color scheme conversion to the LED core since it can be covered by
>>>>> the user space.
>>>>>
>>>> LED synchronization would be an option. So far I've seen it being implemented
>>>> directly in drivers like hid-thingm.
>>>> Where I see potential issues with LED synchronization:
>>>> - It changes the semantics of the entries /sys/class/leds from physical LED's
>>>>     to logical LED's. The grouping of LED's might be recognizable by the name.
>>>>     However a sub-structure like /sys/class/leds/<group>/<member> might be
>>>>     desirable.
>>>
>>> Not necessarily. Actually similar functionality was present in the
>>> mainline for a short time, but was reverted [1], due to the objections
>>> raised in the discussion [2].
>>>
>>> The idea to follow is to have a sysfs attribute which would indicate
>>> the other LED class device to synchronize with. This way it is possible
>>> to define a chain of LEDs to synchronize and whole chain could be driven
>>> with any of chain's elements. On the led-class side we would need only
>>> two additional sysfs attributes - one for defining the LED class device
>>> to synchronize with and the other one for listing LED class devices
>>> available for synchronization.
>>>
>>> The rest of synchronization job will be done by the LED class driver,
>>> basing on the sysfs settings of the LED class devices it exposes per
>>> sub-LED.
>>>
>>>> - Users of the LED core (userspace + kernel triggers) may need more than one
>>>>     call to set a LED to a requested state.
>>>
>>> Not necessarily as stated above.
>>>
>> In the case of RGB LED's the brightness can't be synchronized and the
>> caller needs three calls to set the R/G/B values.
> 
> Right. But this is not a big problem I think. It is just a part of
> user space work that needs to be done to configure the device.

Fair enough for userspace.
When working on the RGB stuff one of my ideas was to support trigger actions
for color LEDs in the future.
Either to signal whatever multi-state information or to visualize a value
in a range (e.g. some temperature monitoring driver might want to map a
temperature into the color range from green to red).

And a trigger most likely has to set a color with one call.

> In case of V4L2 media controllers we also have to configure connections
> between platform sub-devices.
> 
> One thing could be improved - we'd have to add a reservation to the
> brightness attribute semantics. saying that any transition of
> brightness attribute value from 0 to non-zero turns the LED on only
> if it isn't synchronized with any other LED class device, i.e. it is
> a master LED or a single LED. It would allow to avoid the risk of
> noticeable delays between activation of a number of synchronized LEDs.
> 
> Effectively, the whole group would have to be controlled by a single
> master sub-LED and all group members would have to synchronize with
> the master.
> 
>>>> What would need to be synchronized at a first glance:
>>>> - assigning triggers to a LED
>>>> - brightness_set/_get
>>>> - blinking
>>>
>>> Any change of a single LED chain element state would propagate to all
>>> other elements (LED class devices).
> 
> In view of the above let's forget about this "chain" approach.
> 
>>>>
>>>> What "other use cases" are you thinking of?
>>>
>>> E.g. synchronous blinking of a number of sub-LEDs.
>>>
>> Do you mean that just the blinking frequency is synchronized in case the respective
>> LED is switched on and that it still should be possible to switch on / off LEDs
>> individually?
> 
> This is how I see it:
> 1. LED1 selects LED4 as a master LED
> 2. LED1 sets brightness to 10
> 3. LED2 selects LED4 as a master LED
> 4. LED2 sets brightness to 150
> 5. LED3 selects LED4 as a master LED
> 6. LED3 sets brightness to 255
> 7. LED4 sets brightness to 200
>    //which turns LED1, LED2, LED3 and LED4 on,
>    //each of them with their own brightness
> 8. LED4 enables timer triger
>    //LED1, LED2, LED3 and LED4 blink simultaneously
>    //of course some devices may introduce little delays
>    //due to the I2C transmission delays, it depends on
>    //the particular device register design
> 9. LED2 sets brightness to 100
>    //LED2 brightness is changed upon next transition
>    //to "LED on" state
> 10. LED2 sets brightness to 0
>     //LED2 is turned off and removed from the group
> 11. LED4 sets brightness to 0
>     //LED1, LED3 and LED4 are turned off
> 
I understand the idea, still it's not clear to me what benefit would justify
the effort to implement such a feature.
What kind of practical use case do you imagine (maybe except synchronuous blinking) ?

For sync blinking I could imagine a less flexible, but maybe simpler solution:
Set up a timer (independent of a led_classdev) that controls for every led_classdev
that wants to use this feature the on/off blink transitions.
After a brief look at ledtrig-timer.c this trigger seems to provide a similar
(or even equal) functionality already.
Drawback is that all LED's share the same blink frequency (globally, not only
per LED group). But that may be acceptable or even desirable.

> 
>> If not than what's the benefit of x LED's doing something synchronously?
>> The information I get from it is the same as from one LED.
>>
>> As the original synchronization idea was intended for the flash functionality,
>> I assume it could primarily make sense there.
>> (But I have to admit that I have no real idea of the flash functionality.)
>>
>>>>> Drivers should expose device capabilities and not impose the policy on
>>>>> how those capabilities can be used. If with slight modifications we
>>>>> could stick to this rule, we should go in this direction.
>>>>>
>>>> With regard to my HSV proposal I agree that having HSV/RGB conversion in the
>>>> kernel is not necessarily desirable. HSV just fits very well into the current
>>>> implementation of the LED core.
>>>> Color LED's might have different native color schemes (although so far I know
>>>> no color LED using another than RGB). If the LED core wants to provide a
>>>> unified interface to its users some transformation between core color scheme
>>>> and native color scheme (as exposed by the driver) might be needed eventually?
>>>
>>> IMO the synchronization mechanism design I described above is much
>>> neater. The design of sysfs synchronization API seems to be the only
>>> vital problem here. To be more precise the problem is: how to represent,
>>> in an unambiguous and easy to parse way, the LED class devices available
>>> for synchronization. The list of space separated LED class device
>>> names is not an option since we cannot guarantee names without spaces.
>>> The design requiring more complicated parsing was refused [2].
>>>
>>> As a last resort we'd probably need to consult linux-api guys.
>>>
>>>
>>> [1] https://lkml.org/lkml/2015/3/4/952
>>> [2] http://www.spinics.net/lists/linux-leds/msg02995.html
>>>
>> Thanks for the links. Let me try to summarize some requirements + some inquiries:
>> - By synchronizing LEDs all main properties are synchronized
>>    - Should it be configurable which properties should be synchronized?
>>      E.g. in case of RGB LED's the brightness should not be synchronized, however
>>      there might be use cases where synchronizing brightness makes sense.
> 
> I think that synchronization should apply to simultaneous
> activation/deactivation and triggers.
> 
>> - Should defining and changing groups of synchronized LEDs be possible dynamically?
>>    Based on the history of this synchronization proposal I guess so.
>>    Alternatively it could be possible only when registering the led_classdev's,
>>    e.g. by passing a reference to a "master LED" which acts as group id.
>>    (But this might restrict the mechanism to LED's created by the same driver instance.)
>>    Or both methods ..
> 
> Reliable synchronization is possible only for the sub-LEDs of the same
> device. All LED class devices exposed by an instance of a driver should
> register with the predefined scope of the LED class devices available
> for synchronization, the devices exposed by this driver.
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-03 22:08                                       ` Heiner Kallweit
@ 2016-02-04  8:30                                         ` Jacek Anaszewski
  2016-02-07  0:18                                           ` Heiner Kallweit
  0 siblings, 1 reply; 27+ messages in thread
From: Jacek Anaszewski @ 2016-02-04  8:30 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Jacek Anaszewski, linux-leds

On 02/03/2016 11:08 PM, Heiner Kallweit wrote:
> Am 03.02.2016 um 09:28 schrieb Jacek Anaszewski:
>> On 02/03/2016 07:42 AM, Heiner Kallweit wrote:
>>> Am 02.02.2016 um 09:58 schrieb Jacek Anaszewski:
>>>> On 02/01/2016 10:41 PM, Heiner Kallweit wrote:
>>>>> Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
>>>>>> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>>>>>>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>>>>>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>>>>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>>>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>>             drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>             drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>             drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>>>>>>             include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>>>>>>             4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> [...]
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>>>>>>> this was a bad example.
>>>>>>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> So let's take another example:
>>>>>>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>>>>>>
>>>>>>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>>>>>>
>>>>>>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>>>>>>> I don't see where is the problem here either.
>>>>>>>>>>>>
>>>>>>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>>>>>>> isn't used.
>>>>>>>>>>>>
>>>>>>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>>>>>>
>>>>>>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>>>>>>> also disable the timer trigger."
>>>>>>>>>>>>
>>>>>>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>>>>>>
>>>>>>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>>>>>>
>>>>>>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>>>>>>> to make implementation in line with the documentation.
>>>>>>>>>>>>
>>>>>>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>>>>>>> At least I'm a little lost ..
>>>>>>>>>>> My understanding is that this discussion is about whether:
>>>>>>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>>>>>>         (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>>>>>>
>>>>>>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>>>>>>> and I missed something?
>>>>>>>>>>
>>>>>>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>>>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>>>>>>> is not required afterwards because trigger is disabled.
>>>>>>>>>>
>>>>>>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>>>>>>
>>>>>>>>>> I analyzed this basing on the current interpretation of
>>>>>>>>>> brightness. I think we need to focus on your following requirement:
>>>>>>>>>>
>>>>>>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>>>>>>
>>>>>>>>>> Could you provide more details?
>>>>>>>>>>
>>>>>>>>> Basically I want color and brightness to be independent.
>>>>>>>>>
>>>>>>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>>>>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>>>>>>
>>>>>>>> It is possible even now - brightness can be changed during blinking, but
>>>>>>>> it is applied from the next transition to "on" state.
>>>>>>>>
>>>>>>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>>>>>>> the color information if color and brightness are stored together.
>>>>>>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>>>>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>>>>>>
>>>>>>>> It would suffice to implement dedicated trigger for this. IMO it would
>>>>>>>> be an over engineering, though. We can support what you want by
>>>>>>>> exposing each color as a separate LED class device and adding a means
>>>>>>>> for LED class device synchronization, I was proposing in the beginning.
>>>>>>>>
>>>>>>>> It would be convenient because it would allow to avoid code duplication
>>>>>>>> in the LED core. I think that gathering all the colors in the single
>>>>>>>> functionality could be a task for the user space.
>>>>>>>>
>>>>>>> By chance I had to deal with HSV color scheme in another project and
>>>>>>> using this color scheme core-internally for color LED's might help us to
>>>>>>> facilitate backwards compatibility and reduce the needed changes to the core.
>>>>>>> It would also allow to easily solve the question we're discussing here.
>>>>>>>
>>>>>>> The brightness is part of the HSV model anyway so we would just have to
>>>>>>> add hue / saturation. Conversion to / from RGB would be done on demand.
>>>>>>> I have a small prototype that needs much less changes to the core logic.
>>>>>>> I'll come up with a re-worked proposal for handling color LED's.
>>>>>>
>>>>>> At first let's consider if LED synchronization doesn't fit your needs.
>>>>>> I am very much in favour of this solution since it would address also
>>>>>> other use cases. On the other hand I am opposed to adding any mechanisms
>>>>>> for color scheme conversion to the LED core since it can be covered by
>>>>>> the user space.
>>>>>>
>>>>> LED synchronization would be an option. So far I've seen it being implemented
>>>>> directly in drivers like hid-thingm.
>>>>> Where I see potential issues with LED synchronization:
>>>>> - It changes the semantics of the entries /sys/class/leds from physical LED's
>>>>>      to logical LED's. The grouping of LED's might be recognizable by the name.
>>>>>      However a sub-structure like /sys/class/leds/<group>/<member> might be
>>>>>      desirable.
>>>>
>>>> Not necessarily. Actually similar functionality was present in the
>>>> mainline for a short time, but was reverted [1], due to the objections
>>>> raised in the discussion [2].
>>>>
>>>> The idea to follow is to have a sysfs attribute which would indicate
>>>> the other LED class device to synchronize with. This way it is possible
>>>> to define a chain of LEDs to synchronize and whole chain could be driven
>>>> with any of chain's elements. On the led-class side we would need only
>>>> two additional sysfs attributes - one for defining the LED class device
>>>> to synchronize with and the other one for listing LED class devices
>>>> available for synchronization.
>>>>
>>>> The rest of synchronization job will be done by the LED class driver,
>>>> basing on the sysfs settings of the LED class devices it exposes per
>>>> sub-LED.
>>>>
>>>>> - Users of the LED core (userspace + kernel triggers) may need more than one
>>>>>      call to set a LED to a requested state.
>>>>
>>>> Not necessarily as stated above.
>>>>
>>> In the case of RGB LED's the brightness can't be synchronized and the
>>> caller needs three calls to set the R/G/B values.
>>
>> Right. But this is not a big problem I think. It is just a part of
>> user space work that needs to be done to configure the device.
>
> Fair enough for userspace.
> When working on the RGB stuff one of my ideas was to support trigger actions
> for color LEDs in the future.
> Either to signal whatever multi-state information or to visualize a value
> in a range (e.g. some temperature monitoring driver might want to map a
> temperature into the color range from green to red).
>
> And a trigger most likely has to set a color with one call.
>
>> In case of V4L2 media controllers we also have to configure connections
>> between platform sub-devices.
>>
>> One thing could be improved - we'd have to add a reservation to the
>> brightness attribute semantics. saying that any transition of
>> brightness attribute value from 0 to non-zero turns the LED on only
>> if it isn't synchronized with any other LED class device, i.e. it is
>> a master LED or a single LED. It would allow to avoid the risk of
>> noticeable delays between activation of a number of synchronized LEDs.
>>
>> Effectively, the whole group would have to be controlled by a single
>> master sub-LED and all group members would have to synchronize with
>> the master.
>>
>>>>> What would need to be synchronized at a first glance:
>>>>> - assigning triggers to a LED
>>>>> - brightness_set/_get
>>>>> - blinking
>>>>
>>>> Any change of a single LED chain element state would propagate to all
>>>> other elements (LED class devices).
>>
>> In view of the above let's forget about this "chain" approach.
>>
>>>>>
>>>>> What "other use cases" are you thinking of?
>>>>
>>>> E.g. synchronous blinking of a number of sub-LEDs.
>>>>
>>> Do you mean that just the blinking frequency is synchronized in case the respective
>>> LED is switched on and that it still should be possible to switch on / off LEDs
>>> individually?
>>
>> This is how I see it:
>> 1. LED1 selects LED4 as a master LED
>> 2. LED1 sets brightness to 10
>> 3. LED2 selects LED4 as a master LED
>> 4. LED2 sets brightness to 150
>> 5. LED3 selects LED4 as a master LED
>> 6. LED3 sets brightness to 255
>> 7. LED4 sets brightness to 200
>>     //which turns LED1, LED2, LED3 and LED4 on,
>>     //each of them with their own brightness
>> 8. LED4 enables timer triger
>>     //LED1, LED2, LED3 and LED4 blink simultaneously
>>     //of course some devices may introduce little delays
>>     //due to the I2C transmission delays, it depends on
>>     //the particular device register design
>> 9. LED2 sets brightness to 100
>>     //LED2 brightness is changed upon next transition
>>     //to "LED on" state
>> 10. LED2 sets brightness to 0
>>      //LED2 is turned off and removed from the group
>> 11. LED4 sets brightness to 0
>>      //LED1, LED3 and LED4 are turned off
>>
> I understand the idea, still it's not clear to me what benefit would justify
> the effort to implement such a feature.
> What kind of practical use case do you imagine (maybe except synchronuous blinking) ?

I've just realized that the approach I proposed is based on wrong
assumptions. It is not guaranteed that the instructions issued from
user space will be executed in the same order by kernel, since they are
different processes.

It seems that we could try your HSV approach. Nonetheless, I'd prefer
to implement it as a wrapper of LED class, similarly to
led-class-flash.c. It could be named led-class-hsv.c.


> For sync blinking I could imagine a less flexible, but maybe simpler solution:
> Set up a timer (independent of a led_classdev) that controls for every led_classdev
> that wants to use this feature the on/off blink transitions.
> After a brief look at ledtrig-timer.c this trigger seems to provide a similar
> (or even equal) functionality already.

Timer trigger uses either hardware blinking feature provided by a device
or software blinking based on blink_timer, where each LED class device
has a separate instance thereof.

> Drawback is that all LED's share the same blink frequency (globally, not only
> per LED group). But that may be acceptable or even desirable.
>
>>
>>> If not than what's the benefit of x LED's doing something synchronously?
>>> The information I get from it is the same as from one LED.
>>>
>>> As the original synchronization idea was intended for the flash functionality,
>>> I assume it could primarily make sense there.
>>> (But I have to admit that I have no real idea of the flash functionality.)
>>>
>>>>>> Drivers should expose device capabilities and not impose the policy on
>>>>>> how those capabilities can be used. If with slight modifications we
>>>>>> could stick to this rule, we should go in this direction.
>>>>>>
>>>>> With regard to my HSV proposal I agree that having HSV/RGB conversion in the
>>>>> kernel is not necessarily desirable. HSV just fits very well into the current
>>>>> implementation of the LED core.
>>>>> Color LED's might have different native color schemes (although so far I know
>>>>> no color LED using another than RGB). If the LED core wants to provide a
>>>>> unified interface to its users some transformation between core color scheme
>>>>> and native color scheme (as exposed by the driver) might be needed eventually?
>>>>
>>>> IMO the synchronization mechanism design I described above is much
>>>> neater. The design of sysfs synchronization API seems to be the only
>>>> vital problem here. To be more precise the problem is: how to represent,
>>>> in an unambiguous and easy to parse way, the LED class devices available
>>>> for synchronization. The list of space separated LED class device
>>>> names is not an option since we cannot guarantee names without spaces.
>>>> The design requiring more complicated parsing was refused [2].
>>>>
>>>> As a last resort we'd probably need to consult linux-api guys.
>>>>
>>>>
>>>> [1] https://lkml.org/lkml/2015/3/4/952
>>>> [2] http://www.spinics.net/lists/linux-leds/msg02995.html
>>>>
>>> Thanks for the links. Let me try to summarize some requirements + some inquiries:
>>> - By synchronizing LEDs all main properties are synchronized
>>>     - Should it be configurable which properties should be synchronized?
>>>       E.g. in case of RGB LED's the brightness should not be synchronized, however
>>>       there might be use cases where synchronizing brightness makes sense.
>>
>> I think that synchronization should apply to simultaneous
>> activation/deactivation and triggers.
>>
>>> - Should defining and changing groups of synchronized LEDs be possible dynamically?
>>>     Based on the history of this synchronization proposal I guess so.
>>>     Alternatively it could be possible only when registering the led_classdev's,
>>>     e.g. by passing a reference to a "master LED" which acts as group id.
>>>     (But this might restrict the mechanism to LED's created by the same driver instance.)
>>>     Or both methods ..
>>
>> Reliable synchronization is possible only for the sub-LEDs of the same
>> device. All LED class devices exposed by an instance of a driver should
>> register with the predefined scope of the LED class devices available
>> for synchronization, the devices exposed by this driver.
>>
>
>
>


-- 
Best Regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] led: core: RfC - add RGB LED handling to the core
  2016-02-04  8:30                                         ` Jacek Anaszewski
@ 2016-02-07  0:18                                           ` Heiner Kallweit
  0 siblings, 0 replies; 27+ messages in thread
From: Heiner Kallweit @ 2016-02-07  0:18 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Jacek Anaszewski, linux-leds

Am 04.02.2016 um 09:30 schrieb Jacek Anaszewski:
> On 02/03/2016 11:08 PM, Heiner Kallweit wrote:
>> Am 03.02.2016 um 09:28 schrieb Jacek Anaszewski:
>>> On 02/03/2016 07:42 AM, Heiner Kallweit wrote:
>>>> Am 02.02.2016 um 09:58 schrieb Jacek Anaszewski:
>>>>> On 02/01/2016 10:41 PM, Heiner Kallweit wrote:
>>>>>> Am 01.02.2016 um 09:26 schrieb Jacek Anaszewski:
>>>>>>> On 02/01/2016 07:43 AM, Heiner Kallweit wrote:
>>>>>>>> Am 29.01.2016 um 09:24 schrieb Jacek Anaszewski:
>>>>>>>>> On 01/29/2016 08:00 AM, Heiner Kallweit wrote:
>>>>>>>>>> Am 27.01.2016 um 09:27 schrieb Jacek Anaszewski:
>>>>>>>>>>> On 01/26/2016 09:08 PM, Heiner Kallweit wrote:
>>>>>>>>>>>> Am 26.01.2016 um 10:37 schrieb Jacek Anaszewski:
>>>>>>>>>>>>> On 01/25/2016 08:09 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>> Am 25.01.2016 um 11:52 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>> On 01/25/2016 10:51 AM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>> On Mon, Jan 25, 2016 at 9:41 AM, Jacek Anaszewski
>>>>>>>>>>>>>>>> <j.anaszewski@samsung.com> wrote:
>>>>>>>>>>>>>>>>> Hi Heiner,
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> On 01/24/2016 02:38 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Am 17.01.2016 um 23:31 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> On 01/15/2016 09:16 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Am 14.01.2016 um 13:08 schrieb Jacek Anaszewski:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> On 01/10/2016 09:27 PM, Heiner Kallweit wrote:
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> When playing with a ThingM Blink(1) USB RGB LED device I found that
>>>>>>>>>>>>>>>>>>>>>> there
>>>>>>>>>>>>>>>>>>>>>> are few drivers for RGB LED's spread across the kernel and each one
>>>>>>>>>>>>>>>>>>>>>> implements the RGB functionality in its own way.
>>>>>>>>>>>>>>>>>>>>>> Some examples:
>>>>>>>>>>>>>>>>>>>>>> - drivers/hid/hid-thingm.c
>>>>>>>>>>>>>>>>>>>>>> - drivers/usb/misc/usbled.c
>>>>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-bd2802.c
>>>>>>>>>>>>>>>>>>>>>> - drivers/leds/leds-blinkm.c
>>>>>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>>>>>> IMHO it would make sense to add generic RGB functionality to the LED
>>>>>>>>>>>>>>>>>>>>>> core.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Things I didn't like too much in other driver implementations:
>>>>>>>>>>>>>>>>>>>>>> - one led_classdev per color or at least
>>>>>>>>>>>>>>>>>>>>>> - one sysfs attribute per color
>>>>>>>>>>>>>>>>>>>>>> Colors are not really independent therefore I'd prefer one
>>>>>>>>>>>>>>>>>>>>>> led_classdev
>>>>>>>>>>>>>>>>>>>>>> per RGB LED. Also userspace should be able to change the color with
>>>>>>>>>>>>>>>>>>>>>> one
>>>>>>>>>>>>>>>>>>>>>> call -> therefore only one sysfs attribute for the RGB values.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Also the current brightness-related functionality should not be
>>>>>>>>>>>>>>>>>>>>>> effected
>>>>>>>>>>>>>>>>>>>>>> by the RGB extension.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> This patch is intended to demonstrate the idea of the extension. Most
>>>>>>>>>>>>>>>>>>>>>> likely
>>>>>>>>>>>>>>>>>>>>>> it's not ready yet for submission.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Main idea is to base the effective RGB value on a combination of
>>>>>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>>>>>> and a scaled-to-max RGB value.
>>>>>>>>>>>>>>>>>>>>>> The RGB-related callbacks are basically the same as for brightness.
>>>>>>>>>>>>>>>>>>>>>> RGB functionally can be switched on with a new flag in the
>>>>>>>>>>>>>>>>>>>>>> led_classdev.flags
>>>>>>>>>>>>>>>>>>>>>> bitmap.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Experimentally I changed the thingm driver to use this extension and
>>>>>>>>>>>>>>>>>>>>>> it works
>>>>>>>>>>>>>>>>>>>>>> quite well. As one result the driver could be very much simplified.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Any feedback is appreciated.
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>>>>>>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>>>>>>             drivers/leds/led-class.c |  62 +++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>             drivers/leds/led-core.c  | 106
>>>>>>>>>>>>>>>>>>>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>>>>>>>>>>>>>>             drivers/leds/leds.h      |  12 ++++++
>>>>>>>>>>>>>>>>>>>>>>             include/linux/leds.h     |  13 ++++++
>>>>>>>>>>>>>>>>>>>>>>             4 files changed, 193 insertions(+)
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> [...]
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> +static void led_set_rgb_raw(struct led_classdev *cdev, u32 rgb)
>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>> +    u32 red_raw = (rgb >> 16) & 0xff;
>>>>>>>>>>>>>>>>>>>>>> +    u32 green_raw = (rgb >> 8) & 0xff;
>>>>>>>>>>>>>>>>>>>>>> +    u32 blue_raw = rgb & 0xff;
>>>>>>>>>>>>>>>>>>>>>> +    u32 max_raw, red, green, blue;
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    max_raw = max(red_raw, green_raw);
>>>>>>>>>>>>>>>>>>>>>> +    if (blue_raw > max_raw)
>>>>>>>>>>>>>>>>>>>>>> +        max_raw = blue_raw;
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    if (!max_raw) {
>>>>>>>>>>>>>>>>>>>>>> +        cdev->brightness = 0;
>>>>>>>>>>>>>>>>>>>>>> +        cdev->rgb_val = 0;
>>>>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    red = DIV_ROUND_CLOSEST(red_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>>>> +    green = DIV_ROUND_CLOSEST(green_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>>>> +    blue = DIV_ROUND_CLOSEST(blue_raw * LED_FULL, max_raw);
>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>> +    cdev->brightness = max_raw;
>>>>>>>>>>>>>>>>>>>>>> +    cdev->rgb_val = (red << 16) + (green << 8) + blue;
>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> I think that we shouldn't impose specific way of calculating brightness
>>>>>>>>>>>>>>>>>>>>> depending on the rgb value set. We should just pass value from
>>>>>>>>>>>>>>>>>>>>> userspace
>>>>>>>>>>>>>>>>>>>>> to the driver.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Right, setting brightness from userspace is no problem.
>>>>>>>>>>>>>>>>>>>> But how about led_update_brightness? It's supposed to update the
>>>>>>>>>>>>>>>>>>>> brightness
>>>>>>>>>>>>>>>>>>>> value in led_cdev with the actual value. And for a RGB LED we have only
>>>>>>>>>>>>>>>>>>>> the RGB values, so we need to calculate the brightness based on RGB
>>>>>>>>>>>>>>>>>>>> values.
>>>>>>>>>>>>>>>>>>>> Or do you see a better way?
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> I thought that you had some device using both brightness and rgb
>>>>>>>>>>>>>>>>>>> components settings (no idea if this could be sane in any way).
>>>>>>>>>>>>>>>>>>> If there are only three color components, then why not just redefine
>>>>>>>>>>>>>>>>>>> brightness to store three components in case of the LEDs with
>>>>>>>>>>>>>>>>>>> LED_DEV_CAP_RGB flags set?
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> My devices just have the three color components (normal 8 bits per color).
>>>>>>>>>>>>>>>>>> Even in the new RGB mode I don't want to break the current
>>>>>>>>>>>>>>>>>> brightness-based API
>>>>>>>>>>>>>>>>>> but extend it with RGB functionality. Therefore I think it's best to store
>>>>>>>>>>>>>>>>>> brightness and color separately.
>>>>>>>>>>>>>>>>>> Just one argument: If we store the color components only then we'll
>>>>>>>>>>>>>>>>>> lose the color information if the brightness is set to 0.
>>>>>>>>>>>>>>>>>> And I would like to support e.g. software blink in whatever color.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Currently blinking works properly and brightness value isn't being lost.
>>>>>>>>>>>>>>>>> We store it in the led_cdev->blink_brightness property. Could you
>>>>>>>>>>>>>>>>> present exact use case you'd like to support and which is not feasible
>>>>>>>>>>>>>>>>> with current LED core design?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I didn't have a closer look on how soft blinking works and you're right,
>>>>>>>>>>>>>>>> this was a bad example.
>>>>>>>>>>>>>>>> It's not about that something is wrong with the current LED core design
>>>>>>>>>>>>>>>> but that IMHO storing just the raw RGB values wouldn't be a good idea
>>>>>>>>>>>>>>>> and we should store brightness and color separately.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> So let's take another example:
>>>>>>>>>>>>>>>> I set a color via sysfs and a trigger is controling the LED. Once the LED
>>>>>>>>>>>>>>>> brightness is set to LED_OFF by the trigger and e.g. a sysfs read triggers
>>>>>>>>>>>>>>>> an update the stored RGB value is 0,0,0.
>>>>>>>>>>>>>>>> If the LED is switched on again, then which color to choose?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> The one stored in blink_brightness. I don't get how changing the
>>>>>>>>>>>>>>> interpretation of brightness value can affect anything here.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>> But blink_brightness is used in case of soft blink only.
>>>>>>>>>>>>>
>>>>>>>>>>>>> In case of hardware blinking it is driver's responsibility to report
>>>>>>>>>>>>> current brightness while blinking is on. It is hardware specific whether
>>>>>>>>>>>>> it allows to read current LED state during blinking. Nevertheless
>>>>>>>>>>>>> I don't see where is the problem here either.
>>>>>>>>>>>>>
>>>>>>>>>>>>>> If a trigger or userspace sets the brightness to zero then blink_brightness
>>>>>>>>>>>>>> isn't used.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Setting brightness to 0 disables trigger. Excerpt from leds-class.txt:
>>>>>>>>>>>>>
>>>>>>>>>>>>> "However, if you set the brightness value to LED_OFF it will
>>>>>>>>>>>>> also disable the timer trigger."
>>>>>>>>>>>>>
>>>>>>>>>>>>> Now I see that documentation is inexact here. The paragraph says
>>>>>>>>>>>>> about triggers, using timer trigger as an example. This trigger
>>>>>>>>>>>>> is specific because of the software fallback implemented in the core.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Setting brightness to LED_OFF disables any trigger only when set from
>>>>>>>>>>>>> sysfs (in drivers/leds/led-class.c brightness_store calls
>>>>>>>>>>>>> led_trigger_remove if passed brightness is LED_OFF).
>>>>>>>>>>>>>
>>>>>>>>>>>>> However, when setting brightness to LED_OFF directly through LED API,
>>>>>>>>>>>>> and not through sysfs, only blink_timer is being disabled, but trigger
>>>>>>>>>>>>> isn't being removed. Probably we'd have to remove trigger
>>>>>>>>>>>>> from set_brightness_delayed(), in case LED_BLINK_DISABLE is set,
>>>>>>>>>>>>> to make implementation in line with the documentation.
>>>>>>>>>>>>>
>>>>>>>>>>>> Sorry, I'm afraid we lost track of the original subject.
>>>>>>>>>>>> At least I'm a little lost ..
>>>>>>>>>>>> My understanding is that this discussion is about whether:
>>>>>>>>>>>> - use current brightness member of led_classdev to store brightness and color
>>>>>>>>>>>>         (change semantics of brightness to hold a <00000000><RRRRRRRR><GGGGGGGG><BBBBBBBB> value) or
>>>>>>>>>>>> - store brightness and color separately (introduce a new member for the color)
>>>>>>>>>>>>
>>>>>>>>>>>> Is this also your understanding? Or are you focussing on a different aspect
>>>>>>>>>>>> and I missed something?
>>>>>>>>>>>
>>>>>>>>>>> Yes it is. I intended to explain that setting brightness to 0 is
>>>>>>>>>>> supposed to turn the trigger off. In effect the brightness information
>>>>>>>>>>> is not required afterwards because trigger is disabled.
>>>>>>>>>>>
>>>>>>>>>>> During the analysis I realized about little discrepancy between the documentation and the implementation and proposed potential fix.
>>>>>>>>>>>
>>>>>>>>>>> I analyzed this basing on the current interpretation of
>>>>>>>>>>> brightness. I think we need to focus on your following requirement:
>>>>>>>>>>>
>>>>>>>>>>> "And I would like to support e.g. software blink in whatever color"
>>>>>>>>>>>
>>>>>>>>>>> Could you provide more details?
>>>>>>>>>>>
>>>>>>>>>> Basically I want color and brightness to be independent.
>>>>>>>>>>
>>>>>>>>>> One example: If a trigger is switching a LED on and off, then I want to be able to change the color
>>>>>>>>>> from userspace (even if the LED is off in that moment) w/o affecting the trigger operation.
>>>>>>>>>
>>>>>>>>> It is possible even now - brightness can be changed during blinking, but
>>>>>>>>> it is applied from the next transition to "on" state.
>>>>>>>>>
>>>>>>>>>> Or another one: If the brightness is changed from 255 to 1 and back to 255 then we'd partially lose
>>>>>>>>>> the color information if color and brightness are stored together.
>>>>>>>>>> Partially because we'd not lose it if e.g. pure blue is set.
>>>>>>>>>> But if the RGB value was let's say (255, 240, 17) before then we'd not be able to restore this value.
>>>>>>>>>
>>>>>>>>> It would suffice to implement dedicated trigger for this. IMO it would
>>>>>>>>> be an over engineering, though. We can support what you want by
>>>>>>>>> exposing each color as a separate LED class device and adding a means
>>>>>>>>> for LED class device synchronization, I was proposing in the beginning.
>>>>>>>>>
>>>>>>>>> It would be convenient because it would allow to avoid code duplication
>>>>>>>>> in the LED core. I think that gathering all the colors in the single
>>>>>>>>> functionality could be a task for the user space.
>>>>>>>>>
>>>>>>>> By chance I had to deal with HSV color scheme in another project and
>>>>>>>> using this color scheme core-internally for color LED's might help us to
>>>>>>>> facilitate backwards compatibility and reduce the needed changes to the core.
>>>>>>>> It would also allow to easily solve the question we're discussing here.
>>>>>>>>
>>>>>>>> The brightness is part of the HSV model anyway so we would just have to
>>>>>>>> add hue / saturation. Conversion to / from RGB would be done on demand.
>>>>>>>> I have a small prototype that needs much less changes to the core logic.
>>>>>>>> I'll come up with a re-worked proposal for handling color LED's.
>>>>>>>
>>>>>>> At first let's consider if LED synchronization doesn't fit your needs.
>>>>>>> I am very much in favour of this solution since it would address also
>>>>>>> other use cases. On the other hand I am opposed to adding any mechanisms
>>>>>>> for color scheme conversion to the LED core since it can be covered by
>>>>>>> the user space.
>>>>>>>
>>>>>> LED synchronization would be an option. So far I've seen it being implemented
>>>>>> directly in drivers like hid-thingm.
>>>>>> Where I see potential issues with LED synchronization:
>>>>>> - It changes the semantics of the entries /sys/class/leds from physical LED's
>>>>>>      to logical LED's. The grouping of LED's might be recognizable by the name.
>>>>>>      However a sub-structure like /sys/class/leds/<group>/<member> might be
>>>>>>      desirable.
>>>>>
>>>>> Not necessarily. Actually similar functionality was present in the
>>>>> mainline for a short time, but was reverted [1], due to the objections
>>>>> raised in the discussion [2].
>>>>>
>>>>> The idea to follow is to have a sysfs attribute which would indicate
>>>>> the other LED class device to synchronize with. This way it is possible
>>>>> to define a chain of LEDs to synchronize and whole chain could be driven
>>>>> with any of chain's elements. On the led-class side we would need only
>>>>> two additional sysfs attributes - one for defining the LED class device
>>>>> to synchronize with and the other one for listing LED class devices
>>>>> available for synchronization.
>>>>>
>>>>> The rest of synchronization job will be done by the LED class driver,
>>>>> basing on the sysfs settings of the LED class devices it exposes per
>>>>> sub-LED.
>>>>>
>>>>>> - Users of the LED core (userspace + kernel triggers) may need more than one
>>>>>>      call to set a LED to a requested state.
>>>>>
>>>>> Not necessarily as stated above.
>>>>>
>>>> In the case of RGB LED's the brightness can't be synchronized and the
>>>> caller needs three calls to set the R/G/B values.
>>>
>>> Right. But this is not a big problem I think. It is just a part of
>>> user space work that needs to be done to configure the device.
>>
>> Fair enough for userspace.
>> When working on the RGB stuff one of my ideas was to support trigger actions
>> for color LEDs in the future.
>> Either to signal whatever multi-state information or to visualize a value
>> in a range (e.g. some temperature monitoring driver might want to map a
>> temperature into the color range from green to red).
>>
>> And a trigger most likely has to set a color with one call.
>>
>>> In case of V4L2 media controllers we also have to configure connections
>>> between platform sub-devices.
>>>
>>> One thing could be improved - we'd have to add a reservation to the
>>> brightness attribute semantics. saying that any transition of
>>> brightness attribute value from 0 to non-zero turns the LED on only
>>> if it isn't synchronized with any other LED class device, i.e. it is
>>> a master LED or a single LED. It would allow to avoid the risk of
>>> noticeable delays between activation of a number of synchronized LEDs.
>>>
>>> Effectively, the whole group would have to be controlled by a single
>>> master sub-LED and all group members would have to synchronize with
>>> the master.
>>>
>>>>>> What would need to be synchronized at a first glance:
>>>>>> - assigning triggers to a LED
>>>>>> - brightness_set/_get
>>>>>> - blinking
>>>>>
>>>>> Any change of a single LED chain element state would propagate to all
>>>>> other elements (LED class devices).
>>>
>>> In view of the above let's forget about this "chain" approach.
>>>
>>>>>>
>>>>>> What "other use cases" are you thinking of?
>>>>>
>>>>> E.g. synchronous blinking of a number of sub-LEDs.
>>>>>
>>>> Do you mean that just the blinking frequency is synchronized in case the respective
>>>> LED is switched on and that it still should be possible to switch on / off LEDs
>>>> individually?
>>>
>>> This is how I see it:
>>> 1. LED1 selects LED4 as a master LED
>>> 2. LED1 sets brightness to 10
>>> 3. LED2 selects LED4 as a master LED
>>> 4. LED2 sets brightness to 150
>>> 5. LED3 selects LED4 as a master LED
>>> 6. LED3 sets brightness to 255
>>> 7. LED4 sets brightness to 200
>>>     //which turns LED1, LED2, LED3 and LED4 on,
>>>     //each of them with their own brightness
>>> 8. LED4 enables timer triger
>>>     //LED1, LED2, LED3 and LED4 blink simultaneously
>>>     //of course some devices may introduce little delays
>>>     //due to the I2C transmission delays, it depends on
>>>     //the particular device register design
>>> 9. LED2 sets brightness to 100
>>>     //LED2 brightness is changed upon next transition
>>>     //to "LED on" state
>>> 10. LED2 sets brightness to 0
>>>      //LED2 is turned off and removed from the group
>>> 11. LED4 sets brightness to 0
>>>      //LED1, LED3 and LED4 are turned off
>>>
>> I understand the idea, still it's not clear to me what benefit would justify
>> the effort to implement such a feature.
>> What kind of practical use case do you imagine (maybe except synchronuous blinking) ?
> 
> I've just realized that the approach I proposed is based on wrong
> assumptions. It is not guaranteed that the instructions issued from
> user space will be executed in the same order by kernel, since they are
> different processes.
> 
> It seems that we could try your HSV approach. Nonetheless, I'd prefer
> to implement it as a wrapper of LED class, similarly to
> led-class-flash.c. It could be named led-class-hsv.c.
> 
I'll submit a proposal for the HSV extension. I thought about making it
a wrapper but that doesn't really fit. Instead of adding code to the existing
one I have to touch few parts in the existing core.
Appreciate your comments onec I send the HSV proposal.

Upfront I'll send a patch for adding helpers for brightness_set(_blocking).
This makes code more readable anyway and allows to keep the HSV extension simpler.

> 
>> For sync blinking I could imagine a less flexible, but maybe simpler solution:
>> Set up a timer (independent of a led_classdev) that controls for every led_classdev
>> that wants to use this feature the on/off blink transitions.
>> After a brief look at ledtrig-timer.c this trigger seems to provide a similar
>> (or even equal) functionality already.
> 
> Timer trigger uses either hardware blinking feature provided by a device
> or software blinking based on blink_timer, where each LED class device
> has a separate instance thereof.
> 
I see, thanks for the explanation.

>> Drawback is that all LED's share the same blink frequency (globally, not only
>> per LED group). But that may be acceptable or even desirable.
>>
>>>
>>>> If not than what's the benefit of x LED's doing something synchronously?
>>>> The information I get from it is the same as from one LED.
>>>>
>>>> As the original synchronization idea was intended for the flash functionality,
>>>> I assume it could primarily make sense there.
>>>> (But I have to admit that I have no real idea of the flash functionality.)
>>>>
>>>>>>> Drivers should expose device capabilities and not impose the policy on
>>>>>>> how those capabilities can be used. If with slight modifications we
>>>>>>> could stick to this rule, we should go in this direction.
>>>>>>>
>>>>>> With regard to my HSV proposal I agree that having HSV/RGB conversion in the
>>>>>> kernel is not necessarily desirable. HSV just fits very well into the current
>>>>>> implementation of the LED core.
>>>>>> Color LED's might have different native color schemes (although so far I know
>>>>>> no color LED using another than RGB). If the LED core wants to provide a
>>>>>> unified interface to its users some transformation between core color scheme
>>>>>> and native color scheme (as exposed by the driver) might be needed eventually?
>>>>>
>>>>> IMO the synchronization mechanism design I described above is much
>>>>> neater. The design of sysfs synchronization API seems to be the only
>>>>> vital problem here. To be more precise the problem is: how to represent,
>>>>> in an unambiguous and easy to parse way, the LED class devices available
>>>>> for synchronization. The list of space separated LED class device
>>>>> names is not an option since we cannot guarantee names without spaces.
>>>>> The design requiring more complicated parsing was refused [2].
>>>>>
>>>>> As a last resort we'd probably need to consult linux-api guys.
>>>>>
>>>>>
>>>>> [1] https://lkml.org/lkml/2015/3/4/952
>>>>> [2] http://www.spinics.net/lists/linux-leds/msg02995.html
>>>>>
>>>> Thanks for the links. Let me try to summarize some requirements + some inquiries:
>>>> - By synchronizing LEDs all main properties are synchronized
>>>>     - Should it be configurable which properties should be synchronized?
>>>>       E.g. in case of RGB LED's the brightness should not be synchronized, however
>>>>       there might be use cases where synchronizing brightness makes sense.
>>>
>>> I think that synchronization should apply to simultaneous
>>> activation/deactivation and triggers.
>>>
>>>> - Should defining and changing groups of synchronized LEDs be possible dynamically?
>>>>     Based on the history of this synchronization proposal I guess so.
>>>>     Alternatively it could be possible only when registering the led_classdev's,
>>>>     e.g. by passing a reference to a "master LED" which acts as group id.
>>>>     (But this might restrict the mechanism to LED's created by the same driver instance.)
>>>>     Or both methods ..
>>>
>>> Reliable synchronization is possible only for the sub-LEDs of the same
>>> device. All LED class devices exposed by an instance of a driver should
>>> register with the predefined scope of the LED class devices available
>>> for synchronization, the devices exposed by this driver.
>>>
>>
>>
>>
> 
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2016-02-07  0:20 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-10 20:27 [PATCH] led: core: RfC - add RGB LED handling to the core Heiner Kallweit
2016-01-13  9:42 ` Jacek Anaszewski
2016-01-13 19:54   ` Heiner Kallweit
2016-01-14 11:14     ` Jacek Anaszewski
2016-01-14 12:05       ` Jacek Anaszewski
2016-01-14 12:08 ` Jacek Anaszewski
2016-01-15 20:16   ` Heiner Kallweit
2016-01-17 22:31     ` Jacek Anaszewski
2016-01-24 13:38       ` Heiner Kallweit
2016-01-25  8:41         ` Jacek Anaszewski
2016-01-25  9:51           ` Heiner Kallweit
2016-01-25 10:52             ` Jacek Anaszewski
2016-01-25 19:09               ` Heiner Kallweit
2016-01-26  9:37                 ` Jacek Anaszewski
2016-01-26 20:08                   ` Heiner Kallweit
2016-01-27  8:27                     ` Jacek Anaszewski
2016-01-29  7:00                       ` Heiner Kallweit
2016-01-29  8:24                         ` Jacek Anaszewski
2016-02-01  6:43                           ` Heiner Kallweit
2016-02-01  8:26                             ` Jacek Anaszewski
2016-02-01 21:41                               ` Heiner Kallweit
2016-02-02  8:58                                 ` Jacek Anaszewski
2016-02-03  6:42                                   ` Heiner Kallweit
2016-02-03  8:28                                     ` Jacek Anaszewski
2016-02-03 22:08                                       ` Heiner Kallweit
2016-02-04  8:30                                         ` Jacek Anaszewski
2016-02-07  0:18                                           ` Heiner Kallweit

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.