From 528c1d71ec954347fa240837e3c30ead0925dcb3 Mon Sep 17 00:00:00 2001 From: Flavio Suligoi Date: Wed, 3 Jun 2020 11:15:16 +0200 Subject: [PATCH 01/37] dt-bindings: leds: fix macro names for pca955x The documentation reports the wrong macro names related to the pca9532 instead of the pca955x Signed-off-by: Flavio Suligoi Acked-by: Rob Herring Signed-off-by: Pavel Machek --- Documentation/devicetree/bindings/leds/leds-pca955x.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/leds/leds-pca955x.txt b/Documentation/devicetree/bindings/leds/leds-pca955x.txt index 7984efb767b4..7a5830f8d5ab 100644 --- a/Documentation/devicetree/bindings/leds/leds-pca955x.txt +++ b/Documentation/devicetree/bindings/leds/leds-pca955x.txt @@ -26,9 +26,9 @@ LED sub-node properties: from 0 to 15 for the pca9552 from 0 to 3 for the pca9553 - type: (optional) either - PCA9532_TYPE_NONE - PCA9532_TYPE_LED - PCA9532_TYPE_GPIO + PCA955X_TYPE_NONE + PCA955X_TYPE_LED + PCA955X_TYPE_GPIO see dt-bindings/leds/leds-pca955x.h (default to LED) - label : (optional) see Documentation/devicetree/bindings/leds/common.txt From 985b1f596f9ed56f42b8c2280005f943e1434c06 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 5 May 2020 16:19:17 +0200 Subject: [PATCH 02/37] leds: lm355x: avoid enum conversion warning clang points out that doing arithmetic between diffent enums is usually a mistake: drivers/leds/leds-lm355x.c:167:28: warning: bitwise operation between different enumeration types ('enum lm355x_tx2' and 'enum lm355x_ntc') [-Wenum-enum-conversion] reg_val = pdata->pin_tx2 | pdata->ntc_pin; ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~ drivers/leds/leds-lm355x.c:178:28: warning: bitwise operation between different enumeration types ('enum lm355x_tx2' and 'enum lm355x_ntc') [-Wenum-enum-conversion] reg_val = pdata->pin_tx2 | pdata->ntc_pin | pdata->pass_mode; ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~ In this driver, it is intentional, so add a cast to hide the false-positive warning. It appears to be the only instance of this warning at the moment. Fixes: b98d13c72592 ("leds: Add new LED driver for lm355x chips") Signed-off-by: Arnd Bergmann Signed-off-by: Pavel Machek --- drivers/leds/leds-lm355x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c index 11ce05249751..b2eb2e1e9c04 100644 --- a/drivers/leds/leds-lm355x.c +++ b/drivers/leds/leds-lm355x.c @@ -164,18 +164,19 @@ static int lm355x_chip_init(struct lm355x_chip_data *chip) /* input and output pins configuration */ switch (chip->type) { case CHIP_LM3554: - reg_val = pdata->pin_tx2 | pdata->ntc_pin; + reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin; ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); if (ret < 0) goto out; - reg_val = pdata->pass_mode; + reg_val = (u32)pdata->pass_mode; ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); if (ret < 0) goto out; break; case CHIP_LM3556: - reg_val = pdata->pin_tx2 | pdata->ntc_pin | pdata->pass_mode; + reg_val = (u32)pdata->pin_tx2 | (u32)pdata->ntc_pin | + (u32)pdata->pass_mode; ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); if (ret < 0) goto out; From eca21c2d8655387823d695b26e6fe78cf3975c05 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Jun 2020 15:39:45 +0200 Subject: [PATCH 03/37] leds: 88pm860x: fix use-after-free on unbind Several MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered. Fixes: 375446df95ee ("leds: 88pm860x: Use devm_led_classdev_register") Cc: stable # 4.6 Cc: Amitoj Kaur Chawla Signed-off-by: Johan Hovold Signed-off-by: Pavel Machek --- drivers/leds/leds-88pm860x.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index b3044c9a8120..465c3755cf2e 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -203,21 +203,33 @@ static int pm860x_led_probe(struct platform_device *pdev) data->cdev.brightness_set_blocking = pm860x_led_set; mutex_init(&data->lock); - ret = devm_led_classdev_register(chip->dev, &data->cdev); + ret = led_classdev_register(chip->dev, &data->cdev); if (ret < 0) { dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); return ret; } pm860x_led_set(&data->cdev, 0); + + platform_set_drvdata(pdev, data); + return 0; } +static int 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 = { .driver = { .name = "88pm860x-led", }, .probe = pm860x_led_probe, + .remove = pm860x_led_remove, }; module_platform_driver(pm860x_led_driver); From 6f4aa35744f69ed9b0bf5a736c9ca9b44bc1dcea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Jun 2020 15:39:46 +0200 Subject: [PATCH 04/37] leds: da903x: fix use-after-free on unbind Several MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered. Fixes: eed16255d66b ("leds: da903x: Use devm_led_classdev_register") Cc: stable # 4.6 Cc: Amitoj Kaur Chawla Signed-off-by: Johan Hovold Signed-off-by: Pavel Machek --- drivers/leds/leds-da903x.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index ed1b303f699f..2b5fb00438a2 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -110,12 +110,23 @@ static int da903x_led_probe(struct platform_device *pdev) led->flags = pdata->flags; led->master = pdev->dev.parent; - ret = devm_led_classdev_register(led->master, &led->cdev); + ret = led_classdev_register(led->master, &led->cdev); if (ret) { dev_err(&pdev->dev, "failed to register LED %d\n", id); return ret; } + platform_set_drvdata(pdev, led); + + return 0; +} + +static int da903x_led_remove(struct platform_device *pdev) +{ + struct da903x_led *led = platform_get_drvdata(pdev); + + led_classdev_unregister(&led->cdev); + return 0; } @@ -124,6 +135,7 @@ static struct platform_driver da903x_led_driver = { .name = "da903x-led", }, .probe = da903x_led_probe, + .remove = da903x_led_remove, }; module_platform_driver(da903x_led_driver); From d584221e683bbd173738603b83a315f27d27d043 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Jun 2020 15:39:47 +0200 Subject: [PATCH 05/37] leds: lm3533: fix use-after-free on unbind Several MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered. Fixes: 50154e29e5cc ("leds: lm3533: Use devm_led_classdev_register") Cc: stable # 4.6 Cc: Amitoj Kaur Chawla Signed-off-by: Johan Hovold Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3533.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 9504ad405aef..b3edee703193 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -694,7 +694,7 @@ static int lm3533_led_probe(struct platform_device *pdev) platform_set_drvdata(pdev, led); - ret = devm_led_classdev_register(pdev->dev.parent, &led->cdev); + ret = led_classdev_register(pdev->dev.parent, &led->cdev); if (ret) { dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id); return ret; @@ -704,13 +704,18 @@ static int lm3533_led_probe(struct platform_device *pdev) ret = lm3533_led_setup(led, pdata); if (ret) - return ret; + goto err_deregister; ret = lm3533_ctrlbank_enable(&led->cb); if (ret) - return ret; + goto err_deregister; return 0; + +err_deregister: + led_classdev_unregister(&led->cdev); + + return ret; } static int lm3533_led_remove(struct platform_device *pdev) @@ -720,6 +725,7 @@ static int lm3533_led_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); lm3533_ctrlbank_disable(&led->cb); + led_classdev_unregister(&led->cdev); return 0; } From a0972fff09479dd09b731360a3a0b09e4fb4d415 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Jun 2020 15:39:48 +0200 Subject: [PATCH 06/37] leds: lm36274: fix use-after-free on unbind Several MFD child drivers register their class devices directly under the parent device. This means you cannot use devres so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered. Fixes: 11e1bbc116a7 ("leds: lm36274: Introduce the TI LM36274 LED driver") Cc: stable # 5.3 Cc: Dan Murphy Signed-off-by: Johan Hovold Signed-off-by: Pavel Machek --- drivers/leds/leds-lm36274.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-lm36274.c b/drivers/leds/leds-lm36274.c index 836b60c9a2b8..db842eeb7ca2 100644 --- a/drivers/leds/leds-lm36274.c +++ b/drivers/leds/leds-lm36274.c @@ -133,7 +133,7 @@ static int lm36274_probe(struct platform_device *pdev) lm36274_data->pdev = pdev; lm36274_data->dev = lmu->dev; lm36274_data->regmap = lmu->regmap; - dev_set_drvdata(&pdev->dev, lm36274_data); + platform_set_drvdata(pdev, lm36274_data); ret = lm36274_parse_dt(lm36274_data); if (ret) { @@ -147,8 +147,16 @@ static int lm36274_probe(struct platform_device *pdev) return ret; } - return devm_led_classdev_register(lm36274_data->dev, - &lm36274_data->led_dev); + return led_classdev_register(lm36274_data->dev, &lm36274_data->led_dev); +} + +static int lm36274_remove(struct platform_device *pdev) +{ + struct lm36274 *lm36274_data = platform_get_drvdata(pdev); + + led_classdev_unregister(&lm36274_data->led_dev); + + return 0; } static const struct of_device_id of_lm36274_leds_match[] = { @@ -159,6 +167,7 @@ MODULE_DEVICE_TABLE(of, of_lm36274_leds_match); static struct platform_driver lm36274_driver = { .probe = lm36274_probe, + .remove = lm36274_remove, .driver = { .name = "lm36274-leds", }, From 47a459ecc800a17109d0c496a4e21e478806ee40 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Jun 2020 15:39:49 +0200 Subject: [PATCH 07/37] leds: wm831x-status: fix use-after-free on unbind Several MFD child drivers register their class devices directly under the parent device. This means you cannot blindly do devres conversions so that deregistration ends up being tied to the parent device, something which leads to use-after-free on driver unbind when the class device is released while still being registered. Fixes: 8d3b6a4001ce ("leds: wm831x-status: Use devm_led_classdev_register") Cc: stable # 4.6 Cc: Amitoj Kaur Chawla Signed-off-by: Johan Hovold Signed-off-by: Pavel Machek --- drivers/leds/leds-wm831x-status.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index 082df7f1dd90..67f4235cb28a 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -269,12 +269,23 @@ static int wm831x_status_probe(struct platform_device *pdev) drvdata->cdev.blink_set = wm831x_status_blink_set; drvdata->cdev.groups = wm831x_status_groups; - ret = devm_led_classdev_register(wm831x->dev, &drvdata->cdev); + ret = led_classdev_register(wm831x->dev, &drvdata->cdev); if (ret < 0) { dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); return ret; } + platform_set_drvdata(pdev, drvdata); + + return 0; +} + +static int wm831x_status_remove(struct platform_device *pdev) +{ + struct wm831x_status *drvdata = platform_get_drvdata(pdev); + + led_classdev_unregister(&drvdata->cdev); + return 0; } @@ -283,6 +294,7 @@ static struct platform_driver wm831x_status_driver = { .name = "wm831x-status", }, .probe = wm831x_status_probe, + .remove = wm831x_status_remove, }; module_platform_driver(wm831x_status_driver); From b5684a73d2e9ce13d253ecb824ecd1e327b63773 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 1 Jun 2020 15:39:50 +0200 Subject: [PATCH 08/37] leds: drop redundant struct-device pointer casts Drop the pointless and needlessly confusing casts of struct-device pointers. Signed-off-by: Johan Hovold Signed-off-by: Pavel Machek --- drivers/leds/leds-lm355x.c | 9 +++------ drivers/leds/leds-lm3642.c | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c index b2eb2e1e9c04..1505521249b5 100644 --- a/drivers/leds/leds-lm355x.c +++ b/drivers/leds/leds-lm355x.c @@ -453,8 +453,7 @@ static int lm355x_probe(struct i2c_client *client, chip->cdev_flash.max_brightness = 16; chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set; chip->cdev_flash.default_trigger = "flash"; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_flash); + err = led_classdev_register(&client->dev, &chip->cdev_flash); if (err < 0) goto err_out; /* torch */ @@ -462,8 +461,7 @@ static int lm355x_probe(struct i2c_client *client, chip->cdev_torch.max_brightness = 8; chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set; chip->cdev_torch.default_trigger = "torch"; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_torch); + err = led_classdev_register(&client->dev, &chip->cdev_torch); if (err < 0) goto err_create_torch_file; /* indicator */ @@ -477,8 +475,7 @@ static int lm355x_probe(struct i2c_client *client, /* indicator pattern control only for LM3556 */ if (id->driver_data == CHIP_LM3556) chip->cdev_indicator.groups = lm355x_indicator_groups; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_indicator); + err = led_classdev_register(&client->dev, &chip->cdev_indicator); if (err < 0) goto err_create_indicator_file; diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c index 4232906fcb75..62c14872caf7 100644 --- a/drivers/leds/leds-lm3642.c +++ b/drivers/leds/leds-lm3642.c @@ -340,8 +340,7 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_flash.brightness_set_blocking = lm3642_strobe_brightness_set; chip->cdev_flash.default_trigger = "flash"; chip->cdev_flash.groups = lm3642_flash_groups, - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_flash); + err = led_classdev_register(&client->dev, &chip->cdev_flash); if (err < 0) { dev_err(chip->dev, "failed to register flash\n"); goto err_out; @@ -353,8 +352,7 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_torch.brightness_set_blocking = lm3642_torch_brightness_set; chip->cdev_torch.default_trigger = "torch"; chip->cdev_torch.groups = lm3642_torch_groups, - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_torch); + err = led_classdev_register(&client->dev, &chip->cdev_torch); if (err < 0) { dev_err(chip->dev, "failed to register torch\n"); goto err_create_torch_file; @@ -365,8 +363,7 @@ static int lm3642_probe(struct i2c_client *client, chip->cdev_indicator.max_brightness = 8; chip->cdev_indicator.brightness_set_blocking = lm3642_indicator_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_indicator); + err = led_classdev_register(&client->dev, &chip->cdev_indicator); if (err < 0) { dev_err(chip->dev, "failed to register indicator\n"); goto err_create_indicator_file; From e190f57df3c7e7713687905c14e72fbcbd81c5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 4 Jun 2020 15:59:05 +0200 Subject: [PATCH 09/37] leds-bcm6328: support second hw blinking interval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now the driver uses only 3 LED modes: 0: On 1: HW Blinking (Interval 1) 3: Off However, the controller supports a second HW blinking interval, which results in 4 possible LED modes: 0: On 1: HW Blinking (Interval 1) 2: HW Blinking (Interval 2) 3: Off Signed-off-by: Álvaro Fernández Rojas Signed-off-by: Pavel Machek --- drivers/leds/leds-bcm6328.c | 97 ++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c index 42e1b7598c3a..bad7efb75112 100644 --- a/drivers/leds/leds-bcm6328.c +++ b/drivers/leds/leds-bcm6328.c @@ -24,12 +24,17 @@ #define BCM6328_LED_MAX_COUNT 24 #define BCM6328_LED_DEF_DELAY 500 -#define BCM6328_LED_INTERVAL_MS 20 -#define BCM6328_LED_INTV_MASK 0x3f -#define BCM6328_LED_FAST_INTV_SHIFT 6 -#define BCM6328_LED_FAST_INTV_MASK (BCM6328_LED_INTV_MASK << \ - BCM6328_LED_FAST_INTV_SHIFT) +#define BCM6328_LED_BLINK_DELAYS 2 +#define BCM6328_LED_BLINK_MS 20 + +#define BCM6328_LED_BLINK_MASK 0x3f +#define BCM6328_LED_BLINK1_SHIFT 0 +#define BCM6328_LED_BLINK1_MASK (BCM6328_LED_BLINK_MASK << \ + BCM6328_LED_BLINK1_SHIFT) +#define BCM6328_LED_BLINK2_SHIFT 6 +#define BCM6328_LED_BLINK2_MASK (BCM6328_LED_BLINK_MASK << \ + BCM6328_LED_BLINK2_SHIFT) #define BCM6328_SERIAL_LED_EN BIT(12) #define BCM6328_SERIAL_LED_MUX BIT(13) #define BCM6328_SERIAL_LED_CLK_NPOL BIT(14) @@ -45,8 +50,8 @@ #define BCM6328_LED_MODE_MASK 3 #define BCM6328_LED_MODE_ON 0 -#define BCM6328_LED_MODE_FAST 1 -#define BCM6328_LED_MODE_BLINK 2 +#define BCM6328_LED_MODE_BLINK1 1 +#define BCM6328_LED_MODE_BLINK2 2 #define BCM6328_LED_MODE_OFF 3 #define BCM6328_LED_SHIFT(X) ((X) << 1) @@ -127,12 +132,18 @@ static void bcm6328_led_set(struct led_classdev *led_cdev, unsigned long flags; spin_lock_irqsave(led->lock, flags); - *(led->blink_leds) &= ~BIT(led->pin); + + /* Remove LED from cached HW blinking intervals */ + led->blink_leds[0] &= ~BIT(led->pin); + led->blink_leds[1] &= ~BIT(led->pin); + + /* Set LED on/off */ if ((led->active_low && value == LED_OFF) || (!led->active_low && value != LED_OFF)) bcm6328_led_mode(led, BCM6328_LED_MODE_ON); else bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); + spin_unlock_irqrestore(led->lock, flags); } @@ -140,8 +151,8 @@ static unsigned long bcm6328_blink_delay(unsigned long delay) { unsigned long bcm6328_delay; - bcm6328_delay = delay + BCM6328_LED_INTERVAL_MS / 2; - bcm6328_delay = bcm6328_delay / BCM6328_LED_INTERVAL_MS; + bcm6328_delay = delay + BCM6328_LED_BLINK_MS / 2; + bcm6328_delay = bcm6328_delay / BCM6328_LED_BLINK_MS; if (bcm6328_delay == 0) bcm6328_delay = 1; @@ -168,28 +179,68 @@ static int bcm6328_blink_set(struct led_classdev *led_cdev, return -EINVAL; } - if (delay > BCM6328_LED_INTV_MASK) { + if (delay > BCM6328_LED_BLINK_MASK) { dev_dbg(led_cdev->dev, "fallback to soft blinking (delay > %ums)\n", - BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS); + BCM6328_LED_BLINK_MASK * BCM6328_LED_BLINK_MS); return -EINVAL; } spin_lock_irqsave(led->lock, flags); - if (*(led->blink_leds) == 0 || - *(led->blink_leds) == BIT(led->pin) || - *(led->blink_delay) == delay) { + /* + * Check if any of the two configurable HW blinking intervals is + * available: + * 1. No LEDs assigned to the HW blinking interval. + * 2. Only this LED is assigned to the HW blinking interval. + * 3. LEDs with the same delay assigned. + */ + if (led->blink_leds[0] == 0 || + led->blink_leds[0] == BIT(led->pin) || + led->blink_delay[0] == delay) { unsigned long val; - *(led->blink_leds) |= BIT(led->pin); - *(led->blink_delay) = delay; + /* Add LED to the first HW blinking interval cache */ + led->blink_leds[0] |= BIT(led->pin); + /* Remove LED from the second HW blinking interval cache */ + led->blink_leds[1] &= ~BIT(led->pin); + + /* Cache first HW blinking interval delay */ + led->blink_delay[0] = delay; + + /* Update the delay for the first HW blinking interval */ val = bcm6328_led_read(led->mem + BCM6328_REG_INIT); - val &= ~BCM6328_LED_FAST_INTV_MASK; - val |= (delay << BCM6328_LED_FAST_INTV_SHIFT); + val &= ~BCM6328_LED_BLINK1_MASK; + val |= (delay << BCM6328_LED_BLINK1_SHIFT); bcm6328_led_write(led->mem + BCM6328_REG_INIT, val); - bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK); + /* Set the LED to first HW blinking interval */ + bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK1); + + rc = 0; + } else if (led->blink_leds[1] == 0 || + led->blink_leds[1] == BIT(led->pin) || + led->blink_delay[1] == delay) { + unsigned long val; + + /* Remove LED from the first HW blinking interval */ + led->blink_leds[0] &= ~BIT(led->pin); + + /* Add LED to the second HW blinking interval */ + led->blink_leds[1] |= BIT(led->pin); + + /* Cache second HW blinking interval delay */ + led->blink_delay[1] = delay; + + /* Update the delay for the second HW blinking interval */ + val = bcm6328_led_read(led->mem + BCM6328_REG_INIT); + val &= ~BCM6328_LED_BLINK2_MASK; + val |= (delay << BCM6328_LED_BLINK2_SHIFT); + bcm6328_led_write(led->mem + BCM6328_REG_INIT, val); + + /* Set the LED to second HW blinking interval */ + bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK2); + rc = 0; } else { dev_dbg(led_cdev->dev, @@ -358,11 +409,13 @@ static int bcm6328_leds_probe(struct platform_device *pdev) if (!lock) return -ENOMEM; - blink_leds = devm_kzalloc(dev, sizeof(*blink_leds), GFP_KERNEL); + blink_leds = devm_kcalloc(dev, BCM6328_LED_BLINK_DELAYS, + sizeof(*blink_leds), GFP_KERNEL); if (!blink_leds) return -ENOMEM; - blink_delay = devm_kzalloc(dev, sizeof(*blink_delay), GFP_KERNEL); + blink_delay = devm_kcalloc(dev, BCM6328_LED_BLINK_DELAYS, + sizeof(*blink_delay), GFP_KERNEL); if (!blink_delay) return -ENOMEM; From ac219bf3c9bdf9200767e8c98a56ad42c75e5cd5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jun 2020 00:40:11 +0200 Subject: [PATCH 10/37] leds: lp55xx: Convert to use GPIO descriptors The LP55xx driver is already using the of_gpio() functions to pick a global GPIO number for "enable" from the device tree and request the line. Simplify it by just using a GPIO descriptor. Make sure to keep the enable GPIO line optional, change the naming from "lp5523_enable" to "LP55xx enable" to reflect that this is used on all LP55xx LED drivers. Cc: Milo Kim Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/leds-lp55xx-common.c | 27 +++++++++-------------- include/linux/platform_data/leds-lp55xx.h | 6 +++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 44ced02b49f9..1354965ac866 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -17,8 +17,7 @@ #include #include #include -#include -#include +#include #include "leds-lp55xx-common.h" @@ -395,18 +394,11 @@ int lp55xx_init_device(struct lp55xx_chip *chip) if (!pdata || !cfg) return -EINVAL; - if (gpio_is_valid(pdata->enable_gpio)) { - ret = devm_gpio_request_one(dev, pdata->enable_gpio, - GPIOF_DIR_OUT, "lp5523_enable"); - if (ret < 0) { - dev_err(dev, "could not acquire enable gpio (err=%d)\n", - ret); - goto err; - } - - gpio_set_value(pdata->enable_gpio, 0); + if (pdata->enable_gpiod) { + gpiod_set_consumer_name(pdata->enable_gpiod, "LP55xx enable"); + gpiod_set_value(pdata->enable_gpiod, 0); usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - gpio_set_value(pdata->enable_gpio, 1); + gpiod_set_value(pdata->enable_gpiod, 1); usleep_range(1000, 2000); /* 500us abs min. */ } @@ -447,8 +439,8 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip) if (chip->clk) clk_disable_unprepare(chip->clk); - if (gpio_is_valid(pdata->enable_gpio)) - gpio_set_value(pdata->enable_gpio, 0); + if (pdata->enable_gpiod) + gpiod_set_value(pdata->enable_gpiod, 0); } EXPORT_SYMBOL_GPL(lp55xx_deinit_device); @@ -579,7 +571,10 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev, of_property_read_string(np, "label", &pdata->label); of_property_read_u8(np, "clock-mode", &pdata->clock_mode); - pdata->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + pdata->enable_gpiod = devm_gpiod_get_optional(dev, "enable", + GPIOD_ASIS); + if (IS_ERR(pdata->enable_gpiod)) + return ERR_CAST(pdata->enable_gpiod); /* LP8501 specific */ of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel); diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h index 96a787100fda..00492d6ff018 100644 --- a/include/linux/platform_data/leds-lp55xx.h +++ b/include/linux/platform_data/leds-lp55xx.h @@ -12,6 +12,8 @@ #ifndef _LEDS_LP55XX_H #define _LEDS_LP55XX_H +#include + /* Clock configuration */ #define LP55XX_CLOCK_AUTO 0 #define LP55XX_CLOCK_INT 1 @@ -49,7 +51,7 @@ enum lp8501_pwr_sel { * @clock_mode : Input clock mode. LP55XX_CLOCK_AUTO or _INT or _EXT * @setup_resources : Platform specific function before enabling the chip * @release_resources : Platform specific function after disabling the chip - * @enable : EN pin control by platform side + * @enable_gpiod : enable GPIO descriptor * @patterns : Predefined pattern data for RGB channels * @num_patterns : Number of patterns * @update_config : Value of CONFIG register @@ -65,7 +67,7 @@ struct lp55xx_platform_data { u8 clock_mode; /* optional enable GPIO */ - int enable_gpio; + struct gpio_desc *enable_gpiod; /* Predefined pattern data */ struct lp55xx_predef_pattern *patterns; From 0987c7df8abce177437780eb983b785147dc058e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 27 Jun 2020 00:47:35 +0200 Subject: [PATCH 11/37] leds: pca955x: Include the right GPIO header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver provides a GPIO chip, so include and not the legacy header. Cc: Andrew Jeffery Cc: Joel Stanley Cc: Matt Spinler Cc: Cédric Le Goater Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/leds-pca955x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 4037c504589c..131f8e922ade 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include #include From 14d3e74f59a4d7f9bc2768376818d7bebb9ad27f Mon Sep 17 00:00:00 2001 From: Flavio Suligoi Date: Tue, 9 Jun 2020 17:49:29 +0200 Subject: [PATCH 12/37] leds: fix spelling mistake Fix typo: "Tigger" --> "Trigger" Signed-off-by: Flavio Suligoi Reviewed-by: Alexander Dahl Signed-off-by: Pavel Machek --- drivers/leds/led-triggers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 79e30d2cb7a5..0836bf7631ea 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -358,7 +358,7 @@ int devm_led_trigger_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_led_trigger_register); -/* Simple LED Tigger Interface */ +/* Simple LED Trigger Interface */ void led_trigger_event(struct led_trigger *trig, enum led_brightness brightness) From 5ad79c20b6d28467db643ac107bbdaa01aa93a23 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 13 Jun 2020 09:15:06 +0200 Subject: [PATCH 13/37] leds: trigger: gpio: Avoid warning on update of inverted If the GPIO has not been configured yet, writing to inverted will raise a kernel warning. Signed-off-by: Jan Kiszka Signed-off-by: Pavel Machek --- drivers/leds/trigger/ledtrig-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index dc64679b1a92..0120faa3dafa 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -99,7 +99,8 @@ static ssize_t gpio_trig_inverted_store(struct device *dev, gpio_data->inverted = inverted; /* After inverting, we need to update the LED. */ - gpio_trig_irq(0, led); + if (gpio_is_valid(gpio_data->gpio)) + gpio_trig_irq(0, led); return n; } From 302a085c20194bfa7df52e0fe684ee0c41da02e6 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Thu, 2 Jul 2020 13:45:00 +0800 Subject: [PATCH 14/37] leds: core: Flush scheduled work for system suspend Sometimes LED won't be turned off by LED_CORE_SUSPENDRESUME flag upon system suspend. led_set_brightness_nopm() uses schedule_work() to set LED brightness. However, there's no guarantee that the scheduled work gets executed because no one flushes the work. So flush the scheduled work to make sure LED gets turned off. Signed-off-by: Kai-Heng Feng Acked-by: Jacek Anaszewski Fixes: 81fe8e5b73e3 ("leds: core: Add led_set_brightness_nosleep{nopm} functions") Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 3363a6551a70..cc3929f858b6 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -173,6 +173,7 @@ void led_classdev_suspend(struct led_classdev *led_cdev) { led_cdev->flags |= LED_SUSPENDED; led_set_brightness_nopm(led_cdev, 0); + flush_work(&led_cdev->set_brightness_work); } EXPORT_SYMBOL_GPL(led_classdev_suspend); From c4e94413922b8d5dd251cffd732b349772d22973 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 3 Jul 2020 20:13:38 +0200 Subject: [PATCH 15/37] leds: gpio: Fix semantic error The leds-gpio driver mixes up the legacy GPIO flags with the GPIO descriptor flags and passes a legacy flag to devm_gpiod_get_index(). Fix this by replacing the flags variable with the strict descriptor flag. Fixes: 45d4c6de4e49 ("leds: gpio: Try to lookup gpiod from device") Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/leds-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 2bf74595610f..200688c602cd 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -220,7 +220,7 @@ 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, flags); + gpiod = devm_gpiod_get_index(dev, NULL, idx, GPIOD_OUT_LOW); if (!IS_ERR(gpiod)) { gpiod_set_consumer_name(gpiod, template->name); return gpiod; From a7ad53cb6687069024efbd3af734c7811f63d2a5 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 17 Jun 2020 18:07:57 -0500 Subject: [PATCH 16/37] leds: ns2: Use struct_size() in devm_kzalloc() Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. Also, remove unnecessary function sizeof_ns2_led_priv(). This code was detected with the help of Coccinelle and, audited and fixed manually. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Pavel Machek --- drivers/leds/leds-ns2.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 538ca5755602..bd806e7c8017 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -348,12 +348,6 @@ struct ns2_led_priv { struct ns2_led_data leds_data[]; }; -static inline int sizeof_ns2_led_priv(int num_leds) -{ - return sizeof(struct ns2_led_priv) + - (sizeof(struct ns2_led_data) * num_leds); -} - static int ns2_led_probe(struct platform_device *pdev) { struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -378,8 +372,7 @@ static int ns2_led_probe(struct platform_device *pdev) return -EINVAL; #endif /* CONFIG_OF_GPIO */ - priv = devm_kzalloc(&pdev->dev, - sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds_data, pdata->num_leds), GFP_KERNEL); if (!priv) return -ENOMEM; priv->num_leds = pdata->num_leds; From cf1a1a6a7d81d73bcb5568b23572d6fd593add87 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 19 Jun 2020 17:14:03 -0500 Subject: [PATCH 17/37] leds: gpio: Use struct_size() in devm_kzalloc() Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. Also, remove unnecessary function sizeof_gpio_leds_priv(). This code was detected with the help of Coccinelle and, audited and fixed manually. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Pavel Machek --- drivers/leds/leds-gpio.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 200688c602cd..cf84096d88ce 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -125,12 +125,6 @@ struct gpio_leds_priv { struct gpio_led_data leds[]; }; -static inline int sizeof_gpio_leds_priv(int num_leds) -{ - return sizeof(struct gpio_leds_priv) + - (sizeof(struct gpio_led_data) * num_leds); -} - static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -142,7 +136,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) if (!count) return ERR_PTR(-ENODEV); - priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL); + priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); if (!priv) return ERR_PTR(-ENOMEM); @@ -260,9 +254,8 @@ static int gpio_led_probe(struct platform_device *pdev) int i, ret = 0; if (pdata && pdata->num_leds) { - priv = devm_kzalloc(&pdev->dev, - sizeof_gpio_leds_priv(pdata->num_leds), - GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, pdata->num_leds), + GFP_KERNEL); if (!priv) return -ENOMEM; From 7e6f7f3ac30c43f8a5f91846a3b2dc8799ea5744 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 13 Jul 2020 10:45:29 -0500 Subject: [PATCH 18/37] leds: lp55xx: Fix file permissions to use DEVICE_ATTR macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the checkpatch warnings for the use of the file permission macros. In converting the file permissions to the DEVICE_ATTR_XX macros the call back function names needed to be updated within the code. This means that the lp55xx_ needed to be dropped in the name to keep in harmony with the ABI documentation. Acked-by: Pavel Machek Acked-by: Jacek Anaszewski Signed-off-by: Dan Murphy Reviewed-by: Marek Behún Signed-off-by: Pavel Machek --- drivers/leds/leds-lp55xx-common.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1354965ac866..3d0bc4dcd9f2 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -77,7 +77,7 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } -static ssize_t lp55xx_show_current(struct device *dev, +static ssize_t led_current_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -86,7 +86,7 @@ static ssize_t lp55xx_show_current(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%d\n", led->led_current); } -static ssize_t lp55xx_store_current(struct device *dev, +static ssize_t led_current_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -110,7 +110,7 @@ static ssize_t lp55xx_store_current(struct device *dev, return len; } -static ssize_t lp55xx_show_max_current(struct device *dev, +static ssize_t max_current_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -119,9 +119,8 @@ static ssize_t lp55xx_show_max_current(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%d\n", led->max_current); } -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current, - lp55xx_store_current); -static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL); +static DEVICE_ATTR_RW(led_current); +static DEVICE_ATTR_RO(max_current); static struct attribute *lp55xx_led_attrs[] = { &dev_attr_led_current.attr, @@ -224,7 +223,7 @@ static int lp55xx_request_firmware(struct lp55xx_chip *chip) GFP_KERNEL, chip, lp55xx_firmware_loaded); } -static ssize_t lp55xx_show_engine_select(struct device *dev, +static ssize_t select_engine_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -234,7 +233,7 @@ static ssize_t lp55xx_show_engine_select(struct device *dev, return sprintf(buf, "%d\n", chip->engine_idx); } -static ssize_t lp55xx_store_engine_select(struct device *dev, +static ssize_t select_engine_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -276,7 +275,7 @@ static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start) chip->cfg->run_engine(chip, start); } -static ssize_t lp55xx_store_engine_run(struct device *dev, +static ssize_t run_engine_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -301,9 +300,8 @@ static ssize_t lp55xx_store_engine_run(struct device *dev, return len; } -static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR, - lp55xx_show_engine_select, lp55xx_store_engine_select); -static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run); +static DEVICE_ATTR_RW(select_engine); +static DEVICE_ATTR_WO(run_engine); static struct attribute *lp55xx_engine_attributes[] = { &dev_attr_select_engine.attr, From 7105e4647bcdbdce746289c00ba0a8a0ec790d20 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 13 Jul 2020 10:45:30 -0500 Subject: [PATCH 19/37] leds: lp5523: Fix various formatting issues in the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix checkpatch errors and warnings for the LP5523.c device driver. Acked-by: Pavel Machek Acked-by: Jacek Anaszewski Signed-off-by: Dan Murphy Reviewed-by: Marek Behún Signed-off-by: Pavel Machek --- drivers/leds/leds-lp5523.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d0b931a136b9..8518de957b48 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -23,13 +23,13 @@ #define LP5523_PROGRAM_LENGTH 32 /* bytes */ /* Memory is used like this: - 0x00 engine 1 program - 0x10 engine 2 program - 0x20 engine 3 program - 0x30 engine 1 muxing info - 0x40 engine 2 muxing info - 0x50 engine 3 muxing info -*/ + * 0x00 engine 1 program + * 0x10 engine 2 program + * 0x20 engine 3 program + * 0x30 engine 1 muxing info + * 0x40 engine 2 muxing info + * 0x50 engine 3 muxing info + */ #define LP5523_MAX_LEDS 9 /* Registers */ @@ -326,7 +326,7 @@ static int lp5523_update_program_memory(struct lp55xx_chip *chip, const u8 *data, size_t size) { u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; - unsigned cmd; + unsigned int cmd; char c[3]; int nrchars; int ret; @@ -468,6 +468,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) static void lp5523_mux_to_array(u16 led_mux, char *array) { int i, pos = 0; + for (i = 0; i < LP5523_MAX_LEDS; i++) pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); @@ -506,7 +507,7 @@ static int lp5523_load_mux(struct lp55xx_chip *chip, u16 mux, int nr) if (ret) return ret; - ret = lp55xx_write(chip, LP5523_REG_PROG_MEM , (u8)(mux >> 8)); + ret = lp55xx_write(chip, LP5523_REG_PROG_MEM, (u8)(mux >> 8)); if (ret) return ret; From 5c7f8ffe741daae7f8d811a2037b2693f02c90c5 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 13 Jul 2020 10:45:31 -0500 Subject: [PATCH 20/37] dt: bindings: Add multicolor class dt bindings documention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add DT bindings for the LEDs multicolor class framework. Add multicolor ID to the color ID list for device tree bindings. CC: Rob Herring Reviewed-by: Rob Herring Acked-by: Pavel Machek Acked-by: Jacek Anaszewski Signed-off-by: Dan Murphy Reviewed-by: Marek Behún Signed-off-by: Pavel Machek --- .../bindings/leds/leds-class-multicolor.yaml | 37 +++++++++++++++++++ include/dt-bindings/leds/common.h | 3 +- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml diff --git a/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml b/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml new file mode 100644 index 000000000000..b55e1f1308a4 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/leds-class-multicolor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Common properties for the multicolor LED class. + +maintainers: + - Dan Murphy + +description: | + Bindings for multi color LEDs show how to describe current outputs of + either integrated multi-color LED elements (like RGB, RGBW, RGBWA-UV + etc.) or standalone LEDs, to achieve logically grouped multi-color LED + modules. This is achieved by adding multi-led nodes layer to the + monochrome LED bindings. + The nodes and properties defined in this document are unique to the multicolor + LED class. Common LED nodes and properties are inherited from the common.txt + within this documentation directory. + +patternProperties: + "^multi-led@([0-9a-f])$": + type: object + description: Represents the LEDs that are to be grouped. + properties: + color: + const: 8 # LED_COLOR_ID_MULTI + description: | + For multicolor LED support this property should be defined as + LED_COLOR_ID_MULTI which can be found in include/linux/leds/common.h. + + $ref: "common.yaml#" + + required: + - color +... diff --git a/include/dt-bindings/leds/common.h b/include/dt-bindings/leds/common.h index 0ce7dfc00dcb..a463ce6a8794 100644 --- a/include/dt-bindings/leds/common.h +++ b/include/dt-bindings/leds/common.h @@ -30,7 +30,8 @@ #define LED_COLOR_ID_VIOLET 5 #define LED_COLOR_ID_YELLOW 6 #define LED_COLOR_ID_IR 7 -#define LED_COLOR_ID_MAX 8 +#define LED_COLOR_ID_MULTI 8 +#define LED_COLOR_ID_MAX 9 /* Standard LED functions */ /* Keyboard LEDs, usually it would be input4::capslock etc. */ From 10d3e0d815879129e916cd83e1034438e06efdaa Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 13 Jul 2020 10:45:32 -0500 Subject: [PATCH 21/37] leds: Add multicolor ID to the color ID list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new color ID that is declared as MULTICOLOR as with the multicolor framework declaring a definitive color is not accurate as the node can contain multiple colors. Signed-off-by: Dan Murphy Reviewed-by: Marek Behún Signed-off-by: Pavel Machek --- drivers/leds/led-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index f1f718dbe0f8..846248a0693d 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -34,6 +34,7 @@ const char * const led_colors[LED_COLOR_ID_MAX] = { [LED_COLOR_ID_VIOLET] = "violet", [LED_COLOR_ID_YELLOW] = "yellow", [LED_COLOR_ID_IR] = "ir", + [LED_COLOR_ID_MULTI] = "multicolor", }; EXPORT_SYMBOL_GPL(led_colors); From 55d5d3b46b08a4dc0b05343d24640744e7430ed7 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 16 Jul 2020 13:19:56 -0500 Subject: [PATCH 22/37] leds: multicolor: Introduce a multicolor class definition Introduce a multicolor class that groups colored LEDs within a LED node. The multicolor class groups monochrome LEDs and allows controlling two aspects of the final combined color: hue and lightness. The former is controlled via the intensity file and the latter is controlled via brightness file. Signed-off-by: Dan Murphy Acked-by: Jacek Anaszewski Signed-off-by: Pavel Machek [squashed leds: multicolor: Fix camel case in documentation in] --- .../ABI/testing/sysfs-class-led-multicolor | 35 +++ Documentation/leds/index.rst | 1 + Documentation/leds/leds-class-multicolor.rst | 86 ++++++++ drivers/leds/Kconfig | 10 + drivers/leds/Makefile | 1 + drivers/leds/led-class-multicolor.c | 203 ++++++++++++++++++ include/linux/led-class-multicolor.h | 121 +++++++++++ 7 files changed, 457 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-led-multicolor create mode 100644 Documentation/leds/leds-class-multicolor.rst create mode 100644 drivers/leds/led-class-multicolor.c create mode 100644 include/linux/led-class-multicolor.h diff --git a/Documentation/ABI/testing/sysfs-class-led-multicolor b/Documentation/ABI/testing/sysfs-class-led-multicolor new file mode 100644 index 000000000000..eeeddcbdbbe3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led-multicolor @@ -0,0 +1,35 @@ +What: /sys/class/leds//brightness +Date: March 2020 +KernelVersion: 5.9 +Contact: Dan Murphy +Description: read/write + Writing to this file will update all LEDs within the group to a + calculated percentage of what each color LED intensity is set + to. The percentage is calculated for each grouped LED via the + equation below: + + led_brightness = brightness * multi_intensity/max_brightness + + For additional details please refer to + Documentation/leds/leds-class-multicolor.rst. + + The value of the LED is from 0 to + /sys/class/leds//max_brightness. + +What: /sys/class/leds//multi_index +Date: March 2020 +KernelVersion: 5.9 +Contact: Dan Murphy +Description: read + The multi_index array, when read, will output the LED colors + as an array of strings as they are indexed in the + multi_intensity file. + +What: /sys/class/leds//multi_intensity +Date: March 2020 +KernelVersion: 5.9 +Contact: Dan Murphy +Description: read/write + This file contains array of integers. Order of components is + described by the multi_index array. The maximum intensity should + not exceed /sys/class/leds//max_brightness. diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst index 060f4e485897..bc70c6aa7138 100644 --- a/Documentation/leds/index.rst +++ b/Documentation/leds/index.rst @@ -9,6 +9,7 @@ LEDs leds-class leds-class-flash + leds-class-multicolor ledtrig-oneshot ledtrig-transient ledtrig-usbport diff --git a/Documentation/leds/leds-class-multicolor.rst b/Documentation/leds/leds-class-multicolor.rst new file mode 100644 index 000000000000..c57b98bfd387 --- /dev/null +++ b/Documentation/leds/leds-class-multicolor.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: GPL-2.0 + +==================================== +Multicolor LED handling under Linux +==================================== + +Description +=========== +The multicolor class groups monochrome LEDs and allows controlling two +aspects of the final combined color: hue and lightness. The former is +controlled via the multi_intensity array file and the latter is controlled +via brightness file. + +Multicolor Class Control +======================== +The multicolor class presents files that groups the colors as indexes in an +array. These files are children under the LED parent node created by the +led_class framework. The led_class framework is documented in led-class.rst +within this documentation directory. + +Each colored LED will be indexed under the multi_* files. The order of the +colors will be arbitrary. The multi_index file can be read to determine the +color name to indexed value. + +The multi_index file is an array that contains the string list of the colors as +they are defined in each multi_* array file. + +The multi_intensity is an array that can be read or written to for the +individual color intensities. All elements within this array must be written in +order for the color LED intensities to be updated. + +Directory Layout Example +======================== +root:/sys/class/leds/multicolor:status# ls -lR +-rw-r--r-- 1 root root 4096 Oct 19 16:16 brightness +-r--r--r-- 1 root root 4096 Oct 19 16:16 max_brightness +-r--r--r-- 1 root root 4096 Oct 19 16:16 multi_index +-rw-r--r-- 1 root root 4096 Oct 19 16:16 multi_intensity + +Multicolor Class Brightness Control +=================================== +The brightness level for each LED is calculated based on the color LED +intensity setting divided by the global max_brightness setting multiplied by +the requested brightness. + +led_brightness = brightness * multi_intensity/max_brightness + +Example: +A user first writes the multi_intensity file with the brightness levels +for each LED that are necessary to achieve a certain color output from a +multicolor LED group. + +cat /sys/class/leds/multicolor:status/multi_index +green blue red + +echo 43 226 138 > /sys/class/leds/multicolor:status/multi_intensity + +red - + intensity = 138 + max_brightness = 255 +green - + intensity = 43 + max_brightness = 255 +blue - + intensity = 226 + max_brightness = 255 + +The user can control the brightness of that multicolor LED group by writing the +global 'brightness' control. Assuming a max_brightness of 255 the user +may want to dim the LED color group to half. The user would write a value of +128 to the global brightness file then the values written to each LED will be +adjusted base on this value. + +cat /sys/class/leds/multicolor:status/max_brightness +255 +echo 128 > /sys/class/leds/multicolor:status/brightness + +adjusted_red_value = 128 * 138/255 = 69 +adjusted_green_value = 128 * 43/255 = 21 +adjusted_blue_value = 128 * 226/255 = 113 + +Reading the global brightness file will return the current brightness value of +the color LED group. + +cat /sys/class/leds/multicolor:status/brightness +128 diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index ed943140e1fd..1de6e8e264a0 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -30,6 +30,16 @@ config LEDS_CLASS_FLASH for the flash related features of a LED device. It can be built as a module. +config LEDS_CLASS_MULTICOLOR + tristate "LED Multicolor Class Support" + depends on LEDS_CLASS + help + This option enables the multicolor LED sysfs class in /sys/class/leds. + It wraps LED class and adds multicolor LED specific sysfs attributes + and kernel internal API to it. You'll need this to provide support + for multicolor LEDs that are grouped together. This class is not + intended for single color LEDs. It can be built as a module. + config LEDS_BRIGHTNESS_HW_CHANGED bool "LED Class brightness_hw_changed attribute support" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index d6b8a792c936..d684bc76d2b2 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_NEW_LEDS) += led-core.o obj-$(CONFIG_LEDS_CLASS) += led-class.o obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o +obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += led-class-multicolor.o obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers (keep this sorted, M-| sort) diff --git a/drivers/leds/led-class-multicolor.c b/drivers/leds/led-class-multicolor.c new file mode 100644 index 000000000000..e317408583df --- /dev/null +++ b/drivers/leds/led-class-multicolor.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +// LED Multicolor class interface +// Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/ +// Author: Dan Murphy + +#include +#include +#include +#include +#include +#include + +#include "leds.h" + +int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, + enum led_brightness brightness) +{ + struct led_classdev *led_cdev = &mcled_cdev->led_cdev; + int i; + + for (i = 0; i < mcled_cdev->num_colors; i++) + mcled_cdev->subled_info[i].brightness = brightness * + mcled_cdev->subled_info[i].intensity / + led_cdev->max_brightness; + + return 0; +} +EXPORT_SYMBOL_GPL(led_mc_calc_color_components); + +static ssize_t multi_intensity_store(struct device *dev, + struct device_attribute *intensity_attr, + const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + int nrchars, offset = 0; + int intensity_value[LED_COLOR_ID_MAX]; + int i; + ssize_t ret; + + mutex_lock(&led_cdev->led_access); + + for (i = 0; i < mcled_cdev->num_colors; i++) { + ret = sscanf(buf + offset, "%i%n", + &intensity_value[i], &nrchars); + if (ret != 1) { + ret = -EINVAL; + goto err_out; + } + offset += nrchars; + } + + offset++; + if (offset < size) { + ret = -EINVAL; + goto err_out; + } + + for (i = 0; i < mcled_cdev->num_colors; i++) + mcled_cdev->subled_info[i].intensity = intensity_value[i]; + + led_set_brightness(led_cdev, led_cdev->brightness); + ret = size; +err_out: + mutex_unlock(&led_cdev->led_access); + return ret; +} + +static ssize_t multi_intensity_show(struct device *dev, + struct device_attribute *intensity_attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + int len = 0; + int i; + + for (i = 0; i < mcled_cdev->num_colors; i++) { + len += sprintf(buf + len, "%d", + mcled_cdev->subled_info[i].intensity); + if (i < mcled_cdev->num_colors - 1) + len += sprintf(buf + len, " "); + } + + buf[len++] = '\n'; + return len; +} +static DEVICE_ATTR_RW(multi_intensity); + +static ssize_t multi_index_show(struct device *dev, + struct device_attribute *multi_index_attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + int len = 0; + int index; + int i; + + for (i = 0; i < mcled_cdev->num_colors; i++) { + index = mcled_cdev->subled_info[i].color_index; + len += sprintf(buf + len, "%s", led_colors[index]); + if (i < mcled_cdev->num_colors - 1) + len += sprintf(buf + len, " "); + } + + buf[len++] = '\n'; + return len; +} +static DEVICE_ATTR_RO(multi_index); + +static struct attribute *led_multicolor_attrs[] = { + &dev_attr_multi_intensity.attr, + &dev_attr_multi_index.attr, + NULL, +}; +ATTRIBUTE_GROUPS(led_multicolor); + +int led_classdev_multicolor_register_ext(struct device *parent, + struct led_classdev_mc *mcled_cdev, + struct led_init_data *init_data) +{ + struct led_classdev *led_cdev; + + if (!mcled_cdev) + return -EINVAL; + + if (mcled_cdev->num_colors <= 0) + return -EINVAL; + + if (mcled_cdev->num_colors > LED_COLOR_ID_MAX) + return -EINVAL; + + led_cdev = &mcled_cdev->led_cdev; + mcled_cdev->led_cdev.groups = led_multicolor_groups; + + return led_classdev_register_ext(parent, led_cdev, init_data); +} +EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext); + +void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev) +{ + if (!mcled_cdev) + return; + + led_classdev_unregister(&mcled_cdev->led_cdev); +} +EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister); + +static void devm_led_classdev_multicolor_release(struct device *dev, void *res) +{ + led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res); +} + +int devm_led_classdev_multicolor_register_ext(struct device *parent, + struct led_classdev_mc *mcled_cdev, + struct led_init_data *init_data) +{ + struct led_classdev_mc **dr; + int ret; + + dr = devres_alloc(devm_led_classdev_multicolor_release, + sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = led_classdev_multicolor_register_ext(parent, mcled_cdev, + init_data); + if (ret) { + devres_free(dr); + return ret; + } + + *dr = mcled_cdev; + devres_add(parent, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext); + +static int devm_led_classdev_multicolor_match(struct device *dev, + void *res, void *data) +{ + struct led_classdev_mc **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +void devm_led_classdev_multicolor_unregister(struct device *dev, + struct led_classdev_mc *mcled_cdev) +{ + WARN_ON(devres_release(dev, + devm_led_classdev_multicolor_release, + devm_led_classdev_multicolor_match, mcled_cdev)); +} +EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_DESCRIPTION("Multicolor LED class interface"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/led-class-multicolor.h b/include/linux/led-class-multicolor.h new file mode 100644 index 000000000000..5116f9a866cc --- /dev/null +++ b/include/linux/led-class-multicolor.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* LED Multicolor class interface + * Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#ifndef _LINUX_MULTICOLOR_LEDS_H_INCLUDED +#define _LINUX_MULTICOLOR_LEDS_H_INCLUDED + +#include +#include + +struct mc_subled { + unsigned int color_index; + unsigned int brightness; + unsigned int intensity; + unsigned int channel; +}; + +struct led_classdev_mc { + /* led class device */ + struct led_classdev led_cdev; + unsigned int num_colors; + + struct mc_subled *subled_info; +}; + +static inline struct led_classdev_mc *lcdev_to_mccdev( + struct led_classdev *led_cdev) +{ + return container_of(led_cdev, struct led_classdev_mc, led_cdev); +} + +#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR) +/** + * led_classdev_multicolor_register_ext - register a new object of led_classdev + * class with support for multicolor LEDs + * @parent: the multicolor LED to register + * @mcled_cdev: the led_classdev_mc structure for this device + * @init_data: the LED class multicolor device initialization data + * + * Returns: 0 on success or negative error value on failure + */ +int led_classdev_multicolor_register_ext(struct device *parent, + struct led_classdev_mc *mcled_cdev, + struct led_init_data *init_data); + +static inline int led_classdev_multicolor_register(struct device *parent, + struct led_classdev_mc *mcled_cdev) +{ + return led_classdev_multicolor_register_ext(parent, mcled_cdev, NULL); +} + +/** + * led_classdev_multicolor_unregister - unregisters an object of led_classdev + * class with support for multicolor LEDs + * @mcled_cdev: the multicolor LED to unregister + * + * Unregister a previously registered via led_classdev_multicolor_register + * object + */ +void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev); + +/* Calculate brightness for the monochrome LED cluster */ +int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, + enum led_brightness brightness); + +int devm_led_classdev_multicolor_register_ext(struct device *parent, + struct led_classdev_mc *mcled_cdev, + struct led_init_data *init_data); + +static inline int devm_led_classdev_multicolor_register(struct device *parent, + struct led_classdev_mc *mcled_cdev) +{ + return devm_led_classdev_multicolor_register_ext(parent, mcled_cdev, + NULL); +} + +void devm_led_classdev_multicolor_unregister(struct device *parent, + struct led_classdev_mc *mcled_cdev); +#else + +static inline int led_classdev_multicolor_register_ext(struct device *parent, + struct led_classdev_mc *mcled_cdev, + struct led_init_data *init_data) +{ + return -EINVAL; +} + +static inline int led_classdev_multicolor_register(struct device *parent, + struct led_classdev_mc *mcled_cdev) +{ + return led_classdev_multicolor_register_ext(parent, mcled_cdev, NULL); +} + +static inline void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev) {}; +static inline int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, + enum led_brightness brightness) +{ + return -EINVAL; +} + +static inline int devm_led_classdev_multicolor_register_ext(struct device *parent, + struct led_classdev_mc *mcled_cdev, + struct led_init_data *init_data) +{ + return -EINVAL; +} + +static inline int devm_led_classdev_multicolor_register(struct device *parent, + struct led_classdev_mc *mcled_cdev) +{ + return devm_led_classdev_multicolor_register_ext(parent, mcled_cdev, + NULL); +} + +static inline void devm_led_classdev_multicolor_unregister(struct device *parent, + struct led_classdev_mc *mcled_cdev) +{}; + +#endif /* IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR) */ +#endif /* _LINUX_MULTICOLOR_LEDS_H_INCLUDED */ From a89d385daa6a943a8e556d8b8b648403909b33f7 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 16 Jul 2020 13:19:59 -0500 Subject: [PATCH 23/37] dt-bindings: leds: Convert leds-lp55xx to yaml Convert the leds-lp55xx.txt to yaml binding. Reviewed-by: Rob Herring Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- .../devicetree/bindings/leds/leds-lp55xx.txt | 228 ------------------ .../devicetree/bindings/leds/leds-lp55xx.yaml | 220 +++++++++++++++++ 2 files changed, 220 insertions(+), 228 deletions(-) delete mode 100644 Documentation/devicetree/bindings/leds/leds-lp55xx.txt create mode 100644 Documentation/devicetree/bindings/leds/leds-lp55xx.yaml diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt deleted file mode 100644 index 1b66a413fb9d..000000000000 --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt +++ /dev/null @@ -1,228 +0,0 @@ -Binding for TI/National Semiconductor LP55xx Led Drivers - -Required properties: -- compatible: one of - national,lp5521 - national,lp5523 - ti,lp55231 - ti,lp5562 - ti,lp8501 - -- reg: I2C slave address -- clock-mode: Input clock mode, (0: automode, 1: internal, 2: external) - -Each child has own specific current settings -- led-cur: Current setting at each led channel (mA x10, 0 if led is not connected) -- max-cur: Maximun current at each led channel. - -Optional properties: -- enable-gpio: GPIO attached to the chip's enable pin -- label: Used for naming LEDs -- pwr-sel: LP8501 specific property. Power selection for output channels. - 0: D1~9 are connected to VDD - 1: D1~6 with VDD, D7~9 with VOUT - 2: D1~6 with VOUT, D7~9 with VDD - 3: D1~9 are connected to VOUT - -Alternatively, each child can have a specific channel name and trigger: -- chan-name (optional): name of channel -- linux,default-trigger (optional): see - Documentation/devicetree/bindings/leds/common.txt - -example 1) LP5521 -3 LED channels, external clock used. Channel names are 'lp5521_pri:channel0', -'lp5521_pri:channel1' and 'lp5521_pri:channel2', with a heartbeat trigger -on channel 0. - -lp5521@32 { - compatible = "national,lp5521"; - reg = <0x32>; - label = "lp5521_pri"; - clock-mode = /bits/ 8 <2>; - - chan0 { - led-cur = /bits/ 8 <0x2f>; - max-cur = /bits/ 8 <0x5f>; - linux,default-trigger = "heartbeat"; - }; - - chan1 { - led-cur = /bits/ 8 <0x2f>; - max-cur = /bits/ 8 <0x5f>; - }; - - chan2 { - led-cur = /bits/ 8 <0x2f>; - max-cur = /bits/ 8 <0x5f>; - }; -}; - -example 2) LP5523 -9 LED channels with specific name. Internal clock used. -The I2C slave address is configurable with ASEL1 and ASEL0 pins. -Available addresses are 32/33/34/35h. - -ASEL1 ASEL0 Address -------------------------- - GND GND 32h - GND VEN 33h - VEN GND 34h - VEN VEN 35h - -lp5523@32 { - compatible = "national,lp5523"; - reg = <0x32>; - clock-mode = /bits/ 8 <1>; - - chan0 { - chan-name = "d1"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan1 { - chan-name = "d2"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan2 { - chan-name = "d3"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan3 { - chan-name = "d4"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan4 { - chan-name = "d5"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan5 { - chan-name = "d6"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan6 { - chan-name = "d7"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan7 { - chan-name = "d8"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan8 { - chan-name = "d9"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; -}; - -example 3) LP5562 -4 channels are defined. - -lp5562@30 { - compatible = "ti,lp5562"; - reg = <0x30>; - clock-mode = /bits/8 <2>; - - chan0 { - chan-name = "R"; - led-cur = /bits/ 8 <0x20>; - max-cur = /bits/ 8 <0x60>; - }; - - chan1 { - chan-name = "G"; - led-cur = /bits/ 8 <0x20>; - max-cur = /bits/ 8 <0x60>; - }; - - chan2 { - chan-name = "B"; - led-cur = /bits/ 8 <0x20>; - max-cur = /bits/ 8 <0x60>; - }; - - chan3 { - chan-name = "W"; - led-cur = /bits/ 8 <0x20>; - max-cur = /bits/ 8 <0x60>; - }; -}; - -example 4) LP8501 -9 channels are defined. The 'pwr-sel' is LP8501 specific property. -Others are same as LP5523. - -lp8501@32 { - compatible = "ti,lp8501"; - reg = <0x32>; - clock-mode = /bits/ 8 <2>; - pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */ - - chan0 { - chan-name = "d1"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan1 { - chan-name = "d2"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan2 { - chan-name = "d3"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan3 { - chan-name = "d4"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan4 { - chan-name = "d5"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan5 { - chan-name = "d6"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan6 { - chan-name = "d7"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan7 { - chan-name = "d8"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; - - chan8 { - chan-name = "d9"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml new file mode 100644 index 000000000000..b1bb3feb0f4d --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml @@ -0,0 +1,220 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/leds-lp55xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI/National Semiconductor LP55xx and LP8501 LED Drivers + +maintainers: + - Jacek Anaszewski + - Pavel Machek + +description: | + Bindings for the TI/National Semiconductor LP55xx and LP8501 multi channel + LED Drivers. + + For more product information please see the link below: + https://www.ti.com/lit/gpn/lp5521 + https://www.ti.com/lit/gpn/lp5523 + https://www.ti.com/lit/gpn/lp55231 + https://www.ti.com/lit/gpn/lp5562 + https://www.ti.com/lit/gpn/lp8501 + +properties: + compatible: + enum: + - national,lp5521 + - national,lp5523 + - ti,lp55231 + - ti,lp5562 + - ti,lp8501 + + reg: + maxItems: 1 + description: I2C slave address + + clock-mode: + $ref: /schemas/types.yaml#definitions/uint8 + description: | + Input clock mode + enum: + - 0 # automode + - 1 # internal + - 2 # external + + enable-gpio: + maxItems: 1 + description: | + GPIO attached to the chip's enable pin + + pwr-sel: + $ref: /schemas/types.yaml#definitions/uint8 + description: | + LP8501 specific property. Power selection for output channels. + enum: + - 0 # D1~9 are connected to VDD + - 1 # D1~6 with VDD, D7~9 with VOUT + - 2 # D1~6 with VOUT, D7~9 with VDD + - 3 # D1~9 are connected to VOUT + +patternProperties: + "(^led@[0-9a-f]$|led)": + type: object + $ref: common.yaml# + properties: + led-cur: + $ref: /schemas/types.yaml#definitions/uint8 + description: | + Current setting at each LED channel (mA x10, 0 if LED is not connected) + minimum: 0 + maximum: 255 + + max-cur: + $ref: /schemas/types.yaml#definitions/uint8 + description: Maximun current at each LED channel. + + reg: + description: | + Output channel for the LED. This is zero based channel identifier and + the data sheet is a one based channel identifier. + reg value to output to LED output number + enum: + - 0 # LED output D1 + - 1 # LED output D2 + - 2 # LED output D3 + - 3 # LED output D4 + - 4 # LED output D5 + - 5 # LED output D6 + - 6 # LED output D7 + - 7 # LED output D8 + - 8 # LED output D9 + + chan-name: + $ref: /schemas/types.yaml#definitions/string + description: name of channel + +required: + - compatible + - reg + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@32 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,lp8501"; + reg = <0x32>; + clock-mode = /bits/ 8 <2>; + pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */ + + led@0 { + reg = <0>; + chan-name = "d1"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@1 { + reg = <1>; + chan-name = "d2"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@2 { + reg = <2>; + chan-name = "d3"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@3 { + reg = <3>; + chan-name = "d4"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@4 { + reg = <4>; + chan-name = "d5"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@5 { + reg = <5>; + chan-name = "d6"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@6 { + reg = <6>; + chan-name = "d7"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@7 { + reg = <7>; + chan-name = "d8"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + led@8 { + reg = <8>; + chan-name = "d9"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + }; + + led-controller@33 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "national,lp5523"; + reg = <0x33>; + clock-mode = /bits/ 8 <0>; + + multi-led@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x2>; + color = ; + function = LED_FUNCTION_STANDBY; + linux,default-trigger = "heartbeat"; + + led@0 { + led-cur = /bits/ 8 <50>; + max-cur = /bits/ 8 <100>; + reg = <0x0>; + color = ; + }; + + led@1 { + led-cur = /bits/ 8 <50>; + max-cur = /bits/ 8 <100>; + reg = <0x1>; + color = ; + }; + + led@6 { + led-cur = /bits/ 8 <50>; + max-cur = /bits/ 8 <100>; + reg = <0x6>; + color = ; + }; + }; + }; + }; + +... From c732eaf01f9c213d34b2d224bcda830089bbcf8a Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 16 Jul 2020 13:20:00 -0500 Subject: [PATCH 24/37] leds: lp55xx: Convert LED class registration to devm_* Convert the LED class registration calls to the LED devm_* registration calls. Acked-by: Jacek Anaszewski Acked-by: Pavel Machek Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/leds-lp5521.c | 9 +++------ drivers/leds/leds-lp5523.c | 9 +++------ drivers/leds/leds-lp5562.c | 9 +++------ drivers/leds/leds-lp55xx-common.c | 15 +-------------- drivers/leds/leds-lp55xx-common.h | 2 -- drivers/leds/leds-lp8501.c | 9 +++------ 6 files changed, 13 insertions(+), 40 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 6f0272249dc8..6d2163c0f625 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -541,19 +541,17 @@ static int lp5521_probe(struct i2c_client *client, ret = lp55xx_register_leds(led, chip); if (ret) - goto err_register_leds; + goto err_out; ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto err_register_sysfs; + goto err_out; } return 0; -err_register_sysfs: - lp55xx_unregister_leds(led, chip); -err_register_leds: +err_out: lp55xx_deinit_device(chip); err_init: return ret; @@ -566,7 +564,6 @@ static int lp5521_remove(struct i2c_client *client) lp5521_stop_all_engines(chip); lp55xx_unregister_sysfs(chip); - lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 8518de957b48..cb550cf19e14 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -909,19 +909,17 @@ static int lp5523_probe(struct i2c_client *client, ret = lp55xx_register_leds(led, chip); if (ret) - goto err_register_leds; + goto err_out; ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto err_register_sysfs; + goto err_out; } return 0; -err_register_sysfs: - lp55xx_unregister_leds(led, chip); -err_register_leds: +err_out: lp55xx_deinit_device(chip); err_init: return ret; @@ -934,7 +932,6 @@ static int lp5523_remove(struct i2c_client *client) lp5523_stop_all_engines(chip); lp55xx_unregister_sysfs(chip); - lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index edb57c42e8b1..1c94422408b0 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -554,19 +554,17 @@ static int lp5562_probe(struct i2c_client *client, ret = lp55xx_register_leds(led, chip); if (ret) - goto err_register_leds; + goto err_out; ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto err_register_sysfs; + goto err_out; } return 0; -err_register_sysfs: - lp55xx_unregister_leds(led, chip); -err_register_leds: +err_out: lp55xx_deinit_device(chip); err_init: return ret; @@ -580,7 +578,6 @@ static int lp5562_remove(struct i2c_client *client) lp5562_stop_engine(chip); lp55xx_unregister_sysfs(chip); - lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 3d0bc4dcd9f2..243c749ebda5 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -179,7 +179,7 @@ static int lp55xx_init_led(struct lp55xx_led *led, led->cdev.name = name; } - ret = led_classdev_register(dev, &led->cdev); + ret = devm_led_classdev_register(dev, &led->cdev); if (ret) { dev_err(dev, "led register err: %d\n", ret); return ret; @@ -480,23 +480,10 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) return 0; err_init_led: - lp55xx_unregister_leds(led, chip); return ret; } EXPORT_SYMBOL_GPL(lp55xx_register_leds); -void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) -{ - int i; - struct lp55xx_led *each; - - for (i = 0; i < chip->num_leds; i++) { - each = led + i; - led_classdev_unregister(&each->cdev); - } -} -EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); - int lp55xx_register_sysfs(struct lp55xx_chip *chip) { struct device *dev = &chip->cl->dev; diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 783ed5103ce5..b9b1041e8143 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -189,8 +189,6 @@ extern void lp55xx_deinit_device(struct lp55xx_chip *chip); /* common LED class device functions */ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); -extern void lp55xx_unregister_leds(struct lp55xx_led *led, - struct lp55xx_chip *chip); /* common device attributes functions */ extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index 2638dbf0e8ac..a58019cdb8c3 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -344,19 +344,17 @@ static int lp8501_probe(struct i2c_client *client, ret = lp55xx_register_leds(led, chip); if (ret) - goto err_register_leds; + goto err_out; ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto err_register_sysfs; + goto err_out; } return 0; -err_register_sysfs: - lp55xx_unregister_leds(led, chip); -err_register_leds: +err_out: lp55xx_deinit_device(chip); err_init: return ret; @@ -369,7 +367,6 @@ static int lp8501_remove(struct i2c_client *client) lp8501_stop_engine(chip); lp55xx_unregister_sysfs(chip); - lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; From 92a81562e695628086acb92f95090ab09d9b9ec0 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 16 Jul 2020 13:20:01 -0500 Subject: [PATCH 25/37] leds: lp55xx: Add multicolor framework support to lp55xx Add multicolor framework support for the lp55xx family. Acked-by: Pavel Machek Acked-by: Jacek Anaszewski Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 11 +- drivers/leds/leds-lp5521.c | 14 +- drivers/leds/leds-lp5523.c | 14 +- drivers/leds/leds-lp5562.c | 13 +- drivers/leds/leds-lp55xx-common.c | 185 +++++++++++++++++++--- drivers/leds/leds-lp55xx-common.h | 14 +- drivers/leds/leds-lp8501.c | 14 +- include/linux/platform_data/leds-lp55xx.h | 7 + 8 files changed, 216 insertions(+), 56 deletions(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1de6e8e264a0..b9002850b5fa 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -386,7 +386,8 @@ config LEDS_LP3952 config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" - depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 + depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR + depends on OF select FW_LOADER select FW_LOADER_USER_HELPER help @@ -396,7 +397,7 @@ config LEDS_LP55XX_COMMON config LEDS_LP5521 tristate "LED Support for N.S. LP5521 LED driver chip" depends on LEDS_CLASS && I2C - select LEDS_LP55XX_COMMON + depends on LEDS_LP55XX_COMMON help If you say yes here you get support for the National Semiconductor LP5521 LED driver. It is 3 channel chip with programmable engines. @@ -406,7 +407,7 @@ config LEDS_LP5521 config LEDS_LP5523 tristate "LED Support for TI/National LP5523/55231 LED driver chip" depends on LEDS_CLASS && I2C - select LEDS_LP55XX_COMMON + depends on LEDS_LP55XX_COMMON help If you say yes here you get support for TI/National Semiconductor LP5523/55231 LED driver. @@ -417,7 +418,7 @@ config LEDS_LP5523 config LEDS_LP5562 tristate "LED Support for TI LP5562 LED driver chip" depends on LEDS_CLASS && I2C - select LEDS_LP55XX_COMMON + depends on LEDS_LP55XX_COMMON help If you say yes here you get support for TI LP5562 LED driver. It is 4 channels chip with programmable engines. @@ -427,7 +428,7 @@ config LEDS_LP5562 config LEDS_LP8501 tristate "LED Support for TI LP8501 LED driver chip" depends on LEDS_CLASS && I2C - select LEDS_LP55XX_COMMON + depends on LEDS_LP55XX_COMMON help If you say yes here you get support for TI LP8501 LED driver. It is 9 channel chip with programmable engines. diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 6d2163c0f625..6ff81d6be789 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -505,9 +505,16 @@ static int lp5521_probe(struct i2c_client *client, struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev); struct device_node *np = client->dev.of_node; + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->cfg = &lp5521_cfg; + if (!pdata) { if (np) { - pdata = lp55xx_of_populate_pdata(&client->dev, np); + pdata = lp55xx_of_populate_pdata(&client->dev, np, + chip); if (IS_ERR(pdata)) return PTR_ERR(pdata); } else { @@ -516,10 +523,6 @@ static int lp5521_probe(struct i2c_client *client, } } - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - led = devm_kcalloc(&client->dev, pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) @@ -527,7 +530,6 @@ static int lp5521_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; - chip->cfg = &lp5521_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cb550cf19e14..bb97549007d7 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -873,9 +873,16 @@ static int lp5523_probe(struct i2c_client *client, struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev); struct device_node *np = client->dev.of_node; + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->cfg = &lp5523_cfg; + if (!pdata) { if (np) { - pdata = lp55xx_of_populate_pdata(&client->dev, np); + pdata = lp55xx_of_populate_pdata(&client->dev, np, + chip); if (IS_ERR(pdata)) return PTR_ERR(pdata); } else { @@ -884,10 +891,6 @@ static int lp5523_probe(struct i2c_client *client, } } - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - led = devm_kcalloc(&client->dev, pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) @@ -895,7 +898,6 @@ static int lp5523_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; - chip->cfg = &lp5523_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 1c94422408b0..7ecdd199d7ef 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -520,9 +520,16 @@ static int lp5562_probe(struct i2c_client *client, struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev); struct device_node *np = client->dev.of_node; + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->cfg = &lp5562_cfg; + if (!pdata) { if (np) { - pdata = lp55xx_of_populate_pdata(&client->dev, np); + pdata = lp55xx_of_populate_pdata(&client->dev, np, + chip); if (IS_ERR(pdata)) return PTR_ERR(pdata); } else { @@ -531,9 +538,6 @@ static int lp5562_probe(struct i2c_client *client, } } - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; led = devm_kcalloc(&client->dev, pdata->num_channels, sizeof(*led), GFP_KERNEL); @@ -542,7 +546,6 @@ static int lp5562_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; - chip->cfg = &lp5562_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 243c749ebda5..af14e2b2d577 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -34,6 +34,11 @@ static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev) return cdev_to_lp55xx_led(dev_get_drvdata(dev)); } +static struct lp55xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev) +{ + return container_of(mc_cdev, struct lp55xx_led, mc_cdev); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -129,6 +134,18 @@ static struct attribute *lp55xx_led_attrs[] = { }; ATTRIBUTE_GROUPS(lp55xx_led); +static int lp55xx_set_mc_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev); + struct lp55xx_led *led = mcled_cdev_to_led(mc_dev); + struct lp55xx_device_config *cfg = led->chip->cfg; + + led_mc_calc_color_components(&led->mc_cdev, brightness); + return cfg->multicolor_brightness_fn(led); + +} + static int lp55xx_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { @@ -145,9 +162,12 @@ static int lp55xx_init_led(struct lp55xx_led *led, struct lp55xx_platform_data *pdata = chip->pdata; struct lp55xx_device_config *cfg = chip->cfg; struct device *dev = &chip->cl->dev; - char name[32]; - int ret; int max_channel = cfg->max_channel; + struct mc_subled *mc_led_info; + struct led_classdev *led_cdev; + char name[32]; + int i, j = 0; + int ret; if (chan >= max_channel) { dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel); @@ -157,20 +177,6 @@ static int lp55xx_init_led(struct lp55xx_led *led, if (pdata->led_config[chan].led_current == 0) return 0; - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - led->cdev.default_trigger = pdata->led_config[chan].default_trigger; - - if (led->chan_nr >= max_channel) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - max_channel - 1); - return -EINVAL; - } - - led->cdev.brightness_set_blocking = lp55xx_set_brightness; - led->cdev.groups = lp55xx_led_groups; - if (pdata->led_config[chan].name) { led->cdev.name = pdata->led_config[chan].name; } else { @@ -179,7 +185,47 @@ static int lp55xx_init_led(struct lp55xx_led *led, led->cdev.name = name; } - ret = devm_led_classdev_register(dev, &led->cdev); + if (pdata->led_config[chan].num_colors > 1) { + mc_led_info = devm_kcalloc(dev, + pdata->led_config[chan].num_colors, + sizeof(*mc_led_info), GFP_KERNEL); + if (!mc_led_info) + return -ENOMEM; + + led_cdev = &led->mc_cdev.led_cdev; + led_cdev->name = led->cdev.name; + led_cdev->brightness_set_blocking = lp55xx_set_mc_brightness; + led->mc_cdev.num_colors = pdata->led_config[chan].num_colors; + for (i = 0; i < led->mc_cdev.num_colors; i++) { + mc_led_info[i].color_index = + pdata->led_config[chan].color_id[i]; + mc_led_info[i].channel = + pdata->led_config[chan].output_num[i]; + j++; + } + + led->mc_cdev.subled_info = mc_led_info; + } else { + led->cdev.brightness_set_blocking = lp55xx_set_brightness; + } + + led->cdev.groups = lp55xx_led_groups; + led->cdev.default_trigger = pdata->led_config[chan].default_trigger; + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= max_channel) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + max_channel - 1); + return -EINVAL; + } + + if (pdata->led_config[chan].num_colors > 1) + ret = devm_led_classdev_multicolor_register(dev, &led->mc_cdev); + else + ret = devm_led_classdev_register(dev, &led->cdev); + if (ret) { dev_err(dev, "led register err: %d\n", ret); return ret; @@ -515,14 +561,105 @@ void lp55xx_unregister_sysfs(struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs); +static int lp55xx_parse_common_child(struct device_node *np, + struct lp55xx_led_config *cfg, + int led_number, int *chan_nr) +{ + int ret; + + of_property_read_string(np, "chan-name", + &cfg[led_number].name); + of_property_read_u8(np, "led-cur", + &cfg[led_number].led_current); + of_property_read_u8(np, "max-cur", + &cfg[led_number].max_current); + + ret = of_property_read_u32(np, "reg", chan_nr); + if (ret) + return ret; + + if (*chan_nr < 0 || *chan_nr > cfg->max_channel) + return -EINVAL; + + return 0; +} + +static int lp55xx_parse_multi_led_child(struct device_node *child, + struct lp55xx_led_config *cfg, + int child_number, int color_number) +{ + int chan_nr, color_id, ret; + + ret = lp55xx_parse_common_child(child, cfg, child_number, &chan_nr); + if (ret) + return ret; + + ret = of_property_read_u32(child, "color", &color_id); + if (ret) + return ret; + + cfg[child_number].color_id[color_number] = color_id; + cfg[child_number].output_num[color_number] = chan_nr; + + return 0; +} + +static int lp55xx_parse_multi_led(struct device_node *np, + struct lp55xx_led_config *cfg, + int child_number) +{ + struct device_node *child; + int num_colors = 0, ret; + + for_each_child_of_node(np, child) { + ret = lp55xx_parse_multi_led_child(child, cfg, child_number, + num_colors); + if (ret) + return ret; + num_colors++; + } + + cfg[child_number].num_colors = num_colors; + + return 0; +} + +static int lp55xx_parse_logical_led(struct device_node *np, + struct lp55xx_led_config *cfg, + int child_number) +{ + int led_color, ret; + int chan_nr = 0; + + cfg[child_number].default_trigger = + of_get_property(np, "linux,default-trigger", NULL); + + ret = of_property_read_u32(np, "color", &led_color); + if (ret) + return ret; + + if (led_color == LED_COLOR_ID_MULTI) + return lp55xx_parse_multi_led(np, cfg, child_number); + + ret = lp55xx_parse_common_child(np, cfg, child_number, &chan_nr); + if (ret < 0) + return ret; + + cfg[child_number].chan_nr = chan_nr; + + return ret; +} + struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev, - struct device_node *np) + struct device_node *np, + struct lp55xx_chip *chip) { struct device_node *child; struct lp55xx_platform_data *pdata; struct lp55xx_led_config *cfg; int num_channels; int i = 0; + int ret; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -540,16 +677,12 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev, pdata->led_config = &cfg[0]; pdata->num_channels = num_channels; + cfg->max_channel = chip->cfg->max_channel; for_each_child_of_node(np, child) { - cfg[i].chan_nr = i; - - of_property_read_string(child, "chan-name", &cfg[i].name); - of_property_read_u8(child, "led-cur", &cfg[i].led_current); - of_property_read_u8(child, "max-cur", &cfg[i].max_current); - cfg[i].default_trigger = - of_get_property(child, "linux,default-trigger", NULL); - + ret = lp55xx_parse_logical_led(child, cfg, i); + if (ret) + return ERR_PTR(-EINVAL); i++; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index b9b1041e8143..2f38c5b33830 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -12,6 +12,8 @@ #ifndef _LEDS_LP55XX_COMMON_H #define _LEDS_LP55XX_COMMON_H +#include + enum lp55xx_engine_index { LP55XX_ENGINE_INVALID, LP55XX_ENGINE_1, @@ -93,6 +95,7 @@ struct lp55xx_reg { * @max_channel : Maximum number of channels * @post_init_device : Chip specific initialization code * @brightness_fn : Brightness function + * @multicolor_brightness_fn : Multicolor brightness function * @set_led_current : LED current set function * @firmware_cb : Call function when the firmware is loaded * @run_engine : Run internal engine for pattern @@ -106,9 +109,12 @@ struct lp55xx_device_config { /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); - /* access brightness register */ + /* set LED brightness */ int (*brightness_fn)(struct lp55xx_led *led); + /* set multicolor LED brightness */ + int (*multicolor_brightness_fn)(struct lp55xx_led *led); + /* current setting function */ void (*set_led_current) (struct lp55xx_led *led, u8 led_current); @@ -159,6 +165,8 @@ struct lp55xx_chip { * struct lp55xx_led * @chan_nr : Channel number * @cdev : LED class device + * @mc_cdev : Multi color class device + * @color_components: Multi color LED map information * @led_current : Current setting at each led channel * @max_current : Maximun current at each led channel * @brightness : Brightness value @@ -167,6 +175,7 @@ struct lp55xx_chip { struct lp55xx_led { int chan_nr; struct led_classdev cdev; + struct led_classdev_mc mc_cdev; u8 led_current; u8 max_current; u8 brightness; @@ -196,6 +205,7 @@ extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip); /* common device tree population function */ extern struct lp55xx_platform_data -*lp55xx_of_populate_pdata(struct device *dev, struct device_node *np); +*lp55xx_of_populate_pdata(struct device *dev, struct device_node *np, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index a58019cdb8c3..ac2c31db4a65 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -308,9 +308,16 @@ static int lp8501_probe(struct i2c_client *client, struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev); struct device_node *np = client->dev.of_node; + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->cfg = &lp8501_cfg; + if (!pdata) { if (np) { - pdata = lp55xx_of_populate_pdata(&client->dev, np); + pdata = lp55xx_of_populate_pdata(&client->dev, np, + chip); if (IS_ERR(pdata)) return PTR_ERR(pdata); } else { @@ -319,10 +326,6 @@ static int lp8501_probe(struct i2c_client *client, } } - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - led = devm_kcalloc(&client->dev, pdata->num_channels, sizeof(*led), GFP_KERNEL); if (!led) @@ -330,7 +333,6 @@ static int lp8501_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; - chip->cfg = &lp8501_cfg; mutex_init(&chip->lock); diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h index 00492d6ff018..3441064713a3 100644 --- a/include/linux/platform_data/leds-lp55xx.h +++ b/include/linux/platform_data/leds-lp55xx.h @@ -13,18 +13,25 @@ #define _LEDS_LP55XX_H #include +#include /* Clock configuration */ #define LP55XX_CLOCK_AUTO 0 #define LP55XX_CLOCK_INT 1 #define LP55XX_CLOCK_EXT 2 +#define LP55XX_MAX_GROUPED_CHAN 4 + struct lp55xx_led_config { const char *name; const char *default_trigger; u8 chan_nr; u8 led_current; /* mA x10, 0 if led is not connected */ u8 max_current; + int num_colors; + unsigned int max_channel; + int color_id[LED_COLOR_ID_MAX]; + int output_num[LED_COLOR_ID_MAX]; }; struct lp55xx_predef_pattern { From 40473132605af27e72d9489327bb0c99f6c5f20a Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 16 Jul 2020 13:20:03 -0500 Subject: [PATCH 26/37] leds: lp5523: Update the lp5523 code to add multicolor brightness function Add the multicolor brightness call back to support the multicolor framework. This call back allows setting brightness on grouped channels in a single function. Acked-by: Pavel Machek Acked-by: Jacek Anaszewski Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/leds-lp5523.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index bb97549007d7..f55d97258d5e 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -792,6 +792,25 @@ leave: return ret; } +static int lp5523_multicolor_brightness(struct lp55xx_led *led) +{ + struct lp55xx_chip *chip = led->chip; + int ret; + int i; + + mutex_lock(&chip->lock); + for (i = 0; i < led->mc_cdev.num_colors; i++) { + ret = lp55xx_write(chip, + LP5523_REG_LED_PWM_BASE + + led->mc_cdev.subled_info[i].channel, + led->mc_cdev.subled_info[i].brightness); + if (ret) + break; + } + mutex_unlock(&chip->lock); + return ret; +} + static int lp5523_led_brightness(struct lp55xx_led *led) { struct lp55xx_chip *chip = led->chip; @@ -858,6 +877,7 @@ static struct lp55xx_device_config lp5523_cfg = { .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, .brightness_fn = lp5523_led_brightness, + .multicolor_brightness_fn = lp5523_multicolor_brightness, .set_led_current = lp5523_set_led_current, .firmware_cb = lp5523_firmware_loaded, .run_engine = lp5523_run_engine, From 00253ec2c9849c2a1101818565c7f4c09dbef327 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 16 Jul 2020 13:20:04 -0500 Subject: [PATCH 27/37] leds: lp5521: Add multicolor framework multicolor brightness support Add the multicolor brightness call back to support the multicolor framework. This function allows setting the brightness across grouped LED channels in a single call. Acked-by: Pavel Machek Acked-by: Jacek Anaszewski Signed-off-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/leds-lp5521.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 6ff81d6be789..ef8c3bfa8f3c 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -349,6 +349,25 @@ static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) return 0; } +static int lp5521_multicolor_brightness(struct lp55xx_led *led) +{ + struct lp55xx_chip *chip = led->chip; + int ret; + int i; + + mutex_lock(&chip->lock); + for (i = 0; i < led->mc_cdev.num_colors; i++) { + ret = lp55xx_write(chip, + LP5521_REG_LED_PWM_BASE + + led->mc_cdev.subled_info[i].channel, + led->mc_cdev.subled_info[i].brightness); + if (ret) + break; + } + mutex_unlock(&chip->lock); + return ret; +} + static int lp5521_led_brightness(struct lp55xx_led *led) { struct lp55xx_chip *chip = led->chip; @@ -490,6 +509,7 @@ static struct lp55xx_device_config lp5521_cfg = { .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, .brightness_fn = lp5521_led_brightness, + .multicolor_brightness_fn = lp5521_multicolor_brightness, .set_led_current = lp5521_set_led_current, .firmware_cb = lp5521_firmware_loaded, .run_engine = lp5521_run_engine, From 93690cdf3060c61dfce813121d0bfc055e7fa30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 16 Jul 2020 19:17:28 +0200 Subject: [PATCH 28/37] leds: trigger: add support for LED-private device triggers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some LED controllers may come with an internal HW triggering mechanism for the LED and the ability to switch between SW control and the internal HW control. This includes most PHYs, various ethernet switches, the Turris Omnia LED controller or AXP20X PMIC. This adds support for registering such triggers. This code is based on work by Pavel Machek and Ondřej Jirman . Signed-off-by: Marek Behún Acked-by: Jacek Anaszewski Signed-off-by: Pavel Machek --- drivers/leds/led-triggers.c | 26 ++++++++++++++++++++------ include/linux/leds.h | 10 ++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 0836bf7631ea..91da90cfb11d 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -27,6 +27,12 @@ LIST_HEAD(trigger_list); /* Used by LED Class */ +static inline bool +trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig) +{ + return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type; +} + ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) @@ -50,7 +56,7 @@ ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, down_read(&triggers_list_lock); list_for_each_entry(trig, &trigger_list, next_trig) { - if (sysfs_streq(buf, trig->name)) { + if (sysfs_streq(buf, trig->name) && trigger_relevant(led_cdev, trig)) { down_write(&led_cdev->trigger_lock); led_trigger_set(led_cdev, trig); up_write(&led_cdev->trigger_lock); @@ -93,8 +99,12 @@ static int led_trigger_format(char *buf, size_t size, led_cdev->trigger ? "none" : "[none]"); list_for_each_entry(trig, &trigger_list, next_trig) { - bool hit = led_cdev->trigger && - !strcmp(led_cdev->trigger->name, trig->name); + bool hit; + + if (!trigger_relevant(led_cdev, trig)) + continue; + + hit = led_cdev->trigger && !strcmp(led_cdev->trigger->name, trig->name); len += led_trigger_snprintf(buf + len, size - len, " %s%s%s", hit ? "[" : "", @@ -243,7 +253,8 @@ void led_trigger_set_default(struct led_classdev *led_cdev) down_read(&triggers_list_lock); down_write(&led_cdev->trigger_lock); list_for_each_entry(trig, &trigger_list, next_trig) { - if (!strcmp(led_cdev->default_trigger, trig->name)) { + if (!strcmp(led_cdev->default_trigger, trig->name) && + trigger_relevant(led_cdev, trig)) { led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER; led_trigger_set(led_cdev, trig); break; @@ -280,7 +291,9 @@ int led_trigger_register(struct led_trigger *trig) down_write(&triggers_list_lock); /* Make sure the trigger's name isn't already in use */ list_for_each_entry(_trig, &trigger_list, next_trig) { - if (!strcmp(_trig->name, trig->name)) { + if (!strcmp(_trig->name, trig->name) && + (trig->trigger_type == _trig->trigger_type || + !trig->trigger_type || !_trig->trigger_type)) { up_write(&triggers_list_lock); return -EEXIST; } @@ -294,7 +307,8 @@ int led_trigger_register(struct led_trigger *trig) list_for_each_entry(led_cdev, &leds_list, node) { down_write(&led_cdev->trigger_lock); if (!led_cdev->trigger && led_cdev->default_trigger && - !strcmp(led_cdev->default_trigger, trig->name)) { + !strcmp(led_cdev->default_trigger, trig->name) && + trigger_relevant(led_cdev, trig)) { led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER; led_trigger_set(led_cdev, trig); } diff --git a/include/linux/leds.h b/include/linux/leds.h index 2451962d1ec5..6a8d6409c993 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -57,6 +57,10 @@ struct led_init_data { bool devname_mandatory; }; +struct led_hw_trigger_type { + int dummy; +}; + struct led_classdev { const char *name; enum led_brightness brightness; @@ -141,6 +145,9 @@ struct led_classdev { void *trigger_data; /* true if activated - deactivate routine uses it to do cleanup */ bool activated; + + /* LEDs that have private triggers have this set */ + struct led_hw_trigger_type *trigger_type; #endif #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED @@ -345,6 +352,9 @@ struct led_trigger { int (*activate)(struct led_classdev *led_cdev); void (*deactivate)(struct led_classdev *led_cdev); + /* LED-private triggers have this set */ + struct led_hw_trigger_type *trigger_type; + /* LEDs under control by this trigger (for simple triggers) */ rwlock_t leddev_list_lock; struct list_head led_cdevs; From c5437338150e4914328c975767056d2dedacfd21 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 16:51:15 +0200 Subject: [PATCH 29/37] leds: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Acked-by: Rob Herring Acked-by: Dan Murphy Signed-off-by: Pavel Machek --- Documentation/devicetree/bindings/leds/leds-lm3532.txt | 2 +- Documentation/devicetree/bindings/leds/leds-lm3601x.txt | 4 ++-- Documentation/devicetree/bindings/leds/leds-lm36274.txt | 2 +- Documentation/devicetree/bindings/leds/leds-lm3692x.txt | 2 +- Documentation/devicetree/bindings/leds/leds-lm3697.txt | 2 +- Documentation/devicetree/bindings/leds/leds-lp8860.txt | 2 +- drivers/leds/leds-lm3532.c | 4 ++-- drivers/leds/leds-lm3601x.c | 2 +- drivers/leds/leds-lm36274.c | 2 +- drivers/leds/leds-lm3692x.c | 2 +- drivers/leds/leds-lm3697.c | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/leds/leds-lm3532.txt b/Documentation/devicetree/bindings/leds/leds-lm3532.txt index 53793213dd52..097490a5ff91 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm3532.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm3532.txt @@ -102,4 +102,4 @@ led-controller@38 { }; For more product information please see the links below: -http://www.ti.com/product/LM3532 +https://www.ti.com/product/LM3532 diff --git a/Documentation/devicetree/bindings/leds/leds-lm3601x.txt b/Documentation/devicetree/bindings/leds/leds-lm3601x.txt index 095dafb6ec7f..17e940025dc2 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm3601x.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm3601x.txt @@ -47,5 +47,5 @@ led-controller@64 { } For more product information please see the links below: -http://www.ti.com/product/LM36010 -http://www.ti.com/product/LM36011 +https://www.ti.com/product/LM36010 +https://www.ti.com/product/LM36011 diff --git a/Documentation/devicetree/bindings/leds/leds-lm36274.txt b/Documentation/devicetree/bindings/leds/leds-lm36274.txt index 39c230d59a4d..de6f4931fb31 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm36274.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm36274.txt @@ -82,4 +82,4 @@ lm36274@11 { }; For more product information please see the link below: -http://www.ti.com/lit/ds/symlink/lm36274.pdf +https://www.ti.com/lit/ds/symlink/lm36274.pdf diff --git a/Documentation/devicetree/bindings/leds/leds-lm3692x.txt b/Documentation/devicetree/bindings/leds/leds-lm3692x.txt index 501468aa4d38..b1103d961d6c 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm3692x.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm3692x.txt @@ -62,4 +62,4 @@ led-controller@36 { } For more product information please see the link below: -http://www.ti.com/lit/ds/snvsa29/snvsa29.pdf +https://www.ti.com/lit/ds/snvsa29/snvsa29.pdf diff --git a/Documentation/devicetree/bindings/leds/leds-lm3697.txt b/Documentation/devicetree/bindings/leds/leds-lm3697.txt index 63992d732959..221b37b6049b 100644 --- a/Documentation/devicetree/bindings/leds/leds-lm3697.txt +++ b/Documentation/devicetree/bindings/leds/leds-lm3697.txt @@ -70,4 +70,4 @@ led-controller@36 { } For more product information please see the link below: -http://www.ti.com/lit/ds/symlink/lm3697.pdf +https://www.ti.com/lit/ds/symlink/lm3697.pdf diff --git a/Documentation/devicetree/bindings/leds/leds-lp8860.txt b/Documentation/devicetree/bindings/leds/leds-lp8860.txt index 9863220db4ba..8bb25749a3da 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp8860.txt +++ b/Documentation/devicetree/bindings/leds/leds-lp8860.txt @@ -47,4 +47,4 @@ led-controller@2d { } For more product information please see the link below: -http://www.ti.com/product/lp8860-q1 +https://www.ti.com/product/lp8860-q1 diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index aa9bf8cda673..946ad67eaecb 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // TI LM3532 LED driver -// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ -// http://www.ti.com/lit/ds/symlink/lm3532.pdf +// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ +// https://www.ti.com/lit/ds/symlink/lm3532.pdf #include #include diff --git a/drivers/leds/leds-lm3601x.c b/drivers/leds/leds-lm3601x.c index fce89f2a2d92..d0e1d4814042 100644 --- a/drivers/leds/leds-lm3601x.c +++ b/drivers/leds/leds-lm3601x.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Flash and torch driver for Texas Instruments LM3601X LED // Flash driver chip family -// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ +// Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ #include #include diff --git a/drivers/leds/leds-lm36274.c b/drivers/leds/leds-lm36274.c index db842eeb7ca2..bfeee03a0053 100644 --- a/drivers/leds/leds-lm36274.c +++ b/drivers/leds/leds-lm36274.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // TI LM36274 LED chip family driver -// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ #include #include diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 28a51aeb28de..e1e2d2b64a56 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // TI LM3692x LED chip family driver -// Copyright (C) 2017-18 Texas Instruments Incorporated - http://www.ti.com/ +// Copyright (C) 2017-18 Texas Instruments Incorporated - https://www.ti.com/ #include #include diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 872d26f9706a..024983088d59 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // TI LM3697 LED chip family driver -// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ +// Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ #include #include From feff72735b316e268da4ccc04091977e8f89a721 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 24 Jul 2020 12:43:47 +0200 Subject: [PATCH 30/37] leds: pattern trigger -- check pattern for validity Don't allow invalid brightness in the pattern. Signed-off-by: Pavel Machek --- drivers/leds/trigger/ledtrig-pattern.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index 3abcafe46278..4d138d5317e9 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -227,10 +227,12 @@ static int pattern_trig_store_patterns_string(struct pattern_trig_data *data, while (offset < count - 1 && data->npatterns < MAX_PATTERNS) { cr = 0; - ccount = sscanf(buf + offset, "%d %u %n", + ccount = sscanf(buf + offset, "%u %u %n", &data->patterns[data->npatterns].brightness, &data->patterns[data->npatterns].delta_t, &cr); - if (ccount != 2) { + + if (ccount != 2 || + data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) { data->npatterns = 0; return -EINVAL; } From ae036f9fb6cd66035a45a1ce1b94db59a7e021ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 23 Jul 2020 14:53:18 +0200 Subject: [PATCH 31/37] dt-bindings: leds: add cznic,turris-omnia-leds binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add device-tree bindings documentation for Turris Omnia RGB LEDs. Signed-off-by: Marek Behún Reviewed-by: Rob Herring Cc: devicetree@vger.kernel.org Signed-off-by: Pavel Machek --- .../leds/cznic,turris-omnia-leds.yaml | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml diff --git a/Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml b/Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml new file mode 100644 index 000000000000..24ad1446445e --- /dev/null +++ b/Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/cznic,turris-omnia-leds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CZ.NIC's Turris Omnia LEDs driver + +maintainers: + - Marek Behún + +description: + This module adds support for the RGB LEDs found on the front panel of the + Turris Omnia router. There are 12 RGB LEDs that are controlled by a + microcontroller that communicates via the I2C bus. Each LED is described + as a subnode of this I2C device. + +properties: + compatible: + const: cznic,turris-omnia-leds + + reg: + description: I2C slave address of the microcontroller. + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^multi-led[0-9a-f]$": + type: object + allOf: + - $ref: leds-class-multicolor.yaml# + description: + This node represents one of the RGB LED devices on Turris Omnia. + No subnodes need to be added for subchannels since this controller only + supports RGB LEDs. + + properties: + reg: + minimum: 0 + maximum: 11 + description: + This property identifies one of the LEDs on the front panel of the + Turris Omnia router. + + required: + - reg + +additionalProperties: false + +examples: + - | + + #include + + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@2b { + compatible = "cznic,turris-omnia-leds"; + reg = <0x2b>; + #address-cells = <1>; + #size-cells = <0>; + + multi-led@0 { + /* + * No subnodes are needed, this controller only supports RGB + * LEDs. + */ + reg = <0>; + color = ; + function = LED_FUNCTION_POWER; + linux,default-trigger = "heartbeat"; + }; + + multi-led@a { + reg = <0xa>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <1>; + }; + }; + }; + +... From 089381b27abe28a54b1e73e1ab975c2d5b24f8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 23 Jul 2020 14:53:19 +0200 Subject: [PATCH 32/37] leds: initial support for Turris Omnia LEDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds basic support for LEDs on the front side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs. The controller supports HW triggering mode for the LEDs, but this driver does not support it yet, and sets all the LEDs defined in device-tree into SW mode upon probe. This driver uses the multicolor LED framework. Signed-off-by: Marek Behún Reviewed-by: Dan Murphy Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 11 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-turris-omnia.c | 295 +++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 drivers/leds/leds-turris-omnia.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b9002850b5fa..d7e4be1b7736 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -176,6 +176,17 @@ config LEDS_EL15203000 To compile this driver as a module, choose M here: the module will be called leds-el15203000. +config LEDS_TURRIS_OMNIA + tristate "LED support for CZ.NIC's Turris Omnia" + depends on LEDS_CLASS_MULTICOLOR + depends on I2C + depends on MACH_ARMADA_38X || COMPILE_TEST + depends on OF + 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 + front panel. + config LEDS_LM3530 tristate "LCD Backlight driver for LM3530" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index d684bc76d2b2..c2c7d7ade0d0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o +obj-$(CONFIG_LEDS_TURRIS_OMNIA) += leds-turris-omnia.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c new file mode 100644 index 000000000000..bb23d8e16614 --- /dev/null +++ b/drivers/leds/leds-turris-omnia.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CZ.NIC's Turris Omnia LEDs driver + * + * 2020 by Marek Behun + */ + +#include +#include +#include +#include +#include +#include "leds.h" + +#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 + +#define CMD_LED_STATE 4 +#define CMD_LED_STATE_LED(l) ((l) & 0x0f) +#define CMD_LED_STATE_ON 0x10 + +#define CMD_LED_COLOR 5 +#define CMD_LED_SET_BRIGHTNESS 7 +#define CMD_LED_GET_BRIGHTNESS 8 + +#define OMNIA_CMD 0 + +#define OMNIA_CMD_LED_COLOR_LED 1 +#define OMNIA_CMD_LED_COLOR_R 2 +#define OMNIA_CMD_LED_COLOR_G 3 +#define OMNIA_CMD_LED_COLOR_B 4 +#define OMNIA_CMD_LED_COLOR_LEN 5 + +struct omnia_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; + int reg; +}; + +#define to_omnia_led(l) container_of(l, struct omnia_led, mc_cdev) + +struct omnia_leds { + struct i2c_client *client; + struct mutex lock; + struct omnia_led leds[]; +}; + +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[OMNIA_CMD_LED_COLOR_LEN], state; + int ret; + + mutex_lock(&leds->lock); + + led_mc_calc_color_components(&led->mc_cdev, brightness); + + buf[OMNIA_CMD] = CMD_LED_COLOR; + buf[OMNIA_CMD_LED_COLOR_LED] = led->reg; + buf[OMNIA_CMD_LED_COLOR_R] = mc_cdev->subled_info[0].brightness; + buf[OMNIA_CMD_LED_COLOR_G] = mc_cdev->subled_info[1].brightness; + buf[OMNIA_CMD_LED_COLOR_B] = mc_cdev->subled_info[2].brightness; + + state = CMD_LED_STATE_LED(led->reg); + if (buf[OMNIA_CMD_LED_COLOR_R] || buf[OMNIA_CMD_LED_COLOR_G] || buf[OMNIA_CMD_LED_COLOR_B]) + state |= CMD_LED_STATE_ON; + + ret = i2c_smbus_write_byte_data(leds->client, CMD_LED_STATE, state); + if (ret >= 0 && (state & CMD_LED_STATE_ON)) + ret = i2c_master_send(leds->client, buf, 5); + + mutex_unlock(&leds->lock); + + return ret; +} + +static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, + struct device_node *np) +{ + struct led_init_data init_data = {}; + struct device *dev = &client->dev; + struct led_classdev *cdev; + int ret, color; + + ret = of_property_read_u32(np, "reg", &led->reg); + if (ret || led->reg >= OMNIA_BOARD_LEDS) { + dev_warn(dev, + "Node %pOF: must contain 'reg' property with values between 0 and %i\n", + np, OMNIA_BOARD_LEDS - 1); + return 0; + } + + ret = of_property_read_u32(np, "color", &color); + if (ret || color != LED_COLOR_ID_MULTI) { + dev_warn(dev, + "Node %pOF: must contain 'color' property with value LED_COLOR_ID_MULTI\n", + np); + return 0; + } + + 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; + + led->mc_cdev.subled_info = led->subled_info; + led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS; + + init_data.fwnode = &np->fwnode; + + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = 255; + cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; + + of_property_read_string(np, "linux,default-trigger", &cdev->default_trigger); + + /* put the LED into software mode */ + ret = i2c_smbus_write_byte_data(client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg) | + CMD_LED_MODE_USER); + if (ret < 0) { + dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, ret); + return ret; + } + + /* disable the LED */ + ret = i2c_smbus_write_byte_data(client, CMD_LED_STATE, CMD_LED_STATE_LED(led->reg)); + if (ret < 0) { + dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); + return ret; + } + + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev, &init_data); + if (ret < 0) { + dev_err(dev, "Cannot register LED %pOF: %i\n", np, ret); + return ret; + } + + return 1; +} + +/* + * On the front panel of the Turris Omnia router there is also a button which + * can be used to control the intensity of all the LEDs at once, so that if they + * are too bright, user can dim them. + * The microcontroller cycles between 8 levels of this global brightness (from + * 100% to 0%), but this setting can have any integer value between 0 and 100. + * It is therefore convenient to be able to change this setting from software. + * We expose this setting via a sysfs attribute file called "brightness". This + * file lives in the device directory of the LED controller, not an individual + * LED, so it should not confuse users. + */ +static ssize_t brightness_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; + + mutex_lock(&leds->lock); + ret = i2c_smbus_read_byte_data(client, CMD_LED_GET_BRIGHTNESS); + mutex_unlock(&leds->lock); + + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t brightness_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); + unsigned int brightness; + int ret; + + if (sscanf(buf, "%u", &brightness) != 1) + return -EINVAL; + + if (brightness > 100) + return -EINVAL; + + mutex_lock(&leds->lock); + ret = i2c_smbus_write_byte_data(client, CMD_LED_SET_BRIGHTNESS, (u8) brightness); + mutex_unlock(&leds->lock); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_RW(brightness); + +static struct attribute *omnia_led_controller_attrs[] = { + &dev_attr_brightness.attr, + NULL, +}; +ATTRIBUTE_GROUPS(omnia_led_controller); + +static int omnia_leds_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node, *child; + struct omnia_leds *leds; + struct omnia_led *led; + int ret, count; + + count = of_get_available_child_count(np); + if (!count) { + dev_err(dev, "LEDs are not defined in device tree!\n"); + return -ENODEV; + } else if (count > OMNIA_BOARD_LEDS) { + dev_err(dev, "Too many LEDs defined in device tree!\n"); + return -EINVAL; + } + + leds = devm_kzalloc(dev, struct_size(leds, leds, count), GFP_KERNEL); + if (!leds) + return -ENOMEM; + + leds->client = client; + i2c_set_clientdata(client, leds); + + mutex_init(&leds->lock); + + led = &leds->leds[0]; + for_each_available_child_of_node(np, child) { + ret = omnia_led_register(client, led, child); + if (ret < 0) + return ret; + + led += ret; + } + + if (devm_device_add_groups(dev, omnia_led_controller_groups)) + dev_warn(dev, "Could not add attribute group!\n"); + + return 0; +} + +static int omnia_leds_remove(struct i2c_client *client) +{ + u8 buf[OMNIA_CMD_LED_COLOR_LEN]; + + /* put all LEDs into default (HW triggered) mode */ + i2c_smbus_write_byte_data(client, CMD_LED_MODE, + CMD_LED_MODE_LED(OMNIA_BOARD_LEDS)); + + /* set all LEDs color to [255, 255, 255] */ + buf[OMNIA_CMD] = CMD_LED_COLOR; + buf[OMNIA_CMD_LED_COLOR_LED] = OMNIA_BOARD_LEDS; + buf[OMNIA_CMD_LED_COLOR_R] = 255; + buf[OMNIA_CMD_LED_COLOR_G] = 255; + buf[OMNIA_CMD_LED_COLOR_B] = 255; + + i2c_master_send(client, buf, 5); + + return 0; +} + +static const struct of_device_id of_omnia_leds_match[] = { + { .compatible = "cznic,turris-omnia-leds", }, + {}, +}; + +static const struct i2c_device_id omnia_id[] = { + { "omnia", 0 }, + { } +}; + +static struct i2c_driver omnia_leds_driver = { + .probe = omnia_leds_probe, + .remove = omnia_leds_remove, + .id_table = omnia_id, + .driver = { + .name = "leds-turris-omnia", + .of_match_table = of_omnia_leds_match, + }, +}; + +module_i2c_driver(omnia_leds_driver); + +MODULE_AUTHOR("Marek Behun "); +MODULE_DESCRIPTION("CZ.NIC's Turris Omnia LEDs"); +MODULE_LICENSE("GPL v2"); From 45c2818f78d31d7f6732ad07baa7902f33c269d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 23 Jul 2020 14:53:20 +0200 Subject: [PATCH 33/37] Documentation: ABI: leds-turris-omnia: document sysfs attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the global brightness attribute for the Turris Omnia LED controller. Signed-off-by: Marek Behún Signed-off-by: Pavel Machek --- .../testing/sysfs-class-led-driver-turris-omnia | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia diff --git a/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia new file mode 100644 index 000000000000..795a5de12fc1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia @@ -0,0 +1,14 @@ +What: /sys/class/leds//device/brightness +Date: July 2020 +KernelVersion: 5.9 +Contact: Marek Behún +Description: (RW) On the front panel of the Turris Omnia router there is also + a button which can be used to control the intensity of all the + LEDs at once, so that if they are too bright, user can dim them. + + The microcontroller cycles between 8 levels of this global + brightness (from 100% to 0%), but this setting can have any + integer value between 0 and 100. It is therefore convenient to be + able to change this setting from software. + + Format: %i From 2742b4192a279c6ec72e55d5474c4c07756c7845 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 28 Jul 2020 12:26:13 +0200 Subject: [PATCH 34/37] Make LEDS_LP55XX_COMMON depend on I2C to fix build errors: leds-lp55xx-common.c:(.text+0x9d): undefined reference to `i2c_smbus_read_byte_data' leds-lp55xx-common.c:(.text+0x8fc): undefined reference to `i2c_smbus_write_byte_data' These errors happened when I2C=m and LEDS_LP55XX_COMMON=y, so prevent that from being possible. Signed-off-by: Randy Dunlap Cc: Jacek Anaszewski Cc: Pavel Machek Cc: Dan Murphy Cc: linux-leds@vger.kernel.org Cc: Milo Kim Cc: Mathias Nyman Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index d7e4be1b7736..1c181df24eae 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -399,6 +399,7 @@ config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR depends on OF + depends on I2C select FW_LOADER select FW_LOADER_USER_HELPER help From 54212f5a1ba3123281877e54c1e5f672bf7563d8 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 3 Aug 2020 13:20:06 +0200 Subject: [PATCH 35/37] leds: add RGB color option, as that is different from multicolor. Multicolor is a bit too abstract. Yes, we can have Green-Magenta-Ultraviolet LED, but so far all the LEDs we support are RGB, and not even RGB-White or RGB-Yellow variants emerged. Multicolor is not a good fit for RGB LED. It does not really know about LED color. In particular, there's no way to make LED "white". Userspace is interested in knowing "this LED can produce arbitrary color", which not all multicolor LEDs can. Signed-off-by: Pavel Machek --- drivers/leds/led-core.c | 1 + drivers/leds/leds-lp55xx-common.c | 2 +- include/dt-bindings/leds/common.h | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 846248a0693d..a6dce01dbd5e 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -35,6 +35,7 @@ const char * const led_colors[LED_COLOR_ID_MAX] = { [LED_COLOR_ID_YELLOW] = "yellow", [LED_COLOR_ID_IR] = "ir", [LED_COLOR_ID_MULTI] = "multicolor", + [LED_COLOR_ID_RGB] = "rgb", }; EXPORT_SYMBOL_GPL(led_colors); diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index af14e2b2d577..56210f4ad919 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -638,7 +638,7 @@ static int lp55xx_parse_logical_led(struct device_node *np, if (ret) return ret; - if (led_color == LED_COLOR_ID_MULTI) + if (led_color == LED_COLOR_ID_RGB) return lp55xx_parse_multi_led(np, cfg, child_number); ret = lp55xx_parse_common_child(np, cfg, child_number, &chan_nr); diff --git a/include/dt-bindings/leds/common.h b/include/dt-bindings/leds/common.h index a463ce6a8794..52b619d44ba2 100644 --- a/include/dt-bindings/leds/common.h +++ b/include/dt-bindings/leds/common.h @@ -30,8 +30,10 @@ #define LED_COLOR_ID_VIOLET 5 #define LED_COLOR_ID_YELLOW 6 #define LED_COLOR_ID_IR 7 -#define LED_COLOR_ID_MULTI 8 -#define LED_COLOR_ID_MAX 9 +#define LED_COLOR_ID_MULTI 8 /* For multicolor LEDs */ +#define LED_COLOR_ID_RGB 9 /* For multicolor LEDs that can do arbitrary color, + so this would include RGBW and similar */ +#define LED_COLOR_ID_MAX 10 /* Standard LED functions */ /* Keyboard LEDs, usually it would be input4::capslock etc. */ From 77dce3a22e8941552a15046d4113df9ce132fb3d Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 3 Aug 2020 14:00:06 +0200 Subject: [PATCH 36/37] leds: disallow /sys/class/leds/*:multi:* for now All the LEDs in the queue are RGB, so they should not use multi for their color. Make sure we don't add such LED by mistake (and make it part of ABI). Signed-off-by: Pavel Machek --- drivers/leds/led-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index a6dce01dbd5e..c4e780bdb385 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -425,6 +425,10 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data, struct fwnode_handle *fwnode = init_data->fwnode; const char *devicename = init_data->devicename; + /* We want to label LEDs that can produce full range of colors + * as RGB, not multicolor */ + BUG_ON(props.color == LED_COLOR_ID_MULTI); + if (!led_classdev_name) return -EINVAL; From bba37471de2d7733b0deef57e03c47fa97a284a7 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Tue, 4 Aug 2020 12:15:25 +0200 Subject: [PATCH 37/37] MAINTAINERS: Remove myself as LED subsystem maintainer It don't have enough time for reviewing patches and thus don't want to be listed as regular LED maintainer. Nonetheless I may still give a review from time to time. Signed-off-by: Jacek Anaszewski Signed-off-by: Pavel Machek --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7b5ffd646c6b..f04b2d8c2135 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9669,12 +9669,10 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tobin/leaks.git F: scripts/leaking_addresses.pl LED SUBSYSTEM -M: Jacek Anaszewski M: Pavel Machek R: Dan Murphy L: linux-leds@vger.kernel.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git F: Documentation/devicetree/bindings/leds/ F: drivers/leds/