2015-05-08 16:02:43 -07:00
/*
* DRV2665 haptics driver family
*
* Author : Dan Murphy < dmurphy @ ti . com >
*
* Copyright : ( C ) 2015 Texas Instruments , Inc .
*
* 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 .
*/
# include <linux/i2c.h>
# include <linux/input.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/regulator/consumer.h>
/* Contol registers */
# define DRV2665_STATUS 0x00
# define DRV2665_CTRL_1 0x01
# define DRV2665_CTRL_2 0x02
# define DRV2665_FIFO 0x0b
/* Status Register */
# define DRV2665_FIFO_FULL BIT(0)
# define DRV2665_FIFO_EMPTY BIT(1)
/* Control 1 Register */
# define DRV2665_25_VPP_GAIN 0x00
# define DRV2665_50_VPP_GAIN 0x01
# define DRV2665_75_VPP_GAIN 0x02
# define DRV2665_100_VPP_GAIN 0x03
# define DRV2665_DIGITAL_IN 0xfc
# define DRV2665_ANALOG_IN BIT(2)
/* Control 2 Register */
# define DRV2665_BOOST_EN BIT(1)
# define DRV2665_STANDBY BIT(6)
# define DRV2665_DEV_RST BIT(7)
# define DRV2665_5_MS_IDLE_TOUT 0x00
# define DRV2665_10_MS_IDLE_TOUT 0x04
# define DRV2665_15_MS_IDLE_TOUT 0x08
# define DRV2665_20_MS_IDLE_TOUT 0x0c
/**
* struct drv2665_data -
* @ input_dev - Pointer to the input device
* @ client - Pointer to the I2C client
* @ regmap - Register map of the device
* @ work - Work item used to off load the enable / disable of the vibration
* @ regulator - Pointer to the regulator for the IC
*/
struct drv2665_data {
struct input_dev * input_dev ;
struct i2c_client * client ;
struct regmap * regmap ;
struct work_struct work ;
struct regulator * regulator ;
} ;
/* 8kHz Sine wave to stream to the FIFO */
static const u8 drv2665_sine_wave_form [ ] = {
0x00 , 0x10 , 0x20 , 0x2e , 0x3c , 0x48 , 0x53 , 0x5b , 0x61 , 0x65 , 0x66 ,
0x65 , 0x61 , 0x5b , 0x53 , 0x48 , 0x3c , 0x2e , 0x20 , 0x10 ,
0x00 , 0xf0 , 0xe0 , 0xd2 , 0xc4 , 0xb8 , 0xad , 0xa5 , 0x9f , 0x9b , 0x9a ,
0x9b , 0x9f , 0xa5 , 0xad , 0xb8 , 0xc4 , 0xd2 , 0xe0 , 0xf0 , 0x00 ,
} ;
2015-07-08 08:25:50 -07:00
static const struct reg_default drv2665_reg_defs [ ] = {
2015-05-08 16:02:43 -07:00
{ DRV2665_STATUS , 0x02 } ,
{ DRV2665_CTRL_1 , 0x28 } ,
{ DRV2665_CTRL_2 , 0x40 } ,
{ DRV2665_FIFO , 0x00 } ,
} ;
static void drv2665_worker ( struct work_struct * work )
{
struct drv2665_data * haptics =
container_of ( work , struct drv2665_data , work ) ;
unsigned int read_buf ;
int error ;
error = regmap_read ( haptics - > regmap , DRV2665_STATUS , & read_buf ) ;
if ( error ) {
dev_err ( & haptics - > client - > dev ,
" Failed to read status: %d \n " , error ) ;
return ;
}
if ( read_buf & DRV2665_FIFO_EMPTY ) {
error = regmap_bulk_write ( haptics - > regmap ,
DRV2665_FIFO ,
drv2665_sine_wave_form ,
ARRAY_SIZE ( drv2665_sine_wave_form ) ) ;
if ( error ) {
dev_err ( & haptics - > client - > dev ,
" Failed to write FIFO: %d \n " , error ) ;
return ;
}
}
}
static int drv2665_haptics_play ( struct input_dev * input , void * data ,
struct ff_effect * effect )
{
struct drv2665_data * haptics = input_get_drvdata ( input ) ;
schedule_work ( & haptics - > work ) ;
return 0 ;
}
static void drv2665_close ( struct input_dev * input )
{
struct drv2665_data * haptics = input_get_drvdata ( input ) ;
int error ;
cancel_work_sync ( & haptics - > work ) ;
error = regmap_update_bits ( haptics - > regmap ,
DRV2665_CTRL_2 , DRV2665_STANDBY , 1 ) ;
if ( error )
dev_err ( & haptics - > client - > dev ,
" Failed to enter standby mode: %d \n " , error ) ;
}
2015-07-16 16:36:21 +01:00
static const struct reg_sequence drv2665_init_regs [ ] = {
2015-05-08 16:02:43 -07:00
{ DRV2665_CTRL_2 , 0 | DRV2665_10_MS_IDLE_TOUT } ,
{ DRV2665_CTRL_1 , DRV2665_25_VPP_GAIN } ,
} ;
static int drv2665_init ( struct drv2665_data * haptics )
{
int error ;
error = regmap_register_patch ( haptics - > regmap ,
drv2665_init_regs ,
ARRAY_SIZE ( drv2665_init_regs ) ) ;
if ( error ) {
dev_err ( & haptics - > client - > dev ,
" Failed to write init registers: %d \n " ,
error ) ;
return error ;
}
return 0 ;
}
static const struct regmap_config drv2665_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = DRV2665_FIFO ,
. reg_defaults = drv2665_reg_defs ,
. num_reg_defaults = ARRAY_SIZE ( drv2665_reg_defs ) ,
. cache_type = REGCACHE_NONE ,
} ;
static int drv2665_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct drv2665_data * haptics ;
int error ;
haptics = devm_kzalloc ( & client - > dev , sizeof ( * haptics ) , GFP_KERNEL ) ;
if ( ! haptics )
return - ENOMEM ;
haptics - > regulator = devm_regulator_get ( & client - > dev , " vbat " ) ;
if ( IS_ERR ( haptics - > regulator ) ) {
error = PTR_ERR ( haptics - > regulator ) ;
dev_err ( & client - > dev ,
" unable to get regulator, error: %d \n " , error ) ;
return error ;
}
haptics - > input_dev = devm_input_allocate_device ( & client - > dev ) ;
if ( ! haptics - > input_dev ) {
dev_err ( & client - > dev , " Failed to allocate input device \n " ) ;
return - ENOMEM ;
}
haptics - > input_dev - > name = " drv2665:haptics " ;
haptics - > input_dev - > dev . parent = client - > dev . parent ;
haptics - > input_dev - > close = drv2665_close ;
input_set_drvdata ( haptics - > input_dev , haptics ) ;
input_set_capability ( haptics - > input_dev , EV_FF , FF_RUMBLE ) ;
error = input_ff_create_memless ( haptics - > input_dev , NULL ,
drv2665_haptics_play ) ;
if ( error ) {
dev_err ( & client - > dev , " input_ff_create() failed: %d \n " ,
error ) ;
return error ;
}
INIT_WORK ( & haptics - > work , drv2665_worker ) ;
haptics - > client = client ;
i2c_set_clientdata ( client , haptics ) ;
haptics - > regmap = devm_regmap_init_i2c ( client , & drv2665_regmap_config ) ;
if ( IS_ERR ( haptics - > regmap ) ) {
error = PTR_ERR ( haptics - > regmap ) ;
dev_err ( & client - > dev , " Failed to allocate register map: %d \n " ,
error ) ;
return error ;
}
error = drv2665_init ( haptics ) ;
if ( error ) {
dev_err ( & client - > dev , " Device init failed: %d \n " , error ) ;
return error ;
}
error = input_register_device ( haptics - > input_dev ) ;
if ( error ) {
dev_err ( & client - > dev , " couldn't register input device: %d \n " ,
error ) ;
return error ;
}
return 0 ;
}
static int __maybe_unused drv2665_suspend ( struct device * dev )
{
struct drv2665_data * haptics = dev_get_drvdata ( dev ) ;
int ret = 0 ;
mutex_lock ( & haptics - > input_dev - > mutex ) ;
if ( haptics - > input_dev - > users ) {
ret = regmap_update_bits ( haptics - > regmap , DRV2665_CTRL_2 ,
DRV2665_STANDBY , 1 ) ;
if ( ret ) {
dev_err ( dev , " Failed to set standby mode \n " ) ;
regulator_disable ( haptics - > regulator ) ;
goto out ;
}
ret = regulator_disable ( haptics - > regulator ) ;
if ( ret ) {
dev_err ( dev , " Failed to disable regulator \n " ) ;
regmap_update_bits ( haptics - > regmap ,
DRV2665_CTRL_2 ,
DRV2665_STANDBY , 0 ) ;
}
}
out :
mutex_unlock ( & haptics - > input_dev - > mutex ) ;
return ret ;
}
static int __maybe_unused drv2665_resume ( struct device * dev )
{
struct drv2665_data * haptics = dev_get_drvdata ( dev ) ;
int ret = 0 ;
mutex_lock ( & haptics - > input_dev - > mutex ) ;
if ( haptics - > input_dev - > users ) {
ret = regulator_enable ( haptics - > regulator ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable regulator \n " ) ;
goto out ;
}
ret = regmap_update_bits ( haptics - > regmap , DRV2665_CTRL_2 ,
DRV2665_STANDBY , 0 ) ;
if ( ret ) {
dev_err ( dev , " Failed to unset standby mode \n " ) ;
regulator_disable ( haptics - > regulator ) ;
goto out ;
}
}
out :
mutex_unlock ( & haptics - > input_dev - > mutex ) ;
return ret ;
}
static SIMPLE_DEV_PM_OPS ( drv2665_pm_ops , drv2665_suspend , drv2665_resume ) ;
static const struct i2c_device_id drv2665_id [ ] = {
{ " drv2665 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , drv2665_id ) ;
# ifdef CONFIG_OF
static const struct of_device_id drv2665_of_match [ ] = {
{ . compatible = " ti,drv2665 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , drv2665_of_match ) ;
# endif
static struct i2c_driver drv2665_driver = {
. probe = drv2665_probe ,
. driver = {
. name = " drv2665-haptics " ,
. of_match_table = of_match_ptr ( drv2665_of_match ) ,
. pm = & drv2665_pm_ops ,
} ,
. id_table = drv2665_id ,
} ;
module_i2c_driver ( drv2665_driver ) ;
MODULE_DESCRIPTION ( " TI DRV2665 haptics driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Dan Murphy <dmurphy@ti.com> " ) ;