- Core Frameworks

- Ensure seldom updated triggers have a brightness value before first update
 
  - New Device Support
    - Add support for Simatic IPC Device BX_59A to IPC LEDs Core
    - Add support for Qualcomm PMI8950 PWM to LPG Core
 
  - New Functionality
    - Add a bunch of new LED function identifiers
    - Add support for High Resolution Timers in LED Trigger Patten
 
  - Fix-ups
    - Shift out Audio Trigger to the Sound subsystem
    - Convert suitable calls to devm_* managed resources
    - Device Tree binding adaptions/conversions/creation
    - Remove superfluous code/variables/attributes and simplify overall
    - Use/convert to new/better APIs/helpers/MACROs instead of hand-rolling implementations
 
  - Bug Fixes
    - Repair enabling Torch Mode from V4L2 on the second LED
    - Ensure PWM is disabled when suspending
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmZNwdoACgkQUa+KL4f8
 d2FUSw/8Dxzt29Ad326K6q7ePlGgdm4JcFeg+1B2WD+5IOCelecXD3QcVvH47Wz3
 50gjHdo3qoBja6IZDwgl0ZFYj6VLKbrEmqjtM9BscdG2gaND1VGYTPtve4EqIyOX
 WXv2InA69QfFNmk/n+AxDa/xYOunsK1S1RB1cuPkoVJii/P2rHiEv2LpG/TS8Sxk
 I7EN45Ebh3Z6hHLpmfEIbVLLlFuyGFnSFnHOiOSAlFh1Ri0DdHBwgK4IdDYtq2dx
 LB9ICem0+6PQxPKpf/ozUS2oV+jd8oS48TDJjx9n8DjblV8zh0IKYi5HEHYZifBb
 03xF/XZ62MYtp6jHwiaNE1WgoARu13RZIcFbpQFgC5+N2gwpe4BGH+6nXXrimsK/
 opVed2UYPdCDlHVVpScgMMUWYrnCWks4/6Iusd5K9YN6At35xuCqQh5laPOF1Cj+
 jKzgxZ6gMzWTTpFQSkYpNn5wFC+p7VosdGvh/d6L5ltVb7bINgZ7mUbVEwRLZaPj
 v+ZS/iWjTptMA9bHk6f/4duSJjJy15Ghdqd1CuvX/VAL9DqSz6O7hf+vj9yvOtjf
 pmI4ZUBaDYX7Ut0CHcjhbg2fSYv3VMg58fLF5mUUIVgRdSHdhrpRUbYphuodhoHu
 zWde2gKEkAqNTaQk56jzLi9K8Knu3PkIOnKZ9SgHfIIkmMLMNOM=
 =rF2l
 -----END PGP SIGNATURE-----

Merge tag 'leds-next-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds

Pull LED updates from Lee Jones:
 "Core Frameworks:
   - Ensure seldom updated triggers have a brightness value before first
     update

  New Device Support:
   - Add support for Simatic IPC Device BX_59A to IPC LEDs Core
   - Add support for Qualcomm PMI8950 PWM to LPG Core

  New Functionality:
   - Add a bunch of new LED function identifiers
   - Add support for High Resolution Timers in LED Trigger Patten

  Fix-ups:
   - Shift out Audio Trigger to the Sound subsystem
   - Convert suitable calls to devm_* managed resources
   - Device Tree binding adaptions/conversions/creation
   - Remove superfluous code/variables/attributes and simplify overall
   - Use/convert to new/better APIs/helpers/MACROs instead of
     hand-rolling implementations

  Bug Fixes:
   - Repair enabling Torch Mode from V4L2 on the second LED
   - Ensure PWM is disabled when suspending"

* tag 'leds-next-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (28 commits)
  leds: mt6370: Remove unused field 'reg_cfgs' from 'struct mt6370_priv'
  leds: lp50xx: Remove unused field 'num_of_banked_leds' from 'struct lp50xx'
  leds: lp50xx: Remove unused field 'bank_modules' from 'struct lp50xx_led'
  leds: aat1290: Remove unused field 'torch_brightness' from 'struct aat1290_led'
  leds: sun50i-a100: Use match_string() helper to simplify the code
  leds: pwm: Disable PWM when going to suspend
  leds: trigger: pattern: Add support for hrtimer
  leds: mt6360: Fix the second LED can not enable torch mode by V4L2
  dt-bindings: leds: leds-qcom-lpg: Add support for PMI8950 PWM
  leds: qcom-lpg: Add support for PMI8950 PWM
  leds: apu: Remove duplicate DMI lookup data
  leds: trigger: netdev: Remove not needed call to led_set_brightness in deactivate
  dt-bindings: leds: Add LED_FUNCTION_SPEED_* for link speed on LAN/WAN
  dt-bindings: leds: Add LED_FUNCTION_MOBILE for mobile network
  leds: simatic-ipc-leds-gpio: Add support for module BX-59A
  dt-bindings: leds: qcom-lpg: Document PM6150L compatible
  dt-bindings: leds: pca963x: Convert text bindings to YAML
  leds: an30259a: Use devm_mutex_init() for mutex initialization
  leds: mlxreg: Use devm_mutex_init() for mutex initialization
  leds: nic78bx: Use devm API to cleanup module's resources
  ...
This commit is contained in:
Linus Torvalds 2024-05-22 10:49:54 -07:00
commit f3033eb791
34 changed files with 501 additions and 275 deletions

View File

@ -12,6 +12,16 @@ Description:
The exact format is described in: The exact format is described in:
Documentation/devicetree/bindings/leds/leds-trigger-pattern.txt Documentation/devicetree/bindings/leds/leds-trigger-pattern.txt
What: /sys/class/leds/<led>/hr_pattern
Date: April 2024
Description:
Specify a software pattern for the LED, that supports altering
the brightness for the specified duration with one software
timer. It can do gradual dimming and step change of brightness.
Unlike the /sys/class/leds/<led>/pattern, this attribute runs
a pattern on high-resolution timer (hrtimer).
What: /sys/class/leds/<led>/hw_pattern What: /sys/class/leds/<led>/hw_pattern
Date: September 2018 Date: September 2018
KernelVersion: 4.20 KernelVersion: 4.20

View File

@ -27,9 +27,14 @@ properties:
- qcom,pm8994-lpg - qcom,pm8994-lpg
- qcom,pmc8180c-lpg - qcom,pmc8180c-lpg
- qcom,pmi632-lpg - qcom,pmi632-lpg
- qcom,pmi8950-pwm
- qcom,pmi8994-lpg - qcom,pmi8994-lpg
- qcom,pmi8998-lpg - qcom,pmi8998-lpg
- qcom,pmk8550-pwm - qcom,pmk8550-pwm
- items:
- enum:
- qcom,pm6150l-lpg
- const: qcom,pm8150l-lpg
- items: - items:
- enum: - enum:
- qcom,pm8550-pwm - qcom,pm8550-pwm
@ -142,6 +147,7 @@ allOf:
- qcom,pm8941-lpg - qcom,pm8941-lpg
- qcom,pm8994-lpg - qcom,pm8994-lpg
- qcom,pmc8180c-lpg - qcom,pmc8180c-lpg
- qcom,pmi8950-pwm
- qcom,pmi8994-lpg - qcom,pmi8994-lpg
- qcom,pmi8998-lpg - qcom,pmi8998-lpg
- qcom,pmk8550-pwm - qcom,pmk8550-pwm
@ -290,5 +296,3 @@ examples:
label = "blue"; label = "blue";
}; };
}; };
...

View File

