Commit f3033eb7 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 7eae27cd f2994f53
......@@ -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";
};
};
...
# 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;
};
};
};
...
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 @@ static int aw200xx_probe(struct i2c_client *client)
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 @@ static int aw2013_probe(struct i2c_client *client)
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
// 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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment