diff options
30 files changed, 441 insertions, 196 deletions
diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml index b1f363747a62..697102707703 100644 --- a/Documentation/devicetree/bindings/leds/common.yaml +++ b/Documentation/devicetree/bindings/leds/common.yaml @@ -128,6 +128,12 @@ properties: as a panic indicator. type: boolean + retain-state-shutdown: + description: + This property specifies that the LED should not be turned off or changed + when the system shuts down. + type: boolean + trigger-sources: description: | List of devices which should be used as a source triggering this LED diff --git a/Documentation/leds/well-known-leds.txt b/Documentation/leds/well-known-leds.txt new file mode 100644 index 000000000000..4a8b9dc4bf52 --- /dev/null +++ b/Documentation/leds/well-known-leds.txt @@ -0,0 +1,58 @@ +-*- org -*- + +It is somehow important to provide consistent interface to the +userland. LED devices have one problem there, and that is naming of +directories in /sys/class/leds. It would be nice if userland would +just know right "name" for given LED function, but situation got more +complex. + +Anyway, if backwards compatibility is not an issue, new code should +use one of the "good" names from this list, and you should extend the +list where applicable. + +Legacy names are listed, too; in case you are writing application that +wants to use particular feature, you should probe for good name, first, +but then try the legacy ones, too. + +Notice there's a list of functions in include/dt-bindings/leds/common.h . + +* Keyboards + +Good: "input*:*:capslock" +Good: "input*:*:scrolllock" +Good: "input*:*:numlock" +Legacy: "shift-key-light" (Motorola Droid 4, capslock) + +Set of common keyboard LEDs, going back to PC AT or so. + +Legacy: "tpacpi::thinklight" (IBM/Lenovo Thinkpads) +Legacy: "lp5523:kb{1,2,3,4,5,6}" (Nokia N900) + +Frontlight/backlight of main keyboard. + +Legacy: "button-backlight" (Motorola Droid 4) + +Some phones have touch buttons below screen; it is different from main +keyboard. And this is their backlight. + +* Sound subsystem + +Good: "platform:*:mute" +Good: "platform:*:micmute" + +LEDs on notebook body, indicating that sound input / output is muted. + +* System notification + +Legacy: "status-led:{red,green,blue}" (Motorola Droid 4) +Legacy: "lp5523:{r,g,b}" (Nokia N900) + +Phones usually have multi-color status LED. + +* Power management + +Good: "platform:*:charging" (allwinner sun50i) + +* Screen + +Good: ":backlight" (Motorola Droid 4) diff --git a/MAINTAINERS b/MAINTAINERS index 142e27504f4d..19d783ea85c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2849,7 +2849,7 @@ AS3645A LED FLASH CONTROLLER DRIVER M: Sakari Ailus <sakari.ailus@iki.fi> L: linux-leds@vger.kernel.org S: Maintained -F: drivers/leds/leds-as3645a.c +F: drivers/leds/flash/leds-as3645a.c ASAHI KASEI AK7375 LENS VOICE COIL DRIVER M: Tianshu Qiu <tian.shu.qiu@intel.com> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index bdf16180f5ff..ed800f5da7d8 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -59,16 +59,6 @@ config LEDS_88PM860X This option enables support for on-chip LED drivers found on Marvell Semiconductor 88PM8606 PMIC. -config LEDS_AAT1290 - tristate "LED support for the AAT1290" - depends on LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - depends on GPIOLIB || COMPILE_TEST - depends on OF - depends on PINCTRL - help - This option enables support for the LEDs on the AAT1290. - config LEDS_AN30259A tristate "LED support for Panasonic AN30259A" depends on LEDS_CLASS && I2C && OF @@ -104,15 +94,6 @@ config LEDS_ARIEL Say Y to if your machine is a Dell Wyse 3020 thin client. -config LEDS_AS3645A - tristate "AS3645A and LM3555 LED flash controllers support" - depends on I2C && LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - help - Enable LED flash class support for AS3645A LED flash - controller. V4L2 flash API is provided as well if - CONFIG_V4L2_FLASH_API is enabled. - config LEDS_AW2013 tristate "LED support for Awinic AW2013" depends on LEDS_CLASS && I2C && OF @@ -239,15 +220,6 @@ config LEDS_LM3692X This option enables support for the TI LM3692x family of white LED string drivers used for backlighting. -config LEDS_LM3601X - tristate "LED support for LM3601x Chips" - depends on LEDS_CLASS && I2C - depends on LEDS_CLASS_FLASH - select REGMAP_I2C - help - This option enables support for the TI LM3601x family - of flash, torch and indicator classes. - config LEDS_LOCOMO tristate "LED Support for Locomo device" depends on LEDS_CLASS @@ -397,7 +369,7 @@ config LEDS_LP3952 module will be called leds-lp3952. config LEDS_LP50XX - tristate "LED Support for TI LP5036/30/24/18/12/9 LED driver chip" + tristate "LED Support for TI LP5036/30/24/18/12/09 LED driver chip" depends on LEDS_CLASS && REGMAP_I2C depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR help @@ -699,17 +671,6 @@ config LEDS_MAX77650 help LEDs driver for MAX77650 family of PMICs from Maxim Integrated. -config LEDS_MAX77693 - tristate "LED support for MAX77693 Flash" - depends on LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - depends on MFD_MAX77693 - depends on OF - help - This option enables support for the flash part of the MAX77693 - multifunction device. It has build in control for two leds in flash - and torch mode. - config LEDS_MAX8997 tristate "LED support for MAX8997 PMIC" depends on LEDS_CLASS && MFD_MAX8997 @@ -741,16 +702,6 @@ config LEDS_MENF21BMC This driver can also be built as a module. If so the module will be called leds-menf21bmc. -config LEDS_KTD2692 - tristate "LED support for KTD2692 flash LED controller" - depends on LEDS_CLASS_FLASH && OF - depends on GPIOLIB || COMPILE_TEST - help - This option enables support for KTD2692 LED flash connected - through ExpressWire interface. - - Say Y to enable this driver. - config LEDS_IS31FL319X tristate "LED Support for ISSI IS31FL319x I2C LED controller family" depends on LEDS_CLASS && I2C && OF @@ -913,14 +864,6 @@ config LEDS_IP30 To compile this driver as a module, choose M here: the module will be called leds-ip30. -config LEDS_SGM3140 - tristate "LED support for the SGM3140" - depends on LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - help - This option enables support for the SGM3140 500mA Buck/Boost Charge - Pump LED Driver. - config LEDS_ACER_A500 tristate "Power button LED support for Acer Iconia Tab A500" depends on LEDS_CLASS && MFD_ACER_A500_EC diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 7e604d3028c8..c636ec069612 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -9,13 +9,11 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers (keep this sorted, M-| sort) obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o -obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o obj-$(CONFIG_LEDS_APU) += leds-apu.o obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o -obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o @@ -37,12 +35,10 @@ obj-$(CONFIG_LEDS_IP30) += leds-ip30.o obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o -obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o -obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o @@ -60,7 +56,6 @@ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o -obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o @@ -82,7 +77,6 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o -obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 7eb2f44f16be..fd8b7573285a 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -611,9 +611,6 @@ static void sso_led_shutdown(struct sso_led *led) if (led->desc.hw_trig) regmap_update_bits(priv->mmap, SSO_CON3, BIT(led->desc.pin), 0); - if (led->gpiod) - devm_gpiod_put(priv->dev, led->gpiod); - led->priv = NULL; } @@ -624,15 +621,16 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) struct device *dev = priv->dev; struct sso_led_desc *desc; struct sso_led *led; - struct list_head *p; const char *tmp; u32 prop; int ret; fwnode_for_each_child_node(fw_ssoled, fwnode_child) { led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; + if (!led) { + ret = -ENOMEM; + goto __dt_err; + } INIT_LIST_HEAD(&led->list); led->priv = priv; @@ -642,7 +640,7 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) fwnode_child, GPIOD_ASIS, NULL); if (IS_ERR(led->gpiod)) { - dev_err(dev, "led: get gpio fail!\n"); + ret = dev_err_probe(dev, PTR_ERR(led->gpiod), "led: get gpio fail!\n"); goto __dt_err; } @@ -662,8 +660,11 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) desc->panic_indicator = 1; ret = fwnode_property_read_u32(fwnode_child, "reg", &prop); - if (ret != 0 || prop >= SSO_LED_MAX_NUM) { + if (ret) + goto __dt_err; + if (prop >= SSO_LED_MAX_NUM) { dev_err(dev, "invalid LED pin:%u\n", prop); + ret = -EINVAL; goto __dt_err; } desc->pin = prop; @@ -699,21 +700,20 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) desc->brightness = LED_FULL; } - if (sso_create_led(priv, led, fwnode_child)) + ret = sso_create_led(priv, led, fwnode_child); + if (ret) goto __dt_err; } - fwnode_handle_put(fw_ssoled); return 0; + __dt_err: - fwnode_handle_put(fw_ssoled); + fwnode_handle_put(fwnode_child); /* unregister leds */ - list_for_each(p, &priv->led_list) { - led = list_entry(p, struct sso_led, list); + list_for_each_entry(led, &priv->led_list, list) sso_led_shutdown(led); - } - return -EINVAL; + return ret; } static int sso_led_dt_parse(struct sso_led_priv *priv) @@ -731,6 +731,7 @@ static int sso_led_dt_parse(struct sso_led_priv *priv) fw_ssoled = fwnode_get_named_child_node(fwnode, "ssoled"); if (fw_ssoled) { ret = __sso_led_dt_parse(priv, fw_ssoled); + fwnode_handle_put(fw_ssoled); if (ret) return ret; } @@ -841,14 +842,12 @@ static int intel_sso_led_probe(struct platform_device *pdev) static int intel_sso_led_remove(struct platform_device *pdev) { struct sso_led_priv *priv; - struct list_head *pos, *n; - struct sso_led *led; + struct sso_led *led, *n; priv = platform_get_drvdata(pdev); - list_for_each_safe(pos, n, &priv->led_list) { - list_del(pos); - led = list_entry(pos, struct sso_led, list); + list_for_each_entry_safe(led, n, &priv->led_list, list) { + list_del(&led->list); sso_led_shutdown(led); } diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 3f49f3edbffb..b230f3d65eb0 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -2,6 +2,52 @@ if LEDS_CLASS_FLASH +config LEDS_AAT1290 + tristate "LED support for the AAT1290" + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + depends on GPIOLIB || COMPILE_TEST + depends on OF + depends on PINCTRL + help + This option enables support for the LEDs on the AAT1290. + +config LEDS_AS3645A + tristate "AS3645A and LM3555 LED flash controllers support" + depends on I2C + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + help + Enable LED flash class support for AS3645A LED flash + controller. V4L2 flash API is provided as well if + CONFIG_V4L2_FLASH_API is enabled. + +config LEDS_KTD2692 + tristate "LED support for Kinetic KTD2692 flash LED controller" + depends on OF + depends on GPIOLIB || COMPILE_TEST + help + This option enables support for Kinetic KTD2692 LED flash connected + through ExpressWire interface. + + Say Y to enable this driver. + +config LEDS_LM3601X + tristate "LED support for LM3601x Chips" + depends on LEDS_CLASS && I2C + select REGMAP_I2C + help + This option enables support for the TI LM3601x family + of flash, torch and indicator classes. + +config LEDS_MAX77693 + tristate "LED support for MAX77693 Flash" + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + depends on MFD_MAX77693 + depends on OF + help + This option enables support for the flash part of the MAX77693 + multifunction device. It has build in control for two leds in flash + and torch mode. + config LEDS_RT4505 tristate "LED support for RT4505 flashlight controller" depends on I2C && OF @@ -24,4 +70,11 @@ config LEDS_RT8515 To compile this driver as a module, choose M here: the module will be called leds-rt8515. +config LEDS_SGM3140 + tristate "LED support for the SGM3140" + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + help + This option enables support for the SGM3140 500mA Buck/Boost Charge + Pump LED Driver. + endif # LEDS_CLASS_FLASH diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 09aee561f769..ebea42f9c37e 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -1,4 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o +obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o +obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o +obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o +obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o +obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c index 589484b22c79..589484b22c79 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/flash/leds-aat1290.c diff --git a/drivers/leds/leds-as3645a.c b/drivers/leds/flash/leds-as3645a.c index aa3f82be0a9c..aa3f82be0a9c 100644 --- a/drivers/leds/leds-as3645a.c +++ b/drivers/leds/flash/leds-as3645a.c diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/flash/leds-ktd2692.c index f341da1503a4..f341da1503a4 100644 --- a/drivers/leds/leds-ktd2692.c +++ b/drivers/leds/flash/leds-ktd2692.c diff --git a/drivers/leds/leds-lm3601x.c b/drivers/leds/flash/leds-lm3601x.c index d0e1d4814042..d0e1d4814042 100644 --- a/drivers/leds/leds-lm3601x.c +++ b/drivers/leds/flash/leds-lm3601x.c diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/flash/leds-max77693.c index 5c1faeb55a31..5c1faeb55a31 100644 --- a/drivers/leds/leds-max77693.c +++ b/drivers/leds/flash/leds-max77693.c diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c index 590bfa180d10..44904fdee3cc 100644 --- a/drivers/leds/flash/leds-rt8515.c +++ b/drivers/leds/flash/leds-rt8515.c @@ -343,8 +343,9 @@ static int rt8515_probe(struct platform_device *pdev) ret = devm_led_classdev_flash_register_ext(dev, fled, &init_data); if (ret) { - dev_err(dev, "can't register LED %s\n", led->name); + fwnode_handle_put(child); mutex_destroy(&rt->lock); + dev_err(dev, "can't register LED %s\n", led->name); return ret; } @@ -362,6 +363,7 @@ static int rt8515_probe(struct platform_device *pdev) */ } + fwnode_handle_put(child); return 0; } diff --git a/drivers/leds/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c index f4f831570f11..f4f831570f11 100644 --- a/drivers/leds/leds-sgm3140.c +++ b/drivers/leds/flash/leds-sgm3140.c diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c index 6eeb9effcf65..185e17055317 100644 --- a/drivers/leds/led-class-flash.c +++ b/drivers/leds/led-class-flash.c @@ -92,14 +92,12 @@ static ssize_t flash_strobe_store(struct device *dev, struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); unsigned long state; - ssize_t ret = -EINVAL; + ssize_t ret = -EBUSY; mutex_lock(&led_cdev->led_access); - if (led_sysfs_is_disabled(led_cdev)) { - ret = -EBUSY; + if (led_sysfs_is_disabled(led_cdev)) goto unlock; - } ret = kstrtoul(buf, 10, &state); if (ret) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f704391d57a8..f4bb02f6e042 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -350,10 +350,15 @@ int led_classdev_register_ext(struct device *parent, if (ret < 0) return ret; - if (init_data->fwnode) + if (init_data->fwnode) { fwnode_property_read_string(init_data->fwnode, "linux,default-trigger", &led_cdev->default_trigger); + + if (fwnode_property_present(init_data->fwnode, + "retain-state-shutdown")) + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + } } else { proposed_name = led_cdev->name; } @@ -444,7 +449,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev) /* Stop blinking */ led_stop_software_blink(led_cdev); - led_set_brightness(led_cdev, LED_OFF); + if (!(led_cdev->flags & LED_RETAIN_AT_SHUTDOWN)) + led_set_brightness(led_cdev, LED_OFF); flush_work(&led_cdev->set_brightness_work); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 8eb8054ef9c6..4a97cb745788 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -477,3 +477,18 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data, return 0; } EXPORT_SYMBOL_GPL(led_compose_name); + +enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode) +{ + const char *state = NULL; + + if (!fwnode_property_read_string(fwnode, "default-state", &state)) { + if (!strcmp(state, "keep")) + return LEDS_DEFSTATE_KEEP; + if (!strcmp(state, "on")) + return LEDS_DEFSTATE_ON; + } + + return LEDS_DEFSTATE_OFF; +} +EXPORT_SYMBOL_GPL(led_init_default_state_get); diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c index 76b455e87574..f9eb59a25570 100644 --- a/drivers/leds/leds-el15203000.c +++ b/drivers/leds/leds-el15203000.c @@ -4,8 +4,9 @@ #include <linux/delay.h> #include <linux/leds.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/property.h> #include <linux/spi/spi.h> /* diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b5d5e22d2d1e..092eb59a7d32 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> +#include "leds.h" struct gpio_led_data { struct led_classdev cdev; @@ -144,7 +145,6 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) device_for_each_child_node(dev, child) { struct gpio_led_data *led_dat = &priv->leds[priv->num_leds]; struct gpio_led led = {}; - const char *state = NULL; /* * Acquire gpiod from DT with uninitialized label, which @@ -161,15 +161,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) led_dat->gpiod = led.gpiod; - if (!fwnode_property_read_string(child, "default-state", - &state)) { - if (!strcmp(state, "keep")) - led.default_state = LEDS_GPIO_DEFSTATE_KEEP; - else if (!strcmp(state, "on")) - led.default_state = LEDS_GPIO_DEFSTATE_ON; - else - led.default_state = LEDS_GPIO_DEFSTATE_OFF; - } + led.default_state = led_init_default_state_get(child); if (fwnode_property_present(child, "retain-state-suspended")) led.retain_state_suspended = 1; diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index 3b55af9a8c58..22c092a4394a 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -386,6 +386,7 @@ static int is31fl32xx_parse_dt(struct device *dev, dev_err(dev, "Node %pOF 'reg' conflicts with another LED\n", child); + ret = -EINVAL; goto err; } diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index a02756d7ed8f..afe6fb297855 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -7,10 +7,9 @@ #include <linux/init.h> #include <linux/leds.h> #include <linux/log2.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 970a4f34791b..a8c9322558cc 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -2,11 +2,16 @@ // TI LM3697 LED chip family driver // Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ +#include <linux/bits.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> -#include <linux/of.h> -#include <linux/of_gpio.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/types.h> + #include <linux/leds-ti-lmu-common.h> #define LM3697_REV 0x0 @@ -221,14 +226,12 @@ static int lm3697_probe_dt(struct lm3697 *priv) ret = fwnode_property_read_u32(child, "reg", &control_bank); if (ret) { dev_err(dev, "reg property missing\n"); - fwnode_handle_put(child); goto child_out; } if (control_bank > LM3697_CONTROL_B) { dev_err(dev, "reg property is invalid\n"); ret = -EINVAL; - fwnode_handle_put(child); goto child_out; } @@ -259,7 +262,6 @@ static int lm3697_probe_dt(struct lm3697 *priv) led->num_leds); if (ret) { dev_err(dev, "led-sources property missing\n"); - fwnode_handle_put(child); goto child_out; } @@ -284,14 +286,16 @@ static int lm3697_probe_dt(struct lm3697 *priv) &init_data); if (ret) { dev_err(dev, "led register err: %d\n", ret); - fwnode_handle_put(child); goto child_out; } i++; } + return ret; + child_out: + fwnode_handle_put(child); return ret; } diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 3bb52d3165d9..d0160fde0f94 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -97,10 +97,9 @@ static int lt3593_led_probe(struct platform_device *pdev) init_data.default_label = ":"; ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); - if (ret < 0) { - fwnode_handle_put(child); + fwnode_handle_put(child); + if (ret < 0) return ret; - } platform_set_drvdata(pdev, led_data); diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 7087ca4592fc..a6b5699aeae4 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -127,9 +127,9 @@ struct pca955x_led { struct pca955x *pca955x; struct led_classdev led_cdev; int led_num; /* 0 .. 15 potentially */ - char name[32]; u32 type; - const char *default_trigger; + int default_state; + struct fwnode_handle *fwnode; }; struct pca955x_platform_data { @@ -166,11 +166,10 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + (2 * n); int ret; - ret = i2c_smbus_write_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, - val); + ret = i2c_smbus_write_byte_data(client, cmd, val); if (ret < 0) dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val, ret); @@ -187,11 +186,10 @@ static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); int ret; - ret = i2c_smbus_write_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, - val); + ret = i2c_smbus_write_byte_data(client, cmd, val); if (ret < 0) dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val, ret); @@ -205,11 +203,10 @@ static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n; int ret; - ret = i2c_smbus_write_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, - val); + ret = i2c_smbus_write_byte_data(client, cmd, val); if (ret < 0) dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val, ret); @@ -223,10 +220,10 @@ static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n; int ret; - ret = i2c_smbus_read_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); + ret = i2c_smbus_read_byte_data(client, cmd); if (ret < 0) { dev_err(&client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret); @@ -236,6 +233,57 @@ static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) return 0; } +static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val) +{ + struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); + int ret; + + ret = i2c_smbus_read_byte_data(client, cmd); + if (ret < 0) { + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, n, ret); + return ret; + } + *val = (u8)ret; + return 0; +} + +static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev) +{ + struct pca955x_led *pca955x_led = container_of(led_cdev, + struct pca955x_led, + led_cdev); + struct pca955x *pca955x = pca955x_led->pca955x; + u8 ls, pwm; + int ret; + + ret = pca955x_read_ls(pca955x->client, pca955x_led->led_num / 4, &ls); + if (ret) + return ret; + + ls = (ls >> ((pca955x_led->led_num % 4) << 1)) & 0x3; + switch (ls) { + case PCA955X_LS_LED_ON: + ret = LED_FULL; + break; + case PCA955X_LS_LED_OFF: + ret = LED_OFF; + break; + case PCA955X_LS_BLINK0: + ret = LED_HALF; + break; + case PCA955X_LS_BLINK1: + ret = pca955x_read_pwm(pca955x->client, 1, &pwm); + if (ret) + return ret; + ret = 255 - pwm; + break; + } + + return ret; +} + static int pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) { @@ -371,6 +419,7 @@ static struct pca955x_platform_data * pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) { struct pca955x_platform_data *pdata; + struct pca955x_led *led; struct fwnode_handle *child; int count; @@ -389,7 +438,7 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) return ERR_PTR(-ENOMEM); device_for_each_child_node(&client->dev, child) { - const char *name; + const char *state; u32 reg; int res; @@ -397,17 +446,22 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) if ((res != 0) || (reg >= chip->bits)) continue; - res = fwnode_property_read_string(child, "label", &name); - if ((res != 0) && is_of_node(child)) - name = to_of_node(child)->name; - - snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), - "%s", name); - - pdata->leds[reg].type = PCA955X_TYPE_LED; - fwnode_property_read_u32(child, "type", &pdata->leds[reg].type); - fwnode_property_read_string(child, "linux,default-trigger", - &pdata->leds[reg].default_trigger); + led = &pdata->leds[reg]; + led->type = PCA955X_TYPE_LED; + led->fwnode = child; + fwnode_property_read_u32(child, "type", &led->type); + + if (!fwnode_property_read_string(child, "default-state", + &state)) { + if (!strcmp(state, "keep")) + led->default_state = LEDS_GPIO_DEFSTATE_KEEP; + else if (!strcmp(state, "on")) + led->default_state = LEDS_GPIO_DEFSTATE_ON; + else + led->default_state = LEDS_GPIO_DEFSTATE_OFF; + } else { + led->default_state = LEDS_GPIO_DEFSTATE_OFF; + } } pdata->num_leds = chip->bits; @@ -425,18 +479,38 @@ static const struct of_device_id of_pca955x_match[] = { }; MODULE_DEVICE_TABLE(of, of_pca955x_match); -static int pca955x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pca955x_probe(struct i2c_client *client) { struct pca955x *pca955x; struct pca955x_led *pca955x_led; struct pca955x_chipdef *chip; + struct led_classdev *led; + struct led_init_data init_data; struct i2c_adapter *adapter; int i, err; struct pca955x_platform_data *pdata; int ngpios = 0; + bool set_default_label = false; + bool keep_pwm = false; + char default_label[8]; + enum pca955x_type chip_type; + const void *md = device_get_match_data(&client->dev); + + if (md) { + chip_type = (enum pca955x_type)md; + } else { + const struct i2c_device_id *id = i2c_match_id(pca955x_id, + client); + + if (id) { + chip_type = (enum pca955x_type)id->driver_data; + } else { + dev_err(&client->dev, "unknown chip\n"); + return -ENODEV; + } + } - chip = &pca955x_chipdefs[id->driver_data]; + chip = &pca955x_chipdefs[chip_type]; adapter = client->adapter; pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -449,13 +523,13 @@ static int pca955x_probe(struct i2c_client *client, if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != chip->slv_addr) { dev_err(&client->dev, "invalid slave address %02x\n", - client->addr); + client->addr); return -ENODEV; } dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " - "slave address 0x%02x\n", - client->name, chip->bits, client->addr); + "slave address 0x%02x\n", client->name, chip->bits, + client->addr); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; @@ -471,8 +545,8 @@ static int pca955x_probe(struct i2c_client *client, if (!pca955x) return -ENOMEM; - pca955x->leds = devm_kcalloc(&client->dev, - chip->bits, sizeof(*pca955x_led), GFP_KERNEL); + pca955x->leds = devm_kcalloc(&client->dev, chip->bits, + sizeof(*pca955x_led), GFP_KERNEL); if (!pca955x->leds) return -ENOMEM; @@ -482,6 +556,9 @@ static int pca955x_probe(struct i2c_client *client, pca955x->client = client; pca955x->chipdef = chip; + init_data.devname_mandatory = false; + init_data.devicename = "pca955x"; + for (i = 0; i < chip->bits; i++) { pca955x_led = &pca955x->leds[i]; pca955x_led->led_num = i; @@ -495,35 +572,60 @@ static int pca955x_probe(struct i2c_client *client, ngpios++; break; case PCA955X_TYPE_LED: - /* - * Platform data can specify LED names and - * default triggers - */ - if (pdata->leds[i].name[0] == '\0') - snprintf(pdata->leds[i].name, - sizeof(pdata->leds[i].name), "%d", i); - - snprintf(pca955x_led->name, - sizeof(pca955x_led->name), "pca955x:%s", - pdata->leds[i].name); - - if (pdata->leds[i].default_trigger) - pca955x_led->led_cdev.default_trigger = - pdata->leds[i].default_trigger; - - pca955x_led->led_cdev.name = pca955x_led->name; - pca955x_led->led_cdev.brightness_set_blocking = - pca955x_led_set; - - err = devm_led_classdev_register(&client->dev, - &pca955x_led->led_cdev); + led = &pca955x_led->led_cdev; + led->brightness_set_blocking = pca955x_led_set; + led->brightness_get = pca955x_led_get; + + if (pdata->leds[i].default_state == + LEDS_GPIO_DEFSTATE_OFF) { + err = pca955x_led_set(led, LED_OFF); + if (err) + return err; + } else if (pdata->leds[i].default_state == + LEDS_GPIO_DEFSTATE_ON) { + err = pca955x_led_set(led, LED_FULL); + if (err) + return err; + } + + init_data.fwnode = pdata->leds[i].fwnode; + + if (is_of_node(init_data.fwnode)) { + if (to_of_node(init_data.fwnode)->name[0] == + '\0') + set_default_label = true; + else + set_default_label = false; + } else { + set_default_label = true; + } + + if (set_default_label) { + snprintf(default_label, sizeof(default_label), + "%d", i); + init_data.default_label = default_label; + } else { + init_data.default_label = NULL; + } + + err = devm_led_classdev_register_ext(&client->dev, led, + &init_data); if (err) return err; - /* Turn off LED */ - err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); - if (err) - return err; + /* + * For default-state == "keep", let the core update the + * brightness from the hardware, then check the + * brightness to see if it's using PWM1. If so, PWM1 + * should not be written below. + */ + if (pdata->leds[i].default_state == + LEDS_GPIO_DEFSTATE_KEEP) { + if (led->brightness != LED_FULL && + led->brightness != LED_OFF && + led->brightness != LED_HALF) + keep_pwm = true; + } } } @@ -532,10 +634,12 @@ static int pca955x_probe(struct i2c_client *client, if (err) return err; - /* PWM1 is used for variable brightness, default to OFF */ - err = pca955x_write_pwm(client, 1, 0); - if (err) - return err; + if (!keep_pwm) { + /* PWM1 is used for variable brightness, default to OFF */ + err = pca955x_write_pwm(client, 1, 0); + if (err) + return err; + } /* Set to fast frequency so we do not see flashing */ err = pca955x_write_psc(client, 0, 0); @@ -581,7 +685,7 @@ static struct i2c_driver pca955x_driver = { .name = "leds-pca955x", .of_match_table = of_pca955x_match, }, - .probe = pca955x_probe, + .probe_new = pca955x_probe, .id_table = pca955x_id, }; diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index d71e9fa5c8de..6832180c1c54 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -17,10 +17,12 @@ #include <linux/err.h> #include <linux/pwm.h> #include <linux/slab.h> +#include "leds.h" struct led_pwm { const char *name; u8 active_low; + u8 default_state; unsigned int max_brightness; }; @@ -77,7 +79,38 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, led_data->cdev.brightness_set_blocking = led_pwm_set; - pwm_init_state(led_data->pwm, &led_data->pwmstate); + /* init PWM state */ + switch (led->default_state) { + case LEDS_DEFSTATE_KEEP: + pwm_get_state(led_data->pwm, &led_data->pwmstate); + if (led_data->pwmstate.period) + break; + led->default_state = LEDS_DEFSTATE_OFF; + dev_warn(dev, + "failed to read period for %s, default to off", + led->name); + fallthrough; + default: + pwm_init_state(led_data->pwm, &led_data->pwmstate); + break; + } + + /* set brightness */ + switch (led->default_state) { + case LEDS_DEFSTATE_ON: + led_data->cdev.brightness = led->max_brightness; + break; + case LEDS_DEFSTATE_KEEP: + { + uint64_t brightness; + + brightness = led->max_brightness; + brightness *= led_data->pwmstate.duty_cycle; + do_div(brightness, led_data->pwmstate.period); + led_data->cdev.brightness = brightness; + } + break; + } ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); if (ret) { @@ -86,11 +119,13 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, return ret; } - ret = led_pwm_set(&led_data->cdev, led_data->cdev.brightness); - if (ret) { - dev_err(dev, "failed to set led PWM value for %s: %d", - led->name, ret); - return ret; + if (led->default_state != LEDS_DEFSTATE_KEEP) { + ret = led_pwm_set(&led_data->cdev, led_data->cdev.brightness); + if (ret) { + dev_err(dev, "failed to set led PWM value for %s: %d", + led->name, ret); + return ret; + } } priv->num_leds++; @@ -120,6 +155,8 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv) fwnode_property_read_u32(fwnode, "max-brightness", &led.max_brightness); + led.default_state = led_init_default_state_get(fwnode); + ret = led_pwm_add(dev, priv, &led, fwnode); if (ret) goto err_child_out; diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 345062ccabda..aa64757a4d89 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -27,6 +27,7 @@ ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count); +enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode); extern struct rw_semaphore leds_list_lock; extern struct list_head leds_list; diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index b77a01bd27f4..1f1d57288085 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -34,7 +34,7 @@ config LEDS_TRIGGER_ONESHOT config LEDS_TRIGGER_DISK bool "LED Disk Trigger" - depends on IDE_GD_ATA || ATA + depends on ATA help This allows LEDs to be controlled by disk activity. If unsure, say Y. diff --git a/drivers/leds/trigger/ledtrig-audio.c b/drivers/leds/trigger/ledtrig-audio.c index f76621e88482..c6b437e6369b 100644 --- a/drivers/leds/trigger/ledtrig-audio.c +++ b/drivers/leds/trigger/ledtrig-audio.c @@ -6,10 +6,33 @@ #include <linux/kernel.h> #include <linux/leds.h> #include <linux/module.h> +#include "../leds.h" -static struct led_trigger *ledtrig_audio[NUM_AUDIO_LEDS]; static enum led_brightness audio_state[NUM_AUDIO_LEDS]; +static int ledtrig_audio_mute_activate(struct led_classdev *led_cdev) +{ + led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MUTE]); + return 0; +} + +static int ledtrig_audio_micmute_activate(struct led_classdev *led_cdev) +{ + led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MICMUTE]); + return 0; +} + +static struct led_trigger ledtrig_audio[NUM_AUDIO_LEDS] = { + [LED_AUDIO_MUTE] = { + .name = "audio-mute", + .activate = ledtrig_audio_mute_activate, + }, + [LED_AUDIO_MICMUTE] = { + .name = "audio-micmute", + .activate = ledtrig_audio_micmute_activate, + }, +}; + enum led_brightness ledtrig_audio_get(enum led_audio type) { return audio_state[type]; @@ -19,24 +42,22 @@ EXPORT_SYMBOL_GPL(ledtrig_audio_get); void ledtrig_audio_set(enum led_audio type, enum led_brightness state) { audio_state[type] = state; - led_trigger_event(ledtrig_audio[type], state); + led_trigger_event(&ledtrig_audio[type], state); } EXPORT_SYMBOL_GPL(ledtrig_audio_set); static int __init ledtrig_audio_init(void) { - led_trigger_register_simple("audio-mute", - &ledtrig_audio[LED_AUDIO_MUTE]); - led_trigger_register_simple("audio-micmute", - &ledtrig_audio[LED_AUDIO_MICMUTE]); + led_trigger_register(&ledtrig_audio[LED_AUDIO_MUTE]); + led_trigger_register(&ledtrig_audio[LED_AUDIO_MICMUTE]); return 0; } module_init(ledtrig_audio_init); static void __exit ledtrig_audio_exit(void) { - led_trigger_unregister_simple(ledtrig_audio[LED_AUDIO_MUTE]); - led_trigger_unregister_simple(ledtrig_audio[LED_AUDIO_MICMUTE]); + led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MUTE]); + led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MICMUTE]); } module_exit(ledtrig_audio_exit); diff --git a/include/linux/leds.h b/include/linux/leds.h index 329fd914cf24..a0b730be40ad 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -33,6 +33,12 @@ enum led_brightness { LED_FULL = 255, }; +enum led_default_state { + LEDS_DEFSTATE_OFF = 0, + LEDS_DEFSTATE_ON = 1, + LEDS_DEFSTATE_KEEP = 2, +}; + struct led_init_data { /* device fwnode handle */ struct fwnode_handle *fwnode; @@ -520,9 +526,9 @@ struct gpio_led { /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ struct gpio_desc *gpiod; }; -#define LEDS_GPIO_DEFSTATE_OFF 0 -#define LEDS_GPIO_DEFSTATE_ON 1 -#define LEDS_GPIO_DEFSTATE_KEEP 2 +#define LEDS_GPIO_DEFSTATE_OFF LEDS_DEFSTATE_OFF +#define LEDS_GPIO_DEFSTATE_ON LEDS_DEFSTATE_ON +#define LEDS_GPIO_DEFSTATE_KEEP LEDS_DEFSTATE_KEEP struct gpio_led_platform_data { int num_leds; |