// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010, Lars-Peter Clausen * Copyright (C) 2021, Ahmad Fatoum */ #include #include #include #include #include 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.p_enable = true; state.period_ns = 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_d *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->device_node, NULL); if (IS_ERR(beeper->pwm)) { error = PTR_ERR(beeper->pwm); if (error != -EPROBE_DEFER) dev_err(dev, "Failed to request PWM device: %d\n", error); return error; } /* Sync up PWM state and ensure it is off. */ pwm_init_state(beeper->pwm, &state); state.p_enable = 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)) { error = PTR_ERR(beeper->amplifier); if (error != -EPROBE_DEFER) dev_err(dev, "Failed to get 'amp' regulator: %d\n", error); return error; } error = of_property_read_u32(dev->device_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->device_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_d *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", }, { }, }; static struct driver_d 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);