aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/pwm-beeper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc/pwm-beeper.c')
-rw-r--r--drivers/input/misc/pwm-beeper.c156
1 files changed, 94 insertions, 62 deletions
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
index 5f9655d49a65..e53801dbd560 100644
--- a/drivers/input/misc/pwm-beeper.c
+++ b/drivers/input/misc/pwm-beeper.c
@@ -14,6 +14,7 @@
14 */ 14 */
15 15
16#include <linux/input.h> 16#include <linux/input.h>
17#include <linux/regulator/consumer.h>
17#include <linux/module.h> 18#include <linux/module.h>
18#include <linux/kernel.h> 19#include <linux/kernel.h>
19#include <linux/of.h> 20#include <linux/of.h>
@@ -25,29 +26,62 @@
25struct pwm_beeper { 26struct pwm_beeper {
26 struct input_dev *input; 27 struct input_dev *input;
27 struct pwm_device *pwm; 28 struct pwm_device *pwm;
29 struct regulator *amplifier;
28 struct work_struct work; 30 struct work_struct work;
29 unsigned long period; 31 unsigned long period;
32 bool suspended;
33 bool amplifier_on;
30}; 34};
31 35
32#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) 36#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
33 37
34static void __pwm_beeper_set(struct pwm_beeper *beeper) 38static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period)
35{ 39{
36 unsigned long period = beeper->period; 40 struct pwm_state state;
41 int error;
42
43 pwm_get_state(beeper->pwm, &state);
44
45 state.enabled = true;
46 state.period = period;
47 pwm_set_relative_duty_cycle(&state, 50, 100);
48
49 error = pwm_apply_state(beeper->pwm, &state);
50 if (error)
51 return error;
52
53 if (!beeper->amplifier_on) {
54 error = regulator_enable(beeper->amplifier);
55 if (error) {
56 pwm_disable(beeper->pwm);
57 return error;
58 }
59
60 beeper->amplifier_on = true;
61 }
62
63 return 0;
64}
37 65
38 if (period) { 66static void pwm_beeper_off(struct pwm_beeper *beeper)
39 pwm_config(beeper->pwm, period / 2, period); 67{
40 pwm_enable(beeper->pwm); 68 if (beeper->amplifier_on) {
41 } else 69 regulator_disable(beeper->amplifier);
42 pwm_disable(beeper->pwm); 70 beeper->amplifier_on = false;
71 }
72
73 pwm_disable(beeper->pwm);
43} 74}
44 75
45static void pwm_beeper_work(struct work_struct *work) 76static void pwm_beeper_work(struct work_struct *work)
46{ 77{
47 struct pwm_beeper *beeper = 78 struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work);
48 container_of(work, struct pwm_beeper, work); 79 unsigned long period = READ_ONCE(beeper->period);
49 80
50 __pwm_beeper_set(beeper); 81 if (period)
82 pwm_beeper_on(beeper, period);
83 else
84 pwm_beeper_off(beeper);
51} 85}
52 86
53static int pwm_beeper_event(struct input_dev *input, 87static int pwm_beeper_event(struct input_dev *input,
@@ -73,7 +107,8 @@ static int pwm_beeper_event(struct input_dev *input,
73 else 107 else
74 beeper->period = HZ_TO_NANOSECONDS(value); 108 beeper->period = HZ_TO_NANOSECONDS(value);
75 109
76 schedule_work(&beeper->work); 110 if (!beeper->suspended)
111 schedule_work(&beeper->work);
77 112
78 return 0; 113 return 0;
79} 114}
@@ -81,9 +116,7 @@ static int pwm_beeper_event(struct input_dev *input,
81static void pwm_beeper_stop(struct pwm_beeper *beeper) 116static void pwm_beeper_stop(struct pwm_beeper *beeper)
82{ 117{
83 cancel_work_sync(&beeper->work); 118 cancel_work_sync(&beeper->work);
84 119 pwm_beeper_off(beeper);
85 if (beeper->period)
86 pwm_disable(beeper->pwm);
87} 120}
88 121
89static void pwm_beeper_close(struct input_dev *input) 122static void pwm_beeper_close(struct input_dev *input)
@@ -95,41 +128,50 @@ static void pwm_beeper_close(struct input_dev *input)
95 128
96static int pwm_beeper_probe(struct platform_device *pdev) 129static int pwm_beeper_probe(struct platform_device *pdev)
97{ 130{
98 unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); 131 struct device *dev = &pdev->dev;
99 struct pwm_beeper *beeper; 132 struct pwm_beeper *beeper;
133 struct pwm_state state;
100 int error; 134 int error;
101 135
102 beeper = kzalloc(sizeof(*beeper), GFP_KERNEL); 136 beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
103 if (!beeper) 137 if (!beeper)
104 return -ENOMEM; 138 return -ENOMEM;
105 139
106 beeper->pwm = pwm_get(&pdev->dev, NULL); 140 beeper->pwm = devm_pwm_get(dev, NULL);
107 if (IS_ERR(beeper->pwm)) { 141 if (IS_ERR(beeper->pwm)) {
108 dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n"); 142 error = PTR_ERR(beeper->pwm);
109 beeper->pwm = pwm_request(pwm_id, "pwm beeper"); 143 if (error != -EPROBE_DEFER)
144 dev_err(dev, "Failed to request PWM device: %d\n",
145 error);
146 return error;
110 } 147 }
111 148
112 if (IS_ERR(beeper->pwm)) { 149 /* Sync up PWM state and ensure it is off. */
113 error = PTR_ERR(beeper->pwm); 150 pwm_init_state(beeper->pwm, &state);
114 dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); 151 state.enabled = false;
115 goto err_free; 152 error = pwm_apply_state(beeper->pwm, &state);
153 if (error) {
154 dev_err(dev, "failed to apply initial PWM state: %d\n",
155 error);
156 return error;
116 } 157 }
117 158
118 /* 159 beeper->amplifier = devm_regulator_get(dev, "amp");
119 * FIXME: pwm_apply_args() should be removed when switching to 160 if (IS_ERR(beeper->amplifier)) {
120 * the atomic PWM API. 161 error = PTR_ERR(beeper->amplifier);
121 */ 162 if (error != -EPROBE_DEFER)
122 pwm_apply_args(beeper->pwm); 163 dev_err(dev, "Failed to get 'amp' regulator: %d\n",
164 error);
165 return error;
166 }
123 167
124 INIT_WORK(&beeper->work, pwm_beeper_work); 168 INIT_WORK(&beeper->work, pwm_beeper_work);
125 169
126 beeper->input = input_allocate_device(); 170 beeper->input = devm_input_allocate_device(dev);
127 if (!beeper->input) { 171 if (!beeper->input) {
128 dev_err(&pdev->dev, "Failed to allocate input device\n"); 172 dev_err(dev, "Failed to allocate input device\n");
129 error = -ENOMEM; 173 return -ENOMEM;
130 goto err_pwm_free;
131 } 174 }
132 beeper->input->dev.parent = &pdev->dev;
133 175
134 beeper->input->name = "pwm-beeper"; 176 beeper->input->name = "pwm-beeper";
135 beeper->input->phys = "pwm/input0"; 177 beeper->input->phys = "pwm/input0";
@@ -138,8 +180,8 @@ static int pwm_beeper_probe(struct platform_device *pdev)
138 beeper->input->id.product = 0x0001; 180 beeper->input->id.product = 0x0001;
139 beeper->input->id.version = 0x0100; 181 beeper->input->id.version = 0x0100;
140 182
141 beeper->input->evbit[0] = BIT(EV_SND); 183 input_set_capability(beeper->input, EV_SND, SND_TONE);
142 beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); 184 input_set_capability(beeper->input, EV_SND, SND_BELL);
143 185
144 beeper->input->event = pwm_beeper_event; 186 beeper->input->event = pwm_beeper_event;
145 beeper->input->close = pwm_beeper_close; 187 beeper->input->close = pwm_beeper_close;
@@ -148,41 +190,28 @@ static int pwm_beeper_probe(struct platform_device *pdev)
148 190
149 error = input_register_device(beeper->input); 191 error = input_register_device(beeper->input);
150 if (error) { 192 if (error) {
151 dev_err(&pdev->dev, "Failed to register input device: %d\n", error); 193 dev_err(dev, "Failed to register input device: %d\n", error);
152 goto err_input_free; 194 return error;
153 } 195 }
154 196
155 platform_set_drvdata(pdev, beeper); 197 platform_set_drvdata(pdev, beeper);
156 198
157 return 0; 199 return 0;
158
159err_input_free:
160 input_free_device(beeper->input);
161err_pwm_free:
162 pwm_free(beeper->pwm);
163err_free:
164 kfree(beeper);
165
166 return error;
167}
168
169static int pwm_beeper_remove(struct platform_device *pdev)
170{
171 struct pwm_beeper *beeper = platform_get_drvdata(pdev);
172
173 input_unregister_device(beeper->input);
174
175 pwm_free(beeper->pwm);
176
177 kfree(beeper);
178
179 return 0;
180} 200}
181 201
182static int __maybe_unused pwm_beeper_suspend(struct device *dev) 202static int __maybe_unused pwm_beeper_suspend(struct device *dev)
183{ 203{
184 struct pwm_beeper *beeper = dev_get_drvdata(dev); 204 struct pwm_beeper *beeper = dev_get_drvdata(dev);
185 205
206 /*
207 * Spinlock is taken here is not to protect write to
208 * beeper->suspended, but to ensure that pwm_beeper_event
209 * does not re-submit work once flag is set.
210 */
211 spin_lock_irq(&beeper->input->event_lock);
212 beeper->suspended = true;
213 spin_unlock_irq(&beeper->input->event_lock);
214
186 pwm_beeper_stop(beeper); 215 pwm_beeper_stop(beeper);
187 216
188 return 0; 217 return 0;
@@ -192,8 +221,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
192{ 221{
193 struct pwm_beeper *beeper = dev_get_drvdata(dev); 222 struct pwm_beeper *beeper = dev_get_drvdata(dev);
194 223
195 if (beeper->period) 224 spin_lock_irq(&beeper->input->event_lock);
196 __pwm_beeper_set(beeper); 225 beeper->suspended = false;
226 spin_unlock_irq(&beeper->input->event_lock);
227
228 /* Let worker figure out if we should resume beeping */
229 schedule_work(&beeper->work);
197 230
198 return 0; 231 return 0;
199} 232}
@@ -211,7 +244,6 @@ MODULE_DEVICE_TABLE(of, pwm_beeper_match);
211 244
212static struct platform_driver pwm_beeper_driver = { 245static struct platform_driver pwm_beeper_driver = {
213 .probe = pwm_beeper_probe, 246 .probe = pwm_beeper_probe,
214 .remove = pwm_beeper_remove,
215 .driver = { 247 .driver = {
216 .name = "pwm-beeper", 248 .name = "pwm-beeper",
217 .pm = &pwm_beeper_pm_ops, 249 .pm = &pwm_beeper_pm_ops,