diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-22 10:49:54 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-22 10:49:54 -0700 |
commit | f3033eb79136dd27b17e7a192fac0155ceab5eb8 (patch) | |
tree | 444aecf3588c6b9d007e8dbb4a3c6baacec62322 /drivers | |
parent | 7eae27cd12a2d305ffad41a8e10cff3bb8c0dcb0 (diff) | |
parent | f2994f5341e03b8680a88abc5f1dee950033c3a9 (diff) |
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
...
Diffstat (limited to 'drivers')
23 files changed, 271 insertions, 202 deletions
diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c index 0195935a7c99..e8f9dd293592 100644 --- a/drivers/leds/flash/leds-aat1290.c +++ b/drivers/leds/flash/leds-aat1290.c @@ -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( diff --git a/drivers/leds/flash/leds-mt6360.c b/drivers/leds/flash/leds-mt6360.c index a90de82f4568..1b75b4d36834 100644 --- a/drivers/leds/flash/leds-mt6360.c +++ b/drivers/leds/flash/leds-mt6360.c @@ -241,11 +241,21 @@ 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 */ diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 0f5ac30053ad..b1b323b19301 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -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); diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index 0216afed3b6e..decfca447d8a 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -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, }; diff --git a/drivers/leds/leds-apu.c b/drivers/leds/leds-apu.c index c409b80c236d..1c116aaa9b6e 100644 --- a/drivers/leds/leds-apu.c +++ b/drivers/leds/leds-apu.c @@ -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; } diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 6c8c9f2c19e3..f9d9844e0273 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -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); diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c index 17235a5e576a..6475eadcb0df 100644 --- a/drivers/leds/leds-aw2013.c +++ b/drivers/leds/leds-aw2013.c @@ -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); diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 13662a4aa1f2..8c90701dc50d 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -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, diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c index 5d18bbfd1f23..ff7bae2447dd 100644 --- a/drivers/leds/leds-lp3952.c +++ b/drivers/leds/leds-lp3952.c @@ -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, }; diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c index 68c4d9967d68..175d4b06659b 100644 --- a/drivers/leds/leds-lp50xx.c +++ b/drivers/leds/leds-lp50xx.c @@ -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"); diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c index 5595788d98d2..1b70de72376c 100644 --- a/drivers/leds/leds-mlxreg.c +++ b/drivers/leds/leds-mlxreg.c @@ -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); diff --git a/drivers/leds/leds-nic78bx.c b/drivers/leds/leds-nic78bx.c index a86b43dd995e..282d9e4cf116 100644 --- a/drivers/leds/leds-nic78bx.c +++ b/drivers/leds/leds-nic78bx.c @@ -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), diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 4e3936a39d0e..e1b414b40353 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -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); } diff --git a/drivers/leds/leds-sun50i-a100.c b/drivers/leds/leds-sun50i-a100.c index 62d21c3a3575..119eff9471f0 100644 --- a/drivers/leds/leds-sun50i-a100.c +++ b/drivers/leds/leds-sun50i-a100.c @@ -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) diff --git a/drivers/leds/rgb/leds-mt6370-rgb.c b/drivers/leds/rgb/leds-mt6370-rgb.c index 448d0da11848..359ef00498b4 100644 --- a/drivers/leds/rgb/leds-mt6370-rgb.c +++ b/drivers/leds/rgb/leds-mt6370-rgb.c @@ -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; diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 6bdc5b923f98..9467c796bd04 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -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 }, diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c index 667ba1bc3a30..85003fd7f1aa 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c @@ -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; diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c index c7c3a1f986e6..7a5018639aaf 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c @@ -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 = { diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index d11d80176fc0..31576952e181 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -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 diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 25c4db97cdd4..242f6c4e3453 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -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 diff --git a/drivers/leds/trigger/ledtrig-audio.c b/drivers/leds/trigger/ledtrig-audio.c deleted file mode 100644 index 2ecd4b760fc3..000000000000 --- a/drivers/leds/trigger/ledtrig-audio.c +++ /dev/null @@ -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"); diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index ea00f6c70882..22bba8e97642 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -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); diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index fadd87dbe993..aad48c2540fc 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -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); |