2011-05-01 12:51:24 +04:00
/*
* twl6040 - vibra . c - TWL6040 Vibrator driver
*
* Author : Jorge Eduardo Candelaria < jorge . candelaria @ ti . com >
* Author : Misael Lopez Cruz < misael . lopez @ ti . com >
*
* Copyright : ( C ) 2011 Texas Instruments , Inc .
*
* Based on twl4030 - vibra . c by Henrik Saari < henrik . saari @ nokia . com >
* Felipe Balbi < felipe . balbi @ nokia . com >
* Jari Vanhala < ext - javi . vanhala @ nokia . 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* 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 . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/platform_device.h>
2012-05-07 19:45:50 +04:00
# include <linux/of.h>
2011-05-01 12:51:24 +04:00
# include <linux/workqueue.h>
2012-04-03 12:56:51 +04:00
# include <linux/input.h>
2011-05-01 12:51:24 +04:00
# include <linux/mfd/twl6040.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/regulator/consumer.h>
# define EFFECT_DIR_180_DEG 0x8000
/* Recommended modulation index 85% */
# define TWL6040_VIBRA_MOD 85
# define TWL6040_NUM_SUPPLIES 2
struct vibra_info {
struct device * dev ;
struct input_dev * input_dev ;
struct work_struct play_work ;
2016-04-26 00:02:16 +03:00
2011-07-04 20:50:02 +04:00
int irq ;
2011-05-01 12:51:24 +04:00
bool enabled ;
int weak_speed ;
int strong_speed ;
int direction ;
unsigned int vibldrv_res ;
unsigned int vibrdrv_res ;
unsigned int viblmotor_res ;
unsigned int vibrmotor_res ;
struct regulator_bulk_data supplies [ TWL6040_NUM_SUPPLIES ] ;
struct twl6040 * twl6040 ;
} ;
static irqreturn_t twl6040_vib_irq_handler ( int irq , void * data )
{
struct vibra_info * info = data ;
struct twl6040 * twl6040 = info - > twl6040 ;
u8 status ;
status = twl6040_reg_read ( twl6040 , TWL6040_REG_STATUS ) ;
if ( status & TWL6040_VIBLOCDET ) {
dev_warn ( info - > dev , " Left Vibrator overcurrent detected \n " ) ;
twl6040_clear_bits ( twl6040 , TWL6040_REG_VIBCTLL ,
2011-10-12 12:57:53 +04:00
TWL6040_VIBENA ) ;
2011-05-01 12:51:24 +04:00
}
if ( status & TWL6040_VIBROCDET ) {
dev_warn ( info - > dev , " Right Vibrator overcurrent detected \n " ) ;
twl6040_clear_bits ( twl6040 , TWL6040_REG_VIBCTLR ,
2011-10-12 12:57:53 +04:00
TWL6040_VIBENA ) ;
2011-05-01 12:51:24 +04:00
}
return IRQ_HANDLED ;
}
static void twl6040_vibra_enable ( struct vibra_info * info )
{
struct twl6040 * twl6040 = info - > twl6040 ;
int ret ;
ret = regulator_bulk_enable ( ARRAY_SIZE ( info - > supplies ) , info - > supplies ) ;
if ( ret ) {
dev_err ( info - > dev , " failed to enable regulators %d \n " , ret ) ;
return ;
}
twl6040_power ( info - > twl6040 , 1 ) ;
2011-09-15 16:39:25 +04:00
if ( twl6040_get_revid ( twl6040 ) < = TWL6040_REV_ES1_1 ) {
2011-05-01 12:51:24 +04:00
/*
* ERRATA : Disable overcurrent protection for at least
* 3 ms when enabling vibrator drivers to avoid false
* overcurrent detection
*/
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBCTLL ,
2011-10-12 12:57:53 +04:00
TWL6040_VIBENA | TWL6040_VIBCTRL ) ;
2011-05-01 12:51:24 +04:00
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBCTLR ,
2011-10-12 12:57:53 +04:00
TWL6040_VIBENA | TWL6040_VIBCTRL ) ;
2011-05-01 12:51:24 +04:00
usleep_range ( 3000 , 3500 ) ;
}
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBCTLL ,
2011-10-12 12:57:53 +04:00
TWL6040_VIBENA ) ;
2011-05-01 12:51:24 +04:00
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBCTLR ,
2011-10-12 12:57:53 +04:00
TWL6040_VIBENA ) ;
2011-05-01 12:51:24 +04:00
info - > enabled = true ;
}
static void twl6040_vibra_disable ( struct vibra_info * info )
{
struct twl6040 * twl6040 = info - > twl6040 ;
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBCTLL , 0x00 ) ;
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBCTLR , 0x00 ) ;
twl6040_power ( info - > twl6040 , 0 ) ;
regulator_bulk_disable ( ARRAY_SIZE ( info - > supplies ) , info - > supplies ) ;
info - > enabled = false ;
}
static u8 twl6040_vibra_code ( int vddvib , int vibdrv_res , int motor_res ,
int speed , int direction )
{
int vpk , max_code ;
u8 vibdat ;
/* output swing */
vpk = ( vddvib * motor_res * TWL6040_VIBRA_MOD ) /
( 100 * ( vibdrv_res + motor_res ) ) ;
/* 50mV per VIBDAT code step */
max_code = vpk / 50 ;
if ( max_code > TWL6040_VIBDAT_MAX )
max_code = TWL6040_VIBDAT_MAX ;
/* scale speed to max allowed code */
vibdat = ( u8 ) ( ( speed * max_code ) / USHRT_MAX ) ;
/* 2's complement for direction > 180 degrees */
vibdat * = direction ;
return vibdat ;
}
static void twl6040_vibra_set_effect ( struct vibra_info * info )
{
struct twl6040 * twl6040 = info - > twl6040 ;
u8 vibdatl , vibdatr ;
int volt ;
/* weak motor */
volt = regulator_get_voltage ( info - > supplies [ 0 ] . consumer ) / 1000 ;
vibdatl = twl6040_vibra_code ( volt , info - > vibldrv_res ,
info - > viblmotor_res ,
info - > weak_speed , info - > direction ) ;
/* strong motor */
volt = regulator_get_voltage ( info - > supplies [ 1 ] . consumer ) / 1000 ;
vibdatr = twl6040_vibra_code ( volt , info - > vibrdrv_res ,
info - > vibrmotor_res ,
info - > strong_speed , info - > direction ) ;
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBDATL , vibdatl ) ;
twl6040_reg_write ( twl6040 , TWL6040_REG_VIBDATR , vibdatr ) ;
}
static void vibra_play_work ( struct work_struct * work )
{
struct vibra_info * info = container_of ( work ,
struct vibra_info , play_work ) ;
2016-04-26 00:02:36 +03:00
int ret ;
/* Do not allow effect, while the routing is set to use audio */
ret = twl6040_get_vibralr_status ( info - > twl6040 ) ;
if ( ret & TWL6040_VIBSEL ) {
dev_info ( info - > dev , " Vibra is configured for audio \n " ) ;
return ;
}
2011-05-01 12:51:24 +04:00
if ( info - > weak_speed | | info - > strong_speed ) {
if ( ! info - > enabled )
twl6040_vibra_enable ( info ) ;
twl6040_vibra_set_effect ( info ) ;
} else if ( info - > enabled )
twl6040_vibra_disable ( info ) ;
}
static int vibra_play ( struct input_dev * input , void * data ,
struct ff_effect * effect )
{
struct vibra_info * info = input_get_drvdata ( input ) ;
2011-10-12 12:57:56 +04:00
2011-05-01 12:51:24 +04:00
info - > weak_speed = effect - > u . rumble . weak_magnitude ;
info - > strong_speed = effect - > u . rumble . strong_magnitude ;
info - > direction = effect - > direction < EFFECT_DIR_180_DEG ? 1 : - 1 ;
2016-04-19 00:47:14 +03:00
schedule_work ( & info - > play_work ) ;
2011-05-01 12:51:24 +04:00
return 0 ;
}
static void twl6040_vibra_close ( struct input_dev * input )
{
struct vibra_info * info = input_get_drvdata ( input ) ;
cancel_work_sync ( & info - > play_work ) ;
if ( info - > enabled )
twl6040_vibra_disable ( info ) ;
}
2014-11-02 10:02:46 +03:00
static int __maybe_unused twl6040_vibra_suspend ( struct device * dev )
2011-05-01 12:51:24 +04:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct vibra_info * info = platform_get_drvdata ( pdev ) ;
2016-04-26 00:02:16 +03:00
cancel_work_sync ( & info - > play_work ) ;
2011-05-01 12:51:24 +04:00
if ( info - > enabled )
twl6040_vibra_disable ( info ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( twl6040_vibra_pm_ops , twl6040_vibra_suspend , NULL ) ;
2012-11-24 09:38:25 +04:00
static int twl6040_vibra_probe ( struct platform_device * pdev )
2011-05-01 12:51:24 +04:00
{
2012-06-12 12:10:02 +04:00
struct device * twl6040_core_dev = pdev - > dev . parent ;
2014-01-04 12:08:49 +04:00
struct device_node * twl6040_core_node ;
2011-05-01 12:51:24 +04:00
struct vibra_info * info ;
2012-05-07 19:45:50 +04:00
int vddvibl_uV = 0 ;
int vddvibr_uV = 0 ;
2014-04-25 20:21:12 +04:00
int error ;
2011-05-01 12:51:24 +04:00
2016-05-10 03:01:01 +03:00
of_node_get ( twl6040_core_dev - > of_node ) ;
2012-06-12 12:10:02 +04:00
twl6040_core_node = of_find_node_by_name ( twl6040_core_dev - > of_node ,
" vibra " ) ;
2013-07-14 00:36:19 +04:00
if ( ! twl6040_core_node ) {
dev_err ( & pdev - > dev , " parent of node is missing? \n " ) ;
2011-05-01 12:51:24 +04:00
return - EINVAL ;
}
2013-01-25 12:03:50 +04:00
info = devm_kzalloc ( & pdev - > dev , sizeof ( * info ) , GFP_KERNEL ) ;
2011-05-01 12:51:24 +04:00
if ( ! info ) {
2014-01-04 12:00:30 +04:00
of_node_put ( twl6040_core_node ) ;
2011-05-01 12:51:24 +04:00
dev_err ( & pdev - > dev , " couldn't allocate memory \n " ) ;
return - ENOMEM ;
}
info - > dev = & pdev - > dev ;
2012-05-07 19:45:50 +04:00
2011-05-01 12:51:24 +04:00
info - > twl6040 = dev_get_drvdata ( pdev - > dev . parent ) ;
2013-07-14 00:36:19 +04:00
of_property_read_u32 ( twl6040_core_node , " ti,vibldrv-res " ,
& info - > vibldrv_res ) ;
of_property_read_u32 ( twl6040_core_node , " ti,vibrdrv-res " ,
& info - > vibrdrv_res ) ;
of_property_read_u32 ( twl6040_core_node , " ti,viblmotor-res " ,
& info - > viblmotor_res ) ;
of_property_read_u32 ( twl6040_core_node , " ti,vibrmotor-res " ,
& info - > vibrmotor_res ) ;
of_property_read_u32 ( twl6040_core_node , " ti,vddvibl-uV " , & vddvibl_uV ) ;
of_property_read_u32 ( twl6040_core_node , " ti,vddvibr-uV " , & vddvibr_uV ) ;
2012-05-07 19:45:50 +04:00
2014-01-04 12:00:30 +04:00
of_node_put ( twl6040_core_node ) ;
2011-05-01 12:51:24 +04:00
if ( ( ! info - > vibldrv_res & & ! info - > viblmotor_res ) | |
( ! info - > vibrdrv_res & & ! info - > vibrmotor_res ) ) {
dev_err ( info - > dev , " invalid vibra driver/motor resistance \n " ) ;
2013-01-25 12:03:50 +04:00
return - EINVAL ;
2011-05-01 12:51:24 +04:00
}
2011-07-04 20:50:02 +04:00
info - > irq = platform_get_irq ( pdev , 0 ) ;
if ( info - > irq < 0 ) {
dev_err ( info - > dev , " invalid irq \n " ) ;
2013-01-25 12:03:50 +04:00
return - EINVAL ;
2011-07-04 20:50:02 +04:00
}
2014-04-25 20:21:12 +04:00
error = devm_request_threaded_irq ( & pdev - > dev , info - > irq , NULL ,
2015-05-16 01:55:42 +03:00
twl6040_vib_irq_handler ,
IRQF_ONESHOT ,
2014-04-25 20:21:12 +04:00
" twl6040_irq_vib " , info ) ;
if ( error ) {
dev_err ( info - > dev , " VIB IRQ request failed: %d \n " , error ) ;
return error ;
2011-05-01 12:51:24 +04:00
}
info - > supplies [ 0 ] . supply = " vddvibl " ;
info - > supplies [ 1 ] . supply = " vddvibr " ;
2012-06-12 12:10:02 +04:00
/*
* When booted with Device tree the regulators are attached to the
* parent device ( twl6040 MFD core )
*/
2014-04-25 20:21:12 +04:00
error = devm_regulator_bulk_get ( twl6040_core_dev ,
ARRAY_SIZE ( info - > supplies ) ,
info - > supplies ) ;
if ( error ) {
dev_err ( info - > dev , " couldn't get regulators %d \n " , error ) ;
return error ;
2011-05-01 12:51:24 +04:00
}
2012-05-07 19:45:50 +04:00
if ( vddvibl_uV ) {
2014-04-25 20:21:12 +04:00
error = regulator_set_voltage ( info - > supplies [ 0 ] . consumer ,
vddvibl_uV , vddvibl_uV ) ;
if ( error ) {
2011-05-01 12:51:24 +04:00
dev_err ( info - > dev , " failed to set VDDVIBL volt %d \n " ,
2014-04-25 20:21:12 +04:00
error ) ;
return error ;
2011-05-01 12:51:24 +04:00
}
}
2012-05-07 19:45:50 +04:00
if ( vddvibr_uV ) {
2014-04-25 20:21:12 +04:00
error = regulator_set_voltage ( info - > supplies [ 1 ] . consumer ,
vddvibr_uV , vddvibr_uV ) ;
if ( error ) {
2011-05-01 12:51:24 +04:00
dev_err ( info - > dev , " failed to set VDDVIBR volt %d \n " ,
2014-04-25 20:21:12 +04:00
error ) ;
return error ;
2011-05-01 12:51:24 +04:00
}
}
INIT_WORK ( & info - > play_work , vibra_play_work ) ;
2014-04-25 20:21:12 +04:00
info - > input_dev = devm_input_allocate_device ( & pdev - > dev ) ;
if ( ! info - > input_dev ) {
2013-01-25 12:03:50 +04:00
dev_err ( info - > dev , " couldn't allocate input device \n " ) ;
2014-04-25 20:21:12 +04:00
return - ENOMEM ;
2013-01-25 12:03:50 +04:00
}
input_set_drvdata ( info - > input_dev , info ) ;
info - > input_dev - > name = " twl6040:vibrator " ;
info - > input_dev - > id . version = 1 ;
info - > input_dev - > close = twl6040_vibra_close ;
__set_bit ( FF_RUMBLE , info - > input_dev - > ffbit ) ;
2014-04-25 20:21:12 +04:00
error = input_ff_create_memless ( info - > input_dev , NULL , vibra_play ) ;
if ( error ) {
2013-01-25 12:03:50 +04:00
dev_err ( info - > dev , " couldn't register vibrator to FF \n " ) ;
2014-04-25 20:21:12 +04:00
return error ;
2013-01-25 12:03:50 +04:00
}
2014-04-25 20:21:12 +04:00
error = input_register_device ( info - > input_dev ) ;
if ( error ) {
2013-01-25 12:03:50 +04:00
dev_err ( info - > dev , " couldn't register input device \n " ) ;
2014-04-25 20:21:12 +04:00
return error ;
2013-01-25 12:03:50 +04:00
}
platform_set_drvdata ( pdev , info ) ;
2011-05-01 12:51:24 +04:00
return 0 ;
}
static struct platform_driver twl6040_vibra_driver = {
. probe = twl6040_vibra_probe ,
. driver = {
. name = " twl6040-vibra " ,
. pm = & twl6040_vibra_pm_ops ,
} ,
} ;
2011-11-29 23:08:40 +04:00
module_platform_driver ( twl6040_vibra_driver ) ;
2011-05-01 12:51:24 +04:00
MODULE_ALIAS ( " platform:twl6040-vibra " ) ;
MODULE_DESCRIPTION ( " TWL6040 Vibra driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Jorge Eduardo Candelaria <jorge.candelaria@ti.com> " ) ;
MODULE_AUTHOR ( " Misael Lopez Cruz <misael.lopez@ti.com> " ) ;