Skip to contentSkip to conversation

RGB support prototype

DISCUSSION Pavel Machek 2 participants 8 messages Aug 12, 2017 – Sep 2, 2017 LED

lore
Share & tools
lore
#0 lore raw

Hi!

You mentioned you was working on RGB support prototype. Could you post copy of the patches (even if unfinished)?

Thanks,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#1 lore raw

Hi,

Quoted 5 lines · Pavel Machek

Hi!

You mentioned you was working on RGB support prototype. Could you post copy of the patches (even if unfinished)?

Unfortunately it is at the stage of unfinished proof of concept and I haven't managed yet to try how it fits to all API use cases we have. Nor is it in a shape ready to post to the lists.

I think we could try to discuss the design here. I'll list all the issues I encountered during the implementation:

Currently we set LED brightness with following API:

void led_set_brightness(struct led_classdev *led_cdev,

                        enum led_brightness brightness);

In case of RGB LED we could have something like this:

struct led_color_triplet {
        enum led_brightness red;
        enum led_brightness green;
        enum led_brightness blue;
};

void led_rgb_set_brightness(struct led_rgb_classdev *led_rgb_cdev,

                        struct led_color_triplet *color);

We've agreed that LED RGB class device color could be set by writing space separated list of "r g b" color values to a sysfs "color" file.

While the above itself shouldn't raise too many doubts, they arise quickly while trying to adapt it to the internal LED core facilities:

- led_base_timer_function()
- set_brightness_delayed()
- led_blink_* API family

All these introduce problems especially with brightness/color type. I tried to add a new abstraction layer by introducing
struct led_base_cdev with a set of generic ops that could be initialized by particular type of LED but still the problem with brightness type generalization remains. One solution could be an union with fields mapping to either single or three brightness components.

Other option could be void *brightness type which could be then cast to the required brightness type basing on the LED flag.

Generally it seems that all existing LED class API should get its generic counterparts.

Other ideas are welcome.

Signature · 2 lines
Best regards,
Jacek Anaszewski
#2 lore raw

Hi!

Quoted 44 lines
Quoted 2 lines

You mentioned you was working on RGB support prototype. Could you post copy of the patches (even if unfinished)?

Unfortunately it is at the stage of unfinished proof of concept and I haven't managed yet to try how it fits to all API use cases we have. Nor is it in a shape ready to post to the lists.

I think we could try to discuss the design here. I'll list all the issues I encountered during the implementation:

Currently we set LED brightness with following API:

void led_set_brightness(struct led_classdev *led_cdev,

                        enum led_brightness brightness);

In case of RGB LED we could have something like this:

struct led_color_triplet {
        enum led_brightness red;
        enum led_brightness green;
        enum led_brightness blue;
};

void led_rgb_set_brightness(struct led_rgb_classdev *led_rgb_cdev,

                        struct led_color_triplet *color);

We've agreed that LED RGB class device color could be set by writing space separated list of "r g b" color values to a sysfs "color" file.

While the above itself shouldn't raise too many doubts, they arise quickly while trying to adapt it to the internal LED core facilities:

- led_base_timer_function()
- set_brightness_delayed()
- led_blink_* API family

All these introduce problems especially with brightness/color type. I tried to add a new abstraction layer by introducing
struct led_base_cdev with a set of generic ops that could be initialized by particular type of LED but still the problem with brightness type generalization remains. One solution could be an union with fields mapping to either single or three brightness components.

Quoted 2 lines

Other option could be void *brightness type which could be then cast to the required brightness type basing on the LED flag.

Void *brightness is not really a good option.

We could pass triplet even in case of single-color LEDs, and then use just one component.

Another option would be to store color in HSV colorspace (not RGB). Then existing functions would get brightness (== value in HSV), and RGB-aware functions would operate on all 3 components. Triggers/blinking/etc. would then continue operating, without modifications.

We could even export (read-only) hue/saturation for single-color LEDs...

Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#3 lore raw

Hi,

Quoted 54 lines · Pavel Machek

Hi!

Quoted 44 lines
Quoted 2 lines

You mentioned you was working on RGB support prototype. Could you post copy of the patches (even if unfinished)?

Unfortunately it is at the stage of unfinished proof of concept and I haven't managed yet to try how it fits to all API use cases we have. Nor is it in a shape ready to post to the lists.

I think we could try to discuss the design here. I'll list all the issues I encountered during the implementation:

Currently we set LED brightness with following API:

void led_set_brightness(struct led_classdev *led_cdev,

                        enum led_brightness brightness);

In case of RGB LED we could have something like this:

struct led_color_triplet {
        enum led_brightness red;
        enum led_brightness green;
        enum led_brightness blue;
};

void led_rgb_set_brightness(struct led_rgb_classdev *led_rgb_cdev,

                        struct led_color_triplet *color);

We've agreed that LED RGB class device color could be set by writing space separated list of "r g b" color values to a sysfs "color" file.

While the above itself shouldn't raise too many doubts, they arise quickly while trying to adapt it to the internal LED core facilities:

- led_base_timer_function()
- set_brightness_delayed()
- led_blink_* API family

All these introduce problems especially with brightness/color type. I tried to add a new abstraction layer by introducing
struct led_base_cdev with a set of generic ops that could be initialized by particular type of LED but still the problem with brightness type generalization remains. One solution could be an union with fields mapping to either single or three brightness components.

Quoted 2 lines

Other option could be void *brightness type which could be then cast to the required brightness type basing on the LED flag.

Void *brightness is not really a good option.

We could pass triplet even in case of single-color LEDs, and then use just one component.

Quoted 6 lines

Another option would be to store color in HSV colorspace (not RGB). Then existing functions would get brightness (== value in HSV), and RGB-aware functions would operate on all 3 components. Triggers/blinking/etc. would then continue operating, without modifications.

We could even export (read-only) hue/saturation for single-color LEDs...

Related patches from Heiner Kallweit are still sitting on devel branch of linux-leds.git:

https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git/log/?h=devel

Possibly it can serve as a basis for further development.

I liked that approach because it was compatible with monochrome LEDs and triggers.

Signature · 2 lines
Best regards,
Jacek Anaszewski
#4 lore raw

Hi!

Quoted 11 lines
Quoted 1 line

We could even export (read-only) hue/saturation for single-color LEDs...

Related patches from Heiner Kallweit are still sitting on devel branch of linux-leds.git:

https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git/log/?h=devel

Possibly it can serve as a basis for further development.

I liked that approach because it was compatible with monochrome LEDs and triggers.

Yeah, I like that. I don't like the implementation and the sysfs interface.

Let me try and see if I can turn them into something better...

I'm using full 32-bits for parameters; that should give us enough precision. In particular, 32-bits is important for "brightness"; I already have light that goes from .1lm to 1000lm -- which is more than 8-bits can handle.

lp5523 conversion... is a very quick hack.

Pavel

drivers/leds/Kconfig +12 -0
index 594b24d..bad8a6e 100644
@@ -19,6 +19,14 @@ config LEDS_CLASS
 	  This option enables the led sysfs class in /sys/class/leds.  You'll
 	  need this to do anything useful with LEDs.  If unsure, say N.
 
+config LEDS_CLASS_RGB
+	bool "LED RGB Class Support"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for RGB LED devices.
+	  Sysfs attribute brightness is interpreted as a HSV color value.
+	  For details see Documentation/leds/leds-class.txt.
+
 config LEDS_CLASS_FLASH
 	tristate "LED Flash Class Support"
 	depends on LEDS_CLASS
@@ -38,6 +46,10 @@ config LEDS_BRIGHTNESS_HW_CHANGED
 
 	  See Documentation/ABI/testing/sysfs-class-led for details.
 
+if LEDS_CLASS_RGB
+comment "RGB LED drivers"
+endif # LEDS_CLASS_RGB
+
 comment "LED drivers"
 
 config LEDS_88PM860X
drivers/leds/Makefile +3 -1
index 909dae6..d5b5e76 100644
@@ -1,6 +1,8 @@
 
 # LED Core
-obj-$(CONFIG_NEW_LEDS)			+= led-core.o
+obj-$(CONFIG_NEW_LEDS)			+= led-core-objs.o
+led-core-objs-y				:= led-core.o
+led-core-objs-$(CONFIG_LEDS_CLASS_RGB)	+= led-rgb-core.o
 obj-$(CONFIG_LEDS_CLASS)		+= led-class.o
 obj-$(CONFIG_LEDS_CLASS_FLASH)		+= led-class-flash.o
 obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
drivers/leds/led-class.c +79 -0

91 lines not on this page · raw message

drivers/leds/led-rgb-core.c new file +60 -0

60 lines not on this page · raw message

drivers/leds/leds-lp5523.c +20 -0
index 924e50a..861f4df2 100644
@@ -802,6 +802,26 @@ static ssize_t store_master_fader_leds(struct device *dev,
 	return ret;
 }
 
+int lp5523_rgb_brightness(struct lp55xx_led *led, struct led_rgb r)
+{
+	struct lp55xx_chip *chip = led->chip;
+	int ret;
+
+	mutex_lock(&chip->lock);
+	r.red >>= 24;
+	r.green >>= 24;
+	r.blue >>= 24;	
+	printk("RGB brightness %d %d %d\n", r.red, r.green, r.blue);
+	ret = lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, r.red);
+	if (!ret)
+		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr - 1, r.green);
+	if (!ret)
+		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr - 1, r.blue);
+	mutex_unlock(&chip->lock);
+	return ret;
+
+}
+
 static int lp5523_led_brightness(struct lp55xx_led *led)
 {
 	struct lp55xx_chip *chip = led->chip;
drivers/leds/leds-lp55xx-common.c +16 -0
index 5377f22..16e65dc 100644
@@ -134,6 +134,8 @@ static struct attribute *lp55xx_led_attrs[] = {
 };
 ATTRIBUTE_GROUPS(lp55xx_led);
 
+extern int lp5523_rgb_brightness(struct lp55xx_led *led, struct led_rgb r);
+
 static int lp55xx_set_brightness(struct led_classdev *cdev,
 			     enum led_brightness brightness)
 {
@@ -141,6 +143,18 @@ static int lp55xx_set_brightness(struct led_classdev *cdev,
 	struct lp55xx_device_config *cfg = led->chip->cfg;
 
 	led->brightness = (u8)brightness;
+
+	if (led->chan_nr == 6) {
+		struct led_rgb r;
+		struct led_hsv hsv;
+
+		printk("RGB set request: %d %d %d\n", cdev->brightness, cdev->hue >> 24, cdev->saturation >> 24);
+		hsv.value = brightness << 24;
+		hsv.hue = cdev->hue;
+		hsv.saturation = cdev->saturation;
+		r = led_hsv_to_rgb(hsv);
+		return lp5523_rgb_brightness(led, r);
+	}
 	return cfg->brightness_fn(led);
 }
 
@@ -176,6 +190,8 @@ static int lp55xx_init_led(struct lp55xx_led *led,
 	led->cdev.brightness_set_blocking = lp55xx_set_brightness;
 	led->cdev.groups = lp55xx_led_groups;
 
+	printk("Led %d name %s\n", chan, pdata->led_config[chan].name);
+
 	if (pdata->led_config[chan].name) {
 		led->cdev.name = pdata->led_config[chan].name;
 	} else {
include/linux/leds.h +23 -0
index 64c56d4..bd0ae46 100644
@@ -37,6 +37,8 @@ struct led_classdev {
 	const char		*name;
 	enum led_brightness	 brightness;
 	enum led_brightness	 max_brightness;
+	int			 hue;
+	int			 saturation;
 	int			 flags;
 
 	/* Lower 16 bits reflect status */
@@ -49,6 +51,7 @@ struct led_classdev {
 #define LED_HW_PLUGGABLE	(1 << 19)
 #define LED_PANIC_INDICATOR	(1 << 20)
 #define LED_BRIGHT_HW_CHANGED	(1 << 21)
+#define LED_DEV_CAP_RGB		(1 << 24)
 
 	/* set_brightness_work / blink_timer flags, atomic, private. */
 	unsigned long		work_flags;
@@ -238,6 +241,26 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev)
 	return led_cdev->flags & LED_SYSFS_DISABLE;
 }
 
+struct led_hsv {
+	u32 hue;
+	u32 saturation;
+	u32 value;
+};
+
+struct led_rgb {
+	u32 red;
+	u32 green;
+	u32 blue;
+};
+
+/**
+ * led_hsv_to_rgb - convert a hsv color value to rgb color model
+ * @hsv: the hsv value to convert
+ *
+ * Returns: the resulting rgb value
+ */
+struct led_rgb led_hsv_to_rgb(struct led_hsv hsv);
+
 /*
  * LED Triggers
  */
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#5 lore raw

Hi!

Quoted 15 lines
Quoted 11 lines
Quoted 1 line

We could even export (read-only) hue/saturation for single-color LEDs...

Related patches from Heiner Kallweit are still sitting on devel branch of linux-leds.git:

https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git/log/?h=devel

Possibly it can serve as a basis for further development.

I liked that approach because it was compatible with monochrome LEDs and triggers.

Yeah, I like that. I don't like the implementation and the sysfs interface.

Quoted 8 lines

Let me try and see if I can turn them into something better...

I'm using full 32-bits for parameters; that should give us enough precision. In particular, 32-bits is important for "brightness"; I already have light that goes from .1lm to 1000lm -- which is more than 8-bits can handle.

lp5523 conversion... is a very quick hack.

And here's a better version. I'll still need to adjust white to be white... but I guess that would need to be done with any solution.

I've checked, and triggers seem to work as expected (and I can select a color for them).

drivers/leds/Kconfig +12 -0
index 594b24d..bad8a6e 100644
@@ -19,6 +19,14 @@ config LEDS_CLASS
 	  This option enables the led sysfs class in /sys/class/leds.  You'll
 	  need this to do anything useful with LEDs.  If unsure, say N.
 
+config LEDS_CLASS_RGB
+	bool "LED RGB Class Support"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for RGB LED devices.
+	  Sysfs attribute brightness is interpreted as a HSV color value.
+	  For details see Documentation/leds/leds-class.txt.
+
 config LEDS_CLASS_FLASH
 	tristate "LED Flash Class Support"
 	depends on LEDS_CLASS
@@ -38,6 +46,10 @@ config LEDS_BRIGHTNESS_HW_CHANGED
 
 	  See Documentation/ABI/testing/sysfs-class-led for details.
 
+if LEDS_CLASS_RGB
+comment "RGB LED drivers"
+endif # LEDS_CLASS_RGB
+
 comment "LED drivers"
 
 config LEDS_88PM860X
drivers/leds/Makefile +3 -1
index 909dae6..d5b5e76 100644
@@ -1,6 +1,8 @@
 
 # LED Core
-obj-$(CONFIG_NEW_LEDS)			+= led-core.o
+obj-$(CONFIG_NEW_LEDS)			+= led-core-objs.o
+led-core-objs-y				:= led-core.o
+led-core-objs-$(CONFIG_LEDS_CLASS_RGB)	+= led-rgb-core.o
 obj-$(CONFIG_LEDS_CLASS)		+= led-class.o
 obj-$(CONFIG_LEDS_CLASS_FLASH)		+= led-class-flash.o
 obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
drivers/leds/led-class.c +79 -0

91 lines not on this page · raw message

drivers/leds/led-rgb-core.c new file +64 -0

64 lines not on this page · raw message

drivers/leds/leds-lp5523.c +20 -0
index 924e50a..3244c24 100644
@@ -802,6 +802,26 @@ static ssize_t store_master_fader_leds(struct device *dev,
 	return ret;
 }
 
+int lp5523_rgb_brightness(struct lp55xx_led *led, struct led_rgb r)
+{
+	struct lp55xx_chip *chip = led->chip;
+	int ret;
+
+	mutex_lock(&chip->lock);
+	r.red >>= 24;
+	r.green >>= 24;
+	r.blue >>= 24;	
+	printk("RGB brightness %d %d %d\n", r.red, r.green, r.blue);
+	ret = lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, r.red);
+	if (!ret)
+		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr - 1, r.green);
+	if (!ret)
+		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr - 2, r.blue);
+	mutex_unlock(&chip->lock);
+	return ret;
+
+}
+
 static int lp5523_led_brightness(struct lp55xx_led *led)
 {
 	struct lp55xx_chip *chip = led->chip;
drivers/leds/leds-lp55xx-common.c +16 -0
index 5377f22..16e65dc 100644
@@ -134,6 +134,8 @@ static struct attribute *lp55xx_led_attrs[] = {
 };
 ATTRIBUTE_GROUPS(lp55xx_led);
 
+extern int lp5523_rgb_brightness(struct lp55xx_led *led, struct led_rgb r);
+
 static int lp55xx_set_brightness(struct led_classdev *cdev,
 			     enum led_brightness brightness)
 {
@@ -141,6 +143,18 @@ static int lp55xx_set_brightness(struct led_classdev *cdev,
 	struct lp55xx_device_config *cfg = led->chip->cfg;
 
 	led->brightness = (u8)brightness;
+
+	if (led->chan_nr == 6) {
+		struct led_rgb r;
+		struct led_hsv hsv;
+
+		printk("RGB set request: %d %d %d\n", cdev->brightness, cdev->hue >> 24, cdev->saturation >> 24);
+		hsv.value = brightness << 24;
+		hsv.hue = cdev->hue;
+		hsv.saturation = cdev->saturation;
+		r = led_hsv_to_rgb(hsv);
+		return lp5523_rgb_brightness(led, r);
+	}
 	return cfg->brightness_fn(led);
 }
 
@@ -176,6 +190,8 @@ static int lp55xx_init_led(struct lp55xx_led *led,
 	led->cdev.brightness_set_blocking = lp55xx_set_brightness;
 	led->cdev.groups = lp55xx_led_groups;
 
+	printk("Led %d name %s\n", chan, pdata->led_config[chan].name);
+
 	if (pdata->led_config[chan].name) {
 		led->cdev.name = pdata->led_config[chan].name;
 	} else {
include/linux/leds.h +23 -0
index 64c56d4..bd0ae46 100644
@@ -37,6 +37,8 @@ struct led_classdev {
 	const char		*name;
 	enum led_brightness	 brightness;
 	enum led_brightness	 max_brightness;
+	int			 hue;
+	int			 saturation;
 	int			 flags;
 
 	/* Lower 16 bits reflect status */
@@ -49,6 +51,7 @@ struct led_classdev {
 #define LED_HW_PLUGGABLE	(1 << 19)
 #define LED_PANIC_INDICATOR	(1 << 20)
 #define LED_BRIGHT_HW_CHANGED	(1 << 21)
+#define LED_DEV_CAP_RGB		(1 << 24)
 
 	/* set_brightness_work / blink_timer flags, atomic, private. */
 	unsigned long		work_flags;
@@ -238,6 +241,26 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev)
 	return led_cdev->flags & LED_SYSFS_DISABLE;
 }
 
+struct led_hsv {
+	u32 hue;
+	u32 saturation;
+	u32 value;
+};
+
+struct led_rgb {
+	u32 red;
+	u32 green;
+	u32 blue;
+};
+
+/**
+ * led_hsv_to_rgb - convert a hsv color value to rgb color model
+ * @hsv: the hsv value to convert
+ *
+ * Returns: the resulting rgb value
+ */
+struct led_rgb led_hsv_to_rgb(struct led_hsv hsv);
+
 /*
  * LED Triggers
  */
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#6 lore raw

Hi Pavel,

Thanks for your work. Few comments below.

Quoted 83 lines · Pavel Machek

Hi!

Quoted 15 lines
Quoted 11 lines
Quoted 1 line

We could even export (read-only) hue/saturation for single-color LEDs...

Related patches from Heiner Kallweit are still sitting on devel branch of linux-leds.git:

https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git/log/?h=devel

Possibly it can serve as a basis for further development.

I liked that approach because it was compatible with monochrome LEDs and triggers.

Yeah, I like that. I don't like the implementation and the sysfs interface.

Quoted 8 lines

Let me try and see if I can turn them into something better...

I'm using full 32-bits for parameters; that should give us enough precision. In particular, 32-bits is important for "brightness"; I already have light that goes from .1lm to 1000lm -- which is more than 8-bits can handle.

lp5523 conversion... is a very quick hack.

And here's a better version. I'll still need to adjust white to be white... but I guess that would need to be done with any solution.

I've checked, and triggers seem to work as expected (and I can select a color for them).

drivers/leds/Kconfig +12 -0
index 594b24d..bad8a6e 100644
@@ -19,6 +19,14 @@ config LEDS_CLASS
 	  This option enables the led sysfs class in /sys/class/leds.  You'll
 	  need this to do anything useful with LEDs.  If unsure, say N.
 
+config LEDS_CLASS_RGB
+	bool "LED RGB Class Support"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for RGB LED devices.
+	  Sysfs attribute brightness is interpreted as a HSV color value.
+	  For details see Documentation/leds/leds-class.txt.
+
 config LEDS_CLASS_FLASH
 	tristate "LED Flash Class Support"
 	depends on LEDS_CLASS
@@ -38,6 +46,10 @@ config LEDS_BRIGHTNESS_HW_CHANGED
 
 	  See Documentation/ABI/testing/sysfs-class-led for details.
 
+if LEDS_CLASS_RGB
+comment "RGB LED drivers"
+endif # LEDS_CLASS_RGB
+
 comment "LED drivers"
 
 config LEDS_88PM860X
drivers/leds/Makefile +3 -1
index 909dae6..d5b5e76 100644
@@ -1,6 +1,8 @@
 
 # LED Core
-obj-$(CONFIG_NEW_LEDS)			+= led-core.o
+obj-$(CONFIG_NEW_LEDS)			+= led-core-objs.o
+led-core-objs-y				:= led-core.o
+led-core-objs-$(CONFIG_LEDS_CLASS_RGB)	+= led-rgb-core.o
 obj-$(CONFIG_LEDS_CLASS)		+= led-class.o
 obj-$(CONFIG_LEDS_CLASS_FLASH)		+= led-class-flash.o
 obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
drivers/leds/led-class.c +0 -0
index b0e2d55..0bd68a4 100644

drivers/leds/led-class.c static ssize_t brightness_store(struct device *dev, · line 74

Quoted 10 lines
drivers/leds/led-class.c +7 -0
@@ -65,6 +65,83 @@ static ssize_t brightness_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(brightness);
 
+static ssize_t saturation_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+	/* no lock needed for this */
+	led_update_brightness(led_cdev);

We will need to update documentation of brightness_get to require updating hue and saturation for RGB LED drivers.

Also hue and saturation attributes will need related ABI documentation entries. From what I see you're proposing that hue and sat would be written to the device on the occasion of brightness setting, right? We're tossing this responsibility to to drivers, without exposing an API for it, so it would have to be highlighted.

Quoted 110 lines
drivers/leds/led-class.c +72 -0
+
+	return sprintf(buf, "%u\n", led_cdev->saturation);
+}
+
+static ssize_t saturation_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, 10, &state);
+	if (ret)
+		goto unlock;
+
+	led_cdev->saturation = state;
+
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_access);
+	return ret;
+}
+static DEVICE_ATTR_RW(saturation);
+
+static ssize_t hue_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+	/* no lock needed for this */
+	led_update_brightness(led_cdev);
+
+	return sprintf(buf, "%u\n", led_cdev->hue);
+}
+
+static ssize_t hue_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, 10, &state);
+	if (ret)
+		goto unlock;
+
+	led_cdev->hue = state;
+
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_access);
+	return ret;
+}
+static DEVICE_ATTR_RW(hue);
+
+
 static ssize_t max_brightness_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -88,6 +165,8 @@ static const struct attribute_group led_trigger_group = {
 static struct attribute *led_class_attrs[] = {
 	&dev_attr_brightness.attr,
 	&dev_attr_max_brightness.attr,
+	&dev_attr_hue.attr,
+	&dev_attr_saturation.attr,
 	NULL,
 };
 
drivers/leds/led-rgb-core.c new file +22 -0
new file mode 100644
index 0000000..3733032
@@ -0,0 +1,64 @@
+/*
+ * LED RGB Class Support
+ *
+ * Author: Heiner Kallweit <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/leds.h>
+#include "leds.h"
+
+struct led_rgb led_hsv_to_rgb(struct led_hsv hsv)
+{
+	unsigned int h = hsv.hue >> 24;
+	unsigned int s = hsv.saturation >> 24;
+	unsigned int v = hsv.value >> 24;
+	unsigned int f, p, q, t, r, g, b;
+	struct led_rgb res;
+

drivers/leds/led-rgb-core.c line 32

Quoted 10 lines
drivers/leds/led-rgb-core.c new file +10 -0
+	if (!v) {
+		res.red = 0;
+		res.green = 0;
+		res.blue = 0;
+		return res;
+	}
+	if (!s) {
+		res.red = v << 24;
+		res.green = v << 24;
+		res.blue = v << 24;

Why the shifting?

Quoted 154 lines
drivers/leds/led-rgb-core.c new file +32 -0
+		return res;
+	}
+
+	f = DIV_ROUND_CLOSEST((h % 42) * 255, 42);
+	p = v - DIV_ROUND_CLOSEST(s * v, 255);
+	q = v - DIV_ROUND_CLOSEST(f * s * v, 255 * 255);
+	t = v - DIV_ROUND_CLOSEST((255 - f) * s * v, 255 * 255);
+
+	switch (h / 42) {
+	case 0:
+		r = v; g = t; b = p; break;
+	case 1:
+		r = q; g = v; b = p; break;
+	case 2:
+		r = p; g = v; b = t; break;
+	case 3:
+		r = p; g = q; b = v; break;
+	case 4:
+		r = t; g = p; b = v; break;
+	case 5:
+		r = v; g = p; b = q; break;
+	}
+
+	printk("hsv_to: h %5d, s %5d, v %5d -> %5d, %5d, %5d\n",
+	       h*390, s*390, v*390, r*390, g*390, b*390);
+	
+	res.red = r << 24;
+	res.green = g << 24;
+	res.blue = b << 24;
+	return res;
+}
+EXPORT_SYMBOL_GPL(led_hsv_to_rgb);
drivers/leds/leds-lp5523.c +20 -0
index 924e50a..3244c24 100644
@@ -802,6 +802,26 @@ static ssize_t store_master_fader_leds(struct device *dev,
 	return ret;
 }
 
+int lp5523_rgb_brightness(struct lp55xx_led *led, struct led_rgb r)
+{
+	struct lp55xx_chip *chip = led->chip;
+	int ret;
+
+	mutex_lock(&chip->lock);
+	r.red >>= 24;
+	r.green >>= 24;
+	r.blue >>= 24;	
+	printk("RGB brightness %d %d %d\n", r.red, r.green, r.blue);
+	ret = lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, r.red);
+	if (!ret)
+		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr - 1, r.green);
+	if (!ret)
+		lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr - 2, r.blue);
+	mutex_unlock(&chip->lock);
+	return ret;
+
+}
+
 static int lp5523_led_brightness(struct lp55xx_led *led)
 {
 	struct lp55xx_chip *chip = led->chip;
drivers/leds/leds-lp55xx-common.c +16 -0
index 5377f22..16e65dc 100644
@@ -134,6 +134,8 @@ static struct attribute *lp55xx_led_attrs[] = {
 };
 ATTRIBUTE_GROUPS(lp55xx_led);
 
+extern int lp5523_rgb_brightness(struct lp55xx_led *led, struct led_rgb r);
+
 static int lp55xx_set_brightness(struct led_classdev *cdev,
 			     enum led_brightness brightness)
 {
@@ -141,6 +143,18 @@ static int lp55xx_set_brightness(struct led_classdev *cdev,
 	struct lp55xx_device_config *cfg = led->chip->cfg;
 
 	led->brightness = (u8)brightness;
+
+	if (led->chan_nr == 6) {
+		struct led_rgb r;
+		struct led_hsv hsv;
+
+		printk("RGB set request: %d %d %d\n", cdev->brightness, cdev->hue >> 24, cdev->saturation >> 24);
+		hsv.value = brightness << 24;
+		hsv.hue = cdev->hue;
+		hsv.saturation = cdev->saturation;
+		r = led_hsv_to_rgb(hsv);
+		return lp5523_rgb_brightness(led, r);
+	}
 	return cfg->brightness_fn(led);
 }
 
@@ -176,6 +190,8 @@ static int lp55xx_init_led(struct lp55xx_led *led,
 	led->cdev.brightness_set_blocking = lp55xx_set_brightness;
 	led->cdev.groups = lp55xx_led_groups;
 
+	printk("Led %d name %s\n", chan, pdata->led_config[chan].name);
+
 	if (pdata->led_config[chan].name) {
 		led->cdev.name = pdata->led_config[chan].name;
 	} else {
include/linux/leds.h +23 -0
index 64c56d4..bd0ae46 100644
@@ -37,6 +37,8 @@ struct led_classdev {
 	const char		*name;
 	enum led_brightness	 brightness;
 	enum led_brightness	 max_brightness;
+	int			 hue;
+	int			 saturation;
 	int			 flags;
 
 	/* Lower 16 bits reflect status */
@@ -49,6 +51,7 @@ struct led_classdev {
 #define LED_HW_PLUGGABLE	(1 << 19)
 #define LED_PANIC_INDICATOR	(1 << 20)
 #define LED_BRIGHT_HW_CHANGED	(1 << 21)
+#define LED_DEV_CAP_RGB		(1 << 24)
 
 	/* set_brightness_work / blink_timer flags, atomic, private. */
 	unsigned long		work_flags;
@@ -238,6 +241,26 @@ static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev)
 	return led_cdev->flags & LED_SYSFS_DISABLE;
 }
 
+struct led_hsv {
+	u32 hue;
+	u32 saturation;
+	u32 value;
+};
+
+struct led_rgb {
+	u32 red;
+	u32 green;
+	u32 blue;
+};
+
+/**
+ * led_hsv_to_rgb - convert a hsv color value to rgb color model
+ * @hsv: the hsv value to convert
+ *
+ * Returns: the resulting rgb value
+ */
+struct led_rgb led_hsv_to_rgb(struct led_hsv hsv);
+
 /*
  * LED Triggers
  */
Signature · 2 lines
Best regards,
Jacek Anaszewski
#7 lore raw
In reply to Jacek Anaszewski

Hi!

Quoted 6 lines
Quoted 2 lines

You mentioned you was working on RGB support prototype. Could you post copy of the patches (even if unfinished)?

Unfortunately it is at the stage of unfinished proof of concept and I haven't managed yet to try how it fits to all API use cases we have. Nor is it in a shape ready to post to the lists.

Well, if it compiles, it is in shape. If it does not compile, it is in shape, too :-).
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html