2014-12-17 21:31:08 +03:00
/*
* Regulator haptic driver
*
* Copyright ( c ) 2014 Samsung Electronics Co . , Ltd .
* Author : Jaewon Kim < jaewon02 . kim @ samsung . com >
* Author : Hyunhee Kim < hyunhee . kim @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/input.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_data/regulator-haptic.h>
# include <linux/platform_device.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# define MAX_MAGNITUDE_SHIFT 16
struct regulator_haptic {
struct device * dev ;
struct input_dev * input_dev ;
struct regulator * regulator ;
struct work_struct work ;
struct mutex mutex ;
bool active ;
bool suspended ;
unsigned int max_volt ;
unsigned int min_volt ;
unsigned int magnitude ;
} ;
static int regulator_haptic_toggle ( struct regulator_haptic * haptic , bool on )
{
int error ;
if ( haptic - > active ! = on ) {
error = on ? regulator_enable ( haptic - > regulator ) :
regulator_disable ( haptic - > regulator ) ;
if ( error ) {
dev_err ( haptic - > dev ,
" failed to switch regulator %s: %d \n " ,
on ? " on " : " off " , error ) ;
return error ;
}
haptic - > active = on ;
}
return 0 ;
}
static int regulator_haptic_set_voltage ( struct regulator_haptic * haptic ,
unsigned int magnitude )
{
u64 volt_mag_multi ;
unsigned int intensity ;
int error ;
volt_mag_multi = ( u64 ) ( haptic - > max_volt - haptic - > min_volt ) * magnitude ;
intensity = ( unsigned int ) ( volt_mag_multi > > MAX_MAGNITUDE_SHIFT ) ;
error = regulator_set_voltage ( haptic - > regulator ,
intensity + haptic - > min_volt ,
haptic - > max_volt ) ;
if ( error ) {
dev_err ( haptic - > dev , " cannot set regulator voltage to %d: %d \n " ,
intensity + haptic - > min_volt , error ) ;
return error ;
}
2015-01-10 04:43:06 +03:00
regulator_haptic_toggle ( haptic , ! ! magnitude ) ;
2014-12-17 21:31:08 +03:00
return 0 ;
}
static void regulator_haptic_work ( struct work_struct * work )
{
struct regulator_haptic * haptic = container_of ( work ,
struct regulator_haptic , work ) ;
mutex_lock ( & haptic - > mutex ) ;
2015-01-10 04:43:06 +03:00
if ( ! haptic - > suspended )
regulator_haptic_set_voltage ( haptic , haptic - > magnitude ) ;
2014-12-17 21:31:08 +03:00
mutex_unlock ( & haptic - > mutex ) ;
}
static int regulator_haptic_play_effect ( struct input_dev * input , void * data ,
struct ff_effect * effect )
{
struct regulator_haptic * haptic = input_get_drvdata ( input ) ;
haptic - > magnitude = effect - > u . rumble . strong_magnitude ;
if ( ! haptic - > magnitude )
haptic - > magnitude = effect - > u . rumble . weak_magnitude ;
schedule_work ( & haptic - > work ) ;
return 0 ;
}
static void regulator_haptic_close ( struct input_dev * input )
{
struct regulator_haptic * haptic = input_get_drvdata ( input ) ;
cancel_work_sync ( & haptic - > work ) ;
regulator_haptic_set_voltage ( haptic , 0 ) ;
}
static int __maybe_unused
regulator_haptic_parse_dt ( struct device * dev , struct regulator_haptic * haptic )
{
struct device_node * node ;
int error ;
node = dev - > of_node ;
if ( ! node ) {
dev_err ( dev , " Missing dveice tree data \n " ) ;
return - EINVAL ;
}
error = of_property_read_u32 ( node , " max-microvolt " , & haptic - > max_volt ) ;
if ( error ) {
dev_err ( dev , " cannot parse max-microvolt \n " ) ;
return error ;
}
error = of_property_read_u32 ( node , " min-microvolt " , & haptic - > min_volt ) ;
if ( error ) {
dev_err ( dev , " cannot parse min-microvolt \n " ) ;
return error ;
}
return 0 ;
}
static int regulator_haptic_probe ( struct platform_device * pdev )
{
const struct regulator_haptic_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
struct regulator_haptic * haptic ;
struct input_dev * input_dev ;
int error ;
haptic = devm_kzalloc ( & pdev - > dev , sizeof ( * haptic ) , GFP_KERNEL ) ;
if ( ! haptic )
return - ENOMEM ;
platform_set_drvdata ( pdev , haptic ) ;
haptic - > dev = & pdev - > dev ;
mutex_init ( & haptic - > mutex ) ;
INIT_WORK ( & haptic - > work , regulator_haptic_work ) ;
if ( pdata ) {
haptic - > max_volt = pdata - > max_volt ;
haptic - > min_volt = pdata - > min_volt ;
} else if ( IS_ENABLED ( CONFIG_OF ) ) {
error = regulator_haptic_parse_dt ( & pdev - > dev , haptic ) ;
if ( error )
return error ;
} else {
dev_err ( & pdev - > dev , " Missing platform data \n " ) ;
return - EINVAL ;
}
haptic - > regulator = devm_regulator_get_exclusive ( & pdev - > dev , " haptic " ) ;
if ( IS_ERR ( haptic - > regulator ) ) {
dev_err ( & pdev - > dev , " failed to get regulator \n " ) ;
return PTR_ERR ( haptic - > regulator ) ;
}
input_dev = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! input_dev )
return - ENOMEM ;
haptic - > input_dev = input_dev ;
haptic - > input_dev - > name = " regulator-haptic " ;
haptic - > input_dev - > dev . parent = & pdev - > dev ;
haptic - > input_dev - > close = regulator_haptic_close ;
input_set_drvdata ( haptic - > input_dev , haptic ) ;
input_set_capability ( haptic - > input_dev , EV_FF , FF_RUMBLE ) ;
error = input_ff_create_memless ( input_dev , NULL ,
regulator_haptic_play_effect ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to create force-feedback \n " ) ;
return error ;
}
error = input_register_device ( haptic - > input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to register input device \n " ) ;
return error ;
}
return 0 ;
}
static int __maybe_unused regulator_haptic_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct regulator_haptic * haptic = platform_get_drvdata ( pdev ) ;
int error ;
error = mutex_lock_interruptible ( & haptic - > mutex ) ;
if ( error )
return error ;
regulator_haptic_set_voltage ( haptic , 0 ) ;
haptic - > suspended = true ;
mutex_unlock ( & haptic - > mutex ) ;
return 0 ;
}
static int __maybe_unused regulator_haptic_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct regulator_haptic * haptic = platform_get_drvdata ( pdev ) ;
unsigned int magnitude ;
mutex_lock ( & haptic - > mutex ) ;
haptic - > suspended = false ;
magnitude = ACCESS_ONCE ( haptic - > magnitude ) ;
2015-01-10 04:43:06 +03:00
if ( magnitude )
2014-12-17 21:31:08 +03:00
regulator_haptic_set_voltage ( haptic , magnitude ) ;
mutex_unlock ( & haptic - > mutex ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( regulator_haptic_pm_ops ,
regulator_haptic_suspend , regulator_haptic_resume ) ;
static struct of_device_id regulator_haptic_dt_match [ ] = {
{ . compatible = " regulator-haptic " } ,
{ /* sentinel */ } ,
} ;
static struct platform_driver regulator_haptic_driver = {
. probe = regulator_haptic_probe ,
. driver = {
. name = " regulator-haptic " ,
. of_match_table = regulator_haptic_dt_match ,
. pm = & regulator_haptic_pm_ops ,
} ,
} ;
module_platform_driver ( regulator_haptic_driver ) ;
MODULE_AUTHOR ( " Jaewon Kim <jaewon02.kim@samsung.com> " ) ;
MODULE_AUTHOR ( " Hyunhee Kim <hyunhee.kim@samsung.com> " ) ;
MODULE_DESCRIPTION ( " Regulator haptic driver " ) ;
MODULE_LICENSE ( " GPL " ) ;