Linux 4.11-rc5
[platform/kernel/linux-starfive.git] / drivers / input / misc / pwm-beeper.c
1 /*
2  *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3  *  PWM beeper driver
4  *
5  *  This program is free software; you can redistribute it and/or modify it
6  *  under  the terms of the GNU General  Public License as published by the
7  *  Free Software Foundation;  either version 2 of the License, or (at your
8  *  option) any later version.
9  *
10  *  You should have received a copy of the GNU General Public License along
11  *  with this program; if not, write to the Free Software Foundation, Inc.,
12  *  675 Mass Ave, Cambridge, MA 02139, USA.
13  *
14  */
15
16 #include <linux/input.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/module.h>
19 #include <linux/kernel.h>
20 #include <linux/of.h>
21 #include <linux/platform_device.h>
22 #include <linux/pwm.h>
23 #include <linux/slab.h>
24 #include <linux/workqueue.h>
25
26 struct pwm_beeper {
27         struct input_dev *input;
28         struct pwm_device *pwm;
29         struct regulator *amplifier;
30         struct work_struct work;
31         unsigned long period;
32         bool suspended;
33         bool amplifier_on;
34 };
35
36 #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
37
38 static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period)
39 {
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 }
65
66 static void pwm_beeper_off(struct pwm_beeper *beeper)
67 {
68         if (beeper->amplifier_on) {
69                 regulator_disable(beeper->amplifier);
70                 beeper->amplifier_on = false;
71         }
72
73         pwm_disable(beeper->pwm);
74 }
75
76 static void pwm_beeper_work(struct work_struct *work)
77 {
78         struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work);
79         unsigned long period = READ_ONCE(beeper->period);
80
81         if (period)
82                 pwm_beeper_on(beeper, period);
83         else
84                 pwm_beeper_off(beeper);
85 }
86
87 static int pwm_beeper_event(struct input_dev *input,
88                             unsigned int type, unsigned int code, int value)
89 {
90         struct pwm_beeper *beeper = input_get_drvdata(input);
91
92         if (type != EV_SND || value < 0)
93                 return -EINVAL;
94
95         switch (code) {
96         case SND_BELL:
97                 value = value ? 1000 : 0;
98                 break;
99         case SND_TONE:
100                 break;
101         default:
102                 return -EINVAL;
103         }
104
105         if (value == 0)
106                 beeper->period = 0;
107         else
108                 beeper->period = HZ_TO_NANOSECONDS(value);
109
110         if (!beeper->suspended)
111                 schedule_work(&beeper->work);
112
113         return 0;
114 }
115
116 static void pwm_beeper_stop(struct pwm_beeper *beeper)
117 {
118         cancel_work_sync(&beeper->work);
119         pwm_beeper_off(beeper);
120 }
121
122 static void pwm_beeper_close(struct input_dev *input)
123 {
124         struct pwm_beeper *beeper = input_get_drvdata(input);
125
126         pwm_beeper_stop(beeper);
127 }
128
129 static int pwm_beeper_probe(struct platform_device *pdev)
130 {
131         struct device *dev = &pdev->dev;
132         struct pwm_beeper *beeper;
133         struct pwm_state state;
134         int error;
135
136         beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
137         if (!beeper)
138                 return -ENOMEM;
139
140         beeper->pwm = devm_pwm_get(dev, NULL);
141         if (IS_ERR(beeper->pwm)) {
142                 error = PTR_ERR(beeper->pwm);
143                 if (error != -EPROBE_DEFER)
144                         dev_err(dev, "Failed to request PWM device: %d\n",
145                                 error);
146                 return error;
147         }
148
149         /* Sync up PWM state and ensure it is off. */
150         pwm_init_state(beeper->pwm, &state);
151         state.enabled = false;
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;
157         }
158
159         beeper->amplifier = devm_regulator_get(dev, "amp");
160         if (IS_ERR(beeper->amplifier)) {
161                 error = PTR_ERR(beeper->amplifier);
162                 if (error != -EPROBE_DEFER)
163                         dev_err(dev, "Failed to get 'amp' regulator: %d\n",
164                                 error);
165                 return error;
166         }
167
168         INIT_WORK(&beeper->work, pwm_beeper_work);
169
170         beeper->input = devm_input_allocate_device(dev);
171         if (!beeper->input) {
172                 dev_err(dev, "Failed to allocate input device\n");
173                 return -ENOMEM;
174         }
175
176         beeper->input->name = "pwm-beeper";
177         beeper->input->phys = "pwm/input0";
178         beeper->input->id.bustype = BUS_HOST;
179         beeper->input->id.vendor = 0x001f;
180         beeper->input->id.product = 0x0001;
181         beeper->input->id.version = 0x0100;
182
183         input_set_capability(beeper->input, EV_SND, SND_TONE);
184         input_set_capability(beeper->input, EV_SND, SND_BELL);
185
186         beeper->input->event = pwm_beeper_event;
187         beeper->input->close = pwm_beeper_close;
188
189         input_set_drvdata(beeper->input, beeper);
190
191         error = input_register_device(beeper->input);
192         if (error) {
193                 dev_err(dev, "Failed to register input device: %d\n", error);
194                 return error;
195         }
196
197         platform_set_drvdata(pdev, beeper);
198
199         return 0;
200 }
201
202 static int __maybe_unused pwm_beeper_suspend(struct device *dev)
203 {
204         struct pwm_beeper *beeper = dev_get_drvdata(dev);
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
215         pwm_beeper_stop(beeper);
216
217         return 0;
218 }
219
220 static int __maybe_unused pwm_beeper_resume(struct device *dev)
221 {
222         struct pwm_beeper *beeper = dev_get_drvdata(dev);
223
224         spin_lock_irq(&beeper->input->event_lock);
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);
230
231         return 0;
232 }
233
234 static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
235                          pwm_beeper_suspend, pwm_beeper_resume);
236
237 #ifdef CONFIG_OF
238 static const struct of_device_id pwm_beeper_match[] = {
239         { .compatible = "pwm-beeper", },
240         { },
241 };
242 MODULE_DEVICE_TABLE(of, pwm_beeper_match);
243 #endif
244
245 static struct platform_driver pwm_beeper_driver = {
246         .probe  = pwm_beeper_probe,
247         .driver = {
248                 .name   = "pwm-beeper",
249                 .pm     = &pwm_beeper_pm_ops,
250                 .of_match_table = of_match_ptr(pwm_beeper_match),
251         },
252 };
253 module_platform_driver(pwm_beeper_driver);
254
255 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
256 MODULE_DESCRIPTION("PWM beeper driver");
257 MODULE_LICENSE("GPL");
258 MODULE_ALIAS("platform:pwm-beeper");