162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> 462306a36Sopenharmony_ci * PWM beeper driver 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/input.h> 862306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/property.h> 1462306a36Sopenharmony_ci#include <linux/pwm.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/workqueue.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct pwm_beeper { 1962306a36Sopenharmony_ci struct input_dev *input; 2062306a36Sopenharmony_ci struct pwm_device *pwm; 2162306a36Sopenharmony_ci struct regulator *amplifier; 2262306a36Sopenharmony_ci struct work_struct work; 2362306a36Sopenharmony_ci unsigned long period; 2462306a36Sopenharmony_ci unsigned int bell_frequency; 2562306a36Sopenharmony_ci bool suspended; 2662306a36Sopenharmony_ci bool amplifier_on; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct pwm_state state; 3462306a36Sopenharmony_ci int error; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci pwm_get_state(beeper->pwm, &state); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci state.enabled = true; 3962306a36Sopenharmony_ci state.period = period; 4062306a36Sopenharmony_ci pwm_set_relative_duty_cycle(&state, 50, 100); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci error = pwm_apply_state(beeper->pwm, &state); 4362306a36Sopenharmony_ci if (error) 4462306a36Sopenharmony_ci return error; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!beeper->amplifier_on) { 4762306a36Sopenharmony_ci error = regulator_enable(beeper->amplifier); 4862306a36Sopenharmony_ci if (error) { 4962306a36Sopenharmony_ci pwm_disable(beeper->pwm); 5062306a36Sopenharmony_ci return error; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci beeper->amplifier_on = true; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void pwm_beeper_off(struct pwm_beeper *beeper) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci if (beeper->amplifier_on) { 6262306a36Sopenharmony_ci regulator_disable(beeper->amplifier); 6362306a36Sopenharmony_ci beeper->amplifier_on = false; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci pwm_disable(beeper->pwm); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void pwm_beeper_work(struct work_struct *work) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work); 7262306a36Sopenharmony_ci unsigned long period = READ_ONCE(beeper->period); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (period) 7562306a36Sopenharmony_ci pwm_beeper_on(beeper, period); 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci pwm_beeper_off(beeper); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int pwm_beeper_event(struct input_dev *input, 8162306a36Sopenharmony_ci unsigned int type, unsigned int code, int value) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct pwm_beeper *beeper = input_get_drvdata(input); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (type != EV_SND || value < 0) 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci switch (code) { 8962306a36Sopenharmony_ci case SND_BELL: 9062306a36Sopenharmony_ci value = value ? beeper->bell_frequency : 0; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case SND_TONE: 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci default: 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (value == 0) 9962306a36Sopenharmony_ci beeper->period = 0; 10062306a36Sopenharmony_ci else 10162306a36Sopenharmony_ci beeper->period = HZ_TO_NANOSECONDS(value); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!beeper->suspended) 10462306a36Sopenharmony_ci schedule_work(&beeper->work); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void pwm_beeper_stop(struct pwm_beeper *beeper) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci cancel_work_sync(&beeper->work); 11262306a36Sopenharmony_ci pwm_beeper_off(beeper); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void pwm_beeper_close(struct input_dev *input) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct pwm_beeper *beeper = input_get_drvdata(input); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci pwm_beeper_stop(beeper); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int pwm_beeper_probe(struct platform_device *pdev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 12562306a36Sopenharmony_ci struct pwm_beeper *beeper; 12662306a36Sopenharmony_ci struct pwm_state state; 12762306a36Sopenharmony_ci u32 bell_frequency; 12862306a36Sopenharmony_ci int error; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL); 13162306a36Sopenharmony_ci if (!beeper) 13262306a36Sopenharmony_ci return -ENOMEM; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci beeper->pwm = devm_pwm_get(dev, NULL); 13562306a36Sopenharmony_ci if (IS_ERR(beeper->pwm)) 13662306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(beeper->pwm), "Failed to request PWM device\n"); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Sync up PWM state and ensure it is off. */ 13962306a36Sopenharmony_ci pwm_init_state(beeper->pwm, &state); 14062306a36Sopenharmony_ci state.enabled = false; 14162306a36Sopenharmony_ci error = pwm_apply_state(beeper->pwm, &state); 14262306a36Sopenharmony_ci if (error) { 14362306a36Sopenharmony_ci dev_err(dev, "failed to apply initial PWM state: %d\n", 14462306a36Sopenharmony_ci error); 14562306a36Sopenharmony_ci return error; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci beeper->amplifier = devm_regulator_get(dev, "amp"); 14962306a36Sopenharmony_ci if (IS_ERR(beeper->amplifier)) 15062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(beeper->amplifier), 15162306a36Sopenharmony_ci "Failed to get 'amp' regulator\n"); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci INIT_WORK(&beeper->work, pwm_beeper_work); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci error = device_property_read_u32(dev, "beeper-hz", &bell_frequency); 15662306a36Sopenharmony_ci if (error) { 15762306a36Sopenharmony_ci bell_frequency = 1000; 15862306a36Sopenharmony_ci dev_dbg(dev, 15962306a36Sopenharmony_ci "failed to parse 'beeper-hz' property, using default: %uHz\n", 16062306a36Sopenharmony_ci bell_frequency); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci beeper->bell_frequency = bell_frequency; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci beeper->input = devm_input_allocate_device(dev); 16662306a36Sopenharmony_ci if (!beeper->input) { 16762306a36Sopenharmony_ci dev_err(dev, "Failed to allocate input device\n"); 16862306a36Sopenharmony_ci return -ENOMEM; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci beeper->input->name = "pwm-beeper"; 17262306a36Sopenharmony_ci beeper->input->phys = "pwm/input0"; 17362306a36Sopenharmony_ci beeper->input->id.bustype = BUS_HOST; 17462306a36Sopenharmony_ci beeper->input->id.vendor = 0x001f; 17562306a36Sopenharmony_ci beeper->input->id.product = 0x0001; 17662306a36Sopenharmony_ci beeper->input->id.version = 0x0100; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci input_set_capability(beeper->input, EV_SND, SND_TONE); 17962306a36Sopenharmony_ci input_set_capability(beeper->input, EV_SND, SND_BELL); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci beeper->input->event = pwm_beeper_event; 18262306a36Sopenharmony_ci beeper->input->close = pwm_beeper_close; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci input_set_drvdata(beeper->input, beeper); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci error = input_register_device(beeper->input); 18762306a36Sopenharmony_ci if (error) { 18862306a36Sopenharmony_ci dev_err(dev, "Failed to register input device: %d\n", error); 18962306a36Sopenharmony_ci return error; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci platform_set_drvdata(pdev, beeper); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int pwm_beeper_suspend(struct device *dev) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct pwm_beeper *beeper = dev_get_drvdata(dev); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Spinlock is taken here is not to protect write to 20362306a36Sopenharmony_ci * beeper->suspended, but to ensure that pwm_beeper_event 20462306a36Sopenharmony_ci * does not re-submit work once flag is set. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci spin_lock_irq(&beeper->input->event_lock); 20762306a36Sopenharmony_ci beeper->suspended = true; 20862306a36Sopenharmony_ci spin_unlock_irq(&beeper->input->event_lock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci pwm_beeper_stop(beeper); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int pwm_beeper_resume(struct device *dev) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct pwm_beeper *beeper = dev_get_drvdata(dev); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci spin_lock_irq(&beeper->input->event_lock); 22062306a36Sopenharmony_ci beeper->suspended = false; 22162306a36Sopenharmony_ci spin_unlock_irq(&beeper->input->event_lock); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Let worker figure out if we should resume beeping */ 22462306a36Sopenharmony_ci schedule_work(&beeper->work); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, 23062306a36Sopenharmony_ci pwm_beeper_suspend, pwm_beeper_resume); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#ifdef CONFIG_OF 23362306a36Sopenharmony_cistatic const struct of_device_id pwm_beeper_match[] = { 23462306a36Sopenharmony_ci { .compatible = "pwm-beeper", }, 23562306a36Sopenharmony_ci { }, 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pwm_beeper_match); 23862306a36Sopenharmony_ci#endif 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic struct platform_driver pwm_beeper_driver = { 24162306a36Sopenharmony_ci .probe = pwm_beeper_probe, 24262306a36Sopenharmony_ci .driver = { 24362306a36Sopenharmony_ci .name = "pwm-beeper", 24462306a36Sopenharmony_ci .pm = pm_sleep_ptr(&pwm_beeper_pm_ops), 24562306a36Sopenharmony_ci .of_match_table = of_match_ptr(pwm_beeper_match), 24662306a36Sopenharmony_ci }, 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_cimodule_platform_driver(pwm_beeper_driver); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 25162306a36Sopenharmony_ciMODULE_DESCRIPTION("PWM beeper driver"); 25262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 25362306a36Sopenharmony_ciMODULE_ALIAS("platform:pwm-beeper"); 254