2010-07-14 11:25:21 +04:00
/*
* Copyright ( C ) 2010 , Lars - Peter Clausen < lars @ metafoo . de >
* PWM beeper driver
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/input.h>
2017-01-16 04:09:43 +03:00
# include <linux/regulator/consumer.h>
2010-07-14 11:25:21 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
2013-10-06 11:56:09 +04:00
# include <linux/of.h>
2010-07-14 11:25:21 +04:00
# include <linux/platform_device.h>
2017-03-07 21:25:27 +03:00
# include <linux/property.h>
2010-07-14 11:25:21 +04:00
# include <linux/pwm.h>
# include <linux/slab.h>
2016-05-28 02:36:36 +03:00
# include <linux/workqueue.h>
2010-07-14 11:25:21 +04:00
struct pwm_beeper {
struct input_dev * input ;
struct pwm_device * pwm ;
2017-01-16 04:09:43 +03:00
struct regulator * amplifier ;
2016-05-28 02:36:36 +03:00
struct work_struct work ;
2010-07-14 11:25:21 +04:00
unsigned long period ;
2017-03-07 21:25:27 +03:00
unsigned int bell_frequency ;
2017-01-19 22:13:37 +03:00
bool suspended ;
2017-01-16 04:09:43 +03:00
bool amplifier_on ;
2010-07-14 11:25:21 +04:00
} ;
# define HZ_TO_NANOSECONDS(x) (1000000000UL / (x))
2017-01-16 04:09:43 +03:00
static int pwm_beeper_on ( struct pwm_beeper * beeper , unsigned long period )
2016-05-28 02:36:36 +03:00
{
2017-01-20 00:52:49 +03:00
struct pwm_state state ;
2017-01-16 04:09:43 +03:00
int error ;
2017-01-20 00:52:49 +03:00
pwm_get_state ( beeper - > pwm , & state ) ;
state . enabled = true ;
state . period = period ;
pwm_set_relative_duty_cycle ( & state , 50 , 100 ) ;
2017-01-16 04:09:43 +03:00
2017-01-20 00:52:49 +03:00
error = pwm_apply_state ( beeper - > pwm , & state ) ;
2017-01-16 04:09:43 +03:00
if ( error )
return error ;
if ( ! beeper - > amplifier_on ) {
error = regulator_enable ( beeper - > amplifier ) ;
if ( error ) {
pwm_disable ( beeper - > pwm ) ;
return error ;
}
2016-05-28 02:36:36 +03:00
2017-01-16 04:09:43 +03:00
beeper - > amplifier_on = true ;
}
return 0 ;
}
static void pwm_beeper_off ( struct pwm_beeper * beeper )
{
if ( beeper - > amplifier_on ) {
regulator_disable ( beeper - > amplifier ) ;
beeper - > amplifier_on = false ;
}
pwm_disable ( beeper - > pwm ) ;
2016-05-28 02:36:36 +03:00
}
static void pwm_beeper_work ( struct work_struct * work )
{
2017-01-16 04:09:43 +03:00
struct pwm_beeper * beeper = container_of ( work , struct pwm_beeper , work ) ;
unsigned long period = READ_ONCE ( beeper - > period ) ;
2016-05-28 02:36:36 +03:00
2017-01-16 04:09:43 +03:00
if ( period )
pwm_beeper_on ( beeper , period ) ;
else
pwm_beeper_off ( beeper ) ;
2016-05-28 02:36:36 +03:00
}
2010-07-14 11:25:21 +04:00
static int pwm_beeper_event ( struct input_dev * input ,
unsigned int type , unsigned int code , int value )
{
struct pwm_beeper * beeper = input_get_drvdata ( input ) ;
if ( type ! = EV_SND | | value < 0 )
return - EINVAL ;
switch ( code ) {
case SND_BELL :
2017-03-07 21:25:27 +03:00
value = value ? beeper - > bell_frequency : 0 ;
2010-07-14 11:25:21 +04:00
break ;
case SND_TONE :
break ;
default :
return - EINVAL ;
}
2016-05-28 02:36:36 +03:00
if ( value = = 0 )
beeper - > period = 0 ;
else
beeper - > period = HZ_TO_NANOSECONDS ( value ) ;
2017-01-19 22:13:37 +03:00
if ( ! beeper - > suspended )
schedule_work ( & beeper - > work ) ;
2010-07-14 11:25:21 +04:00
return 0 ;
}
2016-05-28 02:36:36 +03:00
static void pwm_beeper_stop ( struct pwm_beeper * beeper )
{
cancel_work_sync ( & beeper - > work ) ;
2017-01-16 04:09:43 +03:00
pwm_beeper_off ( beeper ) ;
2016-05-28 02:36:36 +03:00
}
static void pwm_beeper_close ( struct input_dev * input )
{
struct pwm_beeper * beeper = input_get_drvdata ( input ) ;
pwm_beeper_stop ( beeper ) ;
}
2012-11-24 09:38:25 +04:00
static int pwm_beeper_probe ( struct platform_device * pdev )
2010-07-14 11:25:21 +04:00
{
2017-01-06 20:50:14 +03:00
struct device * dev = & pdev - > dev ;
2010-07-14 11:25:21 +04:00
struct pwm_beeper * beeper ;
2017-01-20 00:52:49 +03:00
struct pwm_state state ;
2017-03-07 21:25:27 +03:00
u32 bell_frequency ;
2010-07-14 11:25:21 +04:00
int error ;
2017-01-06 20:50:14 +03:00
beeper = devm_kzalloc ( dev , sizeof ( * beeper ) , GFP_KERNEL ) ;
2010-07-14 11:25:21 +04:00
if ( ! beeper )
return - ENOMEM ;
2017-01-06 20:50:14 +03:00
beeper - > pwm = devm_pwm_get ( dev , NULL ) ;
2010-07-14 11:25:21 +04:00
if ( IS_ERR ( beeper - > pwm ) ) {
error = PTR_ERR ( beeper - > pwm ) ;
2017-01-06 21:35:16 +03:00
if ( error ! = - EPROBE_DEFER )
dev_err ( dev , " Failed to request PWM device: %d \n " ,
error ) ;
2017-01-06 20:50:14 +03:00
return error ;
2010-07-14 11:25:21 +04:00
}
2017-01-20 00:52:49 +03:00
/* 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 ;
}
2016-04-14 22:17:34 +03:00
2017-01-16 04:09:43 +03:00
beeper - > amplifier = devm_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 ;
}
2016-05-28 02:36:36 +03:00
INIT_WORK ( & beeper - > work , pwm_beeper_work ) ;
2017-03-07 21:25:27 +03:00
error = device_property_read_u32 ( dev , " beeper-hz " , & bell_frequency ) ;
if ( error ) {
bell_frequency = 1000 ;
dev_dbg ( dev ,
" failed to parse 'beeper-hz' property, using default: %uHz \n " ,
bell_frequency ) ;
}
beeper - > bell_frequency = bell_frequency ;
2017-01-06 20:50:14 +03:00
beeper - > input = devm_input_allocate_device ( dev ) ;
2010-07-14 11:25:21 +04:00
if ( ! beeper - > input ) {
2017-01-06 20:50:14 +03:00
dev_err ( dev , " Failed to allocate input device \n " ) ;
return - ENOMEM ;
2010-07-14 11:25:21 +04:00
}
beeper - > input - > name = " pwm-beeper " ;
beeper - > input - > phys = " pwm/input0 " ;
beeper - > input - > id . bustype = BUS_HOST ;
beeper - > input - > id . vendor = 0x001f ;
beeper - > input - > id . product = 0x0001 ;
beeper - > input - > id . version = 0x0100 ;
2017-01-06 20:54:14 +03:00
input_set_capability ( beeper - > input , EV_SND , SND_TONE ) ;
input_set_capability ( beeper - > input , EV_SND , SND_BELL ) ;
2010-07-14 11:25:21 +04:00
beeper - > input - > event = pwm_beeper_event ;
2016-05-28 02:36:36 +03:00
beeper - > input - > close = pwm_beeper_close ;
2010-07-14 11:25:21 +04:00
input_set_drvdata ( beeper - > input , beeper ) ;
error = input_register_device ( beeper - > input ) ;
if ( error ) {
2017-01-06 20:50:14 +03:00
dev_err ( dev , " Failed to register input device: %d \n " , error ) ;
return error ;
2010-07-14 11:25:21 +04:00
}
platform_set_drvdata ( pdev , beeper ) ;
return 0 ;
}
2014-11-02 10:02:46 +03:00
static int __maybe_unused pwm_beeper_suspend ( struct device * dev )
2010-07-14 11:25:21 +04:00
{
struct pwm_beeper * beeper = dev_get_drvdata ( dev ) ;
2017-01-19 22:13:37 +03:00
/*
* Spinlock is taken here is not to protect write to
* beeper - > suspended , but to ensure that pwm_beeper_event
* does not re - submit work once flag is set .
*/
spin_lock_irq ( & beeper - > input - > event_lock ) ;
beeper - > suspended = true ;
spin_unlock_irq ( & beeper - > input - > event_lock ) ;
2016-05-28 02:36:36 +03:00
pwm_beeper_stop ( beeper ) ;
2010-07-14 11:25:21 +04:00
return 0 ;
}
2014-11-02 10:02:46 +03:00
static int __maybe_unused pwm_beeper_resume ( struct device * dev )
2010-07-14 11:25:21 +04:00
{
struct pwm_beeper * beeper = dev_get_drvdata ( dev ) ;
2017-01-19 22:13:37 +03:00
spin_lock_irq ( & beeper - > input - > event_lock ) ;
beeper - > suspended = false ;
spin_unlock_irq ( & beeper - > input - > event_lock ) ;
/* Let worker figure out if we should resume beeping */
schedule_work ( & beeper - > work ) ;
2010-07-14 11:25:21 +04:00
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( pwm_beeper_pm_ops ,
pwm_beeper_suspend , pwm_beeper_resume ) ;
2012-11-03 23:05:52 +04:00
# ifdef CONFIG_OF
static const struct of_device_id pwm_beeper_match [ ] = {
{ . compatible = " pwm-beeper " , } ,
{ } ,
} ;
2015-09-03 20:49:46 +03:00
MODULE_DEVICE_TABLE ( of , pwm_beeper_match ) ;
2012-11-03 23:05:52 +04:00
# endif
2010-07-14 11:25:21 +04:00
static struct platform_driver pwm_beeper_driver = {
. probe = pwm_beeper_probe ,
. driver = {
. name = " pwm-beeper " ,
2015-02-20 03:22:31 +03:00
. pm = & pwm_beeper_pm_ops ,
2012-11-03 23:05:52 +04:00
. of_match_table = of_match_ptr ( pwm_beeper_match ) ,
2010-07-14 11:25:21 +04:00
} ,
} ;
2011-11-29 23:08:40 +04:00
module_platform_driver ( pwm_beeper_driver ) ;
2010-07-14 11:25:21 +04:00
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_DESCRIPTION ( " PWM beeper driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:pwm-beeper " ) ;