linux/drivers/input/misc/pwm-beeper.c
<<
>>
Prefs
   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/property.h>
  23#include <linux/pwm.h>
  24#include <linux/slab.h>
  25#include <linux/workqueue.h>
  26
  27struct pwm_beeper {
  28        struct input_dev *input;
  29        struct pwm_device *pwm;
  30        struct regulator *amplifier;
  31        struct work_struct work;
  32        unsigned long period;
  33        unsigned int bell_frequency;
  34        bool suspended;
  35        bool amplifier_on;
  36};
  37
  38#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
  39
  40static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period)
  41{
  42        struct pwm_state state;
  43        int error;
  44
  45        pwm_get_state(beeper->pwm, &state);
  46
  47        state.enabled = true;
  48        state.period = period;
  49        pwm_set_relative_duty_cycle(&state, 50, 100);
  50
  51        error = pwm_apply_state(beeper->pwm, &state);
  52        if (error)
  53                return error;
  54
  55        if (!beeper->amplifier_on) {
  56                error = regulator_enable(beeper->amplifier);
  57                if (error) {
  58                        pwm_disable(beeper->pwm);
  59                        return error;
  60                }
  61
  62                beeper->amplifier_on = true;
  63        }
  64
  65        return 0;
  66}
  67
  68static void pwm_beeper_off(struct pwm_beeper *beeper)
  69{
  70        if (beeper->amplifier_on) {
  71                regulator_disable(beeper->amplifier);
  72                beeper->amplifier_on = false;
  73        }
  74
  75        pwm_disable(beeper->pwm);
  76}
  77
  78static void pwm_beeper_work(struct work_struct *work)
  79{
  80        struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work);
  81        unsigned long period = READ_ONCE(beeper->period);
  82
  83        if (period)
  84                pwm_beeper_on(beeper, period);
  85        else
  86                pwm_beeper_off(beeper);
  87}
  88
  89static int pwm_beeper_event(struct input_dev *input,
  90                            unsigned int type, unsigned int code, int value)
  91{
  92        struct pwm_beeper *beeper = input_get_drvdata(input);
  93
  94        if (type != EV_SND || value < 0)
  95                return -EINVAL;
  96
  97        switch (code) {
  98        case SND_BELL:
  99                value = value ? beeper->bell_frequency : 0;
 100                break;
 101        case SND_TONE:
 102                break;
 103        default:
 104                return -EINVAL;
 105        }
 106
 107        if (value == 0)
 108                beeper->period = 0;
 109        else
 110                beeper->period = HZ_TO_NANOSECONDS(value);
 111
 112        if (!beeper->suspended)
 113                schedule_work(&beeper->work);
 114
 115        return 0;
 116}
 117
 118static void pwm_beeper_stop(struct pwm_beeper *beeper)
 119{
 120        cancel_work_sync(&beeper->work);
 121        pwm_beeper_off(beeper);
 122}
 123
 124static void pwm_beeper_close(struct input_dev *input)
 125{
 126        struct pwm_beeper *beeper = input_get_drvdata(input);
 127
 128        pwm_beeper_stop(beeper);
 129}
 130
 131static int pwm_beeper_probe(struct platform_device *pdev)
 132{
 133        struct device *dev = &pdev->dev;
 134        struct pwm_beeper *beeper;
 135        struct pwm_state state;
 136        u32 bell_frequency;
 137        int error;
 138
 139        beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
 140        if (!beeper)
 141                return -ENOMEM;
 142
 143        beeper->pwm = devm_pwm_get(dev, NULL);
 144        if (IS_ERR(beeper->pwm)) {
 145                error = PTR_ERR(beeper->pwm);
 146                if (error != -EPROBE_DEFER)
 147                        dev_err(dev, "Failed to request PWM device: %d\n",
 148                                error);
 149                return error;
 150        }
 151
 152        /* Sync up PWM state and ensure it is off. */
 153        pwm_init_state(beeper->pwm, &state);
 154        state.enabled = false;
 155        error = pwm_apply_state(beeper->pwm, &state);
 156        if (error) {
 157                dev_err(dev, "failed to apply initial PWM state: %d\n",
 158                        error);
 159                return error;
 160        }
 161
 162        beeper->amplifier = devm_regulator_get(dev, "amp");
 163        if (IS_ERR(beeper->amplifier)) {
 164                error = PTR_ERR(beeper->amplifier);
 165                if (error != -EPROBE_DEFER)
 166                        dev_err(dev, "Failed to get 'amp' regulator: %d\n",
 167                                error);
 168                return error;
 169        }
 170
 171        INIT_WORK(&beeper->work, pwm_beeper_work);
 172
 173        error = device_property_read_u32(dev, "beeper-hz", &bell_frequency);
 174        if (error) {
 175                bell_frequency = 1000;
 176                dev_dbg(dev,
 177                        "failed to parse 'beeper-hz' property, using default: %uHz\n",
 178                        bell_frequency);
 179        }
 180
 181        beeper->bell_frequency = bell_frequency;
 182
 183        beeper->input = devm_input_allocate_device(dev);
 184        if (!beeper->input) {
 185                dev_err(dev, "Failed to allocate input device\n");
 186                return -ENOMEM;
 187        }
 188
 189        beeper->input->name = "pwm-beeper";
 190        beeper->input->phys = "pwm/input0";
 191        beeper->input->id.bustype = BUS_HOST;
 192        beeper->input->id.vendor = 0x001f;
 193        beeper->input->id.product = 0x0001;
 194        beeper->input->id.version = 0x0100;
 195
 196        input_set_capability(beeper->input, EV_SND, SND_TONE);
 197        input_set_capability(beeper->input, EV_SND, SND_BELL);
 198
 199        beeper->input->event = pwm_beeper_event;
 200        beeper->input->close = pwm_beeper_close;
 201
 202        input_set_drvdata(beeper->input, beeper);
 203
 204        error = input_register_device(beeper->input);
 205        if (error) {
 206                dev_err(dev, "Failed to register input device: %d\n", error);
 207                return error;
 208        }
 209
 210        platform_set_drvdata(pdev, beeper);
 211
 212        return 0;
 213}
 214
 215static int __maybe_unused pwm_beeper_suspend(struct device *dev)
 216{
 217        struct pwm_beeper *beeper = dev_get_drvdata(dev);
 218
 219        /*
 220         * Spinlock is taken here is not to protect write to
 221         * beeper->suspended, but to ensure that pwm_beeper_event
 222         * does not re-submit work once flag is set.
 223         */
 224        spin_lock_irq(&beeper->input->event_lock);
 225        beeper->suspended = true;
 226        spin_unlock_irq(&beeper->input->event_lock);
 227
 228        pwm_beeper_stop(beeper);
 229
 230        return 0;
 231}
 232
 233static int __maybe_unused pwm_beeper_resume(struct device *dev)
 234{
 235        struct pwm_beeper *beeper = dev_get_drvdata(dev);
 236
 237        spin_lock_irq(&beeper->input->event_lock);
 238        beeper->suspended = false;
 239        spin_unlock_irq(&beeper->input->event_lock);
 240
 241        /* Let worker figure out if we should resume beeping */
 242        schedule_work(&beeper->work);
 243
 244        return 0;
 245}
 246
 247static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
 248                         pwm_beeper_suspend, pwm_beeper_resume);
 249
 250#ifdef CONFIG_OF
 251static const struct of_device_id pwm_beeper_match[] = {
 252        { .compatible = "pwm-beeper", },
 253        { },
 254};
 255MODULE_DEVICE_TABLE(of, pwm_beeper_match);
 256#endif
 257
 258static struct platform_driver pwm_beeper_driver = {
 259        .probe  = pwm_beeper_probe,
 260        .driver = {
 261                .name   = "pwm-beeper",
 262                .pm     = &pwm_beeper_pm_ops,
 263                .of_match_table = of_match_ptr(pwm_beeper_match),
 264        },
 265};
 266module_platform_driver(pwm_beeper_driver);
 267
 268MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 269MODULE_DESCRIPTION("PWM beeper driver");
 270MODULE_LICENSE("GPL");
 271MODULE_ALIAS("platform:pwm-beeper");
 272