@ -0,0 +1,140 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/nxp,pca963x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP PCA963x LED controllers
maintainers:
- Laurent Pinchart <laurent.pinchart@ideasonboard.com>
description: |
The NXP PCA963x are I2C-controlled LED drivers optimized for
Red/Green/Blue/Amber (RGBA) color mixing applications. Each LED is
individually controllable and has its own PWM controller.
Datasheets are available at
- https://www.nxp.com/docs/en/data-sheet/PCA9632.pdf
- https://www.nxp.com/docs/en/data-sheet/PCA9633.pdf
- https://www.nxp.com/docs/en/data-sheet/PCA9634.pdf
- https://www.nxp.com/docs/en/data-sheet/PCA9635.pdf
properties:
compatible:
enum:
- nxp,pca9632
- nxp,pca9633
- nxp,pca9634
- nxp,pca9635
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
nxp,hw-blink:
type: boolean
description:
Use hardware blinking instead of software blinking
nxp,inverted-out:
type: boolean
description:
Invert the polarity of the generated PWM.
nxp,period-scale:
$ref: /schemas/types.yaml#/definitions/uint32
description:
In some configurations, the chip blinks faster than expected. This
parameter provides a scaling ratio (fixed point, decimal divided by 1000)
to compensate, e.g. 1300=1.3x and 750=0.75x.
nxp,totem-pole:
type: boolean
description:
Use totem pole (push-pull) instead of open-drain (pca9632 defaults to
open-drain, newer chips to totem pole).
patternProperties:
"^led@[0-9a-f]+$":
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
minimum: 0
required:
- reg
allOf:
- if:
properties:
compatible:
contains:
enum:
- nxp,pca9632
- nxp,pca9633
then:
patternProperties:
"^led@[0-9a-f]+$":
properties:
reg:
maximum: 3
else:
patternProperties:
"^led@[0-9a-f]+$":
properties:
reg:
maximum: 7
additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
led-controller@62 {
compatible = "nxp,pca9632";
reg = <0x62>;
#address-cells = <1>;
#size-cells = <0>;
led@0 {
reg = <0>;
color = <LED_COLOR_ID_RED>;
function = LED_FUNCTION_STATUS;
};
led@1 {
reg = <1>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
};
led@2 {
reg = <2>;
color = <LED_COLOR_ID_BLUE>;
function = LED_FUNCTION_STATUS;
};
led@3 {
reg = <3>;
color = <LED_COLOR_ID_WHITE>;
function = LED_FUNCTION_STATUS;
};
};
};
...

View File

@ -1,52 +0,0 @@
LEDs connected to pca9632, pca9633 or pca9634
Required properties:
- compatible : should be : "nxp,pca9632", "nxp,pca9633", "nxp,pca9634" or "nxp,pca9635"
Optional properties:
- nxp,totem-pole : use totem pole (push-pull) instead of open-drain (pca9632 defaults
to open-drain, newer chips to totem pole)
- nxp,hw-blink : use hardware blinking instead of software blinking
- nxp,period-scale : In some configurations, the chip blinks faster than expected.
This parameter provides a scaling ratio (fixed point, decimal divided
by 1000) to compensate, e.g. 1300=1.3x and 750=0.75x.
- nxp,inverted-out: invert the polarity of the generated PWM
Each led is represented as a sub-node of the nxp,pca963x device.
LED sub-node properties:
- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
- reg : number of LED line (could be from 0 to 3 in pca9632 or pca9633,
0 to 7 in pca9634, or 0 to 15 in pca9635)
- linux,default-trigger : (optional)
see Documentation/devicetree/bindings/leds/common.txt
Examples:
pca9632: pca9632 {
compatible = "nxp,pca9632";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x62>;
red@0 {
label = "red";
reg = <0>;
linux,default-trigger = "none";
};
green@1 {
label = "green";
reg = <1>;
linux,default-trigger = "none";
};
blue@2 {
label = "blue";
reg = <2>;
linux,default-trigger = "none";
};
unused@3 {
label = "unused";
reg = <3>;
linux,default-trigger = "none";
};
};

View File

@ -152,7 +152,6 @@ CONFIG_LEDS_TRIGGER_CAMERA=m
CONFIG_LEDS_TRIGGER_PANIC=y CONFIG_LEDS_TRIGGER_PANIC=y
CONFIG_LEDS_TRIGGER_NETDEV=y CONFIG_LEDS_TRIGGER_NETDEV=y
CONFIG_LEDS_TRIGGER_PATTERN=y CONFIG_LEDS_TRIGGER_PATTERN=y
CONFIG_LEDS_TRIGGER_AUDIO=y
CONFIG_RTC_CLASS=y CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_JZ4740=y CONFIG_RTC_DRV_JZ4740=y
CONFIG_DMADEVICES=y CONFIG_DMADEVICES=y

View File

@ -77,8 +77,6 @@ struct aat1290_led {
int *mm_current_scale; int *mm_current_scale;
/* device mode */ /* device mode */
bool movie_mode; bool movie_mode;
/* brightness cache */
unsigned int torch_brightness;
}; };
static struct aat1290_led *fled_cdev_to_led( static struct aat1290_led *fled_cdev_to_led(

View File

@ -241,10 +241,20 @@ static int mt6360_strobe_set(struct led_classdev_flash *fl_cdev, bool state)
u32 enable_mask = MT6360_STROBEN_MASK | MT6360_FLCSEN_MASK(led->led_no); u32 enable_mask = MT6360_STROBEN_MASK | MT6360_FLCSEN_MASK(led->led_no);
u32 val = state ? MT6360_FLCSEN_MASK(led->led_no) : 0; u32 val = state ? MT6360_FLCSEN_MASK(led->led_no) : 0;
u32 prev = priv->fled_strobe_used, curr; u32 prev = priv->fled_strobe_used, curr;
int ret; int ret = 0;
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
/*
* If the state of the upcoming change is the same as the current LED
* device state, then skip the subsequent code to avoid conflict
* with the flow of turning on LED torch mode in V4L2.
*/
if (state == !!(BIT(led->led_no) & prev)) {
dev_info(lcdev->dev, "No change in strobe state [0x%x]\n", prev);
goto unlock;
}
/* /*
* Only one set of flash control logic, use the flag to avoid torch is * Only one set of flash control logic, use the flag to avoid torch is
* currently used * currently used

View File

@ -194,11 +194,11 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
spin_unlock(&trig->leddev_list_lock); spin_unlock(&trig->leddev_list_lock);
led_cdev->trigger = trig; led_cdev->trigger = trig;
ret = 0;
if (trig->activate) if (trig->activate)
ret = trig->activate(led_cdev); ret = trig->activate(led_cdev);
else else
ret = 0; led_set_brightness(led_cdev, trig->brightness);
if (ret) if (ret)
goto err_activate; goto err_activate;
@ -387,6 +387,8 @@ void led_trigger_event(struct led_trigger *trig,
if (!trig) if (!trig)
return; return;
trig->brightness = brightness;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list)
led_set_brightness(led_cdev, brightness); led_set_brightness(led_cdev, brightness);

View File

@ -283,7 +283,10 @@ static int an30259a_probe(struct i2c_client *client)
if (err < 0) if (err < 0)
return err; return err;
mutex_init(&chip->mutex); err = devm_mutex_init(&client->dev, &chip->mutex);
if (err)
return err;
chip->client = client; chip->client = client;
i2c_set_clientdata(client, chip); i2c_set_clientdata(client, chip);
@ -317,17 +320,9 @@ static int an30259a_probe(struct i2c_client *client)
return 0; return 0;
exit: exit:
mutex_destroy(&chip->mutex);
return err; return err;
} }
static void an30259a_remove(struct i2c_client *client)
{
struct an30259a *chip = i2c_get_clientdata(client);
mutex_destroy(&chip->mutex);
}
static const struct of_device_id an30259a_match_table[] = { static const struct of_device_id an30259a_match_table[] = {
{ .compatible = "panasonic,an30259a", }, { .compatible = "panasonic,an30259a", },
{ /* sentinel */ }, { /* sentinel */ },
@ -347,7 +342,6 @@ static struct i2c_driver an30259a_driver = {
.of_match_table = an30259a_match_table, .of_match_table = an30259a_match_table,
}, },
.probe = an30259a_probe, .probe = an30259a_probe,
.remove = an30259a_remove,
.id_table = an30259a_id, .id_table = an30259a_id,
}; };

