From 2031717544cab77b9b6fd8c33fd24cbbe2f607b6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 6 Jan 2017 09:48:20 -0800 Subject: Input: pwm-beeper - remove calls to legacy pwm_request API There are no more users of pwm-beeper driver in mainline relying on this legacy API, so let's remove it and simplify the driver code. Acked-by: Thierry Reding Tested-by: David Lechner Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 5f9655d49a65..cb87e475bd23 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -95,7 +95,6 @@ static void pwm_beeper_close(struct input_dev *input) static int pwm_beeper_probe(struct platform_device *pdev) { - unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); struct pwm_beeper *beeper; int error; @@ -104,11 +103,6 @@ static int pwm_beeper_probe(struct platform_device *pdev) return -ENOMEM; beeper->pwm = pwm_get(&pdev->dev, NULL); - if (IS_ERR(beeper->pwm)) { - dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n"); - beeper->pwm = pwm_request(pwm_id, "pwm beeper"); - } - if (IS_ERR(beeper->pwm)) { error = PTR_ERR(beeper->pwm); dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); -- cgit From bcf4b0460ba558b1e1fcaa7c160076751b560d03 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 6 Jan 2017 09:50:14 -0800 Subject: Input: pwm-beeper - switch to using managed resources Use of managed resources (devm) simplifies error handling and tear down of the driver. Reviewed-by: Thierry Reding Tested-by: David Lechner Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 44 ++++++++++------------------------------- 1 file changed, 10 insertions(+), 34 deletions(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index cb87e475bd23..f8b163b0eb43 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -95,18 +95,19 @@ static void pwm_beeper_close(struct input_dev *input) static int pwm_beeper_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct pwm_beeper *beeper; int error; - beeper = kzalloc(sizeof(*beeper), GFP_KERNEL); + beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL); if (!beeper) return -ENOMEM; - beeper->pwm = pwm_get(&pdev->dev, NULL); + beeper->pwm = devm_pwm_get(dev, NULL); if (IS_ERR(beeper->pwm)) { error = PTR_ERR(beeper->pwm); - dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); - goto err_free; + dev_err(dev, "Failed to request PWM device: %d\n", error); + return error; } /* @@ -117,13 +118,11 @@ static int pwm_beeper_probe(struct platform_device *pdev) INIT_WORK(&beeper->work, pwm_beeper_work); - beeper->input = input_allocate_device(); + beeper->input = devm_input_allocate_device(dev); if (!beeper->input) { - dev_err(&pdev->dev, "Failed to allocate input device\n"); - error = -ENOMEM; - goto err_pwm_free; + dev_err(dev, "Failed to allocate input device\n"); + return -ENOMEM; } - beeper->input->dev.parent = &pdev->dev; beeper->input->name = "pwm-beeper"; beeper->input->phys = "pwm/input0"; @@ -142,34 +141,12 @@ static int pwm_beeper_probe(struct platform_device *pdev) error = input_register_device(beeper->input); if (error) { - dev_err(&pdev->dev, "Failed to register input device: %d\n", error); - goto err_input_free; + dev_err(dev, "Failed to register input device: %d\n", error); + return error; } platform_set_drvdata(pdev, beeper); - return 0; - -err_input_free: - input_free_device(beeper->input); -err_pwm_free: - pwm_free(beeper->pwm); -err_free: - kfree(beeper); - - return error; -} - -static int pwm_beeper_remove(struct platform_device *pdev) -{ - struct pwm_beeper *beeper = platform_get_drvdata(pdev); - - input_unregister_device(beeper->input); - - pwm_free(beeper->pwm); - - kfree(beeper); - return 0; } @@ -205,7 +182,6 @@ MODULE_DEVICE_TABLE(of, pwm_beeper_match); static struct platform_driver pwm_beeper_driver = { .probe = pwm_beeper_probe, - .remove = pwm_beeper_remove, .driver = { .name = "pwm-beeper", .pm = &pwm_beeper_pm_ops, -- cgit From 48a55d7de79f95176f3ab372be66165a60be222f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 6 Jan 2017 09:54:14 -0800 Subject: Input: pwm-beeper - use input_set_capability() Instead of manipulating capability bits directly, let's use input_set_capability() API. Reviewed-by: Thierry Reding Tested-by: David Lechner Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index f8b163b0eb43..58a60b42c836 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -131,8 +131,8 @@ static int pwm_beeper_probe(struct platform_device *pdev) beeper->input->id.product = 0x0001; beeper->input->id.version = 0x0100; - beeper->input->evbit[0] = BIT(EV_SND); - beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); + input_set_capability(beeper->input, EV_SND, SND_TONE); + input_set_capability(beeper->input, EV_SND, SND_BELL); beeper->input->event = pwm_beeper_event; beeper->input->close = pwm_beeper_close; -- cgit From e9728f0dd7dc06fb0f0d18552ab9599005cd2ab7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 19 Jan 2017 11:13:37 -0800 Subject: Input: pwm-beeper - fix race when suspending Usually userspace sends SND_BELL and SND_TONE events, and by the time pwm_beeper_suspend() runs userpsace is already frozen, but theoretically in-kernel users may send these events too, and that may cause pwm_beeper_event() scheduling another work after we canceled it. Let's introduce a "suspended" flag and check it in pwm_beeper_event() to avoid this race. Reviewed-by: Thierry Reding Tested-by: David Lechner Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 58a60b42c836..9d7987d9ba1b 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -27,6 +27,7 @@ struct pwm_beeper { struct pwm_device *pwm; struct work_struct work; unsigned long period; + bool suspended; }; #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) @@ -73,7 +74,8 @@ static int pwm_beeper_event(struct input_dev *input, else beeper->period = HZ_TO_NANOSECONDS(value); - schedule_work(&beeper->work); + if (!beeper->suspended) + schedule_work(&beeper->work); return 0; } @@ -154,6 +156,15 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); + /* + * Spinlock is taken here is not to protect write to + * beeper->suspended, but to ensure that pwm_beeper_event + * does not re-submit work once flag is set. + */ + spin_lock_irq(&beeper->input->event_lock); + beeper->suspended = true; + spin_unlock_irq(&beeper->input->event_lock); + pwm_beeper_stop(beeper); return 0; @@ -163,8 +174,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - if (beeper->period) - __pwm_beeper_set(beeper); + spin_lock_irq(&beeper->input->event_lock); + beeper->suspended = false; + spin_unlock_irq(&beeper->input->event_lock); + + /* Let worker figure out if we should resume beeping */ + schedule_work(&beeper->work); return 0; } -- cgit From 62481881401246859d9bd06b7c7b9a391c78892c Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 6 Jan 2017 10:35:16 -0800 Subject: Input: pwm-beeper - suppress error message on probe defer This suppress printing an error message when pwm_get returns -EPROBE_DEFER. Otherwise you get a bunch of noise in the kernel log. Signed-off-by: David Lechner Reviewed-by: Thierry Reding Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 9d7987d9ba1b..069d594554bd 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -108,7 +108,9 @@ static int pwm_beeper_probe(struct platform_device *pdev) beeper->pwm = devm_pwm_get(dev, NULL); if (IS_ERR(beeper->pwm)) { error = PTR_ERR(beeper->pwm); - dev_err(dev, "Failed to request PWM device: %d\n", error); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to request PWM device: %d\n", + error); return error; } -- cgit From 9e54924432783bfb21e905e0bf7042556bcb4b90 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 15 Jan 2017 17:09:43 -0800 Subject: Input: pwm-beeper - add optional amplifier regulator This adds an optional regulator to the pwm-beeper device. This regulator acts as an amplifier. The amplifier is only enabled while beeping in order to reduce power consumption. Tested on LEGO MINDSTORMS EV3, which has a speaker connected to PWM through an amplifier. Signed-off-by: David Lechner Acked-by: Rob Herring Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 63 ++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 13 deletions(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 069d594554bd..ad9b231e8468 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -25,30 +26,59 @@ struct pwm_beeper { struct input_dev *input; struct pwm_device *pwm; + struct regulator *amplifier; struct work_struct work; unsigned long period; bool suspended; + bool amplifier_on; }; #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) -static void __pwm_beeper_set(struct pwm_beeper *beeper) +static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) { - unsigned long period = beeper->period; + int error; + + error = pwm_config(beeper->pwm, period / 2, period); + if (error) + return error; + + error = pwm_enable(beeper->pwm); + if (error) + return error; + + if (!beeper->amplifier_on) { + error = regulator_enable(beeper->amplifier); + if (error) { + pwm_disable(beeper->pwm); + return error; + } - if (period) { - pwm_config(beeper->pwm, period / 2, period); - pwm_enable(beeper->pwm); - } else - pwm_disable(beeper->pwm); + beeper->amplifier_on = true; + } + + return 0; +} + +static void pwm_beeper_off(struct pwm_beeper *beeper) +{ + if (beeper->amplifier_on) { + regulator_disable(beeper->amplifier); + beeper->amplifier_on = false; + } + + pwm_disable(beeper->pwm); } static void pwm_beeper_work(struct work_struct *work) { - struct pwm_beeper *beeper = - container_of(work, struct pwm_beeper, work); + struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work); + unsigned long period = READ_ONCE(beeper->period); - __pwm_beeper_set(beeper); + if (period) + pwm_beeper_on(beeper, period); + else + pwm_beeper_off(beeper); } static int pwm_beeper_event(struct input_dev *input, @@ -83,9 +113,7 @@ static int pwm_beeper_event(struct input_dev *input, static void pwm_beeper_stop(struct pwm_beeper *beeper) { cancel_work_sync(&beeper->work); - - if (beeper->period) - pwm_disable(beeper->pwm); + pwm_beeper_off(beeper); } static void pwm_beeper_close(struct input_dev *input) @@ -120,6 +148,15 @@ static int pwm_beeper_probe(struct platform_device *pdev) */ pwm_apply_args(beeper->pwm); + beeper->amplifier = devm_regulator_get(dev, "amp"); + if (IS_ERR(beeper->amplifier)) { + error = PTR_ERR(beeper->amplifier); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'amp' regulator: %d\n", + error); + return error; + } + INIT_WORK(&beeper->work, pwm_beeper_work); beeper->input = devm_input_allocate_device(dev); -- cgit From 2de8b4110c82c243530a68381a1c39a4fe05c14f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 19 Jan 2017 13:52:49 -0800 Subject: Input: pwm-beeper - switch to using "atomic" PWM API The "atomic" API allows us to configure PWM period and duty cycle and enable it in one call. Reviewed-by: Thierry Reding Tested-by: David Lechner Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/input/misc/pwm-beeper.c') diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index ad9b231e8468..e53801dbd560 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -37,13 +37,16 @@ struct pwm_beeper { static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) { + struct pwm_state state; int error; - error = pwm_config(beeper->pwm, period / 2, period); - if (error) - return error; + pwm_get_state(beeper->pwm, &state); - error = pwm_enable(beeper->pwm); + state.enabled = true; + state.period = period; + pwm_set_relative_duty_cycle(&state, 50, 100); + + error = pwm_apply_state(beeper->pwm, &state); if (error) return error; @@ -127,6 +130,7 @@ static int pwm_beeper_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct pwm_beeper *beeper; + struct pwm_state state; int error; beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL); @@ -142,11 +146,15 @@ static int pwm_beeper_probe(struct platform_device *pdev) return error; } - /* - * FIXME: pwm_apply_args() should be removed when switching to - * the atomic PWM API. - */ - pwm_apply_args(beeper->pwm); + /* Sync up PWM state and ensure it is off. */ + pwm_init_state(beeper->pwm, &state); + state.enabled = false; + error = pwm_apply_state(beeper->pwm, &state); + if (error) { + dev_err(dev, "failed to apply initial PWM state: %d\n", + error); + return error; + } beeper->amplifier = devm_regulator_get(dev, "amp"); if (IS_ERR(beeper->amplifier)) { -- cgit