2019-04-29 23:56:09 -07:00
// SPDX-License-Identifier: GPL-2.0+
/*
* GPIO vibrator driver
*
* Copyright ( C ) 2019 Luca Weiss < luca @ z3ntu . xyz >
*
* Based on PWM vibrator driver :
* Copyright ( C ) 2017 Collabora Ltd .
*
* Based on previous work from :
* Copyright ( C ) 2012 Dmitry Torokhov < dmitry . torokhov @ gmail . com >
*
* Based on PWM beeper driver :
* Copyright ( C ) 2010 , Lars - Peter Clausen < lars @ metafoo . de >
*/
# include <linux/gpio/consumer.h>
# include <linux/input.h>
# include <linux/kernel.h>
# include <linux/module.h>
2023-07-17 09:03:47 -07:00
# include <linux/of.h>
2019-04-29 23:56:09 -07:00
# include <linux/platform_device.h>
# include <linux/property.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
struct gpio_vibrator {
struct input_dev * input ;
struct gpio_desc * gpio ;
struct regulator * vcc ;
struct work_struct play_work ;
bool running ;
bool vcc_on ;
} ;
static int gpio_vibrator_start ( struct gpio_vibrator * vibrator )
{
struct device * pdev = vibrator - > input - > dev . parent ;
int err ;
if ( ! vibrator - > vcc_on ) {
err = regulator_enable ( vibrator - > vcc ) ;
if ( err ) {
dev_err ( pdev , " failed to enable regulator: %d \n " , err ) ;
return err ;
}
vibrator - > vcc_on = true ;
}
gpiod_set_value_cansleep ( vibrator - > gpio , 1 ) ;
return 0 ;
}
static void gpio_vibrator_stop ( struct gpio_vibrator * vibrator )
{
gpiod_set_value_cansleep ( vibrator - > gpio , 0 ) ;
if ( vibrator - > vcc_on ) {
regulator_disable ( vibrator - > vcc ) ;
vibrator - > vcc_on = false ;
}
}
static void gpio_vibrator_play_work ( struct work_struct * work )
{
struct gpio_vibrator * vibrator =
container_of ( work , struct gpio_vibrator , play_work ) ;
if ( vibrator - > running )
gpio_vibrator_start ( vibrator ) ;
else
gpio_vibrator_stop ( vibrator ) ;
}
static int gpio_vibrator_play_effect ( struct input_dev * dev , void * data ,
struct ff_effect * effect )
{
struct gpio_vibrator * vibrator = input_get_drvdata ( dev ) ;
int level ;
level = effect - > u . rumble . strong_magnitude ;
if ( ! level )
level = effect - > u . rumble . weak_magnitude ;
vibrator - > running = level ;
schedule_work ( & vibrator - > play_work ) ;
return 0 ;
}
static void gpio_vibrator_close ( struct input_dev * input )
{
struct gpio_vibrator * vibrator = input_get_drvdata ( input ) ;
cancel_work_sync ( & vibrator - > play_work ) ;
gpio_vibrator_stop ( vibrator ) ;
vibrator - > running = false ;
}
static int gpio_vibrator_probe ( struct platform_device * pdev )
{
struct gpio_vibrator * vibrator ;
int err ;
vibrator = devm_kzalloc ( & pdev - > dev , sizeof ( * vibrator ) , GFP_KERNEL ) ;
if ( ! vibrator )
return - ENOMEM ;
vibrator - > input = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! vibrator - > input )
return - ENOMEM ;
vibrator - > vcc = devm_regulator_get ( & pdev - > dev , " vcc " ) ;
2023-06-25 18:27:55 +02:00
if ( IS_ERR ( vibrator - > vcc ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( vibrator - > vcc ) ,
" Failed to request regulator \n " ) ;
2019-04-29 23:56:09 -07:00
vibrator - > gpio = devm_gpiod_get ( & pdev - > dev , " enable " , GPIOD_OUT_LOW ) ;
2023-06-25 18:27:55 +02:00
if ( IS_ERR ( vibrator - > gpio ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( vibrator - > gpio ) ,
" Failed to request main gpio \n " ) ;
2019-04-29 23:56:09 -07:00
INIT_WORK ( & vibrator - > play_work , gpio_vibrator_play_work ) ;
vibrator - > input - > name = " gpio-vibrator " ;
vibrator - > input - > id . bustype = BUS_HOST ;
vibrator - > input - > close = gpio_vibrator_close ;
input_set_drvdata ( vibrator - > input , vibrator ) ;
input_set_capability ( vibrator - > input , EV_FF , FF_RUMBLE ) ;
err = input_ff_create_memless ( vibrator - > input , NULL ,
gpio_vibrator_play_effect ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Couldn't create FF dev: %d \n " , err ) ;
return err ;
}
err = input_register_device ( vibrator - > input ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Couldn't register input dev: %d \n " , err ) ;
return err ;
}
platform_set_drvdata ( pdev , vibrator ) ;
return 0 ;
}
2023-01-02 18:17:40 +00:00
static int gpio_vibrator_suspend ( struct device * dev )
2019-04-29 23:56:09 -07:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct gpio_vibrator * vibrator = platform_get_drvdata ( pdev ) ;
cancel_work_sync ( & vibrator - > play_work ) ;
if ( vibrator - > running )
gpio_vibrator_stop ( vibrator ) ;
return 0 ;
}
2023-01-02 18:17:40 +00:00
static int gpio_vibrator_resume ( struct device * dev )
2019-04-29 23:56:09 -07:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct gpio_vibrator * vibrator = platform_get_drvdata ( pdev ) ;
if ( vibrator - > running )
gpio_vibrator_start ( vibrator ) ;
return 0 ;
}
2023-01-02 18:17:40 +00:00
static DEFINE_SIMPLE_DEV_PM_OPS ( gpio_vibrator_pm_ops ,
gpio_vibrator_suspend , gpio_vibrator_resume ) ;
2019-04-29 23:56:09 -07:00
# ifdef CONFIG_OF
static const struct of_device_id gpio_vibra_dt_match_table [ ] = {
{ . compatible = " gpio-vibrator " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , gpio_vibra_dt_match_table ) ;
# endif
static struct platform_driver gpio_vibrator_driver = {
. probe = gpio_vibrator_probe ,
. driver = {
. name = " gpio-vibrator " ,
2023-01-02 18:17:40 +00:00
. pm = pm_sleep_ptr ( & gpio_vibrator_pm_ops ) ,
2019-04-29 23:56:09 -07:00
. of_match_table = of_match_ptr ( gpio_vibra_dt_match_table ) ,
} ,
} ;
module_platform_driver ( gpio_vibrator_driver ) ;
MODULE_AUTHOR ( " Luca Weiss <luca@z3ntu.xy> " ) ;
MODULE_DESCRIPTION ( " GPIO vibrator driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:gpio-vibrator " ) ;