LEDS for v6.19

* Add optional GPIO enable pin support to PWM LED driver.
 
   * 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.
 
   * 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.
 
   * Remove arcxcnn_bl.txt Device Tree binding documentation.
 
   * 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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmkxtx8ACgkQUa+KL4f8
 d2EsAA/+KdtCs9WXrFcQOHZBlZvLeNMklninXPjMFOMcjd6A9l26XzkKW5qS59ax
 1H+gUxjryN5EGtYE1ITKjpwwWJL+VEGsMXcuonMf0g/UwE5CFw3VR1nL3+xK9IM0
 2xzTZwDkbqYUVsEWVparazdvVU/niwqdjyWqfYFM3XXZWk5TpFkzOnluRwSPAt70
 IZRJzYPHlGiZKmFpLtmxFcsJM7MiqsHgP3tzMEAmUHFQg75MhWVUJld8hTaek6hf
 gS+jqdoCV6+dnP76KlRZ2OMHwAG/Nmu91mFp5VnG8vIH6PhERokP6ewi07lJUPTp
 bHyb/oWb+YJvAXnzZ5Yh5q7ygnAa2kFgYAmUBDQyvTwmOgloMl7qb1hzSOqfRkE0
 xyQ8ah6gtTu6Tq0kBB3jV9UFfG1ecDjRVqIbJnEFZjOfb3peyjdNVjgMraBWGCHB
 D93RAft/aByE3bnX20DF2icOGJDmlAVaHPETO8yMBVIKfynjY3y8eTdt126pT7a8
 HOs8UtJZXmwHdgtvgUzAOiklxvuYMKIBqkym8BQ20lCnqw+BKxqQ962+rGJhrxOj
 AKy4gx980HmBxaet4ssn5ba186+jlCR26WFPRAcHF17c8+aKPXH8htdPFZPWAd1Z
 MoQP/VOHzsPE4x75xvVX9DkZiv9WyHxaCzxlXXZhAHiYxoLg4uE=
 =/TDJ
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2025-12-04 15:25:19 -08:00
commit 7b8653a579
25 changed files with 246 additions and 102 deletions

View File

@ -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>;
};
};
...

View File

@ -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>;
};

View File

@ -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,

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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.

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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 @@ static int lp50xx_brightness_set(struct led_classdev *cdev,
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");

View File

@ -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", &reg))

View File

@ -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;

View File

@ -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,

View File

@ -9,12 +9,13 @@
* based on leds-gpio.c by Raphael Assenat <raph@8d.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/leds.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",

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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,