162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic GPIO beeper driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013-2014 Alexander Shiyan <shc_work@mail.ru> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/input.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/workqueue.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define BEEPER_MODNAME "gpio-beeper" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct gpio_beeper { 1862306a36Sopenharmony_ci struct work_struct work; 1962306a36Sopenharmony_ci struct gpio_desc *desc; 2062306a36Sopenharmony_ci bool beeping; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void gpio_beeper_toggle(struct gpio_beeper *beep, bool on) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci gpiod_set_value_cansleep(beep->desc, on); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void gpio_beeper_work(struct work_struct *work) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct gpio_beeper *beep = container_of(work, struct gpio_beeper, work); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci gpio_beeper_toggle(beep, beep->beeping); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int gpio_beeper_event(struct input_dev *dev, unsigned int type, 3662306a36Sopenharmony_ci unsigned int code, int value) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct gpio_beeper *beep = input_get_drvdata(dev); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (type != EV_SND || code != SND_BELL) 4162306a36Sopenharmony_ci return -ENOTSUPP; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (value < 0) 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci beep->beeping = value; 4762306a36Sopenharmony_ci /* Schedule work to actually turn the beeper on or off */ 4862306a36Sopenharmony_ci schedule_work(&beep->work); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void gpio_beeper_close(struct input_dev *input) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct gpio_beeper *beep = input_get_drvdata(input); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci cancel_work_sync(&beep->work); 5862306a36Sopenharmony_ci gpio_beeper_toggle(beep, false); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int gpio_beeper_probe(struct platform_device *pdev) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct gpio_beeper *beep; 6462306a36Sopenharmony_ci struct input_dev *input; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci beep = devm_kzalloc(&pdev->dev, sizeof(*beep), GFP_KERNEL); 6762306a36Sopenharmony_ci if (!beep) 6862306a36Sopenharmony_ci return -ENOMEM; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci beep->desc = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW); 7162306a36Sopenharmony_ci if (IS_ERR(beep->desc)) 7262306a36Sopenharmony_ci return PTR_ERR(beep->desc); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci input = devm_input_allocate_device(&pdev->dev); 7562306a36Sopenharmony_ci if (!input) 7662306a36Sopenharmony_ci return -ENOMEM; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci INIT_WORK(&beep->work, gpio_beeper_work); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci input->name = pdev->name; 8162306a36Sopenharmony_ci input->id.bustype = BUS_HOST; 8262306a36Sopenharmony_ci input->id.vendor = 0x0001; 8362306a36Sopenharmony_ci input->id.product = 0x0001; 8462306a36Sopenharmony_ci input->id.version = 0x0100; 8562306a36Sopenharmony_ci input->close = gpio_beeper_close; 8662306a36Sopenharmony_ci input->event = gpio_beeper_event; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci input_set_capability(input, EV_SND, SND_BELL); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci input_set_drvdata(input, beep); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return input_register_device(input); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#ifdef CONFIG_OF 9662306a36Sopenharmony_cistatic const struct of_device_id gpio_beeper_of_match[] = { 9762306a36Sopenharmony_ci { .compatible = BEEPER_MODNAME, }, 9862306a36Sopenharmony_ci { } 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_beeper_of_match); 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic struct platform_driver gpio_beeper_platform_driver = { 10462306a36Sopenharmony_ci .driver = { 10562306a36Sopenharmony_ci .name = BEEPER_MODNAME, 10662306a36Sopenharmony_ci .of_match_table = of_match_ptr(gpio_beeper_of_match), 10762306a36Sopenharmony_ci }, 10862306a36Sopenharmony_ci .probe = gpio_beeper_probe, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_cimodule_platform_driver(gpio_beeper_platform_driver); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 11362306a36Sopenharmony_ciMODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 11462306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic GPIO beeper driver"); 115