diff options
Diffstat (limited to 'drivers/leds')
54 files changed, 1128 insertions, 360 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 3132439f99..a3a9ac5b53 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -188,6 +188,7 @@ config LEDS_TURRIS_OMNIA depends on I2C depends on MACH_ARMADA_38X || COMPILE_TEST depends on OF + select LEDS_TRIGGERS help This option enables basic support for the LEDs found on the front side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 35c61311e7..7b04ea1462 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -837,7 +837,7 @@ static int intel_sso_led_probe(struct platform_device *pdev) return 0; } -static int intel_sso_led_remove(struct platform_device *pdev) +static void intel_sso_led_remove(struct platform_device *pdev) { struct sso_led_priv *priv; struct sso_led *led, *n; @@ -850,8 +850,6 @@ static int intel_sso_led_remove(struct platform_device *pdev) } regmap_exit(priv->mmap); - - return 0; } static const struct of_device_id of_sso_led_match[] = { @@ -863,7 +861,7 @@ MODULE_DEVICE_TABLE(of, of_sso_led_match); static struct platform_driver intel_sso_led_driver = { .probe = intel_sso_led_probe, - .remove = intel_sso_led_remove, + .remove_new = intel_sso_led_remove, .driver = { .name = "lgm-ssoled", .of_match_table = of_sso_led_match, diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c index f12ecb2c65..0195935a7c 100644 --- a/drivers/leds/flash/leds-aat1290.c +++ b/drivers/leds/flash/leds-aat1290.c @@ -522,7 +522,7 @@ err_flash_register: return ret; } -static int aat1290_led_remove(struct platform_device *pdev) +static void aat1290_led_remove(struct platform_device *pdev) { struct aat1290_led *led = platform_get_drvdata(pdev); @@ -530,8 +530,6 @@ static int aat1290_led_remove(struct platform_device *pdev) led_classdev_flash_unregister(&led->fled_cdev); mutex_destroy(&led->lock); - - return 0; } static const struct of_device_id aat1290_led_dt_match[] = { @@ -542,7 +540,7 @@ MODULE_DEVICE_TABLE(of, aat1290_led_dt_match); static struct platform_driver aat1290_led_driver = { .probe = aat1290_led_probe, - .remove = aat1290_led_remove, + .remove_new = aat1290_led_remove, .driver = { .name = "aat1290", .of_match_table = aat1290_led_dt_match, diff --git a/drivers/leds/flash/leds-ktd2692.c b/drivers/leds/flash/leds-ktd2692.c index 670f3bf2e9..598eee5daa 100644 --- a/drivers/leds/flash/leds-ktd2692.c +++ b/drivers/leds/flash/leds-ktd2692.c @@ -386,15 +386,13 @@ static int ktd2692_probe(struct platform_device *pdev) return 0; } -static int ktd2692_remove(struct platform_device *pdev) +static void ktd2692_remove(struct platform_device *pdev) { struct ktd2692_context *led = platform_get_drvdata(pdev); led_classdev_flash_unregister(&led->fled_cdev); mutex_destroy(&led->lock); - - return 0; } static const struct of_device_id ktd2692_match[] = { @@ -409,7 +407,7 @@ static struct platform_driver ktd2692_driver = { .of_match_table = ktd2692_match, }, .probe = ktd2692_probe, - .remove = ktd2692_remove, + .remove_new = ktd2692_remove, }; module_platform_driver(ktd2692_driver); diff --git a/drivers/leds/flash/leds-lm3601x.c b/drivers/leds/flash/leds-lm3601x.c index b6c524facf..8191be0ef0 100644 --- a/drivers/leds/flash/leds-lm3601x.c +++ b/drivers/leds/flash/leds-lm3601x.c @@ -123,7 +123,7 @@ static const struct regmap_config lm3601x_regmap = { .max_register = LM3601X_DEV_ID_REG, .reg_defaults = lm3601x_regmap_defs, .num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = lm3601x_volatile_reg, }; diff --git a/drivers/leds/flash/leds-max77693.c b/drivers/leds/flash/leds-max77693.c index 5c1faeb55a..9f016b8511 100644 --- a/drivers/leds/flash/leds-max77693.c +++ b/drivers/leds/flash/leds-max77693.c @@ -1016,7 +1016,7 @@ err_register_led1: return ret; } -static int max77693_led_remove(struct platform_device *pdev) +static void max77693_led_remove(struct platform_device *pdev) { struct max77693_led_device *led = platform_get_drvdata(pdev); struct max77693_sub_led *sub_leds = led->sub_leds; @@ -1032,8 +1032,6 @@ static int max77693_led_remove(struct platform_device *pdev) } mutex_destroy(&led->lock); - - return 0; } static const struct of_device_id max77693_led_dt_match[] = { @@ -1044,7 +1042,7 @@ MODULE_DEVICE_TABLE(of, max77693_led_dt_match); static struct platform_driver max77693_led_driver = { .probe = max77693_led_probe, - .remove = max77693_led_remove, + .remove_new = max77693_led_remove, .driver = { .name = "max77693-led", .of_match_table = max77693_led_dt_match, diff --git a/drivers/leds/flash/leds-mt6360.c b/drivers/leds/flash/leds-mt6360.c index 1af6c58983..a90de82f45 100644 --- a/drivers/leds/flash/leds-mt6360.c +++ b/drivers/leds/flash/leds-mt6360.c @@ -91,7 +91,7 @@ struct mt6360_priv { unsigned int fled_torch_used; unsigned int leds_active; unsigned int leds_count; - struct mt6360_led leds[]; + struct mt6360_led leds[] __counted_by(leds_count); }; static int mt6360_mc_brightness_set(struct led_classdev *lcdev, @@ -855,12 +855,11 @@ out_flash_release: return ret; } -static int mt6360_led_remove(struct platform_device *pdev) +static void mt6360_led_remove(struct platform_device *pdev) { struct mt6360_priv *priv = platform_get_drvdata(pdev); mt6360_v4l2_flash_release(priv); - return 0; } static const struct of_device_id __maybe_unused mt6360_led_of_id[] = { @@ -875,7 +874,7 @@ static struct platform_driver mt6360_led_driver = { .of_match_table = mt6360_led_of_id, }, .probe = mt6360_led_probe, - .remove = mt6360_led_remove, + .remove_new = mt6360_led_remove, }; module_platform_driver(mt6360_led_driver); diff --git a/drivers/leds/flash/leds-mt6370-flash.c b/drivers/leds/flash/leds-mt6370-flash.c index 931067c8a7..912d9d6223 100644 --- a/drivers/leds/flash/leds-mt6370-flash.c +++ b/drivers/leds/flash/leds-mt6370-flash.c @@ -81,7 +81,7 @@ struct mt6370_priv { unsigned int fled_torch_used; unsigned int leds_active; unsigned int leds_count; - struct mt6370_led leds[]; + struct mt6370_led leds[] __counted_by(leds_count); }; static int mt6370_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness level) diff --git a/drivers/leds/flash/leds-qcom-flash.c b/drivers/leds/flash/leds-qcom-flash.c index a73d3ea5c9..7c99a30391 100644 --- a/drivers/leds/flash/leds-qcom-flash.c +++ b/drivers/leds/flash/leds-qcom-flash.c @@ -755,7 +755,7 @@ release: return rc; } -static int qcom_flash_led_remove(struct platform_device *pdev) +static void qcom_flash_led_remove(struct platform_device *pdev) { struct qcom_flash_data *flash_data = platform_get_drvdata(pdev); @@ -763,7 +763,6 @@ static int qcom_flash_led_remove(struct platform_device *pdev) v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]); mutex_destroy(&flash_data->lock); - return 0; } static const struct of_device_id qcom_flash_led_match_table[] = { @@ -778,7 +777,7 @@ static struct platform_driver qcom_flash_led_driver = { .of_match_table = qcom_flash_led_match_table, }, .probe = qcom_flash_led_probe, - .remove = qcom_flash_led_remove, + .remove_new = qcom_flash_led_remove, }; module_platform_driver(qcom_flash_led_driver); diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c index 44904fdee3..eef426924e 100644 --- a/drivers/leds/flash/leds-rt8515.c +++ b/drivers/leds/flash/leds-rt8515.c @@ -367,15 +367,13 @@ static int rt8515_probe(struct platform_device *pdev) return 0; } -static int rt8515_remove(struct platform_device *pdev) +static void rt8515_remove(struct platform_device *pdev) { struct rt8515 *rt = platform_get_drvdata(pdev); rt8515_v4l2_flash_release(rt); del_timer_sync(&rt->powerdown_timer); mutex_destroy(&rt->lock); - - return 0; } static const struct of_device_id rt8515_match[] = { @@ -390,7 +388,7 @@ static struct platform_driver rt8515_driver = { .of_match_table = rt8515_match, }, .probe = rt8515_probe, - .remove = rt8515_remove, + .remove_new = rt8515_remove, }; module_platform_driver(rt8515_driver); diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c index d3f50dca51..eb648ff54b 100644 --- a/drivers/leds/flash/leds-sgm3140.c +++ b/drivers/leds/flash/leds-sgm3140.c @@ -278,15 +278,13 @@ err: return ret; } -static int sgm3140_remove(struct platform_device *pdev) +static void sgm3140_remove(struct platform_device *pdev) { struct sgm3140 *priv = platform_get_drvdata(pdev); del_timer_sync(&priv->powerdown_timer); v4l2_flash_release(priv->v4l2_flash); - - return 0; } static const struct of_device_id sgm3140_dt_match[] = { @@ -299,7 +297,7 @@ MODULE_DEVICE_TABLE(of, sgm3140_dt_match); static struct platform_driver sgm3140_driver = { .probe = sgm3140_probe, - .remove = sgm3140_remove, + .remove_new = sgm3140_remove, .driver = { .name = "sgm3140", .of_match_table = sgm3140_dt_match, diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 214ed81eb0..89c9806cc9 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_ID_MAX] = { [LED_COLOR_ID_IR] = "ir", [LED_COLOR_ID_MULTI] = "multicolor", [LED_COLOR_ID_RGB] = "rgb", + [LED_COLOR_ID_PURPLE] = "purple", + [LED_COLOR_ID_ORANGE] = "orange", + [LED_COLOR_ID_PINK] = "pink", + [LED_COLOR_ID_CYAN] = "cyan", + [LED_COLOR_ID_LIME] = "lime", }; EXPORT_SYMBOL_GPL(led_colors); @@ -359,17 +364,17 @@ EXPORT_SYMBOL_GPL(led_set_brightness_sync); int led_update_brightness(struct led_classdev *led_cdev) { - int ret = 0; + int ret; if (led_cdev->brightness_get) { ret = led_cdev->brightness_get(led_cdev); - if (ret >= 0) { - led_cdev->brightness = ret; - return 0; - } + if (ret < 0) + return ret; + + led_cdev->brightness = ret; } - return ret; + return 0; } EXPORT_SYMBOL_GPL(led_update_brightness); diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 508d0d859f..033ab5fed3 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -215,13 +215,11 @@ static int pm860x_led_probe(struct platform_device *pdev) return 0; } -static int pm860x_led_remove(struct platform_device *pdev) +static void pm860x_led_remove(struct platform_device *pdev) { struct pm860x_led *data = platform_get_drvdata(pdev); led_classdev_unregister(&data->cdev); - - return 0; } static struct platform_driver pm860x_led_driver = { @@ -229,7 +227,7 @@ static struct platform_driver pm860x_led_driver = { .name = "88pm860x-led", }, .probe = pm860x_led_probe, - .remove = pm860x_led_remove, + .remove_new = pm860x_led_remove, }; module_platform_driver(pm860x_led_driver); diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index 5a0cc7af2d..d89a4dca50 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -163,7 +163,7 @@ err: return ret; } -static int adp5520_led_remove(struct platform_device *pdev) +static void adp5520_led_remove(struct platform_device *pdev) { struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); struct adp5520_led *led; @@ -177,8 +177,6 @@ static int adp5520_led_remove(struct platform_device *pdev) for (i = 0; i < pdata->num_leds; i++) { led_classdev_unregister(&led[i].cdev); } - - return 0; } static struct platform_driver adp5520_led_driver = { @@ -186,7 +184,7 @@ static struct platform_driver adp5520_led_driver = { .name = "adp5520-led", }, .probe = adp5520_led_probe, - .remove = adp5520_led_remove, + .remove_new = adp5520_led_remove, }; module_platform_driver(adp5520_led_driver); diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 5142efea23..f3bed4f05b 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -116,7 +116,7 @@ struct aw200xx { struct mutex mutex; u32 num_leds; u32 display_rows; - struct aw200xx_led leds[]; + struct aw200xx_led leds[] __counted_by(num_leds); }; static ssize_t dim_show(struct device *dev, struct device_attribute *devattr, @@ -484,7 +484,7 @@ static const struct regmap_config aw200xx_regmap_config = { .num_ranges = ARRAY_SIZE(aw200xx_ranges), .rd_table = &aw200xx_readable_table, .wr_table = &aw200xx_writeable_table, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int aw200xx_probe(struct i2c_client *client) diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index f512e99b97..82da0fe688 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -159,14 +159,13 @@ static int __init clevo_mail_led_probe(struct platform_device *pdev) return led_classdev_register(&pdev->dev, &clevo_mail_led); } -static int clevo_mail_led_remove(struct platform_device *pdev) +static void clevo_mail_led_remove(struct platform_device *pdev) { led_classdev_unregister(&clevo_mail_led); - return 0; } static struct platform_driver clevo_mail_led_driver = { - .remove = clevo_mail_led_remove, + .remove_new = clevo_mail_led_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/leds-cr0014114.c b/drivers/leds/leds-cr0014114.c index b33bca397e..c9914fc51f 100644 --- a/drivers/leds/leds-cr0014114.c +++ b/drivers/leds/leds-cr0014114.c @@ -56,7 +56,7 @@ struct cr0014114 { struct spi_device *spi; u8 *buf; unsigned long delay; - struct cr0014114_led leds[]; + struct cr0014114_led leds[] __counted_by(count); }; static void cr0014114_calc_crc(u8 *buf, const size_t len) diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index 2b5fb00438..f067a5f4d3 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -121,13 +121,11 @@ static int da903x_led_probe(struct platform_device *pdev) return 0; } -static int da903x_led_remove(struct platform_device *pdev) +static void da903x_led_remove(struct platform_device *pdev) { struct da903x_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); - - return 0; } static struct platform_driver da903x_led_driver = { @@ -135,7 +133,7 @@ static struct platform_driver da903x_led_driver = { .name = "da903x-led", }, .probe = da903x_led_probe, - .remove = da903x_led_remove, + .remove_new = da903x_led_remove, }; module_platform_driver(da903x_led_driver); diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c index 04060c862b..64679d6207 100644 --- a/drivers/leds/leds-da9052.c +++ b/drivers/leds/leds-da9052.c @@ -156,7 +156,7 @@ err: return error; } -static int da9052_led_remove(struct platform_device *pdev) +static void da9052_led_remove(struct platform_device *pdev) { struct da9052_led *led = platform_get_drvdata(pdev); struct da9052_pdata *pdata; @@ -172,8 +172,6 @@ static int da9052_led_remove(struct platform_device *pdev) da9052_set_led_brightness(&led[i], LED_OFF); led_classdev_unregister(&led[i].cdev); } - - return 0; } static struct platform_driver da9052_led_driver = { @@ -181,7 +179,7 @@ static struct platform_driver da9052_led_driver = { .name = "da9052-leds", }, .probe = da9052_led_probe, - .remove = da9052_led_remove, + .remove_new = da9052_led_remove, }; module_platform_driver(da9052_led_driver); diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c index 7e7b617bcd..d40194a302 100644 --- a/drivers/leds/leds-el15203000.c +++ b/drivers/leds/leds-el15203000.c @@ -80,7 +80,7 @@ struct el15203000 { struct spi_device *spi; unsigned long delay; size_t count; - struct el15203000_led leds[]; + struct el15203000_led leds[] __counted_by(count); }; #define to_el15203000_led(d) container_of(d, struct el15203000_led, ldev) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 7bfe40a6bf..710c319ad3 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -6,17 +6,21 @@ * Raphael Assenat <raph@8d.com> * Copyright (C) 2008 Freescale Semiconductor, Inc. */ +#include <linux/container_of.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> -#include <linux/kernel.h> #include <linux/leds.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/overflow.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> +#include <linux/types.h> + #include "leds.h" struct gpio_led_data { @@ -125,16 +129,13 @@ static int create_gpio_led(const struct gpio_led *template, return ret; pinctrl = devm_pinctrl_get_select_default(led_dat->cdev.dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - if (ret != -ENODEV) { - dev_warn(led_dat->cdev.dev, - "Failed to select %pOF pinctrl: %d\n", - to_of_node(fwnode), ret); - } else { - /* pinctrl-%d not present, not an error */ - ret = 0; - } + ret = PTR_ERR_OR_ZERO(pinctrl); + /* pinctrl-%d not present, not an error */ + if (ret == -ENODEV) + ret = 0; + if (ret) { + dev_warn(led_dat->cdev.dev, "Failed to select %pfw pinctrl: %d\n", + fwnode, ret); } return ret; @@ -142,12 +143,11 @@ static int create_gpio_led(const struct gpio_led *template, struct gpio_leds_priv { int num_leds; - struct gpio_led_data leds[]; + struct gpio_led_data leds[] __counted_by(num_leds); }; -static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) +static struct gpio_leds_priv *gpio_leds_create(struct device *dev) { - struct device *dev = &pdev->dev; struct fwnode_handle *child; struct gpio_leds_priv *priv; int count, ret; @@ -221,13 +221,13 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx, * device, this will hit the board file, if any and get * the GPIO from there. */ - gpiod = devm_gpiod_get_index(dev, NULL, idx, GPIOD_OUT_LOW); - if (!IS_ERR(gpiod)) { + gpiod = devm_gpiod_get_index_optional(dev, NULL, idx, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) + return gpiod; + if (gpiod) { gpiod_set_consumer_name(gpiod, template->name); return gpiod; } - if (PTR_ERR(gpiod) != -ENOENT) - return gpiod; /* * This is the legacy code path for platform code that @@ -256,13 +256,13 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx, static int gpio_led_probe(struct platform_device *pdev) { - struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct gpio_led_platform_data *pdata = dev_get_platdata(dev); struct gpio_leds_priv *priv; - int i, ret = 0; + int i, ret; if (pdata && pdata->num_leds) { - priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, pdata->num_leds), - GFP_KERNEL); + priv = devm_kzalloc(dev, struct_size(priv, leds, pdata->num_leds), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -275,22 +275,20 @@ static int gpio_led_probe(struct platform_device *pdev) led_dat->gpiod = template->gpiod; else led_dat->gpiod = - gpio_led_get_gpiod(&pdev->dev, - i, template); + gpio_led_get_gpiod(dev, i, template); if (IS_ERR(led_dat->gpiod)) { - dev_info(&pdev->dev, "Skipping unavailable LED gpio %d (%s)\n", + dev_info(dev, "Skipping unavailable LED gpio %d (%s)\n", template->gpio, template->name); continue; } - ret = create_gpio_led(template, led_dat, - &pdev->dev, NULL, + ret = create_gpio_led(template, led_dat, dev, NULL, pdata->gpio_blink_set); if (ret < 0) return ret; } } else { - priv = gpio_leds_create(pdev); + priv = gpio_leds_create(dev); if (IS_ERR(priv)) return PTR_ERR(priv); } diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index bcd414eb47..a3d33165d2 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -718,7 +718,7 @@ err_deregister: return ret; } -static int lm3533_led_remove(struct platform_device *pdev) +static void lm3533_led_remove(struct platform_device *pdev) { struct lm3533_led *led = platform_get_drvdata(pdev); @@ -726,8 +726,6 @@ static int lm3533_led_remove(struct platform_device *pdev) lm3533_ctrlbank_disable(&led->cb); led_classdev_unregister(&led->cdev); - - return 0; } static void lm3533_led_shutdown(struct platform_device *pdev) @@ -746,7 +744,7 @@ static struct platform_driver lm3533_led_driver = { .name = "lm3533-leds", }, .probe = lm3533_led_probe, - .remove = lm3533_led_remove, + .remove_new = lm3533_led_remove, .shutdown = lm3533_led_shutdown, }; module_platform_driver(lm3533_led_driver); diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index f8ad61e47a..c319ff4d70 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -139,7 +139,7 @@ static const struct regmap_config lm3692x_regmap_config = { .max_register = LM3692X_FAULT_FLAGS, .reg_defaults = lm3692x_reg_defs, .num_reg_defaults = ARRAY_SIZE(lm3692x_reg_defs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int lm3692x_fault_check(struct lm3692x_led *led) diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index cfb8ac220d..380d17a58f 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -89,7 +89,7 @@ struct lm3697 { int bank_cfg; int num_banks; - struct lm3697_led leds[]; + struct lm3697_led leds[] __counted_by(num_banks); }; static const struct reg_default lm3697_reg_defs[] = { diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index 3bd55652a7..5d18bbfd1f 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -101,7 +101,7 @@ static int lp3952_get_label(struct device *dev, const char *label, char *dest) if (ret) return ret; - strncpy(dest, str, LP3952_LABEL_MAX_LEN); + strscpy(dest, str, LP3952_LABEL_MAX_LEN); return 0; } @@ -204,7 +204,7 @@ static const struct regmap_config lp3952_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = REG_MAX, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int lp3952_probe(struct i2c_client *client) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 2ef19ad23b..f9c8b568b6 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -301,6 +301,8 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip) /* Set all PWMs to direct control mode */ ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + if (ret) + return ret; /* Update configuration for the clock setting */ val = LP5521_DEFAULT_CFG; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 77bb26906e..8e7074f0fe 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -442,9 +442,9 @@ int lp55xx_init_device(struct lp55xx_chip *chip) gpiod_direction_output(pdata->enable_gpiod, 0); gpiod_set_consumer_name(pdata->enable_gpiod, "LP55xx enable"); - gpiod_set_value(pdata->enable_gpiod, 0); + gpiod_set_value_cansleep(pdata->enable_gpiod, 0); usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - gpiod_set_value(pdata->enable_gpiod, 1); + gpiod_set_value_cansleep(pdata->enable_gpiod, 1); usleep_range(1000, 2000); /* 500us abs min. */ } diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 675502c15c..bbd1d359bb 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -261,15 +261,13 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) return ret; } -static int mc13xxx_led_remove(struct platform_device *pdev) +static void mc13xxx_led_remove(struct platform_device *pdev) { struct mc13xxx_leds *leds = platform_get_drvdata(pdev); int i; for (i = 0; i < leds->num_leds; i++) led_classdev_unregister(&leds->led[i].cdev); - - return 0; } static const struct mc13xxx_led_devtype mc13783_led_devtype = { @@ -305,7 +303,7 @@ static struct platform_driver mc13xxx_led_driver = { .driver = { .name = "mc13xxx-led", }, - .remove = mc13xxx_led_remove, + .remove_new = mc13xxx_led_remove, .id_table = mc13xxx_led_id_table, }; module_platform_driver_probe(mc13xxx_led_driver, mc13xxx_led_probe); diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c index 39210653ac..d8e3d5d8d2 100644 --- a/drivers/leds/leds-mlxreg.c +++ b/drivers/leds/leds-mlxreg.c @@ -275,13 +275,11 @@ static int mlxreg_led_probe(struct platform_device *pdev) return mlxreg_led_config(priv); } -static int mlxreg_led_remove(struct platform_device *pdev) +static void mlxreg_led_remove(struct platform_device *pdev) { struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev); mutex_destroy(&priv->access_lock); - - return 0; } static struct platform_driver mlxreg_led_driver = { @@ -289,7 +287,7 @@ static struct platform_driver mlxreg_led_driver = { .name = "leds-mlxreg", }, .probe = mlxreg_led_probe, - .remove = mlxreg_led_remove, + .remove_new = mlxreg_led_remove, }; module_platform_driver(mlxreg_led_driver); diff --git a/drivers/leds/leds-mt6323.c b/drivers/leds/leds-mt6323.c index 24f35bdb55..40d5085108 100644 --- a/drivers/leds/leds-mt6323.c +++ b/drivers/leds/leds-mt6323.c @@ -632,7 +632,7 @@ put_child_node: return ret; } -static int mt6323_led_remove(struct platform_device *pdev) +static void mt6323_led_remove(struct platform_device *pdev) { struct mt6323_leds *leds = platform_get_drvdata(pdev); const struct mt6323_regs *regs = leds->pdata->regs; @@ -647,8 +647,6 @@ static int mt6323_led_remove(struct platform_device *pdev) RG_DRV_32K_CK_PDN); mutex_destroy(&leds->lock); - - return 0; } static const struct mt6323_regs mt6323_registers = { @@ -723,7 +721,7 @@ MODULE_DEVICE_TABLE(of, mt6323_led_dt_match); static struct platform_driver mt6323_led_driver = { .probe = mt6323_led_probe, - .remove = mt6323_led_remove, + .remove_new = mt6323_led_remove, .driver = { .name = "mt6323-led", .of_match_table = mt6323_led_dt_match, diff --git a/drivers/leds/leds-nic78bx.c b/drivers/leds/leds-nic78bx.c index f196f52eec..a86b43dd99 100644 --- a/drivers/leds/leds-nic78bx.c +++ b/drivers/leds/leds-nic78bx.c @@ -167,15 +167,13 @@ static int nic78bx_probe(struct platform_device *pdev) return ret; } -static int nic78bx_remove(struct platform_device *pdev) +static void nic78bx_remove(struct platform_device *pdev) { struct nic78bx_led_data *led_data = platform_get_drvdata(pdev); /* Lock LED register */ outb(NIC78BX_LOCK_VALUE, led_data->io_base + NIC78BX_LOCK_REG_OFFSET); - - return 0; } static const struct acpi_device_id led_device_ids[] = { @@ -186,7 +184,7 @@ MODULE_DEVICE_TABLE(acpi, led_device_ids); static struct platform_driver led_driver = { .probe = nic78bx_probe, - .remove = nic78bx_remove, + .remove_new = nic78bx_remove, .driver = { .name = KBUILD_MODNAME, .acpi_match_table = ACPI_PTR(led_device_ids), diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index b10e1ef38d..94a9f8a54b 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -76,7 +76,7 @@ struct pca955x_chipdef { int slv_addr_shift; /* Number of bits to ignore */ }; -static struct pca955x_chipdef pca955x_chipdefs[] = { +static const struct pca955x_chipdef pca955x_chipdefs[] = { [pca9550] = { .bits = 2, .slv_addr = /* 110000x */ 0x60, @@ -104,20 +104,10 @@ static struct pca955x_chipdef pca955x_chipdefs[] = { }, }; -static const struct i2c_device_id pca955x_id[] = { - { "pca9550", pca9550 }, - { "pca9551", pca9551 }, - { "pca9552", pca9552 }, - { "ibm-pca9552", ibm_pca9552 }, - { "pca9553", pca9553 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pca955x_id); - struct pca955x { struct mutex lock; struct pca955x_led *leds; - struct pca955x_chipdef *chipdef; + const struct pca955x_chipdef *chipdef; struct i2c_client *client; unsigned long active_pins; #ifdef CONFIG_LEDS_PCA955X_GPIO @@ -415,7 +405,7 @@ static int pca955x_gpio_direction_output(struct gpio_chip *gc, #endif /* CONFIG_LEDS_PCA955X_GPIO */ static struct pca955x_platform_data * -pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) +pca955x_get_pdata(struct i2c_client *client, const struct pca955x_chipdef *chip) { struct pca955x_platform_data *pdata; struct pca955x_led *led; @@ -457,21 +447,11 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) return pdata; } -static const struct of_device_id of_pca955x_match[] = { - { .compatible = "nxp,pca9550", .data = (void *)pca9550 }, - { .compatible = "nxp,pca9551", .data = (void *)pca9551 }, - { .compatible = "nxp,pca9552", .data = (void *)pca9552 }, - { .compatible = "ibm,pca9552", .data = (void *)ibm_pca9552 }, - { .compatible = "nxp,pca9553", .data = (void *)pca9553 }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_pca955x_match); - static int pca955x_probe(struct i2c_client *client) { struct pca955x *pca955x; struct pca955x_led *pca955x_led; - struct pca955x_chipdef *chip; + const struct pca955x_chipdef *chip; struct led_classdev *led; struct led_init_data init_data; struct i2c_adapter *adapter; @@ -480,24 +460,11 @@ static int pca955x_probe(struct i2c_client *client) 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[chip_type]; + chip = i2c_get_match_data(client); + if (!chip) + return dev_err_probe(&client->dev, -ENODEV, "unknown chip\n"); + adapter = client->adapter; pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -663,6 +630,26 @@ static int pca955x_probe(struct i2c_client *client) return 0; } +static const struct i2c_device_id pca955x_id[] = { + { "pca9550", (kernel_ulong_t)&pca955x_chipdefs[pca9550] }, + { "pca9551", (kernel_ulong_t)&pca955x_chipdefs[pca9551] }, + { "pca9552", (kernel_ulong_t)&pca955x_chipdefs[pca9552] }, + { "ibm-pca9552", (kernel_ulong_t)&pca955x_chipdefs[ibm_pca9552] }, + { "pca9553", (kernel_ulong_t)&pca955x_chipdefs[pca9553] }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pca955x_id); + +static const struct of_device_id of_pca955x_match[] = { + { .compatible = "nxp,pca9550", .data = &pca955x_chipdefs[pca9550] }, + { .compatible = "nxp,pca9551", .data = &pca955x_chipdefs[pca9551] }, + { .compatible = "nxp,pca9552", .data = &pca955x_chipdefs[pca9552] }, + { .compatible = "ibm,pca9552", .data = &pca955x_chipdefs[ibm_pca9552] }, + { .compatible = "nxp,pca9553", .data = &pca955x_chipdefs[pca9553] }, + {} +}; +MODULE_DEVICE_TABLE(of, of_pca955x_match); + static struct i2c_driver pca955x_driver = { .driver = { .name = "leds-pca955x", diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c index 743e2cdd08..4f01acb757 100644 --- a/drivers/leds/leds-powernv.c +++ b/drivers/leds/leds-powernv.c @@ -309,7 +309,7 @@ out: } /* Platform driver remove */ -static int powernv_led_remove(struct platform_device *pdev) +static void powernv_led_remove(struct platform_device *pdev) { struct powernv_led_common *powernv_led_common; @@ -321,7 +321,6 @@ static int powernv_led_remove(struct platform_device *pdev) mutex_destroy(&powernv_led_common->lock); dev_info(&pdev->dev, "PowerNV led module unregistered\n"); - return 0; } /* Platform driver property match */ @@ -335,7 +334,7 @@ MODULE_DEVICE_TABLE(of, powernv_led_match); static struct platform_driver powernv_led_driver = { .probe = powernv_led_probe, - .remove = powernv_led_remove, + .remove_new = powernv_led_remove, .driver = { .name = "powernv-led-driver", .of_match_table = powernv_led_match, diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c index b6447c1721..e66f73879c 100644 --- a/drivers/leds/leds-rb532.c +++ b/drivers/leds/leds-rb532.c @@ -42,15 +42,14 @@ static int rb532_led_probe(struct platform_device *pdev) return led_classdev_register(&pdev->dev, &rb532_uled); } -static int rb532_led_remove(struct platform_device *pdev) +static void rb532_led_remove(struct platform_device *pdev) { led_classdev_unregister(&rb532_uled); - return 0; } static struct platform_driver rb532_led_driver = { .probe = rb532_led_probe, - .remove = rb532_led_remove, + .remove_new = rb532_led_remove, .driver = { .name = "rb532-led", }, diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c index 8a8b73b4e3..848e929c4a 100644 --- a/drivers/leds/leds-regulator.c +++ b/drivers/leds/leds-regulator.c @@ -173,13 +173,12 @@ static int regulator_led_probe(struct platform_device *pdev) return 0; } -static int regulator_led_remove(struct platform_device *pdev) +static void regulator_led_remove(struct platform_device *pdev) { struct regulator_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); regulator_led_disable(led); - return 0; } static const struct of_device_id regulator_led_of_match[] = { @@ -194,7 +193,7 @@ static struct platform_driver regulator_led_driver = { .of_match_table = regulator_led_of_match, }, .probe = regulator_led_probe, - .remove = regulator_led_remove, + .remove_new = regulator_led_remove, }; module_platform_driver(regulator_led_driver); diff --git a/drivers/leds/leds-sc27xx-bltc.c b/drivers/leds/leds-sc27xx-bltc.c index e199ea15e4..f04db793e8 100644 --- a/drivers/leds/leds-sc27xx-bltc.c +++ b/drivers/leds/leds-sc27xx-bltc.c @@ -296,7 +296,6 @@ static int sc27xx_led_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, priv); - mutex_init(&priv->lock); priv->base = base; priv->regmap = dev_get_regmap(dev->parent, NULL); if (!priv->regmap) { @@ -309,13 +308,11 @@ static int sc27xx_led_probe(struct platform_device *pdev) err = of_property_read_u32(child, "reg", ®); if (err) { of_node_put(child); - mutex_destroy(&priv->lock); return err; } if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) { of_node_put(child); - mutex_destroy(&priv->lock); return -EINVAL; } @@ -323,6 +320,8 @@ static int sc27xx_led_probe(struct platform_device *pdev) priv->leds[reg].active = true; } + mutex_init(&priv->lock); + err = sc27xx_led_register(dev, priv); if (err) mutex_destroy(&priv->lock); @@ -330,12 +329,11 @@ static int sc27xx_led_probe(struct platform_device *pdev) return err; } -static int sc27xx_led_remove(struct platform_device *pdev) +static void sc27xx_led_remove(struct platform_device *pdev) { struct sc27xx_led_priv *priv = platform_get_drvdata(pdev); mutex_destroy(&priv->lock); - return 0; } static const struct of_device_id sc27xx_led_of_match[] = { @@ -350,7 +348,7 @@ static struct platform_driver sc27xx_led_driver = { .of_match_table = sc27xx_led_of_match, }, .probe = sc27xx_led_probe, - .remove = sc27xx_led_remove, + .remove_new = sc27xx_led_remove, }; module_platform_driver(sc27xx_led_driver); diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index eba7313719..6fd89efb42 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -163,15 +163,13 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, return 0; } -static int sunfire_led_generic_remove(struct platform_device *pdev) +static void sunfire_led_generic_remove(struct platform_device *pdev) { struct sunfire_drvdata *p = platform_get_drvdata(pdev); int i; for (i = 0; i < NUM_LEDS_PER_BOARD; i++) led_classdev_unregister(&p->leds[i].led_cdev); - - return 0; } static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { @@ -221,7 +219,7 @@ MODULE_ALIAS("platform:sunfire-fhc-leds"); static struct platform_driver sunfire_clockboard_led_driver = { .probe = sunfire_clockboard_led_probe, - .remove = sunfire_led_generic_remove, + .remove_new = sunfire_led_generic_remove, .driver = { .name = "sunfire-clockboard-leds", }, @@ -229,7 +227,7 @@ static struct platform_driver sunfire_clockboard_led_driver = { static struct platform_driver sunfire_fhc_led_driver = { .probe = sunfire_fhc_led_probe, - .remove = sunfire_led_generic_remove, + .remove_new = sunfire_led_generic_remove, .driver = { .name = "sunfire-fhc-leds", }, diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index aab8617712..e190746140 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -92,9 +92,6 @@ struct tca6507_platform_data { struct led_platform_data leds; -#ifdef CONFIG_GPIOLIB - int gpio_base; -#endif }; #define TCA6507_MAKE_GPIO 1 @@ -636,7 +633,7 @@ static int tca6507_probe_gpios(struct device *dev, tca->gpio.label = "gpio-tca6507"; tca->gpio.ngpio = gpios; - tca->gpio.base = pdata->gpio_base; + tca->gpio.base = -1; tca->gpio.owner = THIS_MODULE; tca->gpio.direction_output = tca6507_gpio_direction_output; tca->gpio.set = tca6507_gpio_set_value; @@ -715,9 +712,6 @@ tca6507_led_dt_init(struct device *dev) pdata->leds.leds = tca_leds; pdata->leds.num_leds = NUM_LEDS; -#ifdef CONFIG_GPIOLIB - pdata->gpio_base = -1; -#endif return pdata; } diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index b13a547e72..b443f8c989 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -15,21 +15,36 @@ #define OMNIA_BOARD_LEDS 12 #define OMNIA_LED_NUM_CHANNELS 3 -#define CMD_LED_MODE 3 -#define CMD_LED_MODE_LED(l) ((l) & 0x0f) -#define CMD_LED_MODE_USER 0x10 +/* MCU controller commands at I2C address 0x2a */ +#define OMNIA_MCU_I2C_ADDR 0x2a -#define CMD_LED_STATE 4 -#define CMD_LED_STATE_LED(l) ((l) & 0x0f) -#define CMD_LED_STATE_ON 0x10 +#define CMD_GET_STATUS_WORD 0x01 +#define STS_FEATURES_SUPPORTED BIT(2) -#define CMD_LED_COLOR 5 -#define CMD_LED_SET_BRIGHTNESS 7 -#define CMD_LED_GET_BRIGHTNESS 8 +#define CMD_GET_FEATURES 0x10 +#define FEAT_LED_GAMMA_CORRECTION BIT(5) + +/* LED controller commands at I2C address 0x2b */ +#define CMD_LED_MODE 0x03 +#define CMD_LED_MODE_LED(l) ((l) & 0x0f) +#define CMD_LED_MODE_USER 0x10 + +#define CMD_LED_STATE 0x04 +#define CMD_LED_STATE_LED(l) ((l) & 0x0f) +#define CMD_LED_STATE_ON 0x10 + +#define CMD_LED_COLOR 0x05 +#define CMD_LED_SET_BRIGHTNESS 0x07 +#define CMD_LED_GET_BRIGHTNESS 0x08 + +#define CMD_SET_GAMMA_CORRECTION 0x30 +#define CMD_GET_GAMMA_CORRECTION 0x31 struct omnia_led { struct led_classdev_mc mc_cdev; struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; + u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; + bool on, hwtrig; int reg; }; @@ -38,72 +53,204 @@ struct omnia_led { struct omnia_leds { struct i2c_client *client; struct mutex lock; + bool has_gamma_correction; struct omnia_led leds[]; }; static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) { u8 buf[2] = { cmd, val }; + int ret; + + ret = i2c_master_send(client, buf, sizeof(buf)); - return i2c_master_send(client, buf, sizeof(buf)); + return ret < 0 ? ret : 0; } -static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) +static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, + void *reply, size_t len) { struct i2c_msg msgs[2]; - u8 reply; int ret; - msgs[0].addr = client->addr; + msgs[0].addr = addr; msgs[0].flags = 0; msgs[0].len = 1; msgs[0].buf = &cmd; - msgs[1].addr = client->addr; + msgs[1].addr = addr; msgs[1].flags = I2C_M_RD; - msgs[1].len = 1; - msgs[1].buf = &reply; + msgs[1].len = len; + msgs[1].buf = reply; - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); if (likely(ret == ARRAY_SIZE(msgs))) - return reply; + return 0; else if (ret < 0) return ret; else return -EIO; } +static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) +{ + u8 reply; + int err; + + err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); + if (err) + return err; + + return reply; +} + +static int omnia_led_send_color_cmd(const struct i2c_client *client, + struct omnia_led *led) +{ + char cmd[5]; + int ret; + + cmd[0] = CMD_LED_COLOR; + cmd[1] = led->reg; + cmd[2] = led->subled_info[0].brightness; + cmd[3] = led->subled_info[1].brightness; + cmd[4] = led->subled_info[2].brightness; + + /* Send the color change command */ + ret = i2c_master_send(client, cmd, 5); + if (ret < 0) + return ret; + + /* Cache the RGB channel brightnesses */ + for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) + led->cached_channels[i] = led->subled_info[i].brightness; + + return 0; +} + +/* Determine if the computed RGB channels are different from the cached ones */ +static bool omnia_led_channels_changed(struct omnia_led *led) +{ + for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) + if (led->subled_info[i].brightness != led->cached_channels[i]) + return true; + + return false; +} + static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, enum led_brightness brightness) { struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); struct omnia_led *led = to_omnia_led(mc_cdev); - u8 buf[5], state; - int ret; + int err = 0; mutex_lock(&leds->lock); - led_mc_calc_color_components(&led->mc_cdev, brightness); + /* + * Only recalculate RGB brightnesses from intensities if brightness is + * non-zero (if it is zero and the LED is in HW blinking mode, we use + * max_brightness as brightness). Otherwise we won't be using them and + * we can save ourselves some software divisions (Omnia's CPU does not + * implement the division instruction). + */ + if (brightness || led->hwtrig) { + led_mc_calc_color_components(mc_cdev, brightness ?: + cdev->max_brightness); + + /* + * Send color command only if brightness is non-zero and the RGB + * channel brightnesses changed. + */ + if (omnia_led_channels_changed(led)) + err = omnia_led_send_color_cmd(leds->client, led); + } - buf[0] = CMD_LED_COLOR; - buf[1] = led->reg; - buf[2] = mc_cdev->subled_info[0].brightness; - buf[3] = mc_cdev->subled_info[1].brightness; - buf[4] = mc_cdev->subled_info[2].brightness; + /* + * Send on/off state change only if (bool)brightness changed and the LED + * is not being blinked by HW. + */ + if (!err && !led->hwtrig && !brightness != !led->on) { + u8 state = CMD_LED_STATE_LED(led->reg); + + if (brightness) + state |= CMD_LED_STATE_ON; + + err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); + if (!err) + led->on = !!brightness; + } + + mutex_unlock(&leds->lock); + + return err; +} + +static struct led_hw_trigger_type omnia_hw_trigger_type; + +static int omnia_hwtrig_activate(struct led_classdev *cdev) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); + struct omnia_led *led = to_omnia_led(mc_cdev); + int err = 0; + + mutex_lock(&leds->lock); + + if (!led->on) { + /* + * If the LED is off (brightness was set to 0), the last + * configured color was not necessarily sent to the MCU. + * Recompute with max_brightness and send if needed. + */ + led_mc_calc_color_components(mc_cdev, cdev->max_brightness); + + if (omnia_led_channels_changed(led)) + err = omnia_led_send_color_cmd(leds->client, led); + } + + if (!err) { + /* Put the LED into MCU controlled mode */ + err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg)); + if (!err) + led->hwtrig = true; + } + + mutex_unlock(&leds->lock); + + return err; +} + +static void omnia_hwtrig_deactivate(struct led_classdev *cdev) +{ + struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); + struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev)); + int err; - state = CMD_LED_STATE_LED(led->reg); - if (buf[2] || buf[3] || buf[4]) - state |= CMD_LED_STATE_ON; + mutex_lock(&leds->lock); + + led->hwtrig = false; - ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); - if (ret >= 0 && (state & CMD_LED_STATE_ON)) - ret = i2c_master_send(leds->client, buf, 5); + /* Put the LED into software mode */ + err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg) | + CMD_LED_MODE_USER); mutex_unlock(&leds->lock); - return ret; + if (err) + dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", + err); } +static struct led_trigger omnia_hw_trigger = { + .name = "omnia-mcu", + .activate = omnia_hwtrig_activate, + .deactivate = omnia_hwtrig_deactivate, + .trigger_type = &omnia_hw_trigger_type, +}; + static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, struct device_node *np) { @@ -129,11 +276,15 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, } led->subled_info[0].color_index = LED_COLOR_ID_RED; - led->subled_info[0].channel = 0; led->subled_info[1].color_index = LED_COLOR_ID_GREEN; - led->subled_info[1].channel = 1; led->subled_info[2].color_index = LED_COLOR_ID_BLUE; - led->subled_info[2].channel = 2; + + /* Initial color is white */ + for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) { + led->subled_info[i].intensity = 255; + led->subled_info[i].brightness = 255; + led->subled_info[i].channel = i; + } led->mc_cdev.subled_info = led->subled_info; led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS; @@ -143,12 +294,18 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, cdev = &led->mc_cdev.led_cdev; cdev->max_brightness = 255; cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; + cdev->trigger_type = &omnia_hw_trigger_type; + /* + * Use the omnia-mcu trigger as the default trigger. It may be rewritten + * by LED class from the linux,default-trigger property. + */ + cdev->default_trigger = omnia_hw_trigger.name; /* put the LED into software mode */ ret = omnia_cmd_write_u8(client, CMD_LED_MODE, CMD_LED_MODE_LED(led->reg) | CMD_LED_MODE_USER); - if (ret < 0) { + if (ret) { dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, ret); return ret; @@ -157,11 +314,19 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, /* disable the LED */ ret = omnia_cmd_write_u8(client, CMD_LED_STATE, CMD_LED_STATE_LED(led->reg)); - if (ret < 0) { + if (ret) { dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); return ret; } + /* Set initial color and cache it */ + ret = omnia_led_send_color_cmd(client, led); + if (ret < 0) { + dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np, + ret); + return ret; + } + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev, &init_data); if (ret < 0) { @@ -202,7 +367,7 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, { struct i2c_client *client = to_i2c_client(dev); unsigned long brightness; - int ret; + int err; if (kstrtoul(buf, 10, &brightness)) return -EINVAL; @@ -210,18 +375,80 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, if (brightness > 100) return -EINVAL; - ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); + err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); - return ret < 0 ? ret : count; + return err ?: count; } static DEVICE_ATTR_RW(brightness); +static ssize_t gamma_correction_show(struct device *dev, + struct device_attribute *a, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct omnia_leds *leds = i2c_get_clientdata(client); + int ret; + + if (leds->has_gamma_correction) { + ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION); + if (ret < 0) + return ret; + } else { + ret = 0; + } + + return sysfs_emit(buf, "%d\n", !!ret); +} + +static ssize_t gamma_correction_store(struct device *dev, + struct device_attribute *a, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct omnia_leds *leds = i2c_get_clientdata(client); + bool val; + int err; + + if (!leds->has_gamma_correction) + return -EOPNOTSUPP; + + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + + err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); + + return err ?: count; +} +static DEVICE_ATTR_RW(gamma_correction); + static struct attribute *omnia_led_controller_attrs[] = { &dev_attr_brightness.attr, + &dev_attr_gamma_correction.attr, NULL, }; ATTRIBUTE_GROUPS(omnia_led_controller); +static int omnia_mcu_get_features(const struct i2c_client *client) +{ + u16 reply; + int err; + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_STATUS_WORD, &reply, sizeof(reply)); + if (err) + return err; + + /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ + if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED)) + return 0; + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_FEATURES, &reply, sizeof(reply)); + if (err) + return err; + + return le16_to_cpu(reply); +} + static int omnia_leds_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -246,8 +473,29 @@ static int omnia_leds_probe(struct i2c_client *client) leds->client = client; i2c_set_clientdata(client, leds); + ret = omnia_mcu_get_features(client); + if (ret < 0) { + dev_err(dev, "Cannot determine MCU supported features: %d\n", + ret); + return ret; + } + + leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION; + if (!leds->has_gamma_correction) { + dev_info(dev, + "Your board's MCU firmware does not support the LED gamma correction feature.\n"); + dev_info(dev, + "Consider upgrading MCU firmware with the omnia-mcutool utility.\n"); + } + mutex_init(&leds->lock); + ret = devm_led_trigger_register(dev, &omnia_hw_trigger); + if (ret < 0) { + dev_err(dev, "Cannot register private LED trigger: %d\n", ret); + return ret; + } + led = &leds->leds[0]; for_each_available_child_of_node(np, child) { ret = omnia_led_register(client, led, child); diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index c48b80574f..70b32d80f9 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -280,13 +280,11 @@ static int wm831x_status_probe(struct platform_device *pdev) return 0; } -static int wm831x_status_remove(struct platform_device *pdev) +static void wm831x_status_remove(struct platform_device *pdev) { struct wm831x_status *drvdata = platform_get_drvdata(pdev); led_classdev_unregister(&drvdata->cdev); - - return 0; } static struct platform_driver wm831x_status_driver = { @@ -294,7 +292,7 @@ static struct platform_driver wm831x_status_driver = { .name = "wm831x-status", }, .probe = wm831x_status_probe, - .remove = wm831x_status_remove, + .remove_new = wm831x_status_remove, }; module_platform_driver(wm831x_status_driver); diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 8f243c4137..61cbefa057 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -242,13 +242,12 @@ static int wm8350_led_probe(struct platform_device *pdev) return led_classdev_register(&pdev->dev, &led->cdev); } -static int wm8350_led_remove(struct platform_device *pdev) +static void wm8350_led_remove(struct platform_device *pdev) { struct wm8350_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); wm8350_led_disable(led); - return 0; } static struct platform_driver wm8350_led_driver = { @@ -256,7 +255,7 @@ static struct platform_driver wm8350_led_driver = { .name = "wm8350-led", }, .probe = wm8350_led_probe, - .remove = wm8350_led_remove, + .remove_new = wm8350_led_remove, .shutdown = wm8350_led_shutdown, }; diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 183bccc06c..a6a21f5646 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -14,6 +14,19 @@ config LEDS_GROUP_MULTICOLOR To compile this driver as a module, choose M here: the module will be called leds-group-multicolor. +config LEDS_KTD202X + tristate "LED support for KTD202x Chips" + depends on I2C + depends on OF + select REGMAP_I2C + help + This option enables support for the Kinetic KTD2026/KTD2027 + RGB/White LED driver found in different BQ mobile phones. + It is a 3 or 4 channel LED driver programmed via an I2C interface. + + To compile this driver as a module, choose M here: the module + will be called leds-ktd202x. + config LEDS_PWM_MULTICOLOR tristate "PWM driven multi-color LED Support" depends on PWM diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile index c11cc56384..243f31e4d7 100644 --- a/drivers/leds/rgb/Makefile +++ b/drivers/leds/rgb/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o +obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o diff --git a/drivers/leds/rgb/leds-ktd202x.c b/drivers/leds/rgb/leds-ktd202x.c new file mode 100644 index 0000000000..514965795a --- /dev/null +++ b/drivers/leds/rgb/leds-ktd202x.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Kinetic KTD2026/7 RGB/White LED driver with I2C interface + * + * Copyright 2023 AndrĂ© Apitzsch <git@apitzsch.eu> + * + * Datasheet: https://www.kinet-ic.com/uploads/KTD2026-7-04h.pdf + */ + +#include <linux/i2c.h> +#include <linux/led-class-multicolor.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#define KTD2026_NUM_LEDS 3 +#define KTD2027_NUM_LEDS 4 +#define KTD202X_MAX_LEDS 4 + +/* Register bank */ +#define KTD202X_REG_RESET_CONTROL 0x00 +#define KTD202X_REG_FLASH_PERIOD 0x01 +#define KTD202X_REG_PWM1_TIMER 0x02 +#define KTD202X_REG_PWM2_TIMER 0x03 +#define KTD202X_REG_CHANNEL_CTRL 0x04 +#define KTD202X_REG_TRISE_FALL 0x05 +#define KTD202X_REG_LED_IOUT(x) (0x06 + (x)) + +/* Register 0 */ +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT1 0x00 +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT2 0x01 +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT3 0x02 +#define KTD202X_TIMER_SLOT_CONTROL_TSLOT4 0x03 +#define KTD202X_RSTR_RESET 0x07 + +#define KTD202X_ENABLE_CTRL_WAKE 0x00 /* SCL High & SDA High */ +#define KTD202X_ENABLE_CTRL_SLEEP 0x08 /* SCL High & SDA Toggling */ + +#define KTD202X_TRISE_FALL_SCALE_NORMAL 0x00 +#define KTD202X_TRISE_FALL_SCALE_SLOW_X2 0x20 +#define KTD202X_TRISE_FALL_SCALE_SLOW_X4 0x40 +#define KTD202X_TRISE_FALL_SCALE_FAST_X8 0x60 + +/* Register 1 */ +#define KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP 0x00 + +/* Register 2-3 */ +#define KTD202X_FLASH_ON_TIME_0_4_PERCENT 0x01 + +/* Register 4 */ +#define KTD202X_CHANNEL_CTRL_MASK(x) (BIT(2 * (x)) | BIT(2 * (x) + 1)) +#define KTD202X_CHANNEL_CTRL_OFF 0x00 +#define KTD202X_CHANNEL_CTRL_ON(x) BIT(2 * (x)) +#define KTD202X_CHANNEL_CTRL_PWM1(x) BIT(2 * (x) + 1) +#define KTD202X_CHANNEL_CTRL_PWM2(x) (BIT(2 * (x)) | BIT(2 * (x) + 1)) + +/* Register 5 */ +#define KTD202X_RAMP_TIMES_2_MS 0x00 + +/* Register 6-9 */ +#define KTD202X_LED_CURRENT_10_mA 0x4f + +#define KTD202X_FLASH_PERIOD_MIN_MS 256 +#define KTD202X_FLASH_PERIOD_STEP_MS 128 +#define KTD202X_FLASH_PERIOD_MAX_STEPS 126 +#define KTD202X_FLASH_ON_MAX 256 + +#define KTD202X_MAX_BRIGHTNESS 192 + +static const struct reg_default ktd202x_reg_defaults[] = { + { KTD202X_REG_RESET_CONTROL, KTD202X_TIMER_SLOT_CONTROL_TSLOT1 | + KTD202X_ENABLE_CTRL_WAKE | KTD202X_TRISE_FALL_SCALE_NORMAL }, + { KTD202X_REG_FLASH_PERIOD, KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP }, + { KTD202X_REG_PWM1_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT }, + { KTD202X_REG_PWM2_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT }, + { KTD202X_REG_CHANNEL_CTRL, KTD202X_CHANNEL_CTRL_OFF }, + { KTD202X_REG_TRISE_FALL, KTD202X_RAMP_TIMES_2_MS }, + { KTD202X_REG_LED_IOUT(0), KTD202X_LED_CURRENT_10_mA }, + { KTD202X_REG_LED_IOUT(1), KTD202X_LED_CURRENT_10_mA }, + { KTD202X_REG_LED_IOUT(2), KTD202X_LED_CURRENT_10_mA }, + { KTD202X_REG_LED_IOUT(3), KTD202X_LED_CURRENT_10_mA }, +}; + +struct ktd202x_led { + struct ktd202x *chip; + union { + struct led_classdev cdev; + struct led_classdev_mc mcdev; + }; + u32 index; +}; + +struct ktd202x { + struct mutex mutex; + struct regulator_bulk_data regulators[2]; + struct device *dev; + struct regmap *regmap; + bool enabled; + int num_leds; + struct ktd202x_led leds[] __counted_by(num_leds); +}; + +static int ktd202x_chip_disable(struct ktd202x *chip) +{ + int ret; + + if (!chip->enabled) + return 0; + + regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_SLEEP); + + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err(chip->dev, "Failed to disable regulators: %d\n", ret); + return ret; + } + + chip->enabled = false; + return 0; +} + +static int ktd202x_chip_enable(struct ktd202x *chip) +{ + int ret; + + if (chip->enabled) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err(chip->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + chip->enabled = true; + + ret = regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_WAKE); + + if (ret) { + dev_err(chip->dev, "Failed to enable the chip: %d\n", ret); + ktd202x_chip_disable(chip); + } + + return ret; +} + +static bool ktd202x_chip_in_use(struct ktd202x *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + if (chip->leds[i].cdev.brightness) + return true; + } + + return false; +} + +static int ktd202x_brightness_set(struct ktd202x_led *led, + struct mc_subled *subleds, + unsigned int num_channels) +{ + bool mode_blink = false; + int channel; + int state; + int ret; + int i; + + if (ktd202x_chip_in_use(led->chip)) { + ret = ktd202x_chip_enable(led->chip); + if (ret) + return ret; + } + + ret = regmap_read(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, &state); + if (ret) + return ret; + + /* + * In multicolor case, assume blink mode if PWM is set for at least one + * channel because another channel cannot be in state ON at the same time + */ + for (i = 0; i < num_channels; i++) { + int channel_state; + + channel = subleds[i].channel; + channel_state = (state >> 2 * channel) & KTD202X_CHANNEL_CTRL_MASK(0); + if (channel_state == KTD202X_CHANNEL_CTRL_OFF) + continue; + mode_blink = channel_state == KTD202X_CHANNEL_CTRL_PWM1(0); + break; + } + + for (i = 0; i < num_channels; i++) { + enum led_brightness brightness; + int mode; + + brightness = subleds[i].brightness; + channel = subleds[i].channel; + + if (brightness) { + /* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */ + ret = regmap_write(led->chip->regmap, KTD202X_REG_LED_IOUT(channel), + brightness - 1); + if (ret) + return ret; + + if (mode_blink) + mode = KTD202X_CHANNEL_CTRL_PWM1(channel); + else + mode = KTD202X_CHANNEL_CTRL_ON(channel); + } else { + mode = KTD202X_CHANNEL_CTRL_OFF; + } + ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, + KTD202X_CHANNEL_CTRL_MASK(channel), mode); + if (ret) + return ret; + } + + if (!ktd202x_chip_in_use(led->chip)) + return ktd202x_chip_disable(led->chip); + + return 0; +} + +static int ktd202x_brightness_single_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev); + struct mc_subled info; + int ret; + + cdev->brightness = value; + + mutex_lock(&led->chip->mutex); + + info.brightness = value; + info.channel = led->index; + ret = ktd202x_brightness_set(led, &info, 1); + + mutex_unlock(&led->chip->mutex); + + return ret; +} + +static int ktd202x_brightness_mc_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev); + int ret; + + cdev->brightness = value; + + mutex_lock(&led->chip->mutex); + + led_mc_calc_color_components(mc, value); + ret = ktd202x_brightness_set(led, mc->subled_info, mc->num_colors); + + mutex_unlock(&led->chip->mutex); + + return ret; +} + +static int ktd202x_blink_set(struct ktd202x_led *led, unsigned long *delay_on, + unsigned long *delay_off, struct mc_subled *subleds, + unsigned int num_channels) +{ + unsigned long delay_total_ms; + int ret, num_steps, on; + u8 ctrl_mask = 0; + u8 ctrl_pwm1 = 0; + u8 ctrl_on = 0; + int i; + + mutex_lock(&led->chip->mutex); + + for (i = 0; i < num_channels; i++) { + int channel = subleds[i].channel; + + ctrl_mask |= KTD202X_CHANNEL_CTRL_MASK(channel); + ctrl_on |= KTD202X_CHANNEL_CTRL_ON(channel); + ctrl_pwm1 |= KTD202X_CHANNEL_CTRL_PWM1(channel); + } + + /* Never off - brightness is already set, disable blinking */ + if (!*delay_off) { + ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, + ctrl_mask, ctrl_on); + goto out; + } + + /* Convert into values the HW will understand. */ + + /* Integer representation of time of flash period */ + num_steps = (*delay_on + *delay_off - KTD202X_FLASH_PERIOD_MIN_MS) / + KTD202X_FLASH_PERIOD_STEP_MS; + num_steps = clamp(num_steps, 0, KTD202X_FLASH_PERIOD_MAX_STEPS); + + /* Integer representation of percentage of LED ON time */ + on = (*delay_on * KTD202X_FLASH_ON_MAX) / (*delay_on + *delay_off); + + /* Actually used delay_{on,off} values */ + delay_total_ms = num_steps * KTD202X_FLASH_PERIOD_STEP_MS + KTD202X_FLASH_PERIOD_MIN_MS; + *delay_on = (delay_total_ms * on) / KTD202X_FLASH_ON_MAX; + *delay_off = delay_total_ms - *delay_on; + + /* Set timings */ + ret = regmap_write(led->chip->regmap, KTD202X_REG_FLASH_PERIOD, num_steps); + if (ret) + goto out; + + ret = regmap_write(led->chip->regmap, KTD202X_REG_PWM1_TIMER, on); + if (ret) + goto out; + + ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, + ctrl_mask, ctrl_pwm1); +out: + mutex_unlock(&led->chip->mutex); + return ret; +} + +static int ktd202x_blink_single_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev); + struct mc_subled info; + int ret; + + if (!cdev->brightness) { + ret = ktd202x_brightness_single_set(cdev, KTD202X_MAX_BRIGHTNESS); + if (ret) + return ret; + } + + /* If no blink specified, default to 1 Hz. */ + if (!*delay_off && !*delay_on) { + *delay_off = 500; + *delay_on = 500; + } + + /* Never on - just set to off */ + if (!*delay_on) + return ktd202x_brightness_single_set(cdev, LED_OFF); + + info.channel = led->index; + + return ktd202x_blink_set(led, delay_on, delay_off, &info, 1); +} + +static int ktd202x_blink_mc_set(struct led_classdev *cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct led_classdev_mc *mc = lcdev_to_mccdev(cdev); + struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev); + int ret; + + if (!cdev->brightness) { + ret = ktd202x_brightness_mc_set(cdev, KTD202X_MAX_BRIGHTNESS); + if (ret) + return ret; + } + + /* If no blink specified, default to 1 Hz. */ + if (!*delay_off && !*delay_on) { + *delay_off = 500; + *delay_on = 500; + } + + /* Never on - just set to off */ + if (!*delay_on) + return ktd202x_brightness_mc_set(cdev, LED_OFF); + + return ktd202x_blink_set(led, delay_on, delay_off, mc->subled_info, + mc->num_colors); +} + +static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np, + struct ktd202x_led *led, struct led_init_data *init_data) +{ + struct led_classdev *cdev; + struct device_node *child; + struct mc_subled *info; + int num_channels; + int i = 0; + + num_channels = of_get_available_child_count(np); + if (!num_channels || num_channels > chip->num_leds) + return -EINVAL; + + info = devm_kcalloc(chip->dev, num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + for_each_available_child_of_node(np, child) { + u32 mono_color; + u32 reg; + int ret; + + ret = of_property_read_u32(child, "reg", ®); + if (ret != 0 || reg >= chip->num_leds) { + dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child); + of_node_put(child); + return -EINVAL; + } + + ret = of_property_read_u32(child, "color", &mono_color); + if (ret < 0 && ret != -EINVAL) { + dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child); + of_node_put(child); + return ret; + } + + info[i].color_index = mono_color; + info[i].channel = reg; + info[i].intensity = KTD202X_MAX_BRIGHTNESS; + i++; + } + + led->mcdev.subled_info = info; + led->mcdev.num_colors = num_channels; + + cdev = &led->mcdev.led_cdev; + cdev->brightness_set_blocking = ktd202x_brightness_mc_set; + cdev->blink_set = ktd202x_blink_mc_set; + + return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data); +} + +static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np, + struct ktd202x_led *led, struct led_init_data *init_data) +{ + struct led_classdev *cdev; + u32 reg; + int ret; + + ret = of_property_read_u32(np, "reg", ®); + if (ret != 0 || reg >= chip->num_leds) { + dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np); + return -EINVAL; + } + led->index = reg; + + cdev = &led->cdev; + cdev->brightness_set_blocking = ktd202x_brightness_single_set; + cdev->blink_set = ktd202x_blink_single_set; + + return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data); +} + +static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index) +{ + struct ktd202x_led *led = &chip->leds[index]; + struct led_init_data init_data = {}; + struct led_classdev *cdev; + u32 color; + int ret; + + /* Color property is optional in single color case */ + ret = of_property_read_u32(np, "color", &color); + if (ret < 0 && ret != -EINVAL) { + dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np); + return ret; + } + + led->chip = chip; + init_data.fwnode = of_fwnode_handle(np); + + if (color == LED_COLOR_ID_RGB) { + cdev = &led->mcdev.led_cdev; + ret = ktd202x_setup_led_rgb(chip, np, led, &init_data); + } else { + cdev = &led->cdev; + ret = ktd202x_setup_led_single(chip, np, led, &init_data); + } + + if (ret) { + dev_err(chip->dev, "unable to register %s\n", cdev->name); + return ret; + } + + cdev->max_brightness = KTD202X_MAX_BRIGHTNESS; + + return 0; +} + +static int ktd202x_probe_dt(struct ktd202x *chip) +{ + struct device_node *np = dev_of_node(chip->dev), *child; + int count; + int i = 0; + + chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev); + + count = of_get_available_child_count(np); + if (!count || count > chip->num_leds) + return -EINVAL; + + regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET); + + /* Allow the device to execute the complete reset */ + usleep_range(200, 300); + + for_each_available_child_of_node(np, child) { + int ret = ktd202x_add_led(chip, child, i); + + if (ret) { + of_node_put(child); + return ret; + } + i++; + } + + return 0; +} + +static const struct regmap_config ktd202x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x09, + .cache_type = REGCACHE_FLAT, + .reg_defaults = ktd202x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ktd202x_reg_defaults), +}; + +static int ktd202x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ktd202x *chip; + int count; + int ret; + + count = device_get_child_node_count(dev); + if (!count || count > KTD202X_MAX_LEDS) + return dev_err_probe(dev, -EINVAL, "Incorrect number of leds (%d)", count); + + chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + i2c_set_clientdata(client, chip); + + chip->regmap = devm_regmap_init_i2c(client, &ktd202x_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = dev_err_probe(dev, PTR_ERR(chip->regmap), + "Failed to allocate register map.\n"); + return ret; + } + + chip->regulators[0].supply = "vin"; + chip->regulators[1].supply = "vio"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to request regulators.\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err_probe(dev, ret, "Failed to enable regulators.\n"); + return ret; + } + + ret = ktd202x_probe_dt(chip); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators); + if (ret) { + dev_err_probe(dev, ret, "Failed to disable regulators.\n"); + return ret; + } + + mutex_init(&chip->mutex); + + return 0; +} + +static void ktd202x_remove(struct i2c_client *client) +{ + struct ktd202x *chip = i2c_get_clientdata(client); + + ktd202x_chip_disable(chip); + + mutex_destroy(&chip->mutex); +} + +static void ktd202x_shutdown(struct i2c_client *client) +{ + struct ktd202x *chip = i2c_get_clientdata(client); + + /* Reset registers to make sure all LEDs are off before shutdown */ + regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET); +} + +static const struct of_device_id ktd202x_match_table[] = { + { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS }, + { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS }, + {}, +}; +MODULE_DEVICE_TABLE(of, ktd202x_match_table); + +static struct i2c_driver ktd202x_driver = { + .driver = { + .name = "leds-ktd202x", + .of_match_table = ktd202x_match_table, + }, + .probe = ktd202x_probe, + .remove = ktd202x_remove, + .shutdown = ktd202x_shutdown, +}; +module_i2c_driver(ktd202x_driver); + +MODULE_AUTHOR("AndrĂ© Apitzsch <git@apitzsch.eu>"); +MODULE_DESCRIPTION("Kinetic KTD2026/7 LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/rgb/leds-mt6370-rgb.c b/drivers/leds/rgb/leds-mt6370-rgb.c index bb62431efe..448d0da118 100644 --- a/drivers/leds/rgb/leds-mt6370-rgb.c +++ b/drivers/leds/rgb/leds-mt6370-rgb.c @@ -153,7 +153,7 @@ struct mt6370_priv { const struct mt6370_pdata *pdata; unsigned int leds_count; unsigned int leds_active; - struct mt6370_led leds[]; + struct mt6370_led leds[] __counted_by(leds_count); }; static const struct reg_field common_reg_fields[F_MAX_FIELDS] = { diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index df469aaa7e..68d82a682b 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -173,7 +173,7 @@ struct lpg_led { struct led_classdev_mc mcdev; unsigned int num_channels; - struct lpg_channel *channels[]; + struct lpg_channel *channels[] __counted_by(num_channels); }; /** @@ -1085,7 +1085,6 @@ static const struct pwm_ops lpg_pwm_ops = { .request = lpg_pwm_request, .apply = lpg_pwm_apply, .get_state = lpg_pwm_get_state, - .owner = THIS_MODULE, }; static int lpg_add_pwm(struct lpg *lpg) @@ -1364,13 +1363,11 @@ static int lpg_probe(struct platform_device *pdev) return lpg_add_pwm(lpg); } -static int lpg_remove(struct platform_device *pdev) +static void lpg_remove(struct platform_device *pdev) { struct lpg *lpg = platform_get_drvdata(pdev); pwmchip_remove(&lpg->pwm); - - return 0; } static const struct lpg_data pm8916_pwm_data = { @@ -1532,7 +1529,7 @@ MODULE_DEVICE_TABLE(of, lpg_of_table); static struct platform_driver lpg_driver = { .probe = lpg_probe, - .remove = lpg_remove, + .remove_new = lpg_remove, .driver = { .name = "qcom-spmi-lpg", .of_match_table = lpg_of_table, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c index e1c712729d..4183ee71fc 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c @@ -45,15 +45,15 @@ static int simatic_ipc_leds_gpio_apollolake_probe(struct platform_device *pdev) &simatic_ipc_led_gpio_table_extra); } -static int simatic_ipc_leds_gpio_apollolake_remove(struct platform_device *pdev) +static void simatic_ipc_leds_gpio_apollolake_remove(struct platform_device *pdev) { - return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, - &simatic_ipc_led_gpio_table_extra); + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, + &simatic_ipc_led_gpio_table_extra); } static struct platform_driver simatic_ipc_led_gpio_apollolake_driver = { .probe = simatic_ipc_leds_gpio_apollolake_probe, - .remove = simatic_ipc_leds_gpio_apollolake_remove, + .remove_new = simatic_ipc_leds_gpio_apollolake_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c index c552ea73ed..667ba1bc3a 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c @@ -33,15 +33,13 @@ static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = { .leds = simatic_ipc_gpio_leds, }; -int simatic_ipc_leds_gpio_remove(struct platform_device *pdev, +void simatic_ipc_leds_gpio_remove(struct platform_device *pdev, struct gpiod_lookup_table *table, struct gpiod_lookup_table *table_extra) { gpiod_remove_lookup_table(table); gpiod_remove_lookup_table(table_extra); platform_device_unregister(simatic_leds_pdev); - - return 0; } EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_remove); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c index 6ba21dbb3b..4a53d4dbf5 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c @@ -36,15 +36,14 @@ static int simatic_ipc_leds_gpio_elkhartlake_probe(struct platform_device *pdev) NULL); } -static int simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev) +static void simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev) { - return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, - NULL); + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, NULL); } static struct platform_driver simatic_ipc_led_gpio_elkhartlake_driver = { .probe = simatic_ipc_leds_gpio_elkhartlake_probe, - .remove = simatic_ipc_leds_gpio_elkhartlake_remove, + .remove_new = simatic_ipc_leds_gpio_elkhartlake_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c index 583a6b6c7c..c7c3a1f986 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c @@ -45,15 +45,15 @@ static int simatic_ipc_leds_gpio_f7188x_probe(struct platform_device *pdev) &simatic_ipc_led_gpio_table_extra); } -static int simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev) +static void simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev) { - return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, - &simatic_ipc_led_gpio_table_extra); + simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, + &simatic_ipc_led_gpio_table_extra); } static struct platform_driver simatic_ipc_led_gpio_driver = { .probe = simatic_ipc_leds_gpio_f7188x_probe, - .remove = simatic_ipc_leds_gpio_f7188x_remove, + .remove_new = simatic_ipc_leds_gpio_f7188x_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.h b/drivers/leds/simple/simatic-ipc-leds-gpio.h index 3d4877aa4e..6b2519809c 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio.h +++ b/drivers/leds/simple/simatic-ipc-leds-gpio.h @@ -15,8 +15,8 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev, struct gpiod_lookup_table *table, struct gpiod_lookup_table *table_extra); -int simatic_ipc_leds_gpio_remove(struct platform_device *pdev, - struct gpiod_lookup_table *table, - struct gpiod_lookup_table *table_extra); +void simatic_ipc_leds_gpio_remove(struct platform_device *pdev, + struct gpiod_lookup_table *table, + struct gpiod_lookup_table *table_extra); #endif /* _SIMATIC_IPC_LEDS_GPIO_H */ diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 2a57328eca..d11d80176f 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -83,13 +83,10 @@ config LEDS_TRIGGER_ACTIVITY config LEDS_TRIGGER_GPIO tristate "LED GPIO Trigger" depends on GPIOLIB || COMPILE_TEST - depends on BROKEN help This allows LEDs to be controlled by gpio events. It's good when using gpios as switches and triggering the needed LEDs - from there. One use case is n810's keypad LEDs that could - be triggered by this trigger when user slides up to show - keypad. + from there. Triggers are defined as device properties. If unsure, say N. diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index 0120faa3da..9b7fe5dd52 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -3,12 +3,13 @@ * ledtrig-gio.c - LED Trigger Based on GPIO events * * Copyright 2009 Felipe Balbi <me@felipebalbi.com> + * Copyright 2023 Linus Walleij <linus.walleij@linaro.org> */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/leds.h> #include <linux/slab.h> @@ -16,10 +17,8 @@ struct gpio_trig_data { struct led_classdev *led; - unsigned desired_brightness; /* desired brightness when led is on */ - unsigned inverted; /* true when gpio is inverted */ - unsigned gpio; /* gpio that triggers the leds */ + struct gpio_desc *gpiod; /* gpio that triggers the led */ }; static irqreturn_t gpio_trig_irq(int irq, void *_led) @@ -28,10 +27,7 @@ static irqreturn_t gpio_trig_irq(int irq, void *_led) struct gpio_trig_data *gpio_data = led_get_trigger_data(led); int tmp; - tmp = gpio_get_value_cansleep(gpio_data->gpio); - if (gpio_data->inverted) - tmp = !tmp; - + tmp = gpiod_get_value_cansleep(gpio_data->gpiod); if (tmp) { if (gpio_data->desired_brightness) led_set_brightness_nosleep(gpio_data->led, @@ -73,93 +69,8 @@ static ssize_t gpio_trig_brightness_store(struct device *dev, static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, gpio_trig_brightness_store); -static ssize_t gpio_trig_inverted_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - - return sprintf(buf, "%u\n", gpio_data->inverted); -} - -static ssize_t gpio_trig_inverted_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct led_classdev *led = led_trigger_get_led(dev); - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - unsigned long inverted; - int ret; - - ret = kstrtoul(buf, 10, &inverted); - if (ret < 0) - return ret; - - if (inverted > 1) - return -EINVAL; - - gpio_data->inverted = inverted; - - /* After inverting, we need to update the LED. */ - if (gpio_is_valid(gpio_data->gpio)) - gpio_trig_irq(0, led); - - return n; -} -static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show, - gpio_trig_inverted_store); - -static ssize_t gpio_trig_gpio_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - - return sprintf(buf, "%u\n", gpio_data->gpio); -} - -static ssize_t gpio_trig_gpio_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct led_classdev *led = led_trigger_get_led(dev); - struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); - unsigned gpio; - int ret; - - ret = sscanf(buf, "%u", &gpio); - if (ret < 1) { - dev_err(dev, "couldn't read gpio number\n"); - return -EINVAL; - } - - if (gpio_data->gpio == gpio) - return n; - - if (!gpio_is_valid(gpio)) { - if (gpio_is_valid(gpio_data->gpio)) - free_irq(gpio_to_irq(gpio_data->gpio), led); - gpio_data->gpio = gpio; - return n; - } - - ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING - | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); - if (ret) { - dev_err(dev, "request_irq failed with error %d\n", ret); - } else { - if (gpio_is_valid(gpio_data->gpio)) - free_irq(gpio_to_irq(gpio_data->gpio), led); - gpio_data->gpio = gpio; - /* After changing the GPIO, we need to update the LED. */ - gpio_trig_irq(0, led); - } - - return ret ? ret : n; -} -static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store); - static struct attribute *gpio_trig_attrs[] = { &dev_attr_desired_brightness.attr, - &dev_attr_inverted.attr, - &dev_attr_gpio.attr, NULL }; ATTRIBUTE_GROUPS(gpio_trig); @@ -167,16 +78,48 @@ ATTRIBUTE_GROUPS(gpio_trig); static int gpio_trig_activate(struct led_classdev *led) { struct gpio_trig_data *gpio_data; + struct device *dev = led->dev; + int ret; gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); if (!gpio_data) return -ENOMEM; - gpio_data->led = led; - gpio_data->gpio = -ENOENT; + /* + * The generic property "trigger-sources" is followed, + * and we hope that this is a GPIO. + */ + gpio_data->gpiod = fwnode_gpiod_get_index(dev->fwnode, + "trigger-sources", + 0, GPIOD_IN, + "led-trigger"); + if (IS_ERR(gpio_data->gpiod)) { + ret = PTR_ERR(gpio_data->gpiod); + kfree(gpio_data); + return ret; + } + if (!gpio_data->gpiod) { + dev_err(dev, "no valid GPIO for the trigger\n"); + kfree(gpio_data); + return -EINVAL; + } + gpio_data->led = led; led_set_trigger_data(led, gpio_data); + ret = request_threaded_irq(gpiod_to_irq(gpio_data->gpiod), NULL, gpio_trig_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); + if (ret) { + dev_err(dev, "request_irq failed with error %d\n", ret); + gpiod_put(gpio_data->gpiod); + kfree(gpio_data); + return ret; + } + + /* Finally update the LED to initial status */ + gpio_trig_irq(0, led); + return 0; } @@ -184,8 +127,8 @@ static void gpio_trig_deactivate(struct led_classdev *led) { struct gpio_trig_data *gpio_data = led_get_trigger_data(led); - if (gpio_is_valid(gpio_data->gpio)) - free_irq(gpio_to_irq(gpio_data->gpio), led); + free_irq(gpiod_to_irq(gpio_data->gpiod), led); + gpiod_put(gpio_data->gpiod); kfree(gpio_data); } diff --git a/drivers/leds/trigger/ledtrig-panic.c b/drivers/leds/trigger/ledtrig-panic.c index 64abf2e916..5a6b21bfeb 100644 --- a/drivers/leds/trigger/ledtrig-panic.c +++ b/drivers/leds/trigger/ledtrig-panic.c @@ -64,10 +64,13 @@ static long led_panic_blink(int state) static int __init ledtrig_panic_init(void) { + led_trigger_register_simple("panic", &trigger); + if (!trigger) + return -ENOMEM; + atomic_notifier_chain_register(&panic_notifier_list, &led_trigger_panic_nb); - led_trigger_register_simple("panic", &trigger); panic_blink = led_panic_blink; return 0; } |