diff options
Diffstat (limited to 'drivers/leds/leds-pwm.c')
| -rw-r--r-- | drivers/leds/leds-pwm.c | 72 |
1 files changed, 52 insertions, 20 deletions
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 6832180c1c54..6c1f2f50ff85 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -9,15 +9,15 @@ * based on leds-gpio.c by Raphael Assenat <raph@8d.com> */ -#include <linux/module.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/of_platform.h> #include <linux/leds.h> -#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> -#include "leds.h" struct led_pwm { const char *name; @@ -27,6 +27,7 @@ struct led_pwm { }; struct led_pwm_data { + struct gpio_desc *enable_gpio; struct led_classdev cdev; struct pwm_device *pwm; struct pwm_state pwmstate; @@ -52,9 +53,31 @@ static int led_pwm_set(struct led_classdev *led_cdev, if (led_dat->active_low) duty = led_dat->pwmstate.period - duty; + gpiod_set_value_cansleep(led_dat->enable_gpio, !!brightness); + led_dat->pwmstate.duty_cycle = duty; - led_dat->pwmstate.enabled = duty > 0; - return pwm_apply_state(led_dat->pwm, &led_dat->pwmstate); + /* + * Disabling a PWM doesn't guarantee that it emits the inactive level. + * So keep it on. Only for suspending the PWM should be disabled because + * otherwise it refuses to suspend. The possible downside is that the + * LED might stay (or even go) on. + */ + led_dat->pwmstate.enabled = !(led_cdev->flags & LED_SUSPENDED); + return pwm_apply_might_sleep(led_dat->pwm, &led_dat->pwmstate); +} + +static int led_pwm_default_brightness_get(struct fwnode_handle *fwnode, + int max_brightness) +{ + unsigned int default_brightness; + int ret; + + ret = fwnode_property_read_u32(fwnode, "default-brightness", + &default_brightness); + if (ret < 0 || default_brightness > max_brightness) + default_brightness = max_brightness; + + return default_brightness; } __attribute__((nonnull)) @@ -98,7 +121,8 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, /* set brightness */ switch (led->default_state) { case LEDS_DEFSTATE_ON: - led_data->cdev.brightness = led->max_brightness; + led_data->cdev.brightness = + led_pwm_default_brightness_get(fwnode, led->max_brightness); break; case LEDS_DEFSTATE_KEEP: { @@ -112,6 +136,21 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, break; } + /* + * Claim the GPIO as GPIOD_ASIS and set the value + * later on to honor the different default states + */ + led_data->enable_gpio = devm_fwnode_gpiod_get(dev, fwnode, "enable", GPIOD_ASIS, NULL); + if (IS_ERR(led_data->enable_gpio)) { + if (PTR_ERR(led_data->enable_gpio) == -ENOENT) + /* Enable GPIO is optional */ + led_data->enable_gpio = NULL; + else + return PTR_ERR(led_data->enable_gpio); + } + + gpiod_direction_output(led_data->enable_gpio, !!led_data->cdev.brightness); + ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); if (ret) { dev_err(dev, "failed to register PWM led for %s: %d\n", @@ -134,21 +173,18 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv) { - struct fwnode_handle *fwnode; struct led_pwm led; int ret; - memset(&led, 0, sizeof(led)); + device_for_each_child_node_scoped(dev, fwnode) { + memset(&led, 0, sizeof(led)); - device_for_each_child_node(dev, fwnode) { ret = fwnode_property_read_string(fwnode, "label", &led.name); if (ret && is_of_node(fwnode)) led.name = to_of_node(fwnode)->name; - if (!led.name) { - ret = EINVAL; - goto err_child_out; - } + if (!led.name) + return -EINVAL; led.active_low = fwnode_property_read_bool(fwnode, "active-low"); @@ -159,14 +195,10 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv) ret = led_pwm_add(dev, priv, &led, fwnode); if (ret) - goto err_child_out; + return ret; } return 0; - -err_child_out: - fwnode_handle_put(fwnode); - return ret; } static int led_pwm_probe(struct platform_device *pdev) |
