diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-12-04 15:25:19 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-12-04 15:25:19 -0800 |
| commit | 7b8653a5799b9b15d7b3d98a0203d09963be4c87 (patch) | |
| tree | 27b09a687f1023984131b9b3245c820afcf62f49 | |
| parent | 980190a9473dd2c842518057bf9ddcbaeba75209 (diff) | |
| parent | 072cd5f458d76b9e15d89ebdaea8b5cb1312eeef (diff) | |
Merge tag 'leds-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
Pull LED updates from Lee Jones:
"Updates:
- Add optional GPIO enable pin support to PWM LED driver
Fixes:
- Allow LED module 0 to be added to module bank in lp50xx driver
- Fix upboard LED driver module alias to ensure proper auto-loading
- Update LP5009 to support 3 modules for a total of 9 LEDs
- Skip LEDs without color components in cros_ec driver instead of
failing probe
- Fix GPIO descriptor leaks in netxbig error paths by releasing
acquired GPIOs
- Allow LED_COLOR_ID_MULTI in qcom-lpg driver for greater flexibility
- Enable LP55XX common LED use without FW_LOADER_USER_HELPER
- Ensure lp50xx chip is enabled before any I2C communication
Cleanups:
- Use fwnode_for_each_child_node() instead of
fwnode_for_each_available_child_node() in LED drivers
- Use device_get_next_child_node() instead of
fwnode_get_next_available_child_node() in LED flash drivers
- Replace sprintf() with sysfs_emit() in sysfs show functions for
improved bounds checking
- Replace system_wq() with system_percpu_wq() in the input event
trigger
- Reorder include files to alphabetic order in the PWM LED driver
- Do not enable TRILED in qcom-lpg when configuring PWM
- Drop duplicate LEDS_EXPRESSWIRE config from Kconfig
Removals:
- Remove arcxcnn_bl.txt Device Tree binding documentation
Devicetree bindings:
- Convert ArcticSand arc2c0608 LED driver binding to DT Schema
- Add default-brightness property to common LED binding
- Add enable-gpios property to PWM LED binding
- Add PM7550 to qcom,spmi-flash-led compatible
- Explain standalone PWM usage in qcom-lpg binding"
* tag 'leds-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (21 commits)
leds: rgb: leds-qcom-lpg: Don't enable TRILED when configuring PWM
dt-bindings: leds: qcom-lpg: Explain standalone PWM usage
leds: rgb: leds-qcom-lpg: Allow LED_COLOR_ID_MULTI
leds: pwm: Reorder include files to alphabetic order
leds: pwm: Add optional GPIO enable pin support
dt-bindings: leds: pwm: Add enable-gpios property
leds: trigger: Replace use of system_wq() with system_percpu_wq()
leds: led-class: Replace sprintf() with sysfs_emit() in sysfs show functions
dt-bindings: leds: qcom,spmi-flash-led: Add PM7550
leds: netxbig: Fix GPIO descriptor leak in error paths
leds: leds-lp50xx: Enable chip before any communication
leds: Drop duplicate LEDS_EXPRESSWIRE config
leds: leds-cros_ec: Skip LEDs without color components
leds: leds-lp50xx: LP5009 supports 3 modules for a total of 9 LEDs
leds: upboard: Fix module alias
leds: leds-lp50xx: Allow LED 0 to be added to module bank
leds: lp55xx_common: Enable use without FW_LOADER_USER_HELPER
dt-bindings: leds: Add default-brightness property to common.yaml
leds: flash: Use fwnode_get_next_child_node() instead
leds: Use fwnode_for_each_child_node() instead
...
25 files changed, 245 insertions, 101 deletions
diff --git a/Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml b/Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml new file mode 100644 index 000000000000..786beced5590 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/backlight/arc,arc2c0608.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ArcticSand arc2c0608 LED driver + +description: | + The ArcticSand arc2c0608 LED driver provides ultra + efficient notebook backlighting. Optional properties not + specified will default to values in IC EPROM. + + Datasheet: + https://www.murata.com/-/media/webrenewal/products/power/power-semiconductor/overview/lineup/led-boost/arc2/arc2c0608.ashx. + +maintainers: + - Brian Dodge <bdodge@arcticsand.com> + +allOf: + - $ref: /schemas/leds/common.yaml + +properties: + compatible: + const: arc,arc2c0608 + + reg: + maxItems: 1 + + default-brightness: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 4095 + + led-sources: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: List of enabled channels + items: + enum: [0, 1, 2, 3, 4, 5] + minItems: 1 + uniqueItems: true + + arc,led-config-0: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Fading speed (period between intensity + steps) + + arc,led-config-1: + $ref: /schemas/types.yaml#/definitions/uint32 + description: If set, sets ILED_CONFIG register. Used for + fine tuning the maximum LED current. + + arc,dim-freq: + $ref: /schemas/types.yaml#/definitions/uint32 + description: PWM mode frequency setting (bits [3:0] used) + + arc,comp-config: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Setting for register CONFIG_COMP which + controls internal resitances, feed forward freqs, + and initial VOUT at startup. Consult the datasheet. + + arc,filter-config: + $ref: /schemas/types.yaml#/definitions/uint32 + description: RC and PWM Filter settings. + Bit Assignment + 7654 3 2 1 0 + xxxx RCF1 RCF0 PWM1 PWM0 + RCF statuses PWM Filter Statues + 00 = OFF (default) 00 = OFF (default) + 01 = LOW 01 = 2 STEPS + 10 - MEDIUM 10 = 4 STEPS + 11 = HIGH 11 = 8 STEPS + + arc,trim-config: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Sets percentage increase of Maximum LED + Current. + 0x00 = 0% increase. + 0x20 = 20.2%. + 0x3F = 41.5% + + label: true + + linux,default-trigger: true + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@30 { + compatible = "arc,arc2c0608"; + reg = <0x30>; + default-brightness = <500>; + label = "lcd-backlight"; + linux,default-trigger = "backlight"; + led-sources = <0 1 2 5>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt b/Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt deleted file mode 100644 index 230abdefd6e7..000000000000 --- a/Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt +++ /dev/null @@ -1,33 +0,0 @@ -Binding for ArcticSand arc2c0608 LED driver - -Required properties: -- compatible: should be "arc,arc2c0608" -- reg: slave address - -Optional properties: -- default-brightness: brightness value on boot, value from: 0-4095 -- label: The name of the backlight device - See Documentation/devicetree/bindings/leds/common.txt -- led-sources: List of enabled channels from 0 to 5. - See Documentation/devicetree/bindings/leds/common.txt - -- arc,led-config-0: setting for register ILED_CONFIG_0 -- arc,led-config-1: setting for register ILED_CONFIG_1 -- arc,dim-freq: PWM mode frequence setting (bits [3:0] used) -- arc,comp-config: setting for register CONFIG_COMP -- arc,filter-config: setting for register FILTER_CONFIG -- arc,trim-config: setting for register IMAXTUNE - -Note: Optional properties not specified will default to values in IC EPROM - -Example: - -arc2c0608@30 { - compatible = "arc,arc2c0608"; - reg = <0x30>; - default-brightness = <500>; - label = "lcd-backlight"; - linux,default-trigger = "backlight"; - led-sources = <0 1 2 5>; -}; - diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml index 274f83288a92..f4e44b33f56d 100644 --- a/Documentation/devicetree/bindings/leds/common.yaml +++ b/Documentation/devicetree/bindings/leds/common.yaml @@ -173,6 +173,12 @@ properties: led-max-microamp. $ref: /schemas/types.yaml#/definitions/uint32 + default-brightness: + description: + Brightness to be set if LED's default state is on. Used only during + initialization. If the option is not set then max brightness is used. + $ref: /schemas/types.yaml#/definitions/uint32 + panic-indicator: description: This property specifies that the LED should be used, if at all possible, diff --git a/Documentation/devicetree/bindings/leds/leds-pwm.yaml b/Documentation/devicetree/bindings/leds/leds-pwm.yaml index 61b97e8bc36d..6c4fcefbe25f 100644 --- a/Documentation/devicetree/bindings/leds/leds-pwm.yaml +++ b/Documentation/devicetree/bindings/leds/leds-pwm.yaml @@ -40,6 +40,13 @@ patternProperties: initialization. If the option is not set then max brightness is used. $ref: /schemas/types.yaml#/definitions/uint32 + enable-gpios: + description: + GPIO for LED hardware enable control. Set active when brightness is + non-zero and inactive when brightness is zero. + The GPIO default state follows the "default-state" property. + maxItems: 1 + required: - pwms - max-brightness diff --git a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml index 841a0229c472..c4b7e57b2518 100644 --- a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml +++ b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml @@ -13,6 +13,11 @@ description: > The Qualcomm Light Pulse Generator consists of three different hardware blocks; a ramp generator with lookup table (LUT), the light pulse generator and a three channel current sink. These blocks are found in a wide range of Qualcomm PMICs. + The light pulse generator (LPG) can also be used independently to output PWM + signal for standard PWM applications. In this scenario, the LPG output should + be routed to a specific PMIC GPIO by setting the GPIO pin mux to the special + functions indicated in the datasheet, the TRILED driver for the channel will + not be enabled in this configuration. properties: compatible: diff --git a/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml b/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml index bcf0ad4ea57e..05250aefd385 100644 --- a/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml +++ b/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml @@ -24,6 +24,7 @@ properties: - enum: - qcom,pm6150l-flash-led - qcom,pm660l-flash-led + - qcom,pm7550-flash-led - qcom,pm8150c-flash-led - qcom,pm8150l-flash-led - qcom,pm8350c-flash-led diff --git a/Documentation/leds/leds-lp5521.rst b/Documentation/leds/leds-lp5521.rst index 0432615b083d..4c838c88820e 100644 --- a/Documentation/leds/leds-lp5521.rst +++ b/Documentation/leds/leds-lp5521.rst @@ -22,7 +22,7 @@ More details of the instructions can be found from the public data sheet. LP5521 has the internal program memory for running various LED patterns. There are two ways to run LED patterns. -1) Legacy interface - enginex_mode and enginex_load +1) sysfs interface - enginex_mode and enginex_load Control interface for the engines: x is 1 .. 3 diff --git a/Documentation/leds/leds-lp5523.rst b/Documentation/leds/leds-lp5523.rst index 7d7362a1dd57..f5a87b07514a 100644 --- a/Documentation/leds/leds-lp5523.rst +++ b/Documentation/leds/leds-lp5523.rst @@ -35,7 +35,7 @@ If both fields are NULL, 'lp5523' is used by default. LP5523 has the internal program memory for running various LED patterns. There are two ways to run LED patterns. -1) Legacy interface - enginex_mode, enginex_load and enginex_leds +1) sysfs interface - enginex_mode, enginex_load and enginex_leds Control interface for the engines: diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 06e6291be11b..11e7282dc297 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -214,10 +214,6 @@ config LEDS_EL15203000 To compile this driver as a module, choose M here: the module will be called leds-el15203000. -config LEDS_EXPRESSWIRE - bool - depends on GPIOLIB - config LEDS_TURRIS_OMNIA tristate "LED support for CZ.NIC's Turris Omnia" depends on LEDS_CLASS_MULTICOLOR @@ -443,8 +439,8 @@ config LEDS_LP55XX_COMMON depends on LEDS_CLASS_MULTICOLOR depends on OF depends on I2C - select FW_LOADER - select FW_LOADER_USER_HELPER + imply FW_LOADER + imply FW_LOADER_USER_HELPER help This option supports common operations for LP5521/5523/55231/5562/5569/ 8501 devices. diff --git a/drivers/leds/flash/leds-rt4505.c b/drivers/leds/flash/leds-rt4505.c index f16358b8dfc1..18fd5b7e528f 100644 --- a/drivers/leds/flash/leds-rt4505.c +++ b/drivers/leds/flash/leds-rt4505.c @@ -365,7 +365,7 @@ static int rt4505_probe(struct i2c_client *client) return ret; } - child = fwnode_get_next_available_child_node(client->dev.fwnode, NULL); + child = device_get_next_child_node(&client->dev, NULL); if (!child) { dev_err(priv->dev, "Failed to get child node\n"); return -EINVAL; diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c index 6af0d2c7fc56..f6b439674c03 100644 --- a/drivers/leds/flash/leds-rt8515.c +++ b/drivers/leds/flash/leds-rt8515.c @@ -304,7 +304,7 @@ static int rt8515_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(rt->enable_torch), "cannot get ENT (enable torch) GPIO\n"); - child = fwnode_get_next_available_child_node(dev->fwnode, NULL); + child = device_get_next_child_node(dev, NULL); if (!child) { dev_err(dev, "No fwnode child node found for connected LED.\n"); diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c index 3e83200675f2..dc6840357370 100644 --- a/drivers/leds/flash/leds-sgm3140.c +++ b/drivers/leds/flash/leds-sgm3140.c @@ -214,8 +214,7 @@ static int sgm3140_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to request regulator\n"); - child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode, - NULL); + child_node = device_get_next_child_node(&pdev->dev, NULL); if (!child_node) { dev_err(&pdev->dev, "No fwnode child node found for connected LED.\n"); diff --git a/drivers/leds/flash/leds-tps6131x.c b/drivers/leds/flash/leds-tps6131x.c index 6f4d4fd55361..f0f1f2b77d5a 100644 --- a/drivers/leds/flash/leds-tps6131x.c +++ b/drivers/leds/flash/leds-tps6131x.c @@ -544,7 +544,7 @@ static int tps6131x_parse_node(struct tps6131x *tps6131x) tps6131x->valley_current_limit = device_property_read_bool(dev, "ti,valley-current-limit"); - tps6131x->led_node = fwnode_get_next_available_child_node(dev->fwnode, NULL); + tps6131x->led_node = device_get_next_child_node(dev, NULL); if (!tps6131x->led_node) { dev_err(dev, "Missing LED node\n"); return -EINVAL; diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f3faf37f9a08..885399ed0776 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -38,7 +38,7 @@ static ssize_t brightness_show(struct device *dev, brightness = led_cdev->brightness; mutex_unlock(&led_cdev->led_access); - return sprintf(buf, "%u\n", brightness); + return sysfs_emit(buf, "%u\n", brightness); } static ssize_t brightness_store(struct device *dev, @@ -80,7 +80,7 @@ static ssize_t max_brightness_show(struct device *dev, max_brightness = led_cdev->max_brightness; mutex_unlock(&led_cdev->led_access); - return sprintf(buf, "%u\n", max_brightness); + return sysfs_emit(buf, "%u\n", max_brightness); } static DEVICE_ATTR_RO(max_brightness); @@ -122,7 +122,7 @@ static ssize_t brightness_hw_changed_show(struct device *dev, if (led_cdev->brightness_hw_changed == -1) return -ENODATA; - return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed); + return sysfs_emit(buf, "%u\n", led_cdev->brightness_hw_changed); } static DEVICE_ATTR_RO(brightness_hw_changed); diff --git a/drivers/leds/leds-cros_ec.c b/drivers/leds/leds-cros_ec.c index 377cf04e202a..bea3cc3fbfd2 100644 --- a/drivers/leds/leds-cros_ec.c +++ b/drivers/leds/leds-cros_ec.c @@ -142,9 +142,6 @@ static int cros_ec_led_count_subleds(struct device *dev, } } - if (!num_subleds) - return -EINVAL; - *max_brightness = common_range; return num_subleds; } @@ -189,6 +186,8 @@ static int cros_ec_led_probe_one(struct device *dev, struct cros_ec_device *cros &priv->led_mc_cdev.led_cdev.max_brightness); if (num_subleds < 0) return num_subleds; + if (num_subleds == 0) + return 0; /* LED without any colors, skip */ priv->cros_ec = cros_ec; priv->led_id = id; diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c index 94f8ef6b482c..e2a9c8592953 100644 --- a/drivers/leds/leds-lp50xx.c +++ b/drivers/leds/leds-lp50xx.c @@ -50,11 +50,17 @@ #define LP50XX_SW_RESET 0xff #define LP50XX_CHIP_EN BIT(6) +#define LP50XX_CHIP_DISABLE 0x00 +#define LP50XX_START_TIME_US 500 +#define LP50XX_RESET_TIME_US 3 + +#define LP50XX_EN_GPIO_LOW 0 +#define LP50XX_EN_GPIO_HIGH 1 /* There are 3 LED outputs per bank */ #define LP50XX_LEDS_PER_MODULE 3 -#define LP5009_MAX_LED_MODULES 2 +#define LP5009_MAX_LED_MODULES 3 #define LP5012_MAX_LED_MODULES 4 #define LP5018_MAX_LED_MODULES 6 #define LP5024_MAX_LED_MODULES 8 @@ -341,17 +347,15 @@ out: return ret; } -static int lp50xx_set_banks(struct lp50xx *priv, u32 led_banks[]) +static int lp50xx_set_banks(struct lp50xx *priv, u32 led_banks[], int num_leds) { u8 led_config_lo, led_config_hi; u32 bank_enable_mask = 0; int ret; int i; - for (i = 0; i < priv->chip_info->max_modules; i++) { - if (led_banks[i]) - bank_enable_mask |= (1 << led_banks[i]); - } + for (i = 0; i < num_leds; i++) + bank_enable_mask |= (1 << led_banks[i]); led_config_lo = bank_enable_mask; led_config_hi = bank_enable_mask >> 8; @@ -371,19 +375,42 @@ static int lp50xx_reset(struct lp50xx *priv) return regmap_write(priv->regmap, priv->chip_info->reset_reg, LP50XX_SW_RESET); } -static int lp50xx_enable_disable(struct lp50xx *priv, int enable_disable) +static int lp50xx_enable(struct lp50xx *priv) { int ret; - ret = gpiod_direction_output(priv->enable_gpio, enable_disable); + if (priv->enable_gpio) { + ret = gpiod_direction_output(priv->enable_gpio, LP50XX_EN_GPIO_HIGH); + if (ret) + return ret; + + udelay(LP50XX_START_TIME_US); + } + + ret = lp50xx_reset(priv); if (ret) return ret; - if (enable_disable) - return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN); - else - return regmap_write(priv->regmap, LP50XX_DEV_CFG0, 0); + return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN); +} + +static int lp50xx_disable(struct lp50xx *priv) +{ + int ret; + + ret = regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_DISABLE); + if (ret) + return ret; + if (priv->enable_gpio) { + ret = gpiod_direction_output(priv->enable_gpio, LP50XX_EN_GPIO_LOW); + if (ret) + return ret; + + udelay(LP50XX_RESET_TIME_US); + } + + return 0; } static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv, @@ -405,7 +432,7 @@ static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv, return ret; } - ret = lp50xx_set_banks(priv, led_banks); + ret = lp50xx_set_banks(priv, led_banks, num_leds); if (ret) { dev_err(priv->dev, "Cannot setup banked LEDs\n"); return ret; @@ -447,6 +474,10 @@ static int lp50xx_probe_dt(struct lp50xx *priv) return dev_err_probe(priv->dev, PTR_ERR(priv->enable_gpio), "Failed to get enable GPIO\n"); + ret = lp50xx_enable(priv); + if (ret) + return ret; + priv->regulator = devm_regulator_get(priv->dev, "vled"); if (IS_ERR(priv->regulator)) priv->regulator = NULL; @@ -547,14 +578,6 @@ static int lp50xx_probe(struct i2c_client *client) return ret; } - ret = lp50xx_reset(led); - if (ret) - return ret; - - ret = lp50xx_enable_disable(led, 1); - if (ret) - return ret; - return lp50xx_probe_dt(led); } @@ -563,7 +586,7 @@ static void lp50xx_remove(struct i2c_client *client) struct lp50xx *led = i2c_get_clientdata(client); int ret; - ret = lp50xx_enable_disable(led, 0); + ret = lp50xx_disable(led); if (ret) dev_err(led->dev, "Failed to disable chip\n"); diff --git a/drivers/leds/leds-max5970.c b/drivers/leds/leds-max5970.c index 285074c53b23..a1e91a06249c 100644 --- a/drivers/leds/leds-max5970.c +++ b/drivers/leds/leds-max5970.c @@ -60,7 +60,7 @@ static int max5970_led_probe(struct platform_device *pdev) if (!led_node) return -ENODEV; - fwnode_for_each_available_child_node(led_node, child) { + fwnode_for_each_child_node(led_node, child) { u32 reg; if (fwnode_property_read_u32(child, "reg", ®)) diff --git a/drivers/leds/leds-max77705.c b/drivers/leds/leds-max77705.c index b7403b3fcf5e..1e2054c1bf80 100644 --- a/drivers/leds/leds-max77705.c +++ b/drivers/leds/leds-max77705.c @@ -191,7 +191,7 @@ static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fw cdev->brightness_set_blocking = max77705_led_brightness_set_multi; cdev->blink_set = max77705_rgb_blink; - fwnode_for_each_available_child_node(np, child) { + fwnode_for_each_child_node(np, child) { ret = max77705_parse_subled(dev, child, &info[i]); if (ret < 0) return ret; diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index e95287416ef8..99df46f2d9f5 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -364,6 +364,9 @@ static int netxbig_gpio_ext_get(struct device *dev, if (!addr) return -ENOMEM; + gpio_ext->addr = addr; + gpio_ext->num_addr = 0; + /* * We cannot use devm_ managed resources with these GPIO descriptors * since they are associated with the "GPIO extension device" which @@ -375,45 +378,58 @@ static int netxbig_gpio_ext_get(struct device *dev, gpiod = gpiod_get_index(gpio_ext_dev, "addr", i, GPIOD_OUT_LOW); if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + goto err_set_code; gpiod_set_consumer_name(gpiod, "GPIO extension addr"); addr[i] = gpiod; + gpio_ext->num_addr++; } - gpio_ext->addr = addr; - gpio_ext->num_addr = num_addr; ret = gpiod_count(gpio_ext_dev, "data"); if (ret < 0) { dev_err(dev, "Failed to count GPIOs in DT property data-gpios\n"); - return ret; + goto err_free_addr; } num_data = ret; data = devm_kcalloc(dev, num_data, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + if (!data) { + ret = -ENOMEM; + goto err_free_addr; + } + + gpio_ext->data = data; + gpio_ext->num_data = 0; for (i = 0; i < num_data; i++) { gpiod = gpiod_get_index(gpio_ext_dev, "data", i, GPIOD_OUT_LOW); if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + goto err_free_data; gpiod_set_consumer_name(gpiod, "GPIO extension data"); data[i] = gpiod; + gpio_ext->num_data++; } - gpio_ext->data = data; - gpio_ext->num_data = num_data; gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(gpiod)) { dev_err(dev, "Failed to get GPIO from DT property enable-gpio\n"); - return PTR_ERR(gpiod); + goto err_free_data; } gpiod_set_consumer_name(gpiod, "GPIO extension enable"); gpio_ext->enable = gpiod; return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext); + +err_free_data: + for (i = 0; i < gpio_ext->num_data; i++) + gpiod_put(gpio_ext->data[i]); +err_set_code: + ret = PTR_ERR(gpiod); +err_free_addr: + for (i = 0; i < gpio_ext->num_addr; i++) + gpiod_put(gpio_ext->addr[i]); + return ret; } static int netxbig_leds_get_of_pdata(struct device *dev, diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index c73134e7b951..6c1f2f50ff85 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -9,12 +9,13 @@ * based on leds-gpio.c by Raphael Assenat <raph@8d.com> */ -#include <linux/module.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/of.h> #include <linux/leds.h> -#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -26,6 +27,7 @@ struct led_pwm { }; struct led_pwm_data { + struct gpio_desc *enable_gpio; struct led_classdev cdev; struct pwm_device *pwm; struct pwm_state pwmstate; @@ -51,6 +53,8 @@ static int led_pwm_set(struct led_classdev *led_cdev, if (led_dat->active_low) duty = led_dat->pwmstate.period - duty; + gpiod_set_value_cansleep(led_dat->enable_gpio, !!brightness); + led_dat->pwmstate.duty_cycle = duty; /* * Disabling a PWM doesn't guarantee that it emits the inactive level. @@ -132,6 +136,21 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, break; } + /* + * Claim the GPIO as GPIOD_ASIS and set the value + * later on to honor the different default states + */ + led_data->enable_gpio = devm_fwnode_gpiod_get(dev, fwnode, "enable", GPIOD_ASIS, NULL); + if (IS_ERR(led_data->enable_gpio)) { + if (PTR_ERR(led_data->enable_gpio) == -ENOENT) + /* Enable GPIO is optional */ + led_data->enable_gpio = NULL; + else + return PTR_ERR(led_data->enable_gpio); + } + + gpiod_direction_output(led_data->enable_gpio, !!led_data->cdev.brightness); + ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); if (ret) { dev_err(dev, "failed to register PWM led for %s: %d\n", diff --git a/drivers/leds/leds-upboard.c b/drivers/leds/leds-upboard.c index b350eb294280..12989b2f1953 100644 --- a/drivers/leds/leds-upboard.c +++ b/drivers/leds/leds-upboard.c @@ -123,4 +123,4 @@ MODULE_AUTHOR("Gary Wang <garywang@aaeon.com.tw>"); MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); MODULE_DESCRIPTION("UP Board LED driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:upboard-led"); +MODULE_ALIAS("platform:upboard-leds"); diff --git a/drivers/leds/rgb/leds-ktd202x.c b/drivers/leds/rgb/leds-ktd202x.c index 04e62faa3a00..e4f0f25a5e45 100644 --- a/drivers/leds/rgb/leds-ktd202x.c +++ b/drivers/leds/rgb/leds-ktd202x.c @@ -391,7 +391,7 @@ static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwn int i = 0; num_channels = 0; - fwnode_for_each_available_child_node(fwnode, child) + fwnode_for_each_child_node(fwnode, child) num_channels++; if (!num_channels || num_channels > chip->num_leds) @@ -401,7 +401,7 @@ static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwn if (!info) return -ENOMEM; - fwnode_for_each_available_child_node(fwnode, child) { + fwnode_for_each_child_node(fwnode, child) { u32 mono_color; u32 reg; int ret; diff --git a/drivers/leds/rgb/leds-ncp5623.c b/drivers/leds/rgb/leds-ncp5623.c index 7c7d44623a9e..85d6be6fff2b 100644 --- a/drivers/leds/rgb/leds-ncp5623.c +++ b/drivers/leds/rgb/leds-ncp5623.c @@ -180,7 +180,7 @@ static int ncp5623_probe(struct i2c_client *client) goto release_mc_node; } - fwnode_for_each_available_child_node(mc_node, led_node) { + fwnode_for_each_child_node(mc_node, led_node) { ret = fwnode_property_read_u32(led_node, "color", &color_index); if (ret) goto release_led_node; diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 4f2a178e3d26..72da0bf469ad 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2017-2022 Linaro Ltd * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. - * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/bits.h> #include <linux/bitfield.h> @@ -1247,8 +1247,6 @@ static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, lpg_apply(chan); - triled_set(lpg, chan->triled_mask, chan->enabled ? chan->triled_mask : 0); - out_unlock: mutex_unlock(&lpg->lock); @@ -1382,7 +1380,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) return dev_err_probe(lpg->dev, ret, "failed to parse \"color\" of %pOF\n", np); - if (color == LED_COLOR_ID_RGB) + if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI) num_channels = of_get_available_child_count(np); else num_channels = 1; @@ -1394,7 +1392,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) led->lpg = lpg; led->num_channels = num_channels; - if (color == LED_COLOR_ID_RGB) { + if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI) { info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; @@ -1454,7 +1452,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np) init_data.fwnode = of_fwnode_handle(np); - if (color == LED_COLOR_ID_RGB) + if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI) ret = devm_led_classdev_multicolor_register_ext(lpg->dev, &led->mcdev, &init_data); else ret = devm_led_classdev_register_ext(lpg->dev, &led->cdev, &init_data); diff --git a/drivers/leds/trigger/ledtrig-input-events.c b/drivers/leds/trigger/ledtrig-input-events.c index 1c79731562c2..3c6414259c27 100644 --- a/drivers/leds/trigger/ledtrig-input-events.c +++ b/drivers/leds/trigger/ledtrig-input-events.c @@ -66,7 +66,7 @@ static void input_events_event(struct input_handle *handle, unsigned int type, spin_unlock_irqrestore(&data->lock, flags); - mod_delayed_work(system_wq, &data->work, led_off_delay); + mod_delayed_work(system_percpu_wq, &data->work, led_off_delay); } static int input_events_connect(struct input_handler *handler, struct input_dev *dev, |