View File

@ -181,8 +181,7 @@ static int __init apu_led_init(void)
struct platform_device *pdev; struct platform_device *pdev;
int err; int err;
if (!(dmi_match(DMI_SYS_VENDOR, "PC Engines") && if (!dmi_check_system(apu_led_dmi_table)) {
(dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "apu1")))) {
pr_err("No PC Engines APUv1 board detected. For APUv2,3 support, enable CONFIG_PCENGINES_APU2\n"); pr_err("No PC Engines APUv1 board detected. For APUv2,3 support, enable CONFIG_PCENGINES_APU2\n");
return -ENODEV; return -ENODEV;
} }

View File

@ -530,6 +530,16 @@ static const struct regmap_config aw200xx_regmap_config = {
.disable_locking = true, .disable_locking = true,
}; };
static void aw200xx_chip_reset_action(void *data)
{
aw200xx_chip_reset(data);
}
static void aw200xx_disable_action(void *data)
{
aw200xx_disable(data);
}
static int aw200xx_probe(struct i2c_client *client) static int aw200xx_probe(struct i2c_client *client)
{ {
const struct aw200xx_chipdef *cdef; const struct aw200xx_chipdef *cdef;
@ -568,11 +578,17 @@ static int aw200xx_probe(struct i2c_client *client)
aw200xx_enable(chip); aw200xx_enable(chip);
ret = devm_add_action(&client->dev, aw200xx_disable_action, chip);
if (ret)
return ret;
ret = aw200xx_chip_check(chip); ret = aw200xx_chip_check(chip);
if (ret) if (ret)
return ret; return ret;
mutex_init(&chip->mutex); ret = devm_mutex_init(&client->dev, &chip->mutex);
if (ret)
return ret;
/* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */ /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
@ -581,6 +597,10 @@ static int aw200xx_probe(struct i2c_client *client)
if (ret) if (ret)
goto out_unlock; goto out_unlock;
ret = devm_add_action(&client->dev, aw200xx_chip_reset_action, chip);
if (ret)
goto out_unlock;
ret = aw200xx_probe_fw(&client->dev, chip); ret = aw200xx_probe_fw(&client->dev, chip);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
@ -595,15 +615,6 @@ out_unlock:
return ret; return ret;
} }
static void aw200xx_remove(struct i2c_client *client)
{
struct aw200xx *chip = i2c_get_clientdata(client);
aw200xx_chip_reset(chip);
aw200xx_disable(chip);
mutex_destroy(&chip->mutex);
}
static const struct aw200xx_chipdef aw20036_cdef = { static const struct aw200xx_chipdef aw20036_cdef = {
.channels = 36, .channels = 36,
.display_size_rows_max = 3, .display_size_rows_max = 3,
@ -652,7 +663,6 @@ static struct i2c_driver aw200xx_driver = {
.of_match_table = aw200xx_match_table, .of_match_table = aw200xx_match_table,
}, },
.probe = aw200xx_probe, .probe = aw200xx_probe,
.remove = aw200xx_remove,
.id_table = aw200xx_id, .id_table = aw200xx_id,
}; };
module_i2c_driver(aw200xx_driver); module_i2c_driver(aw200xx_driver);

View File

@ -320,6 +320,11 @@ static int aw2013_probe_dt(struct aw2013 *chip)
return 0; return 0;
} }
static void aw2013_chip_disable_action(void *data)
{
aw2013_chip_disable(data);
}
static const struct regmap_config aw2013_regmap_config = { static const struct regmap_config aw2013_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 8, .val_bits = 8,
@ -336,7 +341,10 @@ static int aw2013_probe(struct i2c_client *client)
if (!chip) if (!chip)
return -ENOMEM; return -ENOMEM;
mutex_init(&chip->mutex); ret = devm_mutex_init(&client->dev, &chip->mutex);
if (ret)
return ret;
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
chip->client = client; chip->client = client;
@ -384,6 +392,10 @@ static int aw2013_probe(struct i2c_client *client)
goto error_reg; goto error_reg;
} }
ret = devm_add_action(&client->dev, aw2013_chip_disable_action, chip);
if (ret)
goto error_reg;
ret = aw2013_probe_dt(chip); ret = aw2013_probe_dt(chip);
if (ret < 0) if (ret < 0)
goto error_reg; goto error_reg;
@ -406,19 +418,9 @@ error_reg:
error: error:
mutex_unlock(&chip->mutex); mutex_unlock(&chip->mutex);
mutex_destroy(&chip->mutex);
return ret; return ret;
} }
static void aw2013_remove(struct i2c_client *client)
{
struct aw2013 *chip = i2c_get_clientdata(client);
aw2013_chip_disable(chip);
mutex_destroy(&chip->mutex);
}
static const struct of_device_id aw2013_match_table[] = { static const struct of_device_id aw2013_match_table[] = {
{ .compatible = "awinic,aw2013", }, { .compatible = "awinic,aw2013", },
{ /* sentinel */ }, { /* sentinel */ },
@ -432,7 +434,6 @@ static struct i2c_driver aw2013_driver = {
.of_match_table = aw2013_match_table, .of_match_table = aw2013_match_table,
}, },
.probe = aw2013_probe, .probe = aw2013_probe,
.remove = aw2013_remove,
}; };
module_i2c_driver(aw2013_driver); module_i2c_driver(aw2013_driver);

View File

