diff options
Diffstat (limited to 'drivers/sound/pwm-beeper.c')
-rw-r--r-- | drivers/sound/pwm-beeper.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/sound/pwm-beeper.c b/drivers/sound/pwm-beeper.c new file mode 100644 index 0000000000..94b27359c1 --- /dev/null +++ b/drivers/sound/pwm-beeper.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2021, Ahmad Fatoum + */ + +#include <common.h> +#include <regulator.h> +#include <sound.h> +#include <of.h> +#include <pwm.h> + +struct pwm_beeper { + struct pwm_device *pwm; + struct regulator *amplifier; + struct sound_card card; +}; + +#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) + +static int pwm_beeper_beep(struct sound_card *card, unsigned freq, unsigned duration) +{ + struct pwm_beeper *beeper = container_of(card, struct pwm_beeper, card); + struct pwm_state state; + int error = 0; + + if (!freq) { + regulator_disable(beeper->amplifier); + goto pwm_disable; + } + + pwm_get_state(beeper->pwm, &state); + + state.enabled = true; + state.period = HZ_TO_NANOSECONDS(freq); + pwm_set_relative_duty_cycle(&state, 50, 100); + + error = pwm_apply_state(beeper->pwm, &state); + if (error) + return error; + + error = regulator_enable(beeper->amplifier); + if (error) + goto pwm_disable; + + return 0; +pwm_disable: + pwm_disable(beeper->pwm); + return error; +} + +static int pwm_beeper_probe(struct device *dev) +{ + struct pwm_beeper *beeper; + struct sound_card *card; + struct pwm_state state; + u32 bell_frequency; + int error; + + beeper = xzalloc(sizeof(*beeper)); + dev->priv = beeper; + + beeper->pwm = of_pwm_request(dev->of_node, NULL); + if (IS_ERR(beeper->pwm)) + return dev_errp_probe(dev, beeper->pwm, "requesting PWM device\n"); + + /* 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 = regulator_get(dev, "amp"); + if (IS_ERR(beeper->amplifier)) + return dev_errp_probe(dev, beeper->amplifier, "getting 'amp' regulator\n"); + + error = of_property_read_u32(dev->of_node, "beeper-hz", + &bell_frequency); + if (error) { + bell_frequency = 1000; + dev_dbg(dev, "failed to parse 'beeper-hz' property, using default: %uHz\n", + bell_frequency); + } + + card = &beeper->card; + card->name = dev->of_node->full_name; + card->bell_frequency = bell_frequency; + card->beep = pwm_beeper_beep; + + return sound_card_register(card); +} + +static void pwm_beeper_suspend(struct device *dev) +{ + struct pwm_beeper *beeper = dev->priv; + + pwm_beeper_beep(&beeper->card, 0, 0); +} + +static const struct of_device_id pwm_beeper_match[] = { + { .compatible = "pwm-beeper", }, + { }, +}; +MODULE_DEVICE_TABLE(of, pwm_beeper_match); + +static struct driver pwm_beeper_driver = { + .name = "pwm-beeper", + .probe = pwm_beeper_probe, + .remove = pwm_beeper_suspend, + .of_compatible = pwm_beeper_match, +}; +device_platform_driver(pwm_beeper_driver); |