- 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:
commit
f3033eb791
|
@ -12,6 +12,16 @@ Description:
|
|||
The exact format is described in:
|
||||
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
|
||||
Date: September 2018
|
||||
KernelVersion: 4.20
|
||||
|
|
|
@ -27,9 +27,14 @@ properties:
|
|||
- qcom,pm8994-lpg
|
||||
- qcom,pmc8180c-lpg
|
||||
- qcom,pmi632-lpg
|
||||
- qcom,pmi8950-pwm
|
||||
- qcom,pmi8994-lpg
|
||||
- qcom,pmi8998-lpg
|
||||
- qcom,pmk8550-pwm
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,pm6150l-lpg
|
||||
- const: qcom,pm8150l-lpg
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,pm8550-pwm
|
||||
|
@ -142,6 +147,7 @@ allOf:
|
|||
- qcom,pm8941-lpg
|
||||
- qcom,pm8994-lpg
|
||||
- qcom,pmc8180c-lpg
|
||||
- qcom,pmi8950-pwm
|
||||
- qcom,pmi8994-lpg
|
||||
- qcom,pmi8998-lpg
|
||||
- qcom,pmk8550-pwm
|
||||
|
@ -290,5 +296,3 @@ examples:
|
|||
label = "blue";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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";
|
||||
};
|
||||
};
|
|
@ -152,7 +152,6 @@ CONFIG_LEDS_TRIGGER_CAMERA=m
|
|||
CONFIG_LEDS_TRIGGER_PANIC=y
|
||||
CONFIG_LEDS_TRIGGER_NETDEV=y
|
||||
CONFIG_LEDS_TRIGGER_PATTERN=y
|
||||
CONFIG_LEDS_TRIGGER_AUDIO=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_JZ4740=y
|
||||
CONFIG_DMADEVICES=y
|
||||
|
|
|
@ -77,8 +77,6 @@ struct aat1290_led {
|
|||
int *mm_current_scale;
|
||||
/* device mode */
|
||||
bool movie_mode;
|
||||
/* brightness cache */
|
||||
unsigned int torch_brightness;
|
||||
};
|
||||
|
||||
static struct aat1290_led *fled_cdev_to_led(
|
||||
|
|
|
@ -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 val = state ? MT6360_FLCSEN_MASK(led->led_no) : 0;
|
||||
u32 prev = priv->fled_strobe_used, curr;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
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
|
||||
* currently used
|
||||
|
|
|
@ -194,11 +194,11 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
|||
spin_unlock(&trig->leddev_list_lock);
|
||||
led_cdev->trigger = trig;
|
||||
|
||||
ret = 0;
|
||||
if (trig->activate)
|
||||
ret = trig->activate(led_cdev);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
led_set_brightness(led_cdev, trig->brightness);
|
||||
if (ret)
|
||||
goto err_activate;
|
||||
|
||||
|
@ -387,6 +387,8 @@ void led_trigger_event(struct led_trigger *trig,
|
|||
if (!trig)
|
||||
return;
|
||||
|
||||
trig->brightness = brightness;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list)
|
||||
led_set_brightness(led_cdev, brightness);
|
||||
|
|
|
@ -283,7 +283,10 @@ static int an30259a_probe(struct i2c_client *client)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_init(&chip->mutex);
|
||||
err = devm_mutex_init(&client->dev, &chip->mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
chip->client = client;
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
|
@ -317,17 +320,9 @@ static int an30259a_probe(struct i2c_client *client)
|
|||
return 0;
|
||||
|
||||
exit:
|
||||
mutex_destroy(&chip->mutex);
|
||||
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[] = {
|
||||
{ .compatible = "panasonic,an30259a", },
|
||||
{ /* sentinel */ },
|
||||
|
@ -347,7 +342,6 @@ static struct i2c_driver an30259a_driver = {
|
|||
.of_match_table = an30259a_match_table,
|
||||
},
|
||||
.probe = an30259a_probe,
|
||||
.remove = an30259a_remove,
|
||||
.id_table = an30259a_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -181,8 +181,7 @@ static int __init apu_led_init(void)
|
|||
struct platform_device *pdev;
|
||||
int err;
|
||||
|
||||
if (!(dmi_match(DMI_SYS_VENDOR, "PC Engines") &&
|
||||
(dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "apu1")))) {
|
||||
if (!dmi_check_system(apu_led_dmi_table)) {
|
||||
pr_err("No PC Engines APUv1 board detected. For APUv2,3 support, enable CONFIG_PCENGINES_APU2\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -530,6 +530,16 @@ static const struct regmap_config aw200xx_regmap_config = {
|
|||
.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)
|
||||
{
|
||||
const struct aw200xx_chipdef *cdef;
|
||||
|
@ -568,11 +578,17 @@ static int aw200xx_probe(struct i2c_client *client)
|
|||
|
||||
aw200xx_enable(chip);
|
||||
|
||||
ret = devm_add_action(&client->dev, aw200xx_disable_action, chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = aw200xx_chip_check(chip);
|
||||
if (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 */
|
||||
mutex_lock(&chip->mutex);
|
||||
|
@ -581,6 +597,10 @@ static int aw200xx_probe(struct i2c_client *client)
|
|||
if (ret)
|
||||
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);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
@ -595,15 +615,6 @@ out_unlock:
|
|||
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 = {
|
||||
.channels = 36,
|
||||
.display_size_rows_max = 3,
|
||||
|
@ -652,7 +663,6 @@ static struct i2c_driver aw200xx_driver = {
|
|||
.of_match_table = aw200xx_match_table,
|
||||
},
|
||||
.probe = aw200xx_probe,
|
||||
.remove = aw200xx_remove,
|
||||
.id_table = aw200xx_id,
|
||||
};
|
||||
module_i2c_driver(aw200xx_driver);
|
||||
|
|
|
@ -320,6 +320,11 @@ static int aw2013_probe_dt(struct aw2013 *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void aw2013_chip_disable_action(void *data)
|
||||
{
|
||||
aw2013_chip_disable(data);
|
||||
}
|
||||
|
||||
static const struct regmap_config aw2013_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
@ -336,7 +341,10 @@ static int aw2013_probe(struct i2c_client *client)
|
|||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&chip->mutex);
|
||||
ret = devm_mutex_init(&client->dev, &chip->mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
|
||||
chip->client = client;
|
||||
|
@ -384,6 +392,10 @@ static int aw2013_probe(struct i2c_client *client)
|
|||
goto error_reg;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&client->dev, aw2013_chip_disable_action, chip);
|
||||
if (ret)
|
||||
goto error_reg;
|
||||
|
||||
ret = aw2013_probe_dt(chip);
|
||||
if (ret < 0)
|
||||
goto error_reg;
|
||||
|
@ -406,19 +418,9 @@ error_reg:
|
|||
|
||||
error:
|
||||
mutex_unlock(&chip->mutex);
|
||||
mutex_destroy(&chip->mutex);
|
||||
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[] = {
|
||||
{ .compatible = "awinic,aw2013", },
|
||||
{ /* sentinel */ },
|
||||
|
@ -432,7 +434,6 @@ static struct i2c_driver aw2013_driver = {
|
|||
.of_match_table = aw2013_match_table,
|
||||
},
|
||||
.probe = aw2013_probe,
|
||||
.remove = aw2013_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(aw2013_driver);
|
||||
|
|
|
@ -542,6 +542,13 @@ static int lm3532_parse_als(struct lm3532_data *priv)
|
|||
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)
|
||||
{
|
||||
struct fwnode_handle *child = NULL;
|
||||
|
@ -556,6 +563,12 @@ static int lm3532_parse_node(struct lm3532_data *priv)
|
|||
if (IS_ERR(priv->enable_gpio))
|
||||
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");
|
||||
if (IS_ERR(priv->regulator))
|
||||
priv->regulator = NULL;
|
||||
|
@ -691,7 +704,10 @@ static int lm3532_probe(struct i2c_client *client)
|
|||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&drvdata->lock);
|
||||
ret = devm_mutex_init(&client->dev, &drvdata->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, drvdata);
|
||||
|
||||
ret = lm3532_parse_node(drvdata);
|
||||
|
@ -703,16 +719,6 @@ static int lm3532_probe(struct i2c_client *client)
|
|||
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[] = {
|
||||
{ .compatible = "ti,lm3532", },
|
||||
{},
|
||||
|
@ -727,7 +733,6 @@ MODULE_DEVICE_TABLE(i2c, lm3532_id);
|
|||
|
||||
static struct i2c_driver lm3532_i2c_driver = {
|
||||
.probe = lm3532_probe,
|
||||
.remove = lm3532_remove,
|
||||
.id_table = lm3532_id,
|
||||
.driver = {
|
||||
.name = LM3532_NAME,
|
||||
|
|
|
@ -207,6 +207,13 @@ static const struct regmap_config lp3952_regmap = {
|
|||
.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)
|
||||
{
|
||||
int status;
|
||||
|
@ -226,6 +233,10 @@ static int lp3952_probe(struct i2c_client *client)
|
|||
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);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
int err = PTR_ERR(priv->regmap);
|
||||
|
@ -254,15 +265,6 @@ static int lp3952_probe(struct i2c_client *client)
|
|||
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[] = {
|
||||
{LP3952_NAME, 0},
|
||||
{}
|
||||
|
@ -274,7 +276,6 @@ static struct i2c_driver lp3952_i2c_driver = {
|
|||
.name = LP3952_NAME,
|
||||
},
|
||||
.probe = lp3952_probe,
|
||||
.remove = lp3952_remove,
|
||||
.id_table = lp3952_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -265,7 +265,6 @@ static const struct lp50xx_chip_info lp50xx_chip_info_tbl[] = {
|
|||
struct lp50xx_led {
|
||||
struct led_classdev_mc mc_cdev;
|
||||
struct lp50xx *priv;
|
||||
unsigned long bank_modules;
|
||||
u8 ctrl_bank_enabled;
|
||||
int led_number;
|
||||
};
|
||||
|
@ -279,7 +278,6 @@ struct lp50xx_led {
|
|||
* @dev: pointer to the devices device struct
|
||||
* @lock: lock for reading/writing the device
|
||||
* @chip_info: chip specific information (ie num_leds)
|
||||
* @num_of_banked_leds: holds the number of banked LEDs
|
||||
* @leds: array of LED strings
|
||||
*/
|
||||
struct lp50xx {
|
||||
|
@ -290,7 +288,6 @@ struct lp50xx {
|
|||
struct device *dev;
|
||||
struct mutex lock;
|
||||
const struct lp50xx_chip_info *chip_info;
|
||||
int num_of_banked_leds;
|
||||
|
||||
/* This needs to be at the end of the struct */
|
||||
struct lp50xx_led leds[];
|
||||
|
@ -404,8 +401,6 @@ static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->num_of_banked_leds = num_leds;
|
||||
|
||||
ret = fwnode_property_read_u32_array(child, "reg", led_banks, num_leds);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "reg property is missing\n");
|
||||
|
|
|
@ -256,6 +256,7 @@ static int mlxreg_led_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct mlxreg_core_platform_data *led_pdata;
|
||||
struct mlxreg_led_priv_data *priv;
|
||||
int err;
|
||||
|
||||
led_pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!led_pdata) {
|
||||
|
@ -267,26 +268,21 @@ static int mlxreg_led_probe(struct platform_device *pdev)
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->access_lock);
|
||||
err = devm_mutex_init(&pdev->dev, &priv->access_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
priv->pdev = pdev;
|
||||
priv->pdata = led_pdata;
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = "leds-mlxreg",
|
||||
},
|
||||
.probe = mlxreg_led_probe,
|
||||
.remove_new = mlxreg_led_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mlxreg_led_driver);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -152,6 +161,10 @@ static int nic78bx_probe(struct platform_device *pdev)
|
|||
led_data->io_base = io_rc->start;
|
||||
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++) {
|
||||
nic78bx_leds[i].data = led_data;
|
||||
|
||||
|
@ -167,15 +180,6 @@ static int nic78bx_probe(struct platform_device *pdev)
|
|||
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[] = {
|
||||
{"NIC78B3", 0},
|
||||
{"", 0},
|
||||
|
@ -184,7 +188,6 @@ MODULE_DEVICE_TABLE(acpi, led_device_ids);
|
|||
|
||||
static struct platform_driver led_driver = {
|
||||
.probe = nic78bx_probe,
|
||||
.remove_new = nic78bx_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.acpi_match_table = ACPI_PTR(led_device_ids),
|
||||
|
|
|
@ -53,7 +53,13 @@ static int led_pwm_set(struct led_classdev *led_cdev,
|
|||
duty = led_dat->pwmstate.period - 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -252,18 +252,16 @@ static int sun50i_a100_ledc_parse_format(struct device *dev,
|
|||
struct sun50i_a100_ledc *priv)
|
||||
{
|
||||
const char *format = "grb";
|
||||
u32 i;
|
||||
int i;
|
||||
|
||||
device_property_read_string(dev, "allwinner,pixel-format", &format);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sun50i_a100_ledc_formats); i++) {
|
||||
if (!strcmp(format, sun50i_a100_ledc_formats[i])) {
|
||||
priv->format = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
i = match_string(sun50i_a100_ledc_formats, ARRAY_SIZE(sun50i_a100_ledc_formats), format);
|
||||
if (i < 0)
|
||||
return dev_err_probe(dev, i, "Bad pixel format '%s'\n", format);
|
||||
|
||||
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)
|
||||
|
|
|
@ -149,7 +149,6 @@ struct mt6370_priv {
|
|||
struct regmap_field *fields[F_MAX_FIELDS];
|
||||
const struct reg_field *reg_fields;
|
||||
const struct linear_range *ranges;
|
||||
struct reg_cfg *reg_cfgs;
|
||||
const struct mt6370_pdata *pdata;
|
||||
unsigned int leds_count;
|
||||
unsigned int leds_active;
|
||||
|
|
|
@ -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 = {
|
||||
.lut_base = 0xb000,
|
||||
.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,pm8994-lpg", .data = &pm8994_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,pmi8998-lpg", .data = &pmi8998_lpg_data },
|
||||
{ .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data },
|
||||
|
|
|
@ -56,6 +56,7 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev,
|
|||
case SIMATIC_IPC_DEVICE_127E:
|
||||
case SIMATIC_IPC_DEVICE_227G:
|
||||
case SIMATIC_IPC_DEVICE_BX_21A:
|
||||
case SIMATIC_IPC_DEVICE_BX_59A:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
|
|
|
@ -17,7 +17,12 @@
|
|||
|
||||
#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",
|
||||
.table = {
|
||||
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 */
|
||||
.table = {
|
||||
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)
|
||||
{
|
||||
return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table,
|
||||
&simatic_ipc_led_gpio_table_extra);
|
||||
const struct simatic_ipc_platform *plat = dev_get_platdata(&pdev->dev);
|
||||
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)
|
||||
{
|
||||
simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table,
|
||||
&simatic_ipc_led_gpio_table_extra);
|
||||
struct simatic_ipc_led_tables *led_tables = platform_get_drvdata(pdev);
|
||||
|
||||
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 = {
|
||||
|
|
|
@ -136,13 +136,6 @@ config LEDS_TRIGGER_PATTERN
|
|||
which is a series of tuples, of brightness and duration (ms).
|
||||
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
|
||||
tristate "LED Trigger for TTY devices"
|
||||
depends on TTY
|
||||
|
|
|
@ -14,5 +14,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
|
|||
obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.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
|
||||
|
|
|
@ -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");
|
|
@ -724,8 +724,6 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev)
|
|||
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
led_set_brightness(led_cdev, LED_OFF);
|
||||
|
||||
dev_put(trigger_data->net_dev);
|
||||
|
||||
kfree(trigger_data);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
||||
#define MAX_PATTERNS 1024
|
||||
/*
|
||||
|
@ -21,6 +22,12 @@
|
|||
*/
|
||||
#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 led_classdev *led_cdev;
|
||||
struct led_pattern patterns[MAX_PATTERNS];
|
||||
|
@ -32,8 +39,9 @@ struct pattern_trig_data {
|
|||
int last_repeat;
|
||||
int delta_t;
|
||||
bool is_indefinite;
|
||||
bool is_hw_pattern;
|
||||
enum pattern_type type;
|
||||
struct timer_list timer;
|
||||
struct hrtimer hrtimer;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (;;) {
|
||||
if (!data->is_indefinite && !data->repeat)
|
||||
break;
|
||||
|
@ -83,8 +116,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
|
|||
/* Step change of brightness */
|
||||
led_set_brightness(data->led_cdev,
|
||||
data->curr->brightness);
|
||||
mod_timer(&data->timer,
|
||||
jiffies + msecs_to_jiffies(data->curr->delta_t));
|
||||
pattern_trig_timer_restart(data, data->curr->delta_t);
|
||||
if (!data->next->delta_t) {
|
||||
/* Skip the tuple with zero duration */
|
||||
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,
|
||||
pattern_trig_compute_brightness(data));
|
||||
mod_timer(&data->timer,
|
||||
jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
|
||||
pattern_trig_timer_restart(data, UPDATE_INTERVAL);
|
||||
|
||||
/* Accumulate the gradual dimming time */
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return 0;
|
||||
|
||||
if (data->is_hw_pattern) {
|
||||
if (data->type == PATTERN_TYPE_HW) {
|
||||
return led_cdev->pattern_set(led_cdev, data->patterns,
|
||||
data->npatterns, data->repeat);
|
||||
}
|
||||
|
@ -136,8 +186,7 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
|
|||
data->delta_t = 0;
|
||||
data->curr = data->patterns;
|
||||
data->next = data->patterns + 1;
|
||||
data->timer.expires = jiffies;
|
||||
add_timer(&data->timer);
|
||||
pattern_trig_timer_start(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -175,9 +224,9 @@ static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
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);
|
||||
|
||||
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 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;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern))
|
||||
if (!data->npatterns || data->type != type)
|
||||
goto out;
|
||||
|
||||
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,
|
||||
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;
|
||||
int err = 0;
|
||||
|
||||
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);
|
||||
|
||||
data->is_hw_pattern = hw_pattern;
|
||||
data->type = type;
|
||||
data->npatterns = 0;
|
||||
|
||||
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 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,
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
@ -316,7 +366,7 @@ static ssize_t hw_pattern_show(struct device *dev,
|
|||
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, true);
|
||||
return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HW);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 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,
|
||||
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)
|
||||
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)
|
||||
return attr->mode;
|
||||
|
||||
|
@ -347,6 +421,7 @@ static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
|
|||
static struct attribute *pattern_trig_attrs[] = {
|
||||
&dev_attr_pattern.attr,
|
||||
&dev_attr_hw_pattern.attr,
|
||||
&dev_attr_hr_pattern.attr,
|
||||
&dev_attr_repeat.attr,
|
||||
NULL
|
||||
};
|
||||
|
@ -376,7 +451,8 @@ static void pattern_init(struct led_classdev *led_cdev)
|
|||
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)
|
||||
dev_warn(led_cdev->dev,
|
||||
"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;
|
||||
}
|
||||
|
||||
data->type = PATTERN_TYPE_SW;
|
||||
data->is_indefinite = true;
|
||||
data->last_repeat = -1;
|
||||
mutex_init(&data->lock);
|
||||
data->led_cdev = led_cdev;
|
||||
led_set_trigger_data(led_cdev, data);
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
timer_shutdown_sync(&data->timer);
|
||||
hrtimer_cancel(&data->hrtimer);
|
||||
|
||||
led_set_brightness(led_cdev, LED_OFF);
|
||||
kfree(data);
|
||||
|
|
|
@ -91,11 +91,14 @@
|
|||
#define LED_FUNCTION_INDICATOR "indicator"
|
||||
#define LED_FUNCTION_LAN "lan"
|
||||
#define LED_FUNCTION_MAIL "mail"
|
||||
#define LED_FUNCTION_MOBILE "mobile"
|
||||
#define LED_FUNCTION_MTD "mtd"
|
||||
#define LED_FUNCTION_PANIC "panic"
|
||||
#define LED_FUNCTION_PROGRAMMING "programming"
|
||||
#define LED_FUNCTION_RX "rx"
|
||||
#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_TORCH "torch"
|
||||
#define LED_FUNCTION_TX "tx"
|
||||
|
|
|
@ -455,6 +455,9 @@ struct led_trigger {
|
|||
int (*activate)(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 */
|
||||
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;
|
||||
}
|
||||
|
||||
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) \
|
||||
module_driver(__led_trigger, led_trigger_register, \
|
||||
led_trigger_unregister)
|
||||
|
@ -544,6 +553,12 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline enum led_brightness
|
||||
led_trigger_get_brightness(const struct led_trigger *trigger)
|
||||
{
|
||||
return LED_OFF;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LEDS_TRIGGERS */
|
||||
|
||||
/* Trigger specific enum */
|
||||
|
@ -690,18 +705,4 @@ enum led_audio {
|
|||
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 */
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/cleanup.h>
|
||||
#include <linux/mutex_types.h>
|
||||
|
||||
struct device;
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
|
||||
, .dep_map = { \
|
||||
|
@ -117,6 +119,31 @@ do { \
|
|||
} while (0)
|
||||
#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.
|
||||
* Also see Documentation/locking/mutex-design.rst.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/poison.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -89,6 +90,17 @@ void debug_mutex_init(struct mutex *lock, const char *name,
|
|||
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
|
||||
* @lock: the mutex to be destroyed
|
||||
|
|
|
@ -262,6 +262,5 @@ config SND_CTL_LED
|
|||
tristate
|
||||
select NEW_LEDS if SND_CTL_LED
|
||||
select LEDS_TRIGGERS if SND_CTL_LED
|
||||
select LEDS_TRIGGER_AUDIO if SND_CTL_LED
|
||||
|
||||
source "sound/core/seq/Kconfig"
|
||||
|
|
|
@ -53,6 +53,7 @@ struct snd_ctl_led_ctl {
|
|||
|
||||
static DEFINE_MUTEX(snd_ctl_led_mutex);
|
||||
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] = {
|
||||
{
|
||||
.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_MUTE: /* noop */ break;
|
||||
}
|
||||
if (route >= 0)
|
||||
ledtrig_audio_set(led->trigger_type, route ? LED_OFF : LED_ON);
|
||||
if (route >= 0) {
|
||||
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)
|
||||
|
@ -420,8 +424,9 @@ static ssize_t brightness_show(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
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);
|
||||
|
@ -711,6 +716,9 @@ static int __init snd_ctl_led_init(void)
|
|||
struct snd_ctl_led *led;
|
||||
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);
|
||||
snd_ctl_led_dev.class = &sound_class;
|
||||
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);
|
||||
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_exit(snd_ctl_led_exit)
|
||||
|
||||
MODULE_ALIAS("ledtrig:audio-mute");
|
||||
MODULE_ALIAS("ledtrig:audio-micmute");
|
||||
|
|
Loading…
Reference in New Issue