@ -542,6 +542,13 @@ static int lm3532_parse_als(struct lm3532_data *priv)
return ret; return ret;
} }
static void gpio_set_low_action(void *data)
{
struct lm3532_data *priv = data;
gpiod_direction_output(priv->enable_gpio, 0);
}
static int lm3532_parse_node(struct lm3532_data *priv) static int lm3532_parse_node(struct lm3532_data *priv)
{ {
struct fwnode_handle *child = NULL; struct fwnode_handle *child = NULL;
@ -556,6 +563,12 @@ static int lm3532_parse_node(struct lm3532_data *priv)
if (IS_ERR(priv->enable_gpio)) if (IS_ERR(priv->enable_gpio))
priv->enable_gpio = NULL; priv->enable_gpio = NULL;
if (priv->enable_gpio) {
ret = devm_add_action(&priv->client->dev, gpio_set_low_action, priv);
if (ret)
return ret;
}
priv->regulator = devm_regulator_get(&priv->client->dev, "vin"); priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
if (IS_ERR(priv->regulator)) if (IS_ERR(priv->regulator))
priv->regulator = NULL; priv->regulator = NULL;
@ -691,7 +704,10 @@ static int lm3532_probe(struct i2c_client *client)
return ret; return ret;
} }
mutex_init(&drvdata->lock); ret = devm_mutex_init(&client->dev, &drvdata->lock);
if (ret)
return ret;
i2c_set_clientdata(client, drvdata); i2c_set_clientdata(client, drvdata);
ret = lm3532_parse_node(drvdata); ret = lm3532_parse_node(drvdata);
@ -703,16 +719,6 @@ static int lm3532_probe(struct i2c_client *client)
return ret; return ret;
} }
static void lm3532_remove(struct i2c_client *client)
{
struct lm3532_data *drvdata = i2c_get_clientdata(client);
mutex_destroy(&drvdata->lock);
if (drvdata->enable_gpio)
gpiod_direction_output(drvdata->enable_gpio, 0);
}
static const struct of_device_id of_lm3532_leds_match[] = { static const struct of_device_id of_lm3532_leds_match[] = {
{ .compatible = "ti,lm3532", }, { .compatible = "ti,lm3532", },
{}, {},
@ -727,7 +733,6 @@ MODULE_DEVICE_TABLE(i2c, lm3532_id);
static struct i2c_driver lm3532_i2c_driver = { static struct i2c_driver lm3532_i2c_driver = {
.probe = lm3532_probe, .probe = lm3532_probe,
.remove = lm3532_remove,
.id_table = lm3532_id, .id_table = lm3532_id,
.driver = { .driver = {
.name = LM3532_NAME, .name = LM3532_NAME,

View File

@ -207,6 +207,13 @@ static const struct regmap_config lp3952_regmap = {
.cache_type = REGCACHE_MAPLE, .cache_type = REGCACHE_MAPLE,
}; };
static void gpio_set_low_action(void *data)
{
struct lp3952_led_array *priv = data;
gpiod_set_value(priv->enable_gpio, 0);
}
static int lp3952_probe(struct i2c_client *client) static int lp3952_probe(struct i2c_client *client)
{ {
int status; int status;
@ -226,6 +233,10 @@ static int lp3952_probe(struct i2c_client *client)
return status; return status;
} }
status = devm_add_action(&client->dev, gpio_set_low_action, priv);
if (status)
return status;
priv->regmap = devm_regmap_init_i2c(client, &lp3952_regmap); priv->regmap = devm_regmap_init_i2c(client, &lp3952_regmap);
if (IS_ERR(priv->regmap)) { if (IS_ERR(priv->regmap)) {
int err = PTR_ERR(priv->regmap); int err = PTR_ERR(priv->regmap);
@ -254,15 +265,6 @@ static int lp3952_probe(struct i2c_client *client)
return 0; return 0;
} }
static void lp3952_remove(struct i2c_client *client)
{
struct lp3952_led_array *priv;
priv = i2c_get_clientdata(client);
lp3952_on_off(priv, LP3952_LED_ALL, false);
gpiod_set_value(priv->enable_gpio, 0);
}
static const struct i2c_device_id lp3952_id[] = { static const struct i2c_device_id lp3952_id[] = {
{LP3952_NAME, 0}, {LP3952_NAME, 0},
{} {}
@ -274,7 +276,6 @@ static struct i2c_driver lp3952_i2c_driver = {
.name = LP3952_NAME, .name = LP3952_NAME,
}, },
.probe = lp3952_probe, .probe = lp3952_probe,
.remove = lp3952_remove,
.id_table = lp3952_id, .id_table = lp3952_id,
}; };

View File

@ -265,7 +265,6 @@ static const struct lp50xx_chip_info lp50xx_chip_info_tbl[] = {
struct lp50xx_led { struct lp50xx_led {
struct led_classdev_mc mc_cdev; struct led_classdev_mc mc_cdev;
struct lp50xx *priv; struct lp50xx *priv;
unsigned long bank_modules;
u8 ctrl_bank_enabled; u8 ctrl_bank_enabled;
int led_number; int led_number;
}; };
@ -279,7 +278,6 @@ struct lp50xx_led {
* @dev: pointer to the devices device struct * @dev: pointer to the devices device struct
* @lock: lock for reading/writing the device * @lock: lock for reading/writing the device
* @chip_info: chip specific information (ie num_leds) * @chip_info: chip specific information (ie num_leds)
* @num_of_banked_leds: holds the number of banked LEDs
* @leds: array of LED strings * @leds: array of LED strings
*/ */
struct lp50xx { struct lp50xx {
@ -290,7 +288,6 @@ struct lp50xx {
struct device *dev; struct device *dev;
struct mutex lock; struct mutex lock;
const struct lp50xx_chip_info *chip_info; const struct lp50xx_chip_info *chip_info;
int num_of_banked_leds;
/* This needs to be at the end of the struct */ /* This needs to be at the end of the struct */
struct lp50xx_led leds[]; struct lp50xx_led leds[];
@ -404,8 +401,6 @@ static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
return -EINVAL; return -EINVAL;
} }
priv->num_of_banked_leds = num_leds;
ret = fwnode_property_read_u32_array(child, "reg", led_banks, num_leds); ret = fwnode_property_read_u32_array(child, "reg", led_banks, num_leds);
if (ret) { if (ret) {
dev_err(priv->dev, "reg property is missing\n"); dev_err(priv->dev, "reg property is missing\n");

View File

@ -256,6 +256,7 @@ static int mlxreg_led_probe(struct platform_device *pdev)
{ {
struct mlxreg_core_platform_data *led_pdata; struct mlxreg_core_platform_data *led_pdata;
struct mlxreg_led_priv_data *priv; struct mlxreg_led_priv_data *priv;
int err;
led_pdata = dev_get_platdata(&pdev->dev); led_pdata = dev_get_platdata(&pdev->dev);
if (!led_pdata) { if (!led_pdata) {
@ -267,26 +268,21 @@ static int mlxreg_led_probe(struct platform_device *pdev)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
mutex_init(&priv->access_lock); err = devm_mutex_init(&pdev->dev, &priv->access_lock);
if (err)
return err;
priv->pdev = pdev; priv->pdev = pdev;
priv->pdata = led_pdata; priv->pdata = led_pdata;
return mlxreg_led_config(priv); return mlxreg_led_config(priv);
} }
static void mlxreg_led_remove(struct platform_device *pdev)
{
struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev);
mutex_destroy(&priv->access_lock);
}
static struct platform_driver mlxreg_led_driver = { static struct platform_driver mlxreg_led_driver = {
.driver = { .driver = {
.name = "leds-mlxreg", .name = "leds-mlxreg",
}, },
.probe = mlxreg_led_probe, .probe = mlxreg_led_probe,
.remove_new = mlxreg_led_remove,
}; };
module_platform_driver(mlxreg_led_driver); module_platform_driver(mlxreg_led_driver);

View File

@ -118,6 +118,15 @@ static struct nic78bx_led nic78bx_leds[] = {
} }
}; };
static void lock_led_reg_action(void *data)
{
struct nic78bx_led_data *led_data = data;
/* Lock LED register */
outb(NIC78BX_LOCK_VALUE,
led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
}
static int nic78bx_probe(struct platform_device *pdev) static int nic78bx_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -152,6 +161,10 @@ static int nic78bx_probe(struct platform_device *pdev)
led_data->io_base = io_rc->start; led_data->io_base = io_rc->start;
spin_lock_init(&led_data->lock); spin_lock_init(&led_data->lock);
ret = devm_add_action(dev, lock_led_reg_action, led_data);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) { for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) {
nic78bx_leds[i].data = led_data; nic78bx_leds[i].data = led_data;
@ -167,15 +180,6 @@ static int nic78bx_probe(struct platform_device *pdev)
return ret; return ret;
} }
static void nic78bx_remove(struct platform_device *pdev)
{
struct nic78bx_led_data *led_data = platform_get_drvdata(pdev);
/* Lock LED register */
outb(NIC78BX_LOCK_VALUE,
led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
}
static const struct acpi_device_id led_device_ids[] = { static const struct acpi_device_id led_device_ids[] = {
{"NIC78B3", 0}, {"NIC78B3", 0},
{"", 0}, {"", 0},
@ -184,7 +188,6 @@ MODULE_DEVICE_TABLE(acpi, led_device_ids);
static struct platform_driver led_driver = { static struct platform_driver led_driver = {
.probe = nic78bx_probe, .probe = nic78bx_probe,
.remove_new = nic78bx_remove,
.driver = { .driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.acpi_match_table = ACPI_PTR(led_device_ids), .acpi_match_table = ACPI_PTR(led_device_ids),

View File

@ -53,7 +53,13 @@ static int led_pwm_set(struct led_classdev *led_cdev,
duty = led_dat->pwmstate.period - duty; duty = led_dat->pwmstate.period - duty;
led_dat->pwmstate.duty_cycle = duty; led_dat->pwmstate.duty_cycle = duty;
led_dat->pwmstate.enabled = true; /*
* Disabling a PWM doesn't guarantee that it emits the inactive level.
* So keep it on. Only for suspending the PWM should be disabled because
* otherwise it refuses to suspend. The possible downside is that the
* LED might stay (or even go) on.
*/
led_dat->pwmstate.enabled = !(led_cdev->flags & LED_SUSPENDED);
return pwm_apply_might_sleep(led_dat->pwm, &led_dat->pwmstate); return pwm_apply_might_sleep(led_dat->pwm, &led_dat->pwmstate);
} }

View File

@ -252,18 +252,16 @@ static int sun50i_a100_ledc_parse_format(struct device *dev,
struct sun50i_a100_ledc *priv) struct sun50i_a100_ledc *priv)
{ {
const char *format = "grb"; const char *format = "grb";
u32 i; int i;
device_property_read_string(dev, "allwinner,pixel-format", &format); device_property_read_string(dev, "allwinner,pixel-format", &format);
for (i = 0; i < ARRAY_SIZE(sun50i_a100_ledc_formats); i++) { i = match_string(sun50i_a100_ledc_formats, ARRAY_SIZE(sun50i_a100_ledc_formats), format);
if (!strcmp(format, sun50i_a100_ledc_formats[i])) { if (i < 0)
priv->format = i; return dev_err_probe(dev, i, "Bad pixel format '%s'\n", format);
return 0;
}
}
return dev_err_probe(dev, -EINVAL, "Bad pixel format '%s'\n", format); priv->format = i;
return 0;
} }
static void sun50i_a100_ledc_set_format(struct sun50i_a100_ledc *priv) static void sun50i_a100_ledc_set_format(struct sun50i_a100_ledc *priv)

View File

@ -149,7 +149,6 @@ struct mt6370_priv {
struct regmap_field *fields[F_MAX_FIELDS]; struct regmap_field *fields[F_MAX_FIELDS];
const struct reg_field *reg_fields; const struct reg_field *reg_fields;
const struct linear_range *ranges; const struct linear_range *ranges;
struct reg_cfg *reg_cfgs;
const struct mt6370_pdata *pdata; const struct mt6370_pdata *pdata;
unsigned int leds_count; unsigned int leds_count;
unsigned int leds_active; unsigned int leds_active;

View File

@ -1693,6 +1693,13 @@ static const struct lpg_data pm8941_lpg_data = {
}, },
}; };
static const struct lpg_data pmi8950_pwm_data = {
.num_channels = 1,
.channels = (const struct lpg_channel_data[]) {
{ .base = 0xb000 },
},
};
static const struct lpg_data pm8994_lpg_data = { static const struct lpg_data pm8994_lpg_data = {
.lut_base = 0xb000, .lut_base = 0xb000,
.lut_size = 64, .lut_size = 64,
@ -1819,6 +1826,7 @@ static const struct of_device_id lpg_of_table[] = {
{ .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data }, { .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data },
{ .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data }, { .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data },
{ .compatible = "qcom,pmi632-lpg", .data = &pmi632_lpg_data }, { .compatible = "qcom,pmi632-lpg", .data = &pmi632_lpg_data },
{ .compatible = "qcom,pmi8950-pwm", .data = &pmi8950_pwm_data },
{ .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data }, { .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data },
{ .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data }, { .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data },
{ .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data }, { .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data },

View File

@ -56,6 +56,7 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev,
case SIMATIC_IPC_DEVICE_127E: case SIMATIC_IPC_DEVICE_127E:
case SIMATIC_IPC_DEVICE_227G: case SIMATIC_IPC_DEVICE_227G:
case SIMATIC_IPC_DEVICE_BX_21A: case SIMATIC_IPC_DEVICE_BX_21A:
case SIMATIC_IPC_DEVICE_BX_59A:
break; break;
default: default:
return -ENODEV; return -ENODEV;

View File

@ -17,7 +17,12 @@
#include "simatic-ipc-leds-gpio.h" #include "simatic-ipc-leds-gpio.h"
static struct gpiod_lookup_table simatic_ipc_led_gpio_table = { struct simatic_ipc_led_tables {
struct gpiod_lookup_table *led_lookup_table;
struct gpiod_lookup_table *led_lookup_table_extra;
};
static struct gpiod_lookup_table simatic_ipc_led_gpio_table_227g = {
.dev_id = "leds-gpio", .dev_id = "leds-gpio",
.table = { .table = {
GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW), GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW),
@ -30,7 +35,7 @@ static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
}, },
}; };
static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra = { static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra_227g = {
.dev_id = NULL, /* Filled during initialization */ .dev_id = NULL, /* Filled during initialization */
.table = { .table = {
GPIO_LOOKUP_IDX("gpio-f7188x-3", 6, NULL, 6, GPIO_ACTIVE_HIGH), GPIO_LOOKUP_IDX("gpio-f7188x-3", 6, NULL, 6, GPIO_ACTIVE_HIGH),
@ -39,16 +44,51 @@ static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra = {
}, },
}; };
static struct gpiod_lookup_table simatic_ipc_led_gpio_table_bx_59a = {
.dev_id = "leds-gpio",
.table = {
GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("gpio-f7188x-2", 3, NULL, 1, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("gpio-f7188x-5", 3, NULL, 2, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("gpio-f7188x-5", 2, NULL, 3, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("gpio-f7188x-7", 7, NULL, 4, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("gpio-f7188x-7", 4, NULL, 5, GPIO_ACTIVE_LOW),
{} /* Terminating entry */
}
};
static int simatic_ipc_leds_gpio_f7188x_probe(struct platform_device *pdev) static int simatic_ipc_leds_gpio_f7188x_probe(struct platform_device *pdev)
{ {
return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table, const struct simatic_ipc_platform *plat = dev_get_platdata(&pdev->dev);
&simatic_ipc_led_gpio_table_extra); struct simatic_ipc_led_tables *led_tables;
led_tables = devm_kzalloc(&pdev->dev, sizeof(*led_tables), GFP_KERNEL);
if (!led_tables)
return -ENOMEM;
switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227G:
led_tables->led_lookup_table = &simatic_ipc_led_gpio_table_227g;
led_tables->led_lookup_table_extra = &simatic_ipc_led_gpio_table_extra_227g;
break;
case SIMATIC_IPC_DEVICE_BX_59A:
led_tables->led_lookup_table = &simatic_ipc_led_gpio_table_bx_59a;
break;
default:
return -ENODEV;
}
platform_set_drvdata(pdev, led_tables);
return simatic_ipc_leds_gpio_probe(pdev, led_tables->led_lookup_table,
led_tables->led_lookup_table_extra);
} }
static void simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev) static void simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev)
{ {
simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, struct simatic_ipc_led_tables *led_tables = platform_get_drvdata(pdev);
&simatic_ipc_led_gpio_table_extra);
simatic_ipc_leds_gpio_remove(pdev, led_tables->led_lookup_table,
led_tables->led_lookup_table_extra);
} }
static struct platform_driver simatic_ipc_led_gpio_driver = { static struct platform_driver simatic_ipc_led_gpio_driver = {

View File

@ -136,13 +136,6 @@ config LEDS_TRIGGER_PATTERN
which is a series of tuples, of brightness and duration (ms). which is a series of tuples, of brightness and duration (ms).
If unsure, say N If unsure, say N
config LEDS_TRIGGER_AUDIO
tristate "Audio Mute LED Trigger"
help
This allows LEDs to be controlled by audio drivers for following
the audio mute and mic-mute changes.
If unsure, say N
config LEDS_TRIGGER_TTY config LEDS_TRIGGER_TTY
tristate "LED Trigger for TTY devices" tristate "LED Trigger for TTY devices"
depends on TTY depends on TTY

View File

@ -14,5 +14,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o
obj-$(CONFIG_LEDS_TRIGGER_AUDIO) += ledtrig-audio.o
obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o

View File

@ -1,67 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
//
// Audio Mute LED trigger
//
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include "../leds.h"
static enum led_brightness audio_state[NUM_AUDIO_LEDS];
static int ledtrig_audio_mute_activate(struct led_classdev *led_cdev)
{
led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MUTE]);
return 0;
}
static int ledtrig_audio_micmute_activate(struct led_classdev *led_cdev)
{
led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MICMUTE]);
return 0;
}
static struct led_trigger ledtrig_audio[NUM_AUDIO_LEDS] = {
[LED_AUDIO_MUTE] = {
.name = "audio-mute",
.activate = ledtrig_audio_mute_activate,
},
[LED_AUDIO_MICMUTE] = {
.name = "audio-micmute",
.activate = ledtrig_audio_micmute_activate,
},
};
enum led_brightness ledtrig_audio_get(enum led_audio type)
{
return audio_state[type];
}
EXPORT_SYMBOL_GPL(ledtrig_audio_get);
void ledtrig_audio_set(enum led_audio type, enum led_brightness state)
{
audio_state[type] = state;
led_trigger_event(&ledtrig_audio[type], state);
}
EXPORT_SYMBOL_GPL(ledtrig_audio_set);
static int __init ledtrig_audio_init(void)
{
led_trigger_register(&ledtrig_audio[LED_AUDIO_MUTE]);
led_trigger_register(&ledtrig_audio[LED_AUDIO_MICMUTE]);
return 0;
}
module_init(ledtrig_audio_init);
static void __exit ledtrig_audio_exit(void)
{
led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MUTE]);
led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MICMUTE]);
}
module_exit(ledtrig_audio_exit);
MODULE_DESCRIPTION("LED trigger for audio mute control");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("ledtrig:audio-mute");
MODULE_ALIAS("ledtrig:audio-micmute");

View File

@ -724,8 +724,6 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev)
cancel_delayed_work_sync(&trigger_data->work); cancel_delayed_work_sync(&trigger_data->work);
led_set_brightness(led_cdev, LED_OFF);
dev_put(trigger_data->net_dev); dev_put(trigger_data->net_dev);
kfree(trigger_data); kfree(trigger_data);

View File

@ -13,6 +13,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/hrtimer.h>
#define MAX_PATTERNS 1024 #define MAX_PATTERNS 1024
/* /*
@ -21,6 +22,12 @@
*/ */
#define UPDATE_INTERVAL 50 #define UPDATE_INTERVAL 50
enum pattern_type {
PATTERN_TYPE_SW, /* Use standard timer for software pattern */
PATTERN_TYPE_HR, /* Use hrtimer for software pattern */
PATTERN_TYPE_HW, /* Hardware pattern */
};
struct pattern_trig_data { struct pattern_trig_data {
struct led_classdev *led_cdev; struct led_classdev *led_cdev;
struct led_pattern patterns[MAX_PATTERNS]; struct led_pattern patterns[MAX_PATTERNS];
@ -32,8 +39,9 @@ struct pattern_trig_data {
int last_repeat; int last_repeat;
int delta_t; int delta_t;
bool is_indefinite; bool is_indefinite;
bool is_hw_pattern; enum pattern_type type;
struct timer_list timer; struct timer_list timer;
struct hrtimer hrtimer;
}; };
static void pattern_trig_update_patterns(struct pattern_trig_data *data) static void pattern_trig_update_patterns(struct pattern_trig_data *data)
@ -71,10 +79,35 @@ static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
return data->curr->brightness - step_brightness; return data->curr->brightness - step_brightness;
} }
static void pattern_trig_timer_function(struct timer_list *t) static void pattern_trig_timer_start(struct pattern_trig_data *data)
{ {
struct pattern_trig_data *data = from_timer(data, t, timer); if (data->type == PATTERN_TYPE_HR) {
hrtimer_start(&data->hrtimer, ns_to_ktime(0), HRTIMER_MODE_REL);
} else {
data->timer.expires = jiffies;
add_timer(&data->timer);
}
}
static void pattern_trig_timer_cancel(struct pattern_trig_data *data)
{
if (data->type == PATTERN_TYPE_HR)
hrtimer_cancel(&data->hrtimer);
else
del_timer_sync(&data->timer);
}
static void pattern_trig_timer_restart(struct pattern_trig_data *data,
unsigned long interval)
{
if (data->type == PATTERN_TYPE_HR)
hrtimer_forward_now(&data->hrtimer, ms_to_ktime(interval));
else
mod_timer(&data->timer, jiffies + msecs_to_jiffies(interval));
}
static void pattern_trig_timer_common_function(struct pattern_trig_data *data)
{
for (;;) { for (;;) {
if (!data->is_indefinite && !data->repeat) if (!data->is_indefinite && !data->repeat)
break; break;
@ -83,8 +116,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
/* Step change of brightness */ /* Step change of brightness */
led_set_brightness(data->led_cdev, led_set_brightness(data->led_cdev,
data->curr->brightness); data->curr->brightness);
mod_timer(&data->timer, pattern_trig_timer_restart(data, data->curr->delta_t);
jiffies + msecs_to_jiffies(data->curr->delta_t));
if (!data->next->delta_t) { if (!data->next->delta_t) {
/* Skip the tuple with zero duration */ /* Skip the tuple with zero duration */
pattern_trig_update_patterns(data); pattern_trig_update_patterns(data);
@ -106,8 +138,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
led_set_brightness(data->led_cdev, led_set_brightness(data->led_cdev,
pattern_trig_compute_brightness(data)); pattern_trig_compute_brightness(data));
mod_timer(&data->timer, pattern_trig_timer_restart(data, UPDATE_INTERVAL);
jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
/* Accumulate the gradual dimming time */ /* Accumulate the gradual dimming time */
data->delta_t += UPDATE_INTERVAL; data->delta_t += UPDATE_INTERVAL;
@ -117,6 +148,25 @@ static void pattern_trig_timer_function(struct timer_list *t)
} }
} }
static void pattern_trig_timer_function(struct timer_list *t)
{
struct pattern_trig_data *data = from_timer(data, t, timer);
return pattern_trig_timer_common_function(data);
}
static enum hrtimer_restart pattern_trig_hrtimer_function(struct hrtimer *t)
{
struct pattern_trig_data *data =
container_of(t, struct pattern_trig_data, hrtimer);
pattern_trig_timer_common_function(data);
if (!data->is_indefinite && !data->repeat)
return HRTIMER_NORESTART;
return HRTIMER_RESTART;
}
static int pattern_trig_start_pattern(struct led_classdev *led_cdev) static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
{ {
struct pattern_trig_data *data = led_cdev->trigger_data; struct pattern_trig_data *data = led_cdev->trigger_data;
@ -124,7 +174,7 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
if (!data->npatterns) if (!data->npatterns)
return 0; return 0;
if (data->is_hw_pattern) { if (data->type == PATTERN_TYPE_HW) {
return led_cdev->pattern_set(led_cdev, data->patterns, return led_cdev->pattern_set(led_cdev, data->patterns,
data->npatterns, data->repeat); data->npatterns, data->repeat);
} }
@ -136,8 +186,7 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
data->delta_t = 0; data->delta_t = 0;
data->curr = data->patterns; data->curr = data->patterns;
data->next = data->patterns + 1; data->next = data->patterns + 1;
data->timer.expires = jiffies; pattern_trig_timer_start(data);
add_timer(&data->timer);
return 0; return 0;
} }
@ -175,9 +224,9 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->lock); mutex_lock(&data->lock);
del_timer_sync(&data->timer); pattern_trig_timer_cancel(data);
if (data->is_hw_pattern) if (data->type == PATTERN_TYPE_HW)
led_cdev->pattern_clear(led_cdev); led_cdev->pattern_clear(led_cdev);
data->last_repeat = data->repeat = res; data->last_repeat = data->repeat = res;
@ -196,14 +245,14 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(repeat); static DEVICE_ATTR_RW(repeat);
static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data, static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
char *buf, bool hw_pattern) char *buf, enum pattern_type type)
{ {
ssize_t count = 0; ssize_t count = 0;
int i; int i;
mutex_lock(&data->lock); mutex_lock(&data->lock);
if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern)) if (!data->npatterns || data->type != type)
goto out; goto out;
for (i = 0; i < data->npatterns; i++) { for (i = 0; i < data->npatterns; i++) {
@ -260,19 +309,19 @@ static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev, static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
const char *buf, const u32 *buf_int, const char *buf, const u32 *buf_int,
size_t count, bool hw_pattern) size_t count, enum pattern_type type)
{ {
struct pattern_trig_data *data = led_cdev->trigger_data; struct pattern_trig_data *data = led_cdev->trigger_data;
int err = 0; int err = 0;
mutex_lock(&data->lock); mutex_lock(&data->lock);
del_timer_sync(&data->timer); pattern_trig_timer_cancel(data);
if (data->is_hw_pattern) if (data->type == PATTERN_TYPE_HW)
led_cdev->pattern_clear(led_cdev); led_cdev->pattern_clear(led_cdev);
data->is_hw_pattern = hw_pattern; data->type = type;
data->npatterns = 0; data->npatterns = 0;
if (buf) if (buf)
@ -297,7 +346,7 @@ static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct pattern_trig_data *data = led_cdev->trigger_data; struct pattern_trig_data *data = led_cdev->trigger_data;
return pattern_trig_show_patterns(data, buf, false); return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_SW);
} }
static ssize_t pattern_store(struct device *dev, struct device_attribute *attr, static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
@ -305,7 +354,8 @@ static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
{ {
struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev *led_cdev = dev_get_drvdata(dev);
return pattern_trig_store_patterns(led_cdev, buf, NULL, count, false); return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
PATTERN_TYPE_SW);
} }
static DEVICE_ATTR_RW(pattern); static DEVICE_ATTR_RW(pattern);
@ -316,7 +366,7 @@ static ssize_t hw_pattern_show(struct device *dev,
struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct pattern_trig_data *data = led_cdev->trigger_data; struct pattern_trig_data *data = led_cdev->trigger_data;
return pattern_trig_show_patterns(data, buf, true); return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HW);
} }
static ssize_t hw_pattern_store(struct device *dev, static ssize_t hw_pattern_store(struct device *dev,
@ -325,11 +375,33 @@ static ssize_t hw_pattern_store(struct device *dev,
{ {
struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev *led_cdev = dev_get_drvdata(dev);
return pattern_trig_store_patterns(led_cdev, buf, NULL, count, true); return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
PATTERN_TYPE_HW);
} }
static DEVICE_ATTR_RW(hw_pattern); static DEVICE_ATTR_RW(hw_pattern);
static ssize_t hr_pattern_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct pattern_trig_data *data = led_cdev->trigger_data;
return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HR);
}
static ssize_t hr_pattern_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
PATTERN_TYPE_HR);
}
static DEVICE_ATTR_RW(hr_pattern);
static umode_t pattern_trig_attrs_mode(struct kobject *kobj, static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
struct attribute *attr, int index) struct attribute *attr, int index)
{ {
@ -338,6 +410,8 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr) if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
return attr->mode; return attr->mode;
else if (attr == &dev_attr_hr_pattern.attr)
return attr->mode;
else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set) else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
return attr->mode; return attr->mode;
@ -347,6 +421,7 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
static struct attribute *pattern_trig_attrs[] = { static struct attribute *pattern_trig_attrs[] = {
&dev_attr_pattern.attr, &dev_attr_pattern.attr,
&dev_attr_hw_pattern.attr, &dev_attr_hw_pattern.attr,
&dev_attr_hr_pattern.attr,
&dev_attr_repeat.attr, &dev_attr_repeat.attr,
NULL NULL
}; };
@ -376,7 +451,8 @@ static void pattern_init(struct led_classdev *led_cdev)
goto out; goto out;
} }
err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, false); err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size,
PATTERN_TYPE_SW);
if (err < 0) if (err < 0)
dev_warn(led_cdev->dev, dev_warn(led_cdev->dev,
"Pattern initialization failed with error %d\n", err); "Pattern initialization failed with error %d\n", err);
@ -400,12 +476,15 @@ static int pattern_trig_activate(struct led_classdev *led_cdev)
led_cdev->pattern_clear = NULL; led_cdev->pattern_clear = NULL;
} }
data->type = PATTERN_TYPE_SW;
data->is_indefinite = true; data->is_indefinite = true;
data->last_repeat = -1; data->last_repeat = -1;
mutex_init(&data->lock); mutex_init(&data->lock);
data->led_cdev = led_cdev; data->led_cdev = led_cdev;
led_set_trigger_data(led_cdev, data); led_set_trigger_data(led_cdev, data);
timer_setup(&data->timer, pattern_trig_timer_function, 0); timer_setup(&data->timer, pattern_trig_timer_function, 0);
hrtimer_init(&data->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->hrtimer.function = pattern_trig_hrtimer_function;
led_cdev->activated = true; led_cdev->activated = true;
if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) { if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
@ -431,6 +510,7 @@ static void pattern_trig_deactivate(struct led_classdev *led_cdev)
led_cdev->pattern_clear(led_cdev); led_cdev->pattern_clear(led_cdev);
timer_shutdown_sync(&data->timer); timer_shutdown_sync(&data->timer);
hrtimer_cancel(&data->hrtimer);
led_set_brightness(led_cdev, LED_OFF); led_set_brightness(led_cdev, LED_OFF);
kfree(data); kfree(data);

View File

@ -91,11 +91,14 @@
#define LED_FUNCTION_INDICATOR "indicator" #define LED_FUNCTION_INDICATOR "indicator"
#define LED_FUNCTION_LAN "lan" #define LED_FUNCTION_LAN "lan"
#define LED_FUNCTION_MAIL "mail" #define LED_FUNCTION_MAIL "mail"
#define LED_FUNCTION_MOBILE "mobile"
#define LED_FUNCTION_MTD "mtd" #define LED_FUNCTION_MTD "mtd"
#define LED_FUNCTION_PANIC "panic" #define LED_FUNCTION_PANIC "panic"
#define LED_FUNCTION_PROGRAMMING "programming" #define LED_FUNCTION_PROGRAMMING "programming"
#define LED_FUNCTION_RX "rx" #define LED_FUNCTION_RX "rx"
#define LED_FUNCTION_SD "sd" #define LED_FUNCTION_SD "sd"
#define LED_FUNCTION_SPEED_LAN "speed-lan"
#define LED_FUNCTION_SPEED_WAN "speed-wan"
#define LED_FUNCTION_STANDBY "standby" #define LED_FUNCTION_STANDBY "standby"
#define LED_FUNCTION_TORCH "torch" #define LED_FUNCTION_TORCH "torch"
#define LED_FUNCTION_TX "tx" #define LED_FUNCTION_TX "tx"

View File

@ -455,6 +455,9 @@ struct led_trigger {
int (*activate)(struct led_classdev *led_cdev); int (*activate)(struct led_classdev *led_cdev);
void (*deactivate)(struct led_classdev *led_cdev); void (*deactivate)(struct led_classdev *led_cdev);
/* Brightness set by led_trigger_event */
enum led_brightness brightness;
/* LED-private triggers have this set */ /* LED-private triggers have this set */
struct led_hw_trigger_type *trigger_type; struct led_hw_trigger_type *trigger_type;
@ -508,6 +511,12 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
return led_cdev->trigger_data; return led_cdev->trigger_data;
} }
static inline enum led_brightness
led_trigger_get_brightness(const struct led_trigger *trigger)
{
return trigger ? trigger->brightness : LED_OFF;
}
#define module_led_trigger(__led_trigger) \ #define module_led_trigger(__led_trigger) \
module_driver(__led_trigger, led_trigger_register, \ module_driver(__led_trigger, led_trigger_register, \
led_trigger_unregister) led_trigger_unregister)
@ -544,6 +553,12 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
return NULL; return NULL;
} }
static inline enum led_brightness
led_trigger_get_brightness(const struct led_trigger *trigger)
{
return LED_OFF;
}
#endif /* CONFIG_LEDS_TRIGGERS */ #endif /* CONFIG_LEDS_TRIGGERS */
/* Trigger specific enum */ /* Trigger specific enum */
@ -690,18 +705,4 @@ enum led_audio {
NUM_AUDIO_LEDS NUM_AUDIO_LEDS
}; };
#if IS_ENABLED(CONFIG_LEDS_TRIGGER_AUDIO)
enum led_brightness ledtrig_audio_get(enum led_audio type);
void ledtrig_audio_set(enum led_audio type, enum led_brightness state);
#else
static inline enum led_brightness ledtrig_audio_get(enum led_audio type)
{
return LED_OFF;
}
static inline void ledtrig_audio_set(enum led_audio type,
enum led_brightness state)
{
}
#endif
#endif /* __LINUX_LEDS_H_INCLUDED */ #endif /* __LINUX_LEDS_H_INCLUDED */

