2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-11-27 01:17:21 +04:00
/*
* Arizona haptics driver
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/input.h>
# include <linux/slab.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <linux/mfd/arizona/core.h>
# include <linux/mfd/arizona/pdata.h>
# include <linux/mfd/arizona/registers.h>
struct arizona_haptics {
struct arizona * arizona ;
struct input_dev * input_dev ;
struct work_struct work ;
struct mutex mutex ;
u8 intensity ;
} ;
static void arizona_haptics_work ( struct work_struct * work )
{
struct arizona_haptics * haptics = container_of ( work ,
struct arizona_haptics ,
work ) ;
struct arizona * arizona = haptics - > arizona ;
2016-11-29 18:44:42 +03:00
struct snd_soc_component * component =
snd_soc_dapm_to_component ( arizona - > dapm ) ;
2012-11-27 01:17:21 +04:00
int ret ;
if ( ! haptics - > arizona - > dapm ) {
dev_err ( arizona - > dev , " No DAPM context \n " ) ;
return ;
}
if ( haptics - > intensity ) {
ret = regmap_update_bits ( arizona - > regmap ,
ARIZONA_HAPTICS_PHASE_2_INTENSITY ,
ARIZONA_PHASE2_INTENSITY_MASK ,
haptics - > intensity ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to set intensity: %d \n " ,
ret ) ;
return ;
}
/* This enable sequence will be a noop if already enabled */
ret = regmap_update_bits ( arizona - > regmap ,
ARIZONA_HAPTICS_CONTROL_1 ,
ARIZONA_HAP_CTRL_MASK ,
1 < < ARIZONA_HAP_CTRL_SHIFT ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to start haptics: %d \n " ,
ret ) ;
return ;
}
2016-11-29 18:44:42 +03:00
ret = snd_soc_component_enable_pin ( component , " HAPTICS " ) ;
2012-11-27 01:17:21 +04:00
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to start HAPTICS: %d \n " ,
ret ) ;
return ;
}
ret = snd_soc_dapm_sync ( arizona - > dapm ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to sync DAPM: %d \n " ,
ret ) ;
return ;
}
} else {
/* This disable sequence will be a noop if already enabled */
2016-11-29 18:44:42 +03:00
ret = snd_soc_component_disable_pin ( component , " HAPTICS " ) ;
2012-11-27 01:17:21 +04:00
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to disable HAPTICS: %d \n " ,
ret ) ;
return ;
}
ret = snd_soc_dapm_sync ( arizona - > dapm ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to sync DAPM: %d \n " ,
ret ) ;
return ;
}
ret = regmap_update_bits ( arizona - > regmap ,
ARIZONA_HAPTICS_CONTROL_1 ,
2015-12-03 03:13:54 +03:00
ARIZONA_HAP_CTRL_MASK , 0 ) ;
2012-11-27 01:17:21 +04:00
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to stop haptics: %d \n " ,
ret ) ;
return ;
}
}
}
static int arizona_haptics_play ( struct input_dev * input , void * data ,
struct ff_effect * effect )
{
struct arizona_haptics * haptics = input_get_drvdata ( input ) ;
struct arizona * arizona = haptics - > arizona ;
if ( ! arizona - > dapm ) {
dev_err ( arizona - > dev , " No DAPM context \n " ) ;
return - EBUSY ;
}
if ( effect - > u . rumble . strong_magnitude ) {
/* Scale the magnitude into the range the device supports */
if ( arizona - > pdata . hap_act ) {
haptics - > intensity =
effect - > u . rumble . strong_magnitude > > 9 ;
if ( effect - > direction < 0x8000 )
haptics - > intensity + = 0x7f ;
} else {
haptics - > intensity =
effect - > u . rumble . strong_magnitude > > 8 ;
}
} else {
haptics - > intensity = 0 ;
}
schedule_work ( & haptics - > work ) ;
return 0 ;
}
static void arizona_haptics_close ( struct input_dev * input )
{
struct arizona_haptics * haptics = input_get_drvdata ( input ) ;
2016-11-29 18:44:42 +03:00
struct snd_soc_component * component ;
2012-11-27 01:17:21 +04:00
cancel_work_sync ( & haptics - > work ) ;
2016-11-29 18:44:42 +03:00
if ( haptics - > arizona - > dapm ) {
component = snd_soc_dapm_to_component ( haptics - > arizona - > dapm ) ;
snd_soc_component_disable_pin ( component , " HAPTICS " ) ;
}
2012-11-27 01:17:21 +04:00
}
static int arizona_haptics_probe ( struct platform_device * pdev )
{
struct arizona * arizona = dev_get_drvdata ( pdev - > dev . parent ) ;
struct arizona_haptics * haptics ;
int ret ;
haptics = devm_kzalloc ( & pdev - > dev , sizeof ( * haptics ) , GFP_KERNEL ) ;
if ( ! haptics )
return - ENOMEM ;
haptics - > arizona = arizona ;
ret = regmap_update_bits ( arizona - > regmap , ARIZONA_HAPTICS_CONTROL_1 ,
ARIZONA_HAP_ACT , arizona - > pdata . hap_act ) ;
if ( ret ! = 0 ) {
dev_err ( arizona - > dev , " Failed to set haptics actuator: %d \n " ,
ret ) ;
return ret ;
}
INIT_WORK ( & haptics - > work , arizona_haptics_work ) ;
2015-07-06 20:32:27 +03:00
haptics - > input_dev = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! haptics - > input_dev ) {
2012-11-27 01:17:21 +04:00
dev_err ( arizona - > dev , " Failed to allocate input device \n " ) ;
return - ENOMEM ;
}
input_set_drvdata ( haptics - > input_dev , haptics ) ;
haptics - > input_dev - > name = " arizona:haptics " ;
haptics - > input_dev - > close = arizona_haptics_close ;
__set_bit ( FF_RUMBLE , haptics - > input_dev - > ffbit ) ;
ret = input_ff_create_memless ( haptics - > input_dev , NULL ,
arizona_haptics_play ) ;
if ( ret < 0 ) {
dev_err ( arizona - > dev , " input_ff_create_memless() failed: %d \n " ,
ret ) ;
2015-07-06 20:32:27 +03:00
return ret ;
2012-11-27 01:17:21 +04:00
}
ret = input_register_device ( haptics - > input_dev ) ;
if ( ret < 0 ) {
dev_err ( arizona - > dev , " couldn't register input device: %d \n " ,
ret ) ;
2015-07-06 20:32:27 +03:00
return ret ;
2012-11-27 01:17:21 +04:00
}
return 0 ;
}
static struct platform_driver arizona_haptics_driver = {
. probe = arizona_haptics_probe ,
. driver = {
. name = " arizona-haptics " ,
} ,
} ;
module_platform_driver ( arizona_haptics_driver ) ;
MODULE_ALIAS ( " platform:arizona-haptics " ) ;
MODULE_DESCRIPTION ( " Arizona haptics driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;