2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-03-16 12:28:22 -07:00
/*
* MAX8997 - haptic controller driver
*
* Copyright ( C ) 2012 Samsung Electronics
* Donggeun Kim < dg77 . kim @ samsung . com >
*
* This program is not provided / owned by Maxim Integrated Products .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/pwm.h>
# include <linux/input.h>
# include <linux/mfd/max8997-private.h>
# include <linux/mfd/max8997.h>
# include <linux/regulator/consumer.h>
/* Haptic configuration 2 register */
# define MAX8997_MOTOR_TYPE_SHIFT 7
# define MAX8997_ENABLE_SHIFT 6
# define MAX8997_MODE_SHIFT 5
/* Haptic driver configuration register */
# define MAX8997_CYCLE_SHIFT 6
# define MAX8997_SIG_PERIOD_SHIFT 4
# define MAX8997_SIG_DUTY_SHIFT 2
# define MAX8997_PWM_DUTY_SHIFT 0
struct max8997_haptic {
struct device * dev ;
struct i2c_client * client ;
struct input_dev * input_dev ;
struct regulator * regulator ;
struct work_struct work ;
struct mutex mutex ;
bool enabled ;
unsigned int level ;
struct pwm_device * pwm ;
unsigned int pwm_period ;
enum max8997_haptic_pwm_divisor pwm_divisor ;
enum max8997_haptic_motor_type type ;
enum max8997_haptic_pulse_mode mode ;
unsigned int internal_mode_pattern ;
unsigned int pattern_cycle ;
unsigned int pattern_signal_period ;
} ;
static int max8997_haptic_set_duty_cycle ( struct max8997_haptic * chip )
{
int ret = 0 ;
if ( chip - > mode = = MAX8997_EXTERNAL_MODE ) {
unsigned int duty = chip - > pwm_period * chip - > level / 100 ;
ret = pwm_config ( chip - > pwm , duty , chip - > pwm_period ) ;
} else {
int i ;
u8 duty_index = 0 ;
for ( i = 0 ; i < = 64 ; i + + ) {
if ( chip - > level < = i * 100 / 64 ) {
duty_index = i ;
break ;
}
}
switch ( chip - > internal_mode_pattern ) {
case 0 :
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGPWMDC1 , duty_index ) ;
break ;
case 1 :
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGPWMDC2 , duty_index ) ;
break ;
case 2 :
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGPWMDC3 , duty_index ) ;
break ;
case 3 :
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGPWMDC4 , duty_index ) ;
break ;
default :
break ;
}
}
return ret ;
}
static void max8997_haptic_configure ( struct max8997_haptic * chip )
{
u8 value ;
value = chip - > type < < MAX8997_MOTOR_TYPE_SHIFT |
chip - > enabled < < MAX8997_ENABLE_SHIFT |
chip - > mode < < MAX8997_MODE_SHIFT | chip - > pwm_divisor ;
max8997_write_reg ( chip - > client , MAX8997_HAPTIC_REG_CONF2 , value ) ;
if ( chip - > mode = = MAX8997_INTERNAL_MODE & & chip - > enabled ) {
value = chip - > internal_mode_pattern < < MAX8997_CYCLE_SHIFT |
chip - > internal_mode_pattern < < MAX8997_SIG_PERIOD_SHIFT |
chip - > internal_mode_pattern < < MAX8997_SIG_DUTY_SHIFT |
chip - > internal_mode_pattern < < MAX8997_PWM_DUTY_SHIFT ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_DRVCONF , value ) ;
switch ( chip - > internal_mode_pattern ) {
case 0 :
value = chip - > pattern_cycle < < 4 ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_CYCLECONF1 , value ) ;
value = chip - > pattern_signal_period ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGCONF1 , value ) ;
break ;
case 1 :
value = chip - > pattern_cycle ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_CYCLECONF1 , value ) ;
value = chip - > pattern_signal_period ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGCONF2 , value ) ;
break ;
case 2 :
value = chip - > pattern_cycle < < 4 ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_CYCLECONF2 , value ) ;
value = chip - > pattern_signal_period ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGCONF3 , value ) ;
break ;
case 3 :
value = chip - > pattern_cycle ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_CYCLECONF2 , value ) ;
value = chip - > pattern_signal_period ;
max8997_write_reg ( chip - > client ,
MAX8997_HAPTIC_REG_SIGCONF4 , value ) ;
break ;
default :
break ;
}
}
}
static void max8997_haptic_enable ( struct max8997_haptic * chip )
{
int error ;
mutex_lock ( & chip - > mutex ) ;
error = max8997_haptic_set_duty_cycle ( chip ) ;
if ( error ) {
dev_err ( chip - > dev , " set_pwm_cycle failed, error: %d \n " , error ) ;
goto out ;
}
if ( ! chip - > enabled ) {
2014-06-07 23:10:25 -07:00
error = regulator_enable ( chip - > regulator ) ;
if ( error ) {
dev_err ( chip - > dev , " Failed to enable regulator \n " ) ;
goto out ;
}
2012-03-16 12:28:22 -07:00
max8997_haptic_configure ( chip ) ;
2014-06-07 23:10:25 -07:00
if ( chip - > mode = = MAX8997_EXTERNAL_MODE ) {
error = pwm_enable ( chip - > pwm ) ;
if ( error ) {
dev_err ( chip - > dev , " Failed to enable PWM \n " ) ;
regulator_disable ( chip - > regulator ) ;
goto out ;
}
}
chip - > enabled = true ;
2012-03-16 12:28:22 -07:00
}
out :
mutex_unlock ( & chip - > mutex ) ;
}
static void max8997_haptic_disable ( struct max8997_haptic * chip )
{
mutex_lock ( & chip - > mutex ) ;
if ( chip - > enabled ) {
chip - > enabled = false ;
max8997_haptic_configure ( chip ) ;
if ( chip - > mode = = MAX8997_EXTERNAL_MODE )
pwm_disable ( chip - > pwm ) ;
regulator_disable ( chip - > regulator ) ;
}
mutex_unlock ( & chip - > mutex ) ;
}
static void max8997_haptic_play_effect_work ( struct work_struct * work )
{
struct max8997_haptic * chip =
container_of ( work , struct max8997_haptic , work ) ;
if ( chip - > level )
max8997_haptic_enable ( chip ) ;
else
max8997_haptic_disable ( chip ) ;
}
static int max8997_haptic_play_effect ( struct input_dev * dev , void * data ,
struct ff_effect * effect )
{
struct max8997_haptic * chip = input_get_drvdata ( dev ) ;
chip - > level = effect - > u . rumble . strong_magnitude ;
if ( ! chip - > level )
chip - > level = effect - > u . rumble . weak_magnitude ;
schedule_work ( & chip - > work ) ;
return 0 ;
}
static void max8997_haptic_close ( struct input_dev * dev )
{
struct max8997_haptic * chip = input_get_drvdata ( dev ) ;
cancel_work_sync ( & chip - > work ) ;
max8997_haptic_disable ( chip ) ;
}
2012-11-23 21:38:25 -08:00
static int max8997_haptic_probe ( struct platform_device * pdev )
2012-03-16 12:28:22 -07:00
{
struct max8997_dev * iodev = dev_get_drvdata ( pdev - > dev . parent ) ;
const struct max8997_platform_data * pdata =
dev_get_platdata ( iodev - > dev ) ;
2016-05-09 09:31:47 -07:00
const struct max8997_haptic_platform_data * haptic_pdata = NULL ;
2012-03-16 12:28:22 -07:00
struct max8997_haptic * chip ;
struct input_dev * input_dev ;
int error ;
2016-05-09 09:31:47 -07:00
if ( pdata )
haptic_pdata = pdata - > haptic_pdata ;
2012-03-16 12:28:22 -07:00
if ( ! haptic_pdata ) {
dev_err ( & pdev - > dev , " no haptic platform data \n " ) ;
return - EINVAL ;
}
chip = kzalloc ( sizeof ( struct max8997_haptic ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! chip | | ! input_dev ) {
dev_err ( & pdev - > dev , " unable to allocate memory \n " ) ;
error = - ENOMEM ;
goto err_free_mem ;
}
INIT_WORK ( & chip - > work , max8997_haptic_play_effect_work ) ;
mutex_init ( & chip - > mutex ) ;
chip - > client = iodev - > haptic ;
chip - > dev = & pdev - > dev ;
chip - > input_dev = input_dev ;
chip - > pwm_period = haptic_pdata - > pwm_period ;
chip - > type = haptic_pdata - > type ;
chip - > mode = haptic_pdata - > mode ;
chip - > pwm_divisor = haptic_pdata - > pwm_divisor ;
switch ( chip - > mode ) {
case MAX8997_INTERNAL_MODE :
chip - > internal_mode_pattern =
haptic_pdata - > internal_mode_pattern ;
chip - > pattern_cycle = haptic_pdata - > pattern_cycle ;
chip - > pattern_signal_period =
haptic_pdata - > pattern_signal_period ;
break ;
case MAX8997_EXTERNAL_MODE :
chip - > pwm = pwm_request ( haptic_pdata - > pwm_channel_id ,
" max8997-haptic " ) ;
if ( IS_ERR ( chip - > pwm ) ) {
error = PTR_ERR ( chip - > pwm ) ;
dev_err ( & pdev - > dev ,
" unable to request PWM for haptic, error: %d \n " ,
error ) ;
goto err_free_mem ;
}
2016-04-14 21:17:33 +02:00
/*
* FIXME : pwm_apply_args ( ) should be removed when switching to
* the atomic PWM API .
*/
pwm_apply_args ( chip - > pwm ) ;
2012-03-16 12:28:22 -07:00
break ;
default :
dev_err ( & pdev - > dev ,
" Invalid chip mode specified (%d) \n " , chip - > mode ) ;
error = - EINVAL ;
goto err_free_mem ;
}
chip - > regulator = regulator_get ( & pdev - > dev , " inmotor " ) ;
if ( IS_ERR ( chip - > regulator ) ) {
error = PTR_ERR ( chip - > regulator ) ;
dev_err ( & pdev - > dev ,
" unable to get regulator, error: %d \n " ,
error ) ;
goto err_free_pwm ;
}
input_dev - > name = " max8997-haptic " ;
input_dev - > id . version = 1 ;
input_dev - > dev . parent = & pdev - > dev ;
input_dev - > close = max8997_haptic_close ;
input_set_drvdata ( input_dev , chip ) ;
input_set_capability ( input_dev , EV_FF , FF_RUMBLE ) ;
error = input_ff_create_memless ( input_dev , NULL ,
max8997_haptic_play_effect ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" unable to create FF device, error: %d \n " ,
error ) ;
goto err_put_regulator ;
}
error = input_register_device ( input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev ,
" unable to register input device, error: %d \n " ,
error ) ;
goto err_destroy_ff ;
}
platform_set_drvdata ( pdev , chip ) ;
return 0 ;
err_destroy_ff :
input_ff_destroy ( input_dev ) ;
err_put_regulator :
regulator_put ( chip - > regulator ) ;
err_free_pwm :
if ( chip - > mode = = MAX8997_EXTERNAL_MODE )
pwm_free ( chip - > pwm ) ;
err_free_mem :
input_free_device ( input_dev ) ;
kfree ( chip ) ;
return error ;
}
2012-11-23 21:50:47 -08:00
static int max8997_haptic_remove ( struct platform_device * pdev )
2012-03-16 12:28:22 -07:00
{
struct max8997_haptic * chip = platform_get_drvdata ( pdev ) ;
input_unregister_device ( chip - > input_dev ) ;
regulator_put ( chip - > regulator ) ;
if ( chip - > mode = = MAX8997_EXTERNAL_MODE )
pwm_free ( chip - > pwm ) ;
kfree ( chip ) ;
return 0 ;
}
2014-11-02 00:02:46 -07:00
static int __maybe_unused max8997_haptic_suspend ( struct device * dev )
2012-03-16 12:28:22 -07:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct max8997_haptic * chip = platform_get_drvdata ( pdev ) ;
max8997_haptic_disable ( chip ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( max8997_haptic_pm_ops , max8997_haptic_suspend , NULL ) ;
static const struct platform_device_id max8997_haptic_id [ ] = {
{ " max8997-haptic " , 0 } ,
{ } ,
} ;
2015-08-28 17:30:46 -07:00
MODULE_DEVICE_TABLE ( platform , max8997_haptic_id ) ;
2012-03-16 12:28:22 -07:00
static struct platform_driver max8997_haptic_driver = {
. driver = {
. name = " max8997-haptic " ,
. pm = & max8997_haptic_pm_ops ,
} ,
. probe = max8997_haptic_probe ,
2012-11-23 21:27:39 -08:00
. remove = max8997_haptic_remove ,
2012-03-16 12:28:22 -07:00
. id_table = max8997_haptic_id ,
} ;
module_platform_driver ( max8997_haptic_driver ) ;
MODULE_AUTHOR ( " Donggeun Kim <dg77.kim@samsung.com> " ) ;
MODULE_DESCRIPTION ( " max8997_haptic driver " ) ;
MODULE_LICENSE ( " GPL " ) ;