View File

@ -22,6 +22,8 @@
#include <linux/cleanup.h> #include <linux/cleanup.h>
#include <linux/mutex_types.h> #include <linux/mutex_types.h>
struct device;
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ # define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
, .dep_map = { \ , .dep_map = { \
@ -117,6 +119,31 @@ do { \
} while (0) } while (0)
#endif /* CONFIG_PREEMPT_RT */ #endif /* CONFIG_PREEMPT_RT */
#ifdef CONFIG_DEBUG_MUTEXES
int __devm_mutex_init(struct device *dev, struct mutex *lock);
#else
static inline int __devm_mutex_init(struct device *dev, struct mutex *lock)
{
/*
* When CONFIG_DEBUG_MUTEXES is off mutex_destroy() is just a nop so
* no really need to register it in the devm subsystem.
*/
return 0;
}
#endif
#define devm_mutex_init(dev, mutex) \
({ \
typeof(mutex) mutex_ = (mutex); \
\
mutex_init(mutex_); \
__devm_mutex_init(dev, mutex_); \
})
/* /*
* See kernel/locking/mutex.c for detailed documentation of these APIs. * See kernel/locking/mutex.c for detailed documentation of these APIs.
* Also see Documentation/locking/mutex-design.rst. * Also see Documentation/locking/mutex-design.rst.

View File

@ -12,6 +12,7 @@
*/ */
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/poison.h> #include <linux/poison.h>
#include <linux/sched.h> #include <linux/sched.h>
@ -89,6 +90,17 @@ void debug_mutex_init(struct mutex *lock, const char *name,
lock->magic = lock; lock->magic = lock;
} }
static void devm_mutex_release(void *res)
{
mutex_destroy(res);
}
int __devm_mutex_init(struct device *dev, struct mutex *lock)
{
return devm_add_action_or_reset(dev, devm_mutex_release, lock);
}
EXPORT_SYMBOL_GPL(__devm_mutex_init);
/*** /***
* mutex_destroy - mark a mutex unusable * mutex_destroy - mark a mutex unusable
* @lock: the mutex to be destroyed * @lock: the mutex to be destroyed

View File

@ -262,6 +262,5 @@ config SND_CTL_LED
tristate tristate
select NEW_LEDS if SND_CTL_LED select NEW_LEDS if SND_CTL_LED
select LEDS_TRIGGERS if SND_CTL_LED select LEDS_TRIGGERS if SND_CTL_LED
select LEDS_TRIGGER_AUDIO if SND_CTL_LED
source "sound/core/seq/Kconfig" source "sound/core/seq/Kconfig"

View File

@ -53,6 +53,7 @@ struct snd_ctl_led_ctl {
static DEFINE_MUTEX(snd_ctl_led_mutex); static DEFINE_MUTEX(snd_ctl_led_mutex);
static bool snd_ctl_led_card_valid[SNDRV_CARDS]; static bool snd_ctl_led_card_valid[SNDRV_CARDS];
static struct led_trigger *snd_ctl_ledtrig_audio[NUM_AUDIO_LEDS];
static struct snd_ctl_led snd_ctl_leds[MAX_LED] = { static struct snd_ctl_led snd_ctl_leds[MAX_LED] = {
{ {
.name = "speaker", .name = "speaker",
@ -174,8 +175,11 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break; case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break;
case MODE_FOLLOW_MUTE: /* noop */ break; case MODE_FOLLOW_MUTE: /* noop */ break;
} }
if (route >= 0) if (route >= 0) {
ledtrig_audio_set(led->trigger_type, route ? LED_OFF : LED_ON); struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
led_trigger_event(trig, route ? LED_OFF : LED_ON);
}
} }
static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff) static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff)
@ -420,8 +424,9 @@ static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev); struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
return sysfs_emit(buf, "%u\n", ledtrig_audio_get(led->trigger_type)); return sysfs_emit(buf, "%u\n", led_trigger_get_brightness(trig));
} }
static DEVICE_ATTR_RW(mode); static DEVICE_ATTR_RW(mode);
@ -711,6 +716,9 @@ static int __init snd_ctl_led_init(void)
struct snd_ctl_led *led; struct snd_ctl_led *led;
unsigned int group; unsigned int group;
led_trigger_register_simple("audio-mute", &snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
led_trigger_register_simple("audio-micmute", &snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
device_initialize(&snd_ctl_led_dev); device_initialize(&snd_ctl_led_dev);
snd_ctl_led_dev.class = &sound_class; snd_ctl_led_dev.class = &sound_class;
snd_ctl_led_dev.release = snd_ctl_led_dev_release; snd_ctl_led_dev.release = snd_ctl_led_dev_release;
@ -763,7 +771,13 @@ static void __exit snd_ctl_led_exit(void)
} }
device_unregister(&snd_ctl_led_dev); device_unregister(&snd_ctl_led_dev);
snd_ctl_led_clean(NULL); snd_ctl_led_clean(NULL);
led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
} }
module_init(snd_ctl_led_init) module_init(snd_ctl_led_init)
module_exit(snd_ctl_led_exit) module_exit(snd_ctl_led_exit)
MODULE_ALIAS("ledtrig:audio-mute");
MODULE_ALIAS("ledtrig:audio-micmute");