diff mbox series

[v3,2/3] leds: pwm: add support for default-state device property

Message ID 20200316124851.6303-3-Denis.Osterland@diehl.com
State New
Headers show
Series [v3,1/3] leds: pwm: remove header | expand

Commit Message

Denis OSTERLAND-HEIM March 16, 2020, 12:53 p.m. UTC
This patch adds support for "default-state" devicetree property, which
allows to defer pwm init to first use of led.

This allows to configure the PWM early in bootloader to let the LED
blink until an application in Linux userspace sets something different.

Signed-off-by: Denis Osterland-Heim <Denis.Osterland@diehl.com>
---
 drivers/leds/leds-pwm.c | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index 5f69b6571595..fce7969e7918 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -18,11 +18,16 @@ 
 #include <linux/pwm.h>
 #include <linux/slab.h>
 
+#define LEDS_PWM_DEFSTATE_OFF	0
+#define LEDS_PWM_DEFSTATE_ON	1
+#define LEDS_PWM_DEFSTATE_KEEP	2
+
 struct led_pwm {
 	const char	*name;
 	const char	*default_trigger;
 	unsigned int	pwm_id __deprecated;
 	u8		active_low;
+	u8		default_state;
 	unsigned int	max_brightness;
 	unsigned int	pwm_period_ns;
 };
@@ -72,7 +77,6 @@  static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
 	led_data->active_low = led->active_low;
 	led_data->cdev.name = led->name;
 	led_data->cdev.default_trigger = led->default_trigger;
-	led_data->cdev.brightness = LED_OFF;
 	led_data->cdev.max_brightness = led->max_brightness;
 	led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
 
@@ -92,13 +96,27 @@  static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
 
 	pwm_init_state(led_data->pwm, &led_data->pwmstate);
 
+	if (led->default_state == LEDS_PWM_DEFSTATE_ON)
+		led_data->cdev.brightness = led->max_brightness;
+	else if (led->default_state == LEDS_PWM_DEFSTATE_KEEP) {
+		uint64_t brightness;
+
+		pwm_get_state(led_data->pwm, &led_data->pwmstate);
+		brightness = led->max_brightness;
+		brightness *= led_data->pwmstate.duty_cycle;
+		do_div(brightness, led_data->pwmstate.period);
+		led_data->cdev.brightness = (enum led_brightness)brightness;
+	}
+
 	if (!led_data->pwmstate.period)
 		led_data->pwmstate.period = led->pwm_period_ns;
 
 	ret = devm_led_classdev_register(dev, &led_data->cdev);
 	if (ret == 0) {
 		priv->num_leds++;
-		led_pwm_set(&led_data->cdev, led_data->cdev.brightness);
+		if (led->default_state != LEDS_PWM_DEFSTATE_KEEP)
+			led_pwm_set(&led_data->cdev,
+					led_data->cdev.brightness);
 	} else {
 		dev_err(dev, "failed to register PWM led for %s: %d\n",
 			led->name, ret);
@@ -116,6 +134,8 @@  static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv)
 	memset(&led, 0, sizeof(led));
 
 	device_for_each_child_node(dev, fwnode) {
+		const char *state = NULL;
+
 		ret = fwnode_property_read_string(fwnode, "label", &led.name);
 		if (ret && is_of_node(fwnode))
 			led.name = to_of_node(fwnode)->name;
@@ -133,6 +153,16 @@  static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv)
 		fwnode_property_read_u32(fwnode, "max-brightness",
 					 &led.max_brightness);
 
+		if (!fwnode_property_read_string(fwnode, "default-state",
+						 &state)) {
+			if (!strcmp(state, "keep"))
+				led.default_state = LEDS_PWM_DEFSTATE_KEEP;
+			else if (!strcmp(state, "on"))
+				led.default_state = LEDS_PWM_DEFSTATE_ON;
+			else
+				led.default_state = LEDS_PWM_DEFSTATE_OFF;
+		}
+
 		ret = led_pwm_add(dev, priv, &led, fwnode);
 		if (ret) {
 			fwnode_handle_put(fwnode);