diff options
author | Mark Brown <broonie@kernel.org> | 2024-06-17 20:38:37 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2024-06-17 20:38:37 +0100 |
commit | 2cb4156a2927bc97998db9bfb71ea567e84a8a98 (patch) | |
tree | 581cad2960cfa8bc33a05d3d1b28e9bf4d320393 | |
parent | ae8577e9079568fe89fe3bf6ffa1d90fda2cfe0a (diff) | |
parent | 7c85503b6d597b84ea58fe3dd95cd9eaeb1f3206 (diff) |
Merge branch 'for-leds-next' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git
39 files changed, 367 insertions, 117 deletions
diff --git a/Documentation/leds/leds-blinkm.rst b/Documentation/leds/leds-blinkm.rst index c74b5bc877b1..2d3c226a371a 100644 --- a/Documentation/leds/leds-blinkm.rst +++ b/Documentation/leds/leds-blinkm.rst @@ -7,7 +7,7 @@ The leds-blinkm driver supports the devices of the BlinkM family. They are RGB-LED modules driven by a (AT)tiny microcontroller and communicate through I2C. The default address of these modules is 0x09 but this can be changed through a command. By this you could -dasy-chain up to 127 BlinkMs on an I2C bus. +daisy-chain up to 127 BlinkMs on an I2C bus. The device accepts RGB and HSB color values through separate commands. Also you can store blinking sequences as "scripts" in diff --git a/MAINTAINERS b/MAINTAINERS index e46204e66c56..b1a138fe7477 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12525,7 +12525,7 @@ M: Pavel Machek <pavel@ucw.cz> M: Lee Jones <lee@kernel.org> L: linux-leds@vger.kernel.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds.git F: Documentation/devicetree/bindings/leds/ F: Documentation/leds/ F: drivers/leds/ diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 05e6af88b88c..eafdf6e2ed7e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -869,7 +869,6 @@ config LEDS_SPI_BYTE tristate "LED support for SPI LED controller with a single byte" depends on LEDS_CLASS depends on SPI - depends on OF help This option enables support for LED controller which use a single byte for controlling the brightness. Currently the following controller is diff --git a/drivers/leds/blink/leds-bcm63138.c b/drivers/leds/blink/leds-bcm63138.c index 2cf2761e4914..3a5e0b98bfbc 100644 --- a/drivers/leds/blink/leds-bcm63138.c +++ b/drivers/leds/blink/leds-bcm63138.c @@ -303,5 +303,6 @@ static struct platform_driver bcm63138_leds_driver = { module_platform_driver(bcm63138_leds_driver); MODULE_AUTHOR("Rafał Miłecki"); +MODULE_DESCRIPTION("Broadcom BCM63138 SoC LED driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table); diff --git a/drivers/leds/flash/leds-as3645a.c b/drivers/leds/flash/leds-as3645a.c index 12c2609c1137..2c6ef321b7c8 100644 --- a/drivers/leds/flash/leds-as3645a.c +++ b/drivers/leds/flash/leds-as3645a.c @@ -743,8 +743,8 @@ static void as3645a_remove(struct i2c_client *client) } static const struct i2c_device_id as3645a_id_table[] = { - { AS_NAME, 0 }, - { }, + { AS_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, as3645a_id_table); diff --git a/drivers/leds/flash/leds-mt6360.c b/drivers/leds/flash/leds-mt6360.c index 1b75b4d36834..4c74f1cf01f0 100644 --- a/drivers/leds/flash/leds-mt6360.c +++ b/drivers/leds/flash/leds-mt6360.c @@ -643,14 +643,17 @@ static int mt6360_init_isnk_properties(struct mt6360_led *led, ret = fwnode_property_read_u32(child, "reg", ®); if (ret || reg > MT6360_LED_ISNK3 || - priv->leds_active & BIT(reg)) + priv->leds_active & BIT(reg)) { + fwnode_handle_put(child); return -EINVAL; + } ret = fwnode_property_read_u32(child, "color", &color); if (ret) { dev_err(priv->dev, "led %d, no color specified\n", led->led_no); + fwnode_handle_put(child); return ret; } diff --git a/drivers/leds/flash/leds-rt4505.c b/drivers/leds/flash/leds-rt4505.c index 1ae5b387f4a5..f16358b8dfc1 100644 --- a/drivers/leds/flash/leds-rt4505.c +++ b/drivers/leds/flash/leds-rt4505.c @@ -426,4 +426,5 @@ static struct i2c_driver rt4505_driver = { module_i2c_driver(rt4505_driver); MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); +MODULE_DESCRIPTION("Richtek RT4505 LED driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index ef7d1c6767ca..3b4db39f2326 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -123,15 +123,22 @@ static void led_timer_function(struct timer_list *t) static void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev, unsigned int value) { - int ret = 0; + int ret; ret = __led_set_brightness(led_cdev, value); - if (ret == -ENOTSUPP) + if (ret == -ENOTSUPP) { ret = __led_set_brightness_blocking(led_cdev, value); - if (ret < 0 && - /* LED HW might have been unplugged, therefore don't warn */ - !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) && - (led_cdev->flags & LED_HW_PLUGGABLE))) + if (ret == -ENOTSUPP) + /* No back-end support to set a fixed brightness value */ + return; + } + + /* LED HW might have been unplugged, therefore don't warn */ + if (ret == -ENODEV && led_cdev->flags & LED_UNREGISTERING && + led_cdev->flags & LED_HW_PLUGGABLE) + return; + + if (ret < 0) dev_err(led_cdev->dev, "Setting an LED's brightness failed (%d)\n", ret); } diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 638aa6591092..78eb20093b2c 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -179,9 +179,9 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) cancel_work_sync(&led_cdev->set_brightness_work); led_stop_software_blink(led_cdev); + device_remove_groups(led_cdev->dev, led_cdev->trigger->groups); if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); - device_remove_groups(led_cdev->dev, led_cdev->trigger->groups); led_cdev->trigger = NULL; led_cdev->trigger_data = NULL; led_cdev->activated = false; @@ -194,6 +194,19 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) spin_unlock(&trig->leddev_list_lock); led_cdev->trigger = trig; + /* + * Some activate() calls use led_trigger_event() to initialize + * the brightness of the LED for which the trigger is being set. + * Ensure the led_cdev is visible on trig->led_cdevs for this. + */ + synchronize_rcu(); + + /* + * If "set brightness to 0" is pending in workqueue, + * we don't want that to be reordered after ->activate() + */ + flush_work(&led_cdev->set_brightness_work); + ret = 0; if (trig->activate) ret = trig->activate(led_cdev); diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index decfca447d8a..a42cc4bc6917 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -331,8 +331,8 @@ static const struct of_device_id an30259a_match_table[] = { MODULE_DEVICE_TABLE(of, an30259a_match_table); static const struct i2c_device_id an30259a_id[] = { - { "an30259a", 0 }, - { /* sentinel */ }, + { "an30259a" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, an30259a_id); diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index 0792ea126cea..2a08c5f27608 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -776,7 +776,7 @@ static int bd2802_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume); static const struct i2c_device_id bd2802_id[] = { - { "BD2802", 0 }, + { "BD2802" }, { } }; MODULE_DEVICE_TABLE(i2c, bd2802_id); diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 2782da1a1930..e40b87aead2d 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -718,7 +718,7 @@ static void blinkm_remove(struct i2c_client *client) } static const struct i2c_device_id blinkm_id[] = { - {"blinkm", 0}, + { "blinkm" }, {} }; diff --git a/drivers/leds/leds-is31fl319x.c b/drivers/leds/leds-is31fl319x.c index 66c65741202e..5e1a4d39a107 100644 --- a/drivers/leds/leds-is31fl319x.c +++ b/drivers/leds/leds-is31fl319x.c @@ -140,7 +140,7 @@ static const struct reg_default is31fl3190_reg_defaults[] = { { IS31FL3190_PWM(2), 0x00 }, }; -static struct regmap_config is31fl3190_regmap_config = { +static const struct regmap_config is31fl3190_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = IS31FL3190_RESET, @@ -178,7 +178,7 @@ static const struct reg_default is31fl3196_reg_defaults[] = { { IS31FL3196_PWM(8), 0x00 }, }; -static struct regmap_config is31fl3196_regmap_config = { +static const struct regmap_config is31fl3196_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = IS31FL3196_REG_CNT, diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index a2feef8e4ac5..e44a3db106c3 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -478,7 +478,7 @@ static void lm3530_remove(struct i2c_client *client) } static const struct i2c_device_id lm3530_id[] = { - {LM3530_NAME, 0}, + { LM3530_NAME }, {} }; MODULE_DEVICE_TABLE(i2c, lm3530_id); diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 8c90701dc50d..54b5650877f7 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -726,7 +726,7 @@ static const struct of_device_id of_lm3532_leds_match[] = { MODULE_DEVICE_TABLE(of, of_lm3532_leds_match); static const struct i2c_device_id lm3532_id[] = { - {LM3532_NAME, 0}, + { LM3532_NAME }, {} }; MODULE_DEVICE_TABLE(i2c, lm3532_id); diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 6eee52e211be..61629d5d6703 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -390,7 +390,7 @@ static void lm3642_remove(struct i2c_client *client) } static const struct i2c_device_id lm3642_id[] = { - {LM3642_NAME, 0}, + { LM3642_NAME }, {} }; diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 380d17a58fe9..99de2a331727 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -360,7 +360,7 @@ static void lm3697_remove(struct i2c_client *client) } static const struct i2c_device_id lm3697_id[] = { - { "lm3697", 0 }, + { "lm3697" }, { } }; MODULE_DEVICE_TABLE(i2c, lm3697_id); diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index 8ea746c499d1..ccfeee49ea78 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -417,7 +417,7 @@ static void lp3944_remove(struct i2c_client *client) /* lp3944 i2c driver struct */ static const struct i2c_device_id lp3944_id[] = { - {"lp3944", 0}, + { "lp3944" }, {} }; diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index ff7bae2447dd..17219a582704 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -266,7 +266,7 @@ static int lp3952_probe(struct i2c_client *client) } static const struct i2c_device_id lp3952_id[] = { - {LP3952_NAME, 0}, + { LP3952_NAME }, {} }; MODULE_DEVICE_TABLE(i2c, lp3952_id); diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f9c8b568b652..d242c12e7569 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -591,7 +591,7 @@ static void lp5521_remove(struct i2c_client *client) } static const struct i2c_device_id lp5521_id[] = { - { "lp5521", 0 }, /* Three channel chip */ + { "lp5521" }, /* Three channel chip */ { } }; MODULE_DEVICE_TABLE(i2c, lp5521_id); diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 39db9aeb67c5..e545ca8bd1f6 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -584,7 +584,7 @@ static void lp5562_remove(struct i2c_client *client) } static const struct i2c_device_id lp5562_id[] = { - { "lp5562", 0 }, + { "lp5562" }, { } }; MODULE_DEVICE_TABLE(i2c, lp5562_id); diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index ac50aa88939a..68b5c7ae31b9 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -375,7 +375,7 @@ static void lp8501_remove(struct i2c_client *client) } static const struct i2c_device_id lp8501_id[] = { - { "lp8501", 0 }, + { "lp8501" }, { } }; MODULE_DEVICE_TABLE(i2c, lp8501_id); diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c index 19b621012e58..7a136fd81720 100644 --- a/drivers/leds/leds-lp8860.c +++ b/drivers/leds/leds-lp8860.c @@ -459,7 +459,7 @@ static void lp8860_remove(struct i2c_client *client) } static const struct i2c_device_id lp8860_id[] = { - { "lp8860", 0 }, + { "lp8860" }, { } }; MODULE_DEVICE_TABLE(i2c, lp8860_id); diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c index 4f01acb75727..49ab8c9a3f29 100644 --- a/drivers/leds/leds-powernv.c +++ b/drivers/leds/leds-powernv.c @@ -246,29 +246,25 @@ static int powernv_led_classdev(struct platform_device *pdev, const char *cur = NULL; int rc = -1; struct property *p; - struct device_node *np; struct powernv_led_data *powernv_led; struct device *dev = &pdev->dev; - for_each_available_child_of_node(led_node, np) { + for_each_available_child_of_node_scoped(led_node, np) { p = of_find_property(np, "led-types", NULL); while ((cur = of_prop_next_string(p, cur)) != NULL) { powernv_led = devm_kzalloc(dev, sizeof(*powernv_led), GFP_KERNEL); - if (!powernv_led) { - of_node_put(np); + if (!powernv_led) return -ENOMEM; - } powernv_led->common = powernv_led_common; powernv_led->loc_code = (char *)np->name; rc = powernv_led_create(dev, powernv_led, cur); - if (rc) { - of_node_put(np); + if (rc) return rc; - } + } /* while end */ } @@ -278,12 +274,11 @@ static int powernv_led_classdev(struct platform_device *pdev, /* Platform driver probe */ static int powernv_led_probe(struct platform_device *pdev) { - struct device_node *led_node; struct powernv_led_common *powernv_led_common; struct device *dev = &pdev->dev; - int rc; + struct device_node *led_node + __free(device_node) = of_find_node_by_path("/ibm,opal/leds"); - led_node = of_find_node_by_path("/ibm,opal/leds"); if (!led_node) { dev_err(dev, "%s: LED parent device node not found\n", __func__); @@ -292,20 +287,15 @@ static int powernv_led_probe(struct platform_device *pdev) powernv_led_common = devm_kzalloc(dev, sizeof(*powernv_led_common), GFP_KERNEL); - if (!powernv_led_common) { - rc = -ENOMEM; - goto out; - } + if (!powernv_led_common) + return -ENOMEM; mutex_init(&powernv_led_common->lock); powernv_led_common->max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX); platform_set_drvdata(pdev, powernv_led_common); - rc = powernv_led_classdev(pdev, led_node, powernv_led_common); -out: - of_node_put(led_node); - return rc; + return powernv_led_classdev(pdev, led_node, powernv_led_common); } /* Platform driver remove */ diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c index 96296db5f410..d24d0ddf347c 100644 --- a/drivers/leds/leds-spi-byte.c +++ b/drivers/leds/leds-spi-byte.c @@ -29,10 +29,11 @@ */ #include <linux/leds.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/spi/spi.h> #include <linux/mutex.h> +#include <linux/property.h> +#include <linux/spi/spi.h> #include <uapi/linux/uleds.h> struct spi_byte_chipdef { @@ -55,13 +56,6 @@ static const struct spi_byte_chipdef ubnt_acb_spi_led_cdef = { .max_value = 0x3F, }; -static const struct of_device_id spi_byte_dt_ids[] = { - { .compatible = "ubnt,acb-spi-led", .data = &ubnt_acb_spi_led_cdef }, - {}, -}; - -MODULE_DEVICE_TABLE(of, spi_byte_dt_ids); - static int spi_byte_brightness_set_blocking(struct led_classdev *dev, enum led_brightness brightness) { @@ -80,73 +74,60 @@ static int spi_byte_brightness_set_blocking(struct led_classdev *dev, static int spi_byte_probe(struct spi_device *spi) { - struct device_node *child; + struct fwnode_handle *child __free(fwnode_handle) = NULL; struct device *dev = &spi->dev; struct spi_byte_led *led; struct led_init_data init_data = {}; - const char *state; + enum led_default_state state; int ret; - if (of_get_available_child_count(dev_of_node(dev)) != 1) { + if (device_get_child_node_count(dev) != 1) { dev_err(dev, "Device must have exactly one LED sub-node."); return -EINVAL; } - child = of_get_next_available_child(dev_of_node(dev), NULL); led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; + ret = devm_mutex_init(dev, &led->mutex); + if (ret) + return ret; + led->spi = spi; - mutex_init(&led->mutex); led->cdef = device_get_match_data(dev); led->ldev.brightness = LED_OFF; led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value; led->ldev.brightness_set_blocking = spi_byte_brightness_set_blocking; - state = of_get_property(child, "default-state", NULL); - if (state) { - if (!strcmp(state, "on")) { - led->ldev.brightness = led->ldev.max_brightness; - } else if (strcmp(state, "off")) { - /* all other cases except "off" */ - dev_err(dev, "default-state can only be 'on' or 'off'"); - return -EINVAL; - } - } + child = device_get_next_child_node(dev, NULL); + + state = led_init_default_state_get(child); + if (state == LEDS_DEFSTATE_ON) + led->ldev.brightness = led->ldev.max_brightness; spi_byte_brightness_set_blocking(&led->ldev, led->ldev.brightness); - init_data.fwnode = of_fwnode_handle(child); + init_data.fwnode = child; init_data.devicename = "leds-spi-byte"; init_data.default_label = ":"; - ret = devm_led_classdev_register_ext(&spi->dev, &led->ldev, &init_data); - if (ret) { - mutex_destroy(&led->mutex); - return ret; - } - spi_set_drvdata(spi, led); - - return 0; + return devm_led_classdev_register_ext(dev, &led->ldev, &init_data); } -static void spi_byte_remove(struct spi_device *spi) -{ - struct spi_byte_led *led = spi_get_drvdata(spi); - - mutex_destroy(&led->mutex); -} +static const struct of_device_id spi_byte_dt_ids[] = { + { .compatible = "ubnt,acb-spi-led", .data = &ubnt_acb_spi_led_cdef }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_byte_dt_ids); static struct spi_driver spi_byte_driver = { .probe = spi_byte_probe, - .remove = spi_byte_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = spi_byte_dt_ids, }, }; - module_spi_driver(spi_byte_driver); MODULE_AUTHOR("Christian Mauderer <oss@c-mauderer.de>"); diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index fcaa34706b6c..2ef9fc7371bd 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -356,8 +356,10 @@ static int ich7_lpc_probe(struct pci_dev *dev, nas_gpio_pci_dev = dev; status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base); - if (status) + if (status) { + status = pcibios_err_to_errno(status); goto out; + } g_pm_io_base &= 0x00000ff80; status = pci_read_config_dword(dev, GPIO_CTRL, &gc); @@ -369,8 +371,9 @@ static int ich7_lpc_probe(struct pci_dev *dev, } status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base); - if (0 > status) { + if (status) { dev_info(&dev->dev, "Unable to read GPIOBASE.\n"); + status = pcibios_err_to_errno(status); goto out; } dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base); diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 945e831ef4ac..6605e08a042a 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -146,7 +146,7 @@ MODULE_DEVICE_TABLE(of, of_tlc591xx_leds_match); static int tlc591xx_probe(struct i2c_client *client) { - struct device_node *np, *child; + struct device_node *np; struct device *dev = &client->dev; const struct tlc591xx *tlc591xx; struct tlc591xx_priv *priv; @@ -182,22 +182,20 @@ tlc591xx_probe(struct i2c_client *client) if (err < 0) return err; - for_each_available_child_of_node(np, child) { + for_each_available_child_of_node_scoped(np, child) { struct tlc591xx_led *led; struct led_init_data init_data = {}; init_data.fwnode = of_fwnode_handle(child); err = of_property_read_u32(child, "reg", ®); - if (err) { - of_node_put(child); + if (err) return err; - } + if (reg < 0 || reg >= tlc591xx->max_leds || - priv->leds[reg].active) { - of_node_put(child); + priv->leds[reg].active) return -EINVAL; - } + led = &priv->leds[reg]; led->active = true; @@ -207,12 +205,10 @@ tlc591xx_probe(struct i2c_client *client) led->ldev.max_brightness = TLC591XX_MAX_BRIGHTNESS; err = devm_led_classdev_register_ext(dev, &led->ldev, &init_data); - if (err < 0) { - of_node_put(child); + if (err < 0) return dev_err_probe(dev, err, "couldn't register LED %s\n", led->ldev.name); - } } return 0; } diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index b443f8c989fa..39f740be058f 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -534,7 +534,7 @@ static const struct of_device_id of_omnia_leds_match[] = { }; static const struct i2c_device_id omnia_id[] = { - { "omnia", 0 }, + { "omnia" }, { } }; MODULE_DEVICE_TABLE(i2c, omnia_id); diff --git a/drivers/leds/rgb/leds-ncp5623.c b/drivers/leds/rgb/leds-ncp5623.c index 2be4ff918516..f18156683375 100644 --- a/drivers/leds/rgb/leds-ncp5623.c +++ b/drivers/leds/rgb/leds-ncp5623.c @@ -183,16 +183,12 @@ static int ncp5623_probe(struct i2c_client *client) fwnode_for_each_available_child_node(mc_node, led_node) { ret = fwnode_property_read_u32(led_node, "color", &color_index); - if (ret) { - fwnode_handle_put(led_node); - goto release_mc_node; - } + if (ret) + goto release_led_node; ret = fwnode_property_read_u32(led_node, "reg", ®); - if (ret) { - fwnode_handle_put(led_node); - goto release_mc_node; - } + if (ret) + goto release_led_node; subled_info[ncp->mc_dev.num_colors].channel = reg; subled_info[ncp->mc_dev.num_colors++].color_index = color_index; @@ -223,6 +219,10 @@ release_mc_node: fwnode_handle_put(mc_node); return ret; + +release_led_node: + fwnode_handle_put(led_node); + goto release_mc_node; } static void ncp5623_remove(struct i2c_client *client) diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 9467c796bd04..e74b2ceed1c2 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2017-2022 Linaro Ltd * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. - * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/bits.h> #include <linux/bitfield.h> @@ -254,6 +254,9 @@ static int lpg_clear_pbs_trigger(struct lpg *lpg, unsigned int lut_mask) u8 val = 0; int rc; + if (!lpg->lpg_chan_sdam) + return 0; + lpg->pbs_en_bitmap &= (~lut_mask); if (!lpg->pbs_en_bitmap) { rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val); @@ -276,6 +279,9 @@ static int lpg_set_pbs_trigger(struct lpg *lpg, unsigned int lut_mask) u8 val = PBS_SW_TRIG_BIT; int rc; + if (!lpg->lpg_chan_sdam) + return 0; + if (!lpg->pbs_en_bitmap) { rc = nvmem_device_write(lpg->lpg_chan_sdam, SDAM_REG_PBS_SEQ_EN, 1, &val); if (rc < 0) diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c index 4183ee71fcce..726c186391af 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c @@ -60,6 +60,7 @@ static struct platform_driver simatic_ipc_led_gpio_apollolake_driver = { }; module_platform_driver(simatic_ipc_led_gpio_apollolake_driver); +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs based on Intel Apollo Lake GPIO"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" KBUILD_MODNAME); MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:apollolake-pinctrl"); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c index 85003fd7f1aa..9bc5f361a06b 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c @@ -102,6 +102,7 @@ out: } EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_probe); +MODULE_DESCRIPTION("Siemens SIMATIC IPC core driver for GPIO based LEDs"); MODULE_LICENSE("GPL v2"); MODULE_SOFTDEP("pre: platform:leds-gpio"); MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c index 4a53d4dbf52f..3fec96c549c1 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c @@ -50,6 +50,7 @@ static struct platform_driver simatic_ipc_led_gpio_elkhartlake_driver = { }; module_platform_driver(simatic_ipc_led_gpio_elkhartlake_driver); +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs based on Intel Elkhart Lake GPIO"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" KBUILD_MODNAME); MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:elkhartlake-pinctrl"); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c index 7a5018639aaf..a1f10952513c 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c @@ -100,6 +100,7 @@ static struct platform_driver simatic_ipc_led_gpio_driver = { }; module_platform_driver(simatic_ipc_led_gpio_driver); +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs based on Nuvoton GPIO"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" KBUILD_MODNAME); MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core gpio_f7188x"); diff --git a/drivers/leds/simple/simatic-ipc-leds.c b/drivers/leds/simple/simatic-ipc-leds.c index 2124f6d09930..348679f0d1b7 100644 --- a/drivers/leds/simple/simatic-ipc-leds.c +++ b/drivers/leds/simple/simatic-ipc-leds.c @@ -128,6 +128,7 @@ static struct platform_driver simatic_ipc_led_driver = { }; module_platform_driver(simatic_ipc_led_driver); +MODULE_DESCRIPTION("LED driver for Siemens Simatic IPCs"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" KBUILD_MODNAME); MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>"); diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 31576952e181..c11282a74b5a 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -145,4 +145,20 @@ config LEDS_TRIGGER_TTY When build as a module this driver will be called ledtrig-tty. +config LEDS_TRIGGER_INPUT_EVENTS + tristate "LED Input events trigger" + depends on INPUT + help + Turn LEDs on when there is input (/dev/input/event*) activity and turn + them back off again after there has been no activity for 5 seconds. + + This is primarily intended to control LEDs which are a backlight for + capacitive touch-buttons, such as e.g. the menu / home / back buttons + found on the bottom bezel of many older smartphones and tablets. + + This can also be used to turn on the keyboard backlight LED on + input events and turn the keyboard backlight off again when idle. + + When build as a module this driver will be called ledtrig-input-events. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 242f6c4e3453..3b3628889f68 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o +obj-$(CONFIG_LEDS_TRIGGER_INPUT_EVENTS) += ledtrig-input-events.o diff --git a/drivers/leds/trigger/ledtrig-input-events.c b/drivers/leds/trigger/ledtrig-input-events.c new file mode 100644 index 000000000000..1de0176799f9 --- /dev/null +++ b/drivers/leds/trigger/ledtrig-input-events.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Input Events LED trigger + * + * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> + * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c. + */ + +#include <linux/input.h> +#include <linux/jiffies.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include "../leds.h" + +#define DEFAULT_LED_OFF_DELAY_MS 5000 + +struct input_events_data { + struct input_handler handler; + struct delayed_work work; + spinlock_t lock; + struct led_classdev *led_cdev; + int led_cdev_saved_flags; + /* To avoid repeatedly setting the brightness while there are events */ + bool led_on; + unsigned long led_off_time; + unsigned long led_off_delay; +}; + +static void led_input_events_work(struct work_struct *work) +{ + struct input_events_data *data = + container_of(work, struct input_events_data, work.work); + + spin_lock_irq(&data->lock); + + /* + * This time_after_eq() check avoids a race where this work starts + * running before a new event pushed led_off_time back. + */ + if (time_after_eq(jiffies, data->led_off_time)) { + led_set_brightness_nosleep(data->led_cdev, LED_OFF); + data->led_on = false; + } + + spin_unlock_irq(&data->lock); +} + +static ssize_t delay_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_events_data *input_events_data = led_trigger_get_drvdata(dev); + + return sysfs_emit(buf, "%lu\n", input_events_data->led_off_delay); +} + +static ssize_t delay_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct input_events_data *input_events_data = led_trigger_get_drvdata(dev); + unsigned long delay; + int ret; + + ret = kstrtoul(buf, 0, &delay); + if (ret) + return ret; + + /* Clamp between 0.5 and 1000 seconds */ + delay = clamp_val(delay, 500UL, 1000000UL); + input_events_data->led_off_delay = msecs_to_jiffies(delay); + + return size; +} + +static DEVICE_ATTR_RW(delay); + +static struct attribute *input_events_led_attrs[] = { + &dev_attr_delay.attr, + NULL +}; +ATTRIBUTE_GROUPS(input_events_led); + +static void input_events_event(struct input_handle *handle, unsigned int type, + unsigned int code, int val) +{ + struct input_events_data *data = + container_of(handle->handler, struct input_events_data, handler); + unsigned long led_off_delay = READ_ONCE(data->led_off_delay); + struct led_classdev *led_cdev = data->led_cdev; + unsigned long flags; + + if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags)) + led_cdev->blink_brightness = led_cdev->new_blink_brightness; + + spin_lock_irqsave(&data->lock, flags); + + if (!data->led_on) { + led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness); + data->led_on = true; + } + data->led_off_time = jiffies + led_off_delay; + + spin_unlock_irqrestore(&data->lock, flags); + + mod_delayed_work(system_wq, &data->work, led_off_delay); +} + +static int input_events_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int ret; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "input-events"; + + ret = input_register_handle(handle); + if (ret) + goto err_free_handle; + + ret = input_open_device(handle); + if (ret) + goto err_unregister_handle; + + return 0; + +err_unregister_handle: + input_unregister_handle(handle); +err_free_handle: + kfree(handle); + return ret; +} + +static void input_events_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id input_events_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_REL) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_ABS) }, + }, + { } +}; + +static int input_events_activate(struct led_classdev *led_cdev) +{ + struct input_events_data *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->handler.name = "input-events"; + data->handler.event = input_events_event; + data->handler.connect = input_events_connect; + data->handler.disconnect = input_events_disconnect; + data->handler.id_table = input_events_ids; + + INIT_DELAYED_WORK(&data->work, led_input_events_work); + spin_lock_init(&data->lock); + + data->led_cdev = led_cdev; + data->led_cdev_saved_flags = led_cdev->flags; + data->led_off_delay = msecs_to_jiffies(DEFAULT_LED_OFF_DELAY_MS); + + /* + * Use led_cdev->blink_brightness + LED_BLINK_SW flag so that sysfs + * brightness writes will change led_cdev->new_blink_brightness for + * configuring the on state brightness (like ledtrig-heartbeat). + */ + if (!led_cdev->blink_brightness) + led_cdev->blink_brightness = led_cdev->max_brightness; + + /* Start with LED off */ + led_set_brightness_nosleep(data->led_cdev, LED_OFF); + + ret = input_register_handler(&data->handler); + if (ret) { + kfree(data); + return ret; + } + + set_bit(LED_BLINK_SW, &led_cdev->work_flags); + + /* Turn LED off during suspend, original flags are restored on deactivate() */ + led_cdev->flags |= LED_CORE_SUSPENDRESUME; + + led_set_trigger_data(led_cdev, data); + return 0; +} + +static void input_events_deactivate(struct led_classdev *led_cdev) +{ + struct input_events_data *data = led_get_trigger_data(led_cdev); + + led_cdev->flags = data->led_cdev_saved_flags; + clear_bit(LED_BLINK_SW, &led_cdev->work_flags); + input_unregister_handler(&data->handler); + cancel_delayed_work_sync(&data->work); + kfree(data); +} + +static struct led_trigger input_events_led_trigger = { + .name = "input-events", + .activate = input_events_activate, + .deactivate = input_events_deactivate, + .groups = input_events_led_groups, +}; +module_led_trigger(input_events_led_trigger); + +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_DESCRIPTION("Input Events LED trigger"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ledtrig:input-events"); diff --git a/drivers/leds/trigger/ledtrig-timer.c b/drivers/leds/trigger/ledtrig-timer.c index b4688d1d9d2b..1d213c999d40 100644 --- a/drivers/leds/trigger/ledtrig-timer.c +++ b/drivers/leds/trigger/ledtrig-timer.c @@ -110,11 +110,6 @@ static int timer_trig_activate(struct led_classdev *led_cdev) led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER; } - /* - * If "set brightness to 0" is pending in workqueue, we don't - * want that to be reordered after blink_set() - */ - flush_work(&led_cdev->set_brightness_work); led_blink_set(led_cdev, &led_cdev->blink_delay_on, &led_cdev->blink_delay